package com.jscape.inet.ssh.protocol.v2.marshaling.algorithms.zlib;

public class CRC32
        implements Checksum {

    /*
     *  The following logic has come from RFC1952.
     */
    private int v = 0;
    private static int[] crcTable = null;

    static {
        crcTable = new int[256];
        for (int n = 0; n < 256; n++) {
            int c = n;
            for (int k = 8; --k >= 0; ) {
                if ((c & 1) != 0) {
                    c = 0xedb88320 ^ (c >>> 1);
                } else {
                    c = c >>> 1;
                }
            }
            crcTable[n] = c;
        }
    }

    public void update(byte[] buf, int index, int len) {
        int c = ~v;
        while (--len >= 0) {
            c = crcTable[(c ^ buf[index++]) & 0xff] ^ (c >>> 8);
        }
        v = ~c;
    }

    public void reset() {
        v = 0;
    }

    public void reset(long vv) {
        v = (int) (vv & 0xffffffffL);
    }

    public long getValue() {
        return (long) (v & 0xffffffffL);
    }

    // The following logic has come from zlib.1.2.
    private static final int GF2_DIM = 32;

    static long combine(long crc1, long crc2, long len2) {
        long row;
        long[] even = new long[GF2_DIM];
        long[] odd = new long[GF2_DIM];

        // degenerate case (also disallow negative lengths)
        if (len2 <= 0) {
            return crc1;
        }

        // put operator for one zero bit in odd
        odd[0] = 0xedb88320L;          // CRC-32 polynomial
        row = 1;
        for (int n = 1; n < GF2_DIM; n++) {
            odd[n] = row;
            row <<= 1;
        }

        // put operator for two zero bits in even
        gf2MatrixSquare(even, odd);

        // put operator for four zero bits in odd
        gf2MatrixSquare(odd, even);

        // apply len2 zeros to crc1 (first square will put the operator for one
        // zero byte, eight zero bits, in even)
        do {
            // apply zeros operator for this bit of len2
            gf2MatrixSquare(even, odd);
            if ((len2 & 1) != 0) {
                crc1 = gf2MatrixTimes(even, crc1);
            }

            len2 >>= 1;

            // if no more bits set, then done
            if (len2 == 0) {
                break;
            }

            // another iteration of the loop with odd and even swapped
            gf2MatrixSquare(odd, even);
            if ((len2 & 1) != 0) {
                crc1 = gf2MatrixTimes(odd, crc1);
            }
            len2 >>= 1;

            // if no more bits set, then done
        } while (len2 != 0);

    /* return combined crc */
        crc1 ^= crc2;
        return crc1;
    }

    private static long gf2MatrixTimes(long[] mat, long vec) {
        long sum = 0;
        int index = 0;
        while (vec != 0) {
            if ((vec & 1) != 0) {
                sum ^= mat[index];
            }
            vec >>= 1;
            index++;
        }
        return sum;
    }

    static final void gf2MatrixSquare(long[] square, long[] mat) {
        for (int n = 0; n < GF2_DIM; n++) {
            square[n] = gf2MatrixTimes(mat, mat[n]);
        }
    }

    public CRC32 copy() {
        CRC32 foo = new CRC32();
        foo.v = this.v;
        return foo;
    }

    public static int[] getCRC32Table() {
        int[] tmp = new int[crcTable.length];
        System.arraycopy(crcTable, 0, tmp, 0, tmp.length);
        return tmp;
    }
}
