/*
* Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/
* All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package javaFlacEncoder;
/**
* Class to calculate a CRC16 checksum.
* @author Preston Lacey
*/
public class CRC16 {
/** For Debugging: Higher level equals more debug statements */
public static int DEBUG_LEV = 0;
/** CRC Divisor: 0x18005 */
static final int divisorCRC16 = 0x18005 << 15;
/** working checksum stored between calls to update(..) */
protected int workingCRC16;
/** number of valid bits stored in workingCRC16(valid bits are packed towards
* high-order side of int) */
protected int workingCRC16Count;
/**
* Constructor. Creates a CRC16 object that is ready to be used. Next step
* would be to call updateCRC16, or getCRC16, with appropriate data.
*/
public CRC16() {
reset();
}
/**
* Add data to the crc. Immediately creates checksum on given data. Can be
* called multiple times as it won't finalize the checksum until the method
* checksum() is called.
*
* @param inSet Array holding data to checksum.
* @param start Index of array holding first element
* @param end Index to stop at. Last index used will be end-1.
* @return intermediate result of checksum to this point. This is *not* a
* finalized result, as non-summed data must remain in workingCRC16 until
* checksum() is called.
*/
public short updateCRC16(byte[] inSet, int start, int end) {
//we need at least two bytes to work on. And cache result between calls.
// Follow md5 style, updating many times, finalizing once.
//Copy in saved value(starts out zero).
//Shift value, and divisor to top end of int. When we drop below 17 in
// working int, "OR" it with another byte, or save and return.
// I won't do this, just "assume" it's already there and use instance
// variables.
//While bits available is more than 16:
//while top bit is zero, and >16 bits in buffer. Shift Left.
//if >16 bits remain in working int, divide
//else if 2 bytes remain, "OR" in another byte.
//store working int, return;
int current = start;
int topBit = 0;
int topMask = 1<<31;
int shiftMask = 0x0000FF00;
while(current < end) {//this should leave 16 bits in workingCRC16.
topBit = workingCRC16 & topMask;
while(workingCRC16Count > 16 && topBit == 0) {
workingCRC16Count--;
workingCRC16 = workingCRC16 << 1;
topBit = workingCRC16 & topMask;
}
if( workingCRC16Count > 16 ) {
workingCRC16 = workingCRC16 ^ divisorCRC16;
}
else {//workingCRC16Count <= 16(strictly speeking, count will == 16)
int temp = inSet[current++];
//temp = temp << 24;
temp = temp << 8 & shiftMask;
//temp = temp >>> 16;
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
}
}
return (short)(workingCRC16 >>> 16);
}
/**
* Finalize the intermediate checksum, and return the value. After this is
* called, you must call reset() before attempting a new checksum.
* @return finalized checksum.
*/
public short checksum() {
//add 16 to the count,
//call update with a byte constructed to add no more.
byte[] fake = {0};
workingCRC16Count += 16;
short val = updateCRC16(fake, 0, 1);
//short val = updateCRC16(fake, 0, 2);
return val;
}
/**
* Resets all stored data, preparing object for a new checksum.
*/
public void reset() {
workingCRC16 = 0;
workingCRC16Count = 16;
}
/**
* Add data to the given crc. Immediately creates checksum on given data.
* Can be called multiple times as it won't finalize the checksum until the
* method checksum() is called. This is a static version supplied for
* potential speed improvement(simple tests showed this was about 15% faster
* than the above non-static version when used in this Class's getCRC16
* method)
*
* @param inSet Array holding data to checksum.
* @param start Index of array holding first element
* @param end Index to stop at. Last index used will be end-1.
* @param crc16 CRC16 object to use.
* @return intermediate result of checksum to this point. This is *not* a
* finalized result, as non-summed data must remain in workingCRC16 until
* checksum() is called.
*/
public static short updateCRC16(byte[] inSet, int start, int end, CRC16 crc16) {
//**NOTE: the static and non-static version of this function should, in
//general, be altered together, as they share similar code
//we need at least two bytes to work on. And cache result between calls.
// Follow md5 style, updating many times, finalizing once.
//Copy in saved value(starts out zero).
//Shift value, and divisor to top end of int. When we drop below 17 in
// working int, "OR" it with another byte, or save and return.
// I won't do this, just "assume" it's already there and use instance
// variables.
//While bits available is more than 16:
//while top bit is zero, and >16 bits in buffer. Shift Left.
//if >16 bits remain in working int, divide
//else if 2 bytes remain, "OR" in another byte.
//store working int, return;
int current = start;
int topBit = 0;
int topMask = 1<<31;
int shiftMask = 0x0000FF00;
int shiftMask2 = 0x000000FF;
int workingCRC16 = crc16.workingCRC16;
int workingCRC16Count = crc16.workingCRC16Count;
while(current < end) {//this should leave 16 bits in val.
topBit = workingCRC16 & topMask;
while(workingCRC16Count > 16 && topBit == 0) {
workingCRC16Count--;
workingCRC16 = workingCRC16 << 1;
topBit = workingCRC16 & topMask;
}
if( workingCRC16Count > 16 ) {
workingCRC16 = workingCRC16 ^ divisorCRC16;
}
else {//workingCRC16Count < 16
int temp = inSet[current++];
temp = (temp << 8) & shiftMask;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
if(current < end) {
temp = inSet[current++];
temp = temp & shiftMask2;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
}
}
}
crc16.workingCRC16 = workingCRC16;
crc16.workingCRC16Count = workingCRC16Count;
return (short)(workingCRC16 >>> 16);
}
/**
* This method is provided to conveniently calculate the CRC16 checksum of
* all data stored by an EncodedElement. It uses static functions to allow
* for potential speed improvements. It calls crc16.reset(), so mustn't be
* used on a running checksum.
*
* @param header Element storing data to be used.
* @param crc16 CRC16 object to use. This object's reset() method will be
* called by this method before calculating checksum.
* @return CRC16 result
*/
public static short getCRC16_noninlined(EncodedElement header, CRC16 crc16) {
if(DEBUG_LEV > 0)
System.err.println("Frame::getCRC16 : Begin");
crc16.reset();
//calculate CRC16
int offset = 0;
EncodedElement currentEle = header;
int currentByte = 0;
byte[] unfullByte = {0};
byte[] eleData = null;
int usableBits = 0;
int lastByte = 0;
while(currentEle != null) {
eleData = currentEle.getData();
usableBits = currentEle.getUsableBits();
currentByte = 0;
//if offset is not zero, merge first byte with existing byte
if(offset != 0) {
unfullByte[0] = (byte)(unfullByte[0] | eleData[currentByte++]);
//----updateCRC begin!!!!
CRC16.updateCRC16(unfullByte, 0, 1, crc16);
//crc16.updateCRC16(unfullByte, 0, 1);
}
//checksum all full bytes of element.
lastByte = usableBits/8;
CRC16.updateCRC16(eleData, currentByte, lastByte, crc16);
//save non-full byte(if present), and set "offset" for next element.
offset = usableBits % 8;
if(offset != 0) {
//System.err.println("usablebits: " + usableBits);
unfullByte[0] = eleData[lastByte];
}
//update current.
currentEle = currentEle.getNext();
}
if(offset > 0) {
System.err.println("ERROR: frame was not properly bit padded");
System.exit(0);
}
short crc16Val = crc16.checksum();
if(DEBUG_LEV > 0) {
if(DEBUG_LEV > 10)
System.err.println("Frame::getCRC16: crc16 : "+
Integer.toHexString(crc16Val));
System.err.println("Frame::getCRC16 : End");
}
return crc16Val;
}
/**
* This method is provided to conveniently calculate the CRC16 checksum of
* all data stored by an EncodedElement. It uses static functions to allow
* for potential speed improvements. It calls crc16.reset(), so mustn't be
* used on a running checksum.
*
* @param header Element which stores the data to checksum.
* @param crc16 CRC16 object to use. This object's reset() method will be
* called by this method before calculating checksum.
* @return CRC16 result
*/
public static short getCRC16(EncodedElement header, CRC16 crc16) {
if(DEBUG_LEV > 0)
System.err.println("Frame::getCRC16 : Begin");
crc16.reset();
//calculate CRC16
int offset = 0;
EncodedElement currentEle = header;
int currentByte = 0;
byte[] unfullByte = {0};
byte[] eleData = null;
int usableBits = 0;
int lastByte = 0;
while(currentEle != null) {
eleData = currentEle.getData();
usableBits = currentEle.getUsableBits();
currentByte = 0;
//if offset is not zero, merge first byte with existing byte
if(offset != 0) {
unfullByte[0] = (byte)(unfullByte[0] | eleData[currentByte++]);
//----updateCRC begin!!!!
//CRC16.updateCRC16(unfullByte, 0, 1, crc16);
int end = 1;
byte[] inSet = unfullByte;
int current = 0;
//int topBit = 0;
//int topMask = 1<<31;
int shiftMask = 0x0000FF00;
int shiftMask2 = 0x000000FF;
int workingCRC16 = crc16.workingCRC16;
int workingCRC16Count = crc16.workingCRC16Count;
boolean process = true;
//while(workingCRC16Count > 16) {
while(process) {
int max = workingCRC16Count - 16;
int leading = Integer.numberOfLeadingZeros(workingCRC16);
if(leading > max)
leading = max;
workingCRC16Count -= leading;
workingCRC16 = workingCRC16 << leading;
if(workingCRC16Count > 16) {
workingCRC16 = workingCRC16 ^ divisorCRC16;
}
else if(current < end) {//workingCRC16Count < 16
int temp = inSet[current++];
temp = (temp << 8) & shiftMask;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
if(current < end) {
temp = inSet[current++];
temp = temp & shiftMask2;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
}
}
else {
process = false;
}
}
crc16.workingCRC16 = workingCRC16;
crc16.workingCRC16Count = workingCRC16Count;
//----updateCRC end!!!!!!!!
//crc16.updateCRC16(unfullByte, 0, 1);
}
//checksum all full bytes of element.
lastByte = usableBits/8;
//---updateCRC16 START!!!!!!!!!!
//CRC16.updateCRC16(eleData, currentByte, lastByte, crc16);
int end = lastByte;
byte[] inSet = eleData;
int current = currentByte;
int shiftMask = 0x0000FF00;
int shiftMask2 = 0x000000FF;
int workingCRC16 = crc16.workingCRC16;
int workingCRC16Count = crc16.workingCRC16Count;
boolean process = true;
if(workingCRC16Count <= 16) {
if(current < end) {//workingCRC16Count < 16
int temp = inSet[current++];
temp = (temp << 8) & shiftMask;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
if(current < end) {
temp = inSet[current++];
temp = temp & shiftMask2;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
}
}
if(workingCRC16Count <= 16)
process = false;
}
while(process) {
int max = workingCRC16Count - 16;
int leading = Integer.numberOfLeadingZeros(workingCRC16);
if(leading > max)
leading = max;
workingCRC16Count -= leading;
workingCRC16 = workingCRC16 << leading;
if(workingCRC16Count > 16) {
workingCRC16 = workingCRC16 ^ divisorCRC16;
}
else if(current < end) {//workingCRC16Count < 16
int temp = inSet[current++];
temp = (temp << 8) & shiftMask;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
if(current < end) {
temp = inSet[current++];
temp = temp & shiftMask2;//must mask in case temp is negative
workingCRC16 = workingCRC16 | temp;
workingCRC16Count+=8;
}
}
else {
process = false;
}
}
crc16.workingCRC16 = workingCRC16;
crc16.workingCRC16Count = workingCRC16Count;
//----updateCRC end!!!!!!!!
//save non-full byte(if present), and set "offset" for next element.
offset = usableBits % 8;
if(offset != 0) {
//System.err.println("usablebits: " + usableBits);
unfullByte[0] = eleData[lastByte];
}
//update current.
currentEle = currentEle.getNext();
}
if(offset > 0) {
System.err.println("ERROR: frame was not properly bit padded");
System.exit(0);
}
short crc16Val = crc16.checksum();
if(DEBUG_LEV > 0) {
if(DEBUG_LEV > 10)
System.err.println("Frame::getCRC16: crc16 : "+
Integer.toHexString(crc16Val));
System.err.println("Frame::getCRC16 : End");
}
return crc16Val;
}
}