001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojava.nbio.core.util;
023
024import java.util.zip.Checksum;
025
026/**
027 * Utility class that calculates a CRC64 checksum on a stream of bytes. Code was
028 * based on some from BioPerl. Note that we use longs then cast them to avoid
029 * the lack of an unsigned int in Java. Longs are 64-bit but we are only using
030 * the bottom 32 bits. An int is 32-bit but encodes sign so we can get amusing
031 * results if we don't allow for this.
032 *
033 * @author Unknown. Copied from Expasy4J for convenience. See <a
034 *         href="http://dev.isb-sib.ch/projects/expasy4j/">http://dev.isb-sib.ch/projects/expasy4j/</a>
035 */
036public class CRC64Checksum implements Checksum {
037        private static final long POLY64 = 0xD800000000000000L;
038
039        private static final long[] crcTable = new long[256];
040
041        private long crc;
042
043        static {
044                for (int i = 0; i < 256; ++i) {
045                        long part = i;
046                        for (int j = 0; j < 8; ++j)
047                                part = ((part & 1) != 0) ? (part >>> 1) ^ POLY64 : (part >>> 1);
048                        crcTable[i] = part;
049                }
050        }
051
052        @Override
053        public void update(int b) {
054                long low = crc >>> 8;
055                long high = crcTable[(int) ((crc ^ b) & 0xFF)];
056                crc = low ^ high;
057        }
058
059         /**
060     * Updates the CRC-64 checksum with the specified array of bytes.
061         * <br/>
062         * Note that BioJava before version 6.0 implemented this method incorrectly,
063         * using {@code length} as an index.
064     *
065     * @throws IllegalArgumentException
066     *         if {@code offset} is negative, or {@code length} is negative, or
067     *         {@code offset+length} is negative or greater than the length of
068     *         the array {@code b}.
069     */
070        @Override
071        public void update(byte[] b, int offset, int length) {
072                if (b == null) {
073            throw new IllegalArgumentException("byte array cannot be null");
074        }
075        if (offset < 0 || length < 0 || offset > b.length - length) {
076            throw new IllegalArgumentException("Offset and length must be non-negative"+
077                         " and their sum cannot be greater than length of byte array");
078        }
079                for (int i = offset; i < length + offset; ++i)
080                        update(b[i]);
081        }
082
083        public void update(String s) {
084                // update(s.getBytes(), 0, s.length());
085                int size = s.length();
086                for (int i = 0; i < size; ++i)
087                        update(s.charAt(i));
088
089        }
090
091        @Override
092        public long getValue() {
093                return crc;
094        }
095
096        /**
097         * Returns a zero-padded 16 character wide string containing the current
098         * value of this checksum in uppercase hexadecimal format.
099         */
100        @Override
101        public String toString() {
102                StringBuffer builder = new StringBuffer();
103                builder.append(Long.toHexString(crc >>> 4));
104                builder.append(Long.toHexString(crc & 0xF));
105                for (int i = 16 - builder.length(); i > 0; --i)
106                        builder.insert(0, '0');
107                return builder.toString().toUpperCase();
108        }
109
110        @Override
111        public void reset() {
112                crc = 0;
113        }
114}