text: /* FastCRC library code is placed under the MIT license
   * Copyright (c) 2014,2015,2016 Frank BÃ¶sing
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
   * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  //
  // Thanks to:
  // - Catalogue of parametrised CRC algorithms, CRC RevEng
  // http://reveng.sourceforge.net/crc-catalogue/
  //
  // - Danjel McGougan (CRC-Table-Generator)
  //
  
  #include "Arduino.h"
  #if !defined(KINETISK)
  
  #include "FastCRC.h"
  #include "FastCRC_cpu.h"
  #include "FastCRC_tables.h"
  
  
  // ================= 7-BIT CRC ===================
  
  /** Constructor
   */
  FastCRC7::FastCRC7(){}
  
  /** SMBUS CRC
   * aka CRC-8
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint8_t FastCRC7::crc7_upd(const uint8_t *data, uint16_t datalen)
  {
  	uint8_t crc = seed;
  	if (datalen) do {
  		crc = pgm_read_byte(&crc_table_crc7[crc ^ *data]);
  		data++;
  	} while (--datalen);
  	seed = crc;
  	return crc >> 1;
  }
  
  uint8_t FastCRC7::crc7(const uint8_t *data, const uint16_t datalen)
  {
    // poly=0x09 init=0x00 refin=false refout=false xorout=0x00 check=0x75
    seed = 0x00;
    return crc7_upd(data, datalen);
  }
  
  // ================= 8-BIT CRC ===================
  
  /** Constructor
   */
  FastCRC8::FastCRC8(){}
  
  /** SMBUS CRC
   * aka CRC-8
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint8_t FastCRC8::smbus_upd(const uint8_t *data, uint16_t datalen)
  {
  	uint8_t crc = seed;
  	if (datalen) do {
  		crc = pgm_read_byte(&crc_table_smbus[crc ^ *data]);
  		data++;
  	} while (--datalen);
  	seed = crc;
  	return crc;
  }
  
  uint8_t FastCRC8::smbus(const uint8_t *data, const uint16_t datalen)
  {
    // poly=0x07 init=0x00 refin=false refout=false xorout=0x00 check=0xf4
    seed = 0x00;
    return smbus_upd(data, datalen);
  }
  
  /** MAXIM 8-Bit CRC
   * equivalent to _crc_ibutton_update() in crc16.h from avr_libc
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint8_t FastCRC8::maxim_upd(const uint8_t *data, uint16_t datalen)
  {
  	uint8_t crc = seed;
  	if (datalen) do {
  		crc = pgm_read_byte(&crc_table_maxim[crc ^ *data]);
  		data++;
  	} while (--datalen);
  	seed = crc;
  	return crc;
  }
  uint8_t FastCRC8::maxim(const uint8_t *data, const uint16_t datalen)
  {
    // poly=0x31 init=0x00 refin=true refout=true xorout=0x00  check=0xa1
    seed = 0x00;
    return maxim_upd(data, datalen);
  }
  
  // ================= 16-BIT CRC ===================
  /** Constructor
   */
  FastCRC16::FastCRC16(){}
  
  #define crc_n4(crc, data, table) crc ^= data; \
  	crc = pgm_read_word(&table[(crc & 0xff) + 0x300]) ^		\
  	pgm_read_word(&table[((crc >> 8) & 0xff) + 0x200]) ^	\
  	pgm_read_word(&table[((data >> 16) & 0xff) + 0x100]) ^	\
  	pgm_read_word(&table[data >> 24]);
  
  /** CCITT
   * Alias "false CCITT"
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint16_t FastCRC16::ccitt_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint16_t crc = seed;
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_ccitt[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		crc_n4(crc, ((uint32_t *)data)[0], crc_table_ccitt);
  		crc_n4(crc, ((uint32_t *)data)[1], crc_table_ccitt);
  		crc_n4(crc, ((uint32_t *)data)[2], crc_table_ccitt);
  		crc_n4(crc, ((uint32_t *)data)[3], crc_table_ccitt);
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_ccitt[(crc & 0xff) ^ *data++]);
  	}
  	
  	seed = crc;	
  	crc = REV16(crc);
  	
  	return crc;
  }
  uint16_t FastCRC16::ccitt(const uint8_t *data,const uint16_t datalen)
  {
   // poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1
    seed = 0xffff;
    return ccitt_upd(data, datalen);
  }
  
  /** MCRF4XX
   * equivalent to _crc_ccitt_update() in crc16.h from avr_libc
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  
  uint16_t FastCRC16::mcrf4xx_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint16_t crc = seed;
  
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_mcrf4xx[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		crc_n4(crc, ((uint32_t *)data)[0], crc_table_mcrf4xx);
  		crc_n4(crc, ((uint32_t *)data)[1], crc_table_mcrf4xx);
  		crc_n4(crc, ((uint32_t *)data)[2], crc_table_mcrf4xx);
  		crc_n4(crc, ((uint32_t *)data)[3], crc_table_mcrf4xx);
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_mcrf4xx[(crc & 0xff) ^ *data++]);
  	}
  
  	seed = crc;
  	return crc;
  }
  
  uint16_t FastCRC16::mcrf4xx(const uint8_t *data,const uint16_t datalen)
  {
   // poly=0x1021 init=0xffff refin=true refout=true xorout=0x0000 check=0x6f91
    seed = 0xffff;
    return mcrf4xx_upd(data, datalen);
  }
  
  /** MODBUS
   * equivalent to _crc_16_update() in crc16.h from avr_libc
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint16_t FastCRC16::modbus_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint16_t crc = seed;
  
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_modbus[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		crc_n4(crc, ((uint32_t *)data)[0], crc_table_modbus);
  		crc_n4(crc, ((uint32_t *)data)[1], crc_table_modbus);
  		crc_n4(crc, ((uint32_t *)data)[2], crc_table_modbus);
  		crc_n4(crc, ((uint32_t *)data)[3], crc_table_modbus);
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_modbus[(crc & 0xff) ^ *data++]);
  	}
  
  	seed = crc;
  	return crc;
  }
  
  uint16_t FastCRC16::modbus(const uint8_t *data, const uint16_t datalen)
  {
   // poly=0x8005 init=0xffff refin=true refout=true xorout=0x0000 check=0x4b37
    seed = 0xffff;
    return modbus_upd(data, datalen);
  }
  
  /** KERMIT
   * Alias CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-CCITT
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint16_t FastCRC16::kermit_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint16_t crc = seed;
  
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_kermit[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		crc_n4(crc, ((uint32_t *)data)[0], crc_table_kermit);
  		crc_n4(crc, ((uint32_t *)data)[1], crc_table_kermit);
  		crc_n4(crc, ((uint32_t *)data)[2], crc_table_kermit);
  		crc_n4(crc, ((uint32_t *)data)[3], crc_table_kermit);
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_kermit[(crc & 0xff) ^ *data++]);
  	}
  
  	seed = crc;
  	return crc;
  }
  
  uint16_t FastCRC16::kermit(const uint8_t *data, const uint16_t datalen)
  {
   // poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189
   // sometimes byteswapped presentation of result
    seed = 0x0000;
    return kermit_upd(data, datalen);
  }
  
  /** XMODEM
   * Alias ZMODEM, CRC-16/ACORN
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint16_t FastCRC16::xmodem_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint16_t crc = seed;
  
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_xmodem[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		crc_n4(crc, ((uint32_t *)data)[0], crc_table_xmodem);
  		crc_n4(crc, ((uint32_t *)data)[1], crc_table_xmodem);
  		crc_n4(crc, ((uint32_t *)data)[2], crc_table_xmodem);
  		crc_n4(crc, ((uint32_t *)data)[3], crc_table_xmodem);
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_xmodem[(crc & 0xff) ^ *data++]);
  	}
  
  	seed = crc;
  	crc = REV16(crc);	
  	return crc;
  }
  
  uint16_t FastCRC16::xmodem(const uint8_t *data, const uint16_t datalen)
  {
    //width=16 poly=0x1021 init=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3
    seed = 0x0000;
    return xmodem_upd(data, datalen);
  }
  
  /** X25
   * Alias CRC-16/IBM-SDLC, CRC-16/ISO-HDLC, CRC-B
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  uint16_t FastCRC16::x25_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint16_t crc = seed;
  
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_x25[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		crc_n4(crc, ((uint32_t *)data)[0], crc_table_x25);
  		crc_n4(crc, ((uint32_t *)data)[1], crc_table_x25);
  		crc_n4(crc, ((uint32_t *)data)[2], crc_table_x25);
  		crc_n4(crc, ((uint32_t *)data)[3], crc_table_x25);
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_word(&crc_table_x25[(crc & 0xff) ^ *data++]);
  	}
  
  	seed = crc;
  	crc = ~crc;
  
  	return crc;
  }
  
  uint16_t FastCRC16::x25(const uint8_t *data, const uint16_t datalen)
  {
    // poly=0x1021 init=0xffff refin=true refout=true xorout=0xffff check=0x906e
    seed = 0xffff;
    return x25_upd(data, datalen);
  }
  
  
  
  
  
  // ================= 32-BIT CRC ===================
  /** Constructor
   */
  FastCRC32::FastCRC32(){}
  
  #define crc_n4d(crc, data, table) crc ^= data; \
  	crc = pgm_read_dword(&table[(crc & 0xff) + 0x300]) ^	\
  	pgm_read_dword(&table[((crc >> 8) & 0xff) + 0x200]) ^	\
  	pgm_read_dword(&table[((crc >> 16) & 0xff) + 0x100]) ^	\
  	pgm_read_dword(&table[(crc >> 24) & 0xff]);
  	
  #define crcsm_n4d(crc, data, table) crc ^= data; \
  	crc = (crc >> 8) ^ pgm_read_dword(&table[crc & 0xff]); \
  	crc = (crc >> 8) ^ pgm_read_dword(&table[crc & 0xff]); \
  	crc = (crc >> 8) ^ pgm_read_dword(&table[crc & 0xff]); \
  	crc = (crc >> 8) ^ pgm_read_dword(&table[crc & 0xff]);
  
  /** CRC32
   * Alias CRC-32/ADCCP, PKZIP, Ethernet, 802.3
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
  #if CRC_BIGTABLES
  #define CRC_TABLE_CRC32 crc_table_crc32_big
  #else
  #define CRC_TABLE_CRC32 crc_table_crc32
  #endif
  
  uint32_t FastCRC32::crc32_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint32_t crc = seed;
  
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_dword(&CRC_TABLE_CRC32[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		#if CRC_BIGTABLES
  		crc_n4d(crc, ((uint32_t *)data)[0], CRC_TABLE_CRC32);
  		crc_n4d(crc, ((uint32_t *)data)[1], CRC_TABLE_CRC32);
  		crc_n4d(crc, ((uint32_t *)data)[2], CRC_TABLE_CRC32);
  		crc_n4d(crc, ((uint32_t *)data)[3], CRC_TABLE_CRC32);
  		#else
  		crcsm_n4d(crc, ((uint32_t *)data)[0], CRC_TABLE_CRC32);
  		crcsm_n4d(crc, ((uint32_t *)data)[1], CRC_TABLE_CRC32);
  		crcsm_n4d(crc, ((uint32_t *)data)[2], CRC_TABLE_CRC32);
  		crcsm_n4d(crc, ((uint32_t *)data)[3], CRC_TABLE_CRC32);
  		#endif
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_dword(&CRC_TABLE_CRC32[(crc & 0xff) ^ *data++]);
  	}
  
  	seed = crc;
  	crc = ~crc;
  
  	return crc;
  }
  
  uint32_t FastCRC32::crc32(const uint8_t *data, const uint16_t datalen)
  {
    // poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xcbf43926
    seed = 0xffffffff;
    return crc32_upd(data, datalen);
  }
  
  /** CKSUM
   * Alias CRC-32/POSIX
   * @param data Pointer to Data
   * @param datalen Length of Data
   * @return CRC value
   */
   #if CRC_BIGTABLES
  #define CRC_TABLE_CKSUM crc_table_cksum_big
  #else
  #define CRC_TABLE_CKSUM crc_table_cksum
  #endif
  uint32_t FastCRC32::cksum_upd(const uint8_t *data, uint16_t len)
  {
  
  	uint32_t crc = seed;
  
  	while (((uintptr_t)data & 3) && len) {
  		crc = (crc >> 8) ^ pgm_read_dword(&CRC_TABLE_CKSUM[(crc & 0xff) ^ *data++]);
  		len--;
  	}
  
  	while (len >= 16) {
  		len -= 16;
  		#if CRC_BIGTABLES
  		crc_n4d(crc, ((uint32_t *)data)[0], CRC_TABLE_CKSUM);
  		crc_n4d(crc, ((uint32_t *)data)[1], CRC_TABLE_CKSUM);
  		crc_n4d(crc, ((uint32_t *)data)[2], CRC_TABLE_CKSUM);
  		crc_n4d(crc, ((uint32_t *)data)[3], CRC_TABLE_CKSUM);
  		#else
  		crcsm_n4d(crc, ((uint32_t *)data)[0], CRC_TABLE_CKSUM);
  		crcsm_n4d(crc, ((uint32_t *)data)[1], CRC_TABLE_CKSUM);
  		crcsm_n4d(crc, ((uint32_t *)data)[2], CRC_TABLE_CKSUM);
  		crcsm_n4d(crc, ((uint32_t *)data)[3], CRC_TABLE_CKSUM);
  		#endif
  		data += 16;
  	}
  
  	while (len--) {
  		crc = (crc >> 8) ^ pgm_read_dword(&CRC_TABLE_CKSUM[(crc & 0xff) ^ *data++]);
  	}
  	
  	seed = crc;
  	crc = ~REV32(crc);	
  	return crc;
  }
  
  uint32_t FastCRC32::cksum(const uint8_t *data, const uint16_t datalen)
  {
    // width=32 poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0xffffffff check=0x765e7680
    seed = 0x00;
    return cksum_upd(data, datalen);
  }
  
  #endif // #if !defined(KINETISK)
  