


#include "crc.h"

const CRC crc16(POLY_CRC16, 16);
const CRC crc32(POLY_CRC32, 32);

void
CRC::init(uint32_t _poly, uint8_t _power)
{
  int byte;
  assert(_power <= 32);

  poly = _poly << (32 - _power);
  power = _power;

  for (byte = 0; byte < 256; byte++)
    tbl[byte] = add_bits(0, byte, 8);

  for (byte = 0; byte < 64; byte++)
    tbl6[byte] = add_bits(0, byte, 6);
}





uint32_t 
CRC::calc(uint32_t crc, uint8_t *data, size_t bytes, int bs_type) const
{
  switch (bs_type)
  {
    case BITSTREAM_8:    return calc(crc, data, bytes);
    case BITSTREAM_14BE: return calc_14be(crc, (uint16_t *)data, bytes >> 1);
    case BITSTREAM_14LE: return calc_14le(crc, (uint16_t *)data, bytes >> 1);
    case BITSTREAM_16BE: return calc(crc, data, bytes);
    case BITSTREAM_16LE: return calc_16le(crc, (uint16_t *)data, bytes >> 1);
    case BITSTREAM_32BE: return calc(crc, data, bytes);
    case BITSTREAM_32LE: return calc_32le(crc, (uint32_t *)data, bytes >> 2);
  }
  assert(false);
  return 0;
}


uint32_t 
CRC::calc(uint32_t crc, uint8_t *data, size_t size) const
{
  uint8_t *end = data + size;

  
  
  
  

  while ((data < end) && ((uint32_t)data & 3))
    crc = add_8(crc, *data++);

  
  

  uint32_t *data32 = (uint32_t *)data;
  uint32_t *end32  = (uint32_t *)((uint32_t)end & ~3);
  while (data32 < end32)
  {
    crc = add_32(crc, be2uint32(*data32));
    data32++;
  }

  
  
  
  

  data = (uint8_t *)data32;
  while (data < end)
    crc = add_8(crc, *data++);

  return crc;
}

uint32_t 
CRC::calc_14be(uint32_t crc, uint16_t *data, size_t size) const
{
  
  
  
  
  

  if (size && ((uint32_t)data & 3))
  {
    crc = add_14(crc, be2uint16(*data));
    data++; 
    size--;
  }

  
  

  uint32_t *data32 = (uint32_t *)data;
  uint32_t *end32  = data32 + (size >> 1);
  while (data32 < end32)
  {
    uint32_t value = be2uint32(*data32);
    data32++;
    crc = add_14(crc, value >> 16);
    crc = add_14(crc, value);
  }

  
  
  

  if (size & 1)
  {
    data = (uint16_t *)data32;
    crc = add_14(crc, be2uint16(*data));
  }

  return crc;
}

inline uint32_t 
CRC::calc_14le(uint32_t crc, uint16_t *data, size_t size) const
{
  
  
  
  
  

  if (size && ((uint32_t)data & 3))
  {
    crc = add_14(crc, le2uint16(*data));
    data++; 
    size--;
  }

  
  

  uint32_t *data32 = (uint32_t *)data;
  uint32_t *end32  = data32 + (size >> 1);
  while (data32 < end32)
  {
    uint32_t value = le2uint32(*data32);
    data32++;
    crc = add_14(crc, value);
    crc = add_14(crc, value >> 16);
  }

  
  
  

  if (size & 1)
  {
    data = (uint16_t *)data32;
    crc = add_14(crc, le2uint16(*data));
  }

  return crc;
}

uint32_t 
CRC::calc_16be(uint32_t crc, uint16_t *data, size_t size) const
{
  
  return calc(crc, (uint8_t *)data, size << 1);
}

uint32_t 
CRC::calc_16le(uint32_t crc, uint16_t *data, size_t size) const
{
  
  
  
  
  

  if (size && ((uint32_t)data & 3))
  {
    crc = add_16(crc, le2uint16(*data));
    data++; 
    size--;
  }

  
  

  uint32_t *data32 = (uint32_t *)data;
  uint32_t *end32  = data32 + (size >> 1);
  while (data32 < end32)
  {
    uint32_t value = le2uint32(*data32);
    data32++;
    crc = add_16(crc, value);
    crc = add_16(crc, value >> 16);
  }

  
  
  

  if (size & 1)
  {
    data = (uint16_t *)data32;
    crc = add_16(crc, le2uint16(*data));
  }

  return crc;
}

uint32_t 
CRC::calc_32be(uint32_t crc, uint32_t *data, size_t size) const
{
  
  return calc(crc, (uint8_t *)data, size << 2);
}

uint32_t 
CRC::calc_32le(uint32_t crc, uint32_t *data, size_t size) const
{
  uint32_t *end = data + size;
  while (data < end)
  {
    crc = add_32(crc, le2uint32(*data));
    data++;
  }
  return crc;
}







uint32_t 
CRC::calc_bits(uint32_t crc, uint8_t *data, size_t start_bit, size_t bits, int bs_type) const
{
  switch (bs_type)
  {
    case BITSTREAM_8:    return calc_bits(crc, data, start_bit, bits);
    case BITSTREAM_14BE: return calc_bits_14be(crc, (uint16_t *)data, start_bit, bits);
    case BITSTREAM_14LE: return calc_bits_14le(crc, (uint16_t *)data, start_bit, bits);
    case BITSTREAM_16BE: return calc_bits_16be(crc, (uint16_t *)data, start_bit, bits);
    case BITSTREAM_16LE: return calc_bits_16le(crc, (uint16_t *)data, start_bit, bits);
    case BITSTREAM_32BE: return calc_bits_32be(crc, (uint32_t *)data, start_bit, bits);
    case BITSTREAM_32LE: return calc_bits_32le(crc, (uint32_t *)data, start_bit, bits);
  }

  
  assert(false);
  return 0;
}

uint32_t 
CRC::calc_bits(uint32_t crc, uint8_t *data, size_t start_bit, size_t bits) const
{
  data += start_bit >> 3;
  start_bit &= 7;

  int end_bit = start_bit + bits;
  int size = end_bit >> 3;
  end_bit &= 7;

  if (size)
  {
    
    crc = add_bits(crc, *data, 8 - start_bit);
    data++;
    
    crc = calc(crc, data, size-1);
    data += size-1;
    
    crc = add_bits(crc, (*data) >> (8 - end_bit), end_bit);
  }
  else
  {
    
    crc = add_bits(crc, (*data) >> (8 - end_bit), bits);
  }

  return crc;
}

uint32_t 
CRC::calc_bits_16be(uint32_t crc, uint16_t *data, size_t start_bit, size_t bits) const
{
  
  return calc_bits(crc, (uint8_t *)data, start_bit, bits);
}

uint32_t 
CRC::calc_bits_16le(uint32_t crc, uint16_t *data, size_t start_bit, size_t bits) const
{
  data += start_bit >> 4;
  start_bit &= 15;

  int end_bit = start_bit + bits;
  int size = end_bit >> 4;
  end_bit &= 15;

  if (size)
  {
    
    crc = add_bits(crc, le2uint16(*data), 16 - start_bit);
    data++;
    
    crc = calc_16le(crc, data, size-1);
    data += size-1;
    
    crc = add_bits(crc, le2uint16(*data) >> (16 - end_bit), end_bit);
  }
  else
  {
    
    crc = add_bits(crc, le2uint16(*data) >> (16 - end_bit), bits);
  }

  return crc;
}

uint32_t 
CRC::calc_bits_14be(uint32_t crc, uint16_t *data, size_t start_bit, size_t bits) const
{
  data += start_bit / 14;
  start_bit %= 14;

  int end_bit = start_bit + bits;
  int size = end_bit / 14;
  end_bit %= 14;

  if (size)
  {
    
    crc = add_bits(crc, be2uint16(*data), 14 - start_bit);
    data++;
    
    crc = calc_14be(crc, data, size-1);
    data += size-1;
    
    crc = add_bits(crc, be2uint16(*data) >> (14 - end_bit), end_bit);
  }
  else
  {
    
    crc = add_bits(crc, be2uint16(*data) >> (14 - end_bit), bits);
  }

  return crc;
}
uint32_t 
CRC::calc_bits_14le(uint32_t crc, uint16_t *data, size_t start_bit, size_t bits) const
{
  data += start_bit / 14;
  start_bit %= 14;

  int end_bit = start_bit + bits;
  int size = end_bit / 14;
  end_bit %= 14;

  if (size)
  {
    
    crc = add_bits(crc, le2uint16(*data), 14 - start_bit);
    data++;
    
    crc = calc_14le(crc, data, size-1);
    data += size-1;
    
    crc = add_bits(crc, le2uint16(*data) >> (14 - end_bit), end_bit);
  }
  else
  {
    
    crc = add_bits(crc, le2uint16(*data) >> (14 - end_bit), bits);
  }

  return crc;
}
uint32_t 
CRC::calc_bits_32be(uint32_t crc, uint32_t *data, size_t start_bit, size_t bits) const
{
  
  return calc_bits(crc, (uint8_t *)data, start_bit, bits);
}
uint32_t 
CRC::calc_bits_32le(uint32_t crc, uint32_t *data, size_t start_bit, size_t bits) const
{
  data += start_bit >> 5;
  start_bit &= 31;

  int end_bit = start_bit + bits;
  int size = end_bit >> 5;
  end_bit &= 31;

  if (size)
  {
    
    crc = add_bits(crc, le2uint32(*data), 32 - start_bit);
    data++;
    
    crc = calc_32le(crc, data, size-1);
    data += size-1;
    
    crc = add_bits(crc, le2uint32(*data) >> (32 - end_bit), end_bit);
  }
  else
  {
    
    crc = add_bits(crc, le2uint32(*data) >> (32 - end_bit), bits);
  }

  return crc;
}
