123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- /* aos_crc64.c -- compute CRC-64
- * Copyright (C) 2013 Mark Adler
- * Version 1.4 16 Dec 2013 Mark Adler
- */
- /*
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the author be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- Mark Adler
- madler@alumni.caltech.edu
- */
- /* Compute CRC-64 in the manner of xz, using the ECMA-182 polynomial,
- bit-reversed, with one's complement pre and post processing. Provide a
- means to combine separately computed CRC-64's. */
- /* Version history:
- 1.0 13 Dec 2013 First version
- 1.1 13 Dec 2013 Fix comments in test code
- 1.2 14 Dec 2013 Determine endianess at run time
- 1.3 15 Dec 2013 Add eight-byte processing for big endian as well
- Make use of the pthread library optional
- 1.4 16 Dec 2013 Make once variable volatile for limited thread protection
- */
- #include "aos_crc64.h"
- #include <pthread.h>
- /* 64-bit CRC polynomial with these coefficients, but reversed:
- 64, 62, 57, 55, 54, 53, 52, 47, 46, 45, 40, 39, 38, 37, 35, 33, 32,
- 31, 29, 27, 24, 23, 22, 21, 19, 17, 13, 12, 10, 9, 7, 4, 1, 0 */
- #define POLY 0xc96c5795d7870f42
- /* Tables for CRC calculation -- filled in by initialization functions that are
- called once. These could be replaced by constant tables generated in the
- same way. There are two tables, one for each endianess. Since these are
- static, i.e. local, one should be compiled out of existence if the compiler
- can evaluate the endianess check in crc64() at compile time. */
- static uint64_t crc64_little_table[8][256];
- static uint64_t crc64_big_table[8][256];
- /* Fill in the CRC-64 constants table. */
- static void crc64_init(uint64_t table[][256])
- {
- unsigned n, k;
- uint64_t crc;
- /* generate CRC-64's for all single byte sequences */
- for (n = 0; n < 256; n++) {
- crc = n;
- for (k = 0; k < 8; k++)
- crc = crc & 1 ? POLY ^ (crc >> 1) : crc >> 1;
- table[0][n] = crc;
- }
- /* generate CRC-64's for those followed by 1 to 7 zeros */
- for (n = 0; n < 256; n++) {
- crc = table[0][n];
- for (k = 1; k < 8; k++) {
- crc = table[0][crc & 0xff] ^ (crc >> 8);
- table[k][n] = crc;
- }
- }
- }
- /* This function is called once to initialize the CRC-64 table for use on a
- little-endian architecture. */
- static void crc64_little_init(void)
- {
- crc64_init(crc64_little_table);
- }
- /* Reverse the bytes in a 64-bit word. */
- static inline uint64_t rev8(uint64_t a)
- {
- uint64_t m;
- m = 0xff00ff00ff00ff;
- a = ((a >> 8) & m) | (a & m) << 8;
- m = 0xffff0000ffff;
- a = ((a >> 16) & m) | (a & m) << 16;
- return a >> 32 | a << 32;
- }
- /* This function is called once to initialize the CRC-64 table for use on a
- big-endian architecture. */
- static void crc64_big_init(void)
- {
- unsigned k, n;
- crc64_init(crc64_big_table);
- for (k = 0; k < 8; k++)
- for (n = 0; n < 256; n++)
- crc64_big_table[k][n] = rev8(crc64_big_table[k][n]);
- }
- /* Run the init() function exactly once. If pthread.h is not included, then
- this macro will use a simple static state variable for the purpose, which is
- not thread-safe. The init function must be of the type void init(void). */
- #ifdef PTHREAD_ONCE_INIT
- # define ONCE(init) \
- do { \
- static pthread_once_t once = PTHREAD_ONCE_INIT; \
- pthread_once(&once, init); \
- } while (0)
- #else
- # define ONCE(init) \
- do { \
- static volatile int once = 1; \
- if (once) { \
- if (once++ == 1) { \
- init(); \
- once = 0; \
- } \
- else \
- while (once) \
- ; \
- } \
- } while (0)
- #endif
- /* Calculate a CRC-64 eight bytes at a time on a little-endian architecture. */
- static inline uint64_t crc64_little(uint64_t crc, void *buf, size_t len)
- {
- unsigned char *next = buf;
- ONCE(crc64_little_init);
- crc = ~crc;
- while (len && ((uintptr_t)next & 7) != 0) {
- crc = crc64_little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
- len--;
- }
- while (len >= 8) {
- crc ^= *(uint64_t *)next;
- crc = crc64_little_table[7][crc & 0xff] ^
- crc64_little_table[6][(crc >> 8) & 0xff] ^
- crc64_little_table[5][(crc >> 16) & 0xff] ^
- crc64_little_table[4][(crc >> 24) & 0xff] ^
- crc64_little_table[3][(crc >> 32) & 0xff] ^
- crc64_little_table[2][(crc >> 40) & 0xff] ^
- crc64_little_table[1][(crc >> 48) & 0xff] ^
- crc64_little_table[0][crc >> 56];
- next += 8;
- len -= 8;
- }
- while (len) {
- crc = crc64_little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
- len--;
- }
- return ~crc;
- }
- /* Calculate a CRC-64 eight bytes at a time on a big-endian architecture. */
- static inline uint64_t crc64_big(uint64_t crc, void *buf, size_t len)
- {
- unsigned char *next = buf;
- ONCE(crc64_big_init);
- crc = ~rev8(crc);
- while (len && ((uintptr_t)next & 7) != 0) {
- crc = crc64_big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
- len--;
- }
- while (len >= 8) {
- crc ^= *(uint64_t *)next;
- crc = crc64_big_table[0][crc & 0xff] ^
- crc64_big_table[1][(crc >> 8) & 0xff] ^
- crc64_big_table[2][(crc >> 16) & 0xff] ^
- crc64_big_table[3][(crc >> 24) & 0xff] ^
- crc64_big_table[4][(crc >> 32) & 0xff] ^
- crc64_big_table[5][(crc >> 40) & 0xff] ^
- crc64_big_table[6][(crc >> 48) & 0xff] ^
- crc64_big_table[7][crc >> 56];
- next += 8;
- len -= 8;
- }
- while (len) {
- crc = crc64_big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
- len--;
- }
- return ~rev8(crc);
- }
- /* Return the CRC-64 of buf[0..len-1] with initial crc, processing eight bytes
- at a time. This selects one of two routines depending on the endianess of
- the architecture. A good optimizing compiler will determine the endianess
- at compile time if it can, and get rid of the unused code and table. If the
- endianess can be changed at run time, then this code will handle that as
- well, initializing and using two tables, if called upon to do so. */
- uint64_t aos_crc64(uint64_t crc, void *buf, size_t len)
- {
- uint64_t n = 1;
- return *(char *)&n ? crc64_little(crc, buf, len) :
- crc64_big(crc, buf, len);
- }
- #define GF2_DIM 64 /* dimension of GF(2) vectors (length of CRC) */
- static uint64_t gf2_matrix_times(uint64_t *mat, uint64_t vec)
- {
- uint64_t sum;
- sum = 0;
- while (vec) {
- if (vec & 1)
- sum ^= *mat;
- vec >>= 1;
- mat++;
- }
- return sum;
- }
- static void gf2_matrix_square(uint64_t *square, uint64_t *mat)
- {
- unsigned n;
- for (n = 0; n < GF2_DIM; n++)
- square[n] = gf2_matrix_times(mat, mat[n]);
- }
- /* Return the CRC-64 of two sequential blocks, where crc1 is the CRC-64 of the
- first block, crc2 is the CRC-64 of the second block, and len2 is the length
- of the second block. */
- uint64_t aos_crc64_combine(uint64_t crc1, uint64_t crc2, uintmax_t len2)
- {
- unsigned n;
- uint64_t row;
- uint64_t even[GF2_DIM]; /* even-power-of-two zeros operator */
- uint64_t odd[GF2_DIM]; /* odd-power-of-two zeros operator */
- /* degenerate case */
- if (len2 == 0)
- return crc1;
- /* put operator for one zero bit in odd */
- odd[0] = POLY; /* CRC-64 polynomial */
- row = 1;
- for (n = 1; n < GF2_DIM; n++) {
- odd[n] = row;
- row <<= 1;
- }
- /* put operator for two zero bits in even */
- gf2_matrix_square(even, odd);
- /* put operator for four zero bits in odd */
- gf2_matrix_square(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 */
- gf2_matrix_square(even, odd);
- if (len2 & 1)
- crc1 = gf2_matrix_times(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 */
- gf2_matrix_square(odd, even);
- if (len2 & 1)
- crc1 = gf2_matrix_times(odd, crc1);
- len2 >>= 1;
- /* if no more bits set, then done */
- } while (len2 != 0);
- /* return combined crc */
- crc1 ^= crc2;
- return crc1;
- }
|