/src/netcdf-c/libdispatch/dcrc64.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* crc64.c -- compute CRC-64 |
2 | | * Copyright (C) 2013 Mark Adler |
3 | | * Version 1.4 16 Dec 2013 Mark Adler |
4 | | */ |
5 | | |
6 | | /* |
7 | | This software is provided 'as-is', without any express or implied |
8 | | warranty. In no event will the author be held liable for any damages |
9 | | arising from the use of this software. |
10 | | |
11 | | Permission is granted to anyone to use this software for any purpose, |
12 | | including commercial applications, and to alter it and redistribute it |
13 | | freely, subject to the following restrictions: |
14 | | |
15 | | 1. The origin of this software must not be misrepresented; you must not |
16 | | claim that you wrote the original software. If you use this software |
17 | | in a product, an acknowledgment in the product documentation would be |
18 | | appreciated but is not required. |
19 | | 2. Altered source versions must be plainly marked as such, and must not be |
20 | | misrepresented as being the original software. |
21 | | 3. This notice may not be removed or altered from any source distribution. |
22 | | |
23 | | Mark Adler |
24 | | madler@alumni.caltech.edu |
25 | | */ |
26 | | |
27 | | /* Compute CRC-64 in the manner of xz, using the ECMA-182 polynomial, |
28 | | bit-reversed, with one's complement pre and post processing. Provide a |
29 | | means to combine separately computed CRC-64's. */ |
30 | | |
31 | | /* Version history: |
32 | | 1.0 13 Dec 2013 First version |
33 | | 1.1 13 Dec 2013 Fix comments in test code |
34 | | 1.2 14 Dec 2013 Determine endianess at run time |
35 | | 1.3 15 Dec 2013 Add eight-byte processing for big endian as well |
36 | | Make use of the pthread library optional |
37 | | 1.4 16 Dec 2013 Make once variable volatile for limited thread protection |
38 | | */ |
39 | | |
40 | | #include "config.h" |
41 | | #include <stdio.h> |
42 | | #include <inttypes.h> |
43 | | #include <assert.h> |
44 | | |
45 | | #include "ncexternl.h" |
46 | | |
47 | | /* The include of pthread.h below can be commented out in order to not use the |
48 | | pthread library for table initialization. In that case, the initialization |
49 | | will not be thread-safe. That's fine, so long as it can be assured that |
50 | | there is only one thread using crc64(). */ |
51 | | #if 0 |
52 | | #include <pthread.h> /* link with -lpthread */ |
53 | | #endif |
54 | | |
55 | | /* 64-bit CRC polynomial with these coefficients, but reversed: |
56 | | 64, 62, 57, 55, 54, 53, 52, 47, 46, 45, 40, 39, 38, 37, 35, 33, 32, |
57 | | 31, 29, 27, 24, 23, 22, 21, 19, 17, 13, 12, 10, 9, 7, 4, 1, 0 */ |
58 | 1.02k | #define POLY UINT64_C(0xc96c5795d7870f42) |
59 | | |
60 | | /* Tables for CRC calculation -- filled in by initialization functions that are |
61 | | called once. These could be replaced by constant tables generated in the |
62 | | same way. There are two tables, one for each endianess. Since these are |
63 | | static, i.e. local, one should be compiled out of existence if the compiler |
64 | | can evaluate the endianess check in crc64() at compile time. */ |
65 | | static uint64 crc64_little_table[8][256]; |
66 | | static uint64 crc64_big_table[8][256]; |
67 | | |
68 | | /* Fill in the CRC-64 constants table. */ |
69 | | static void crc64_init(uint64 table[][256]) |
70 | 1 | { |
71 | 1 | unsigned n, k; |
72 | 1 | uint64 crc; |
73 | | |
74 | | /* generate CRC-64's for all single byte sequences */ |
75 | 257 | for (n = 0; n < 256; n++) { |
76 | 256 | crc = n; |
77 | 2.30k | for (k = 0; k < 8; k++) |
78 | 2.04k | crc = crc & 1 ? POLY ^ (crc >> 1) : crc >> 1; |
79 | 256 | table[0][n] = crc; |
80 | 256 | } |
81 | | |
82 | | /* generate CRC-64's for those followed by 1 to 7 zeros */ |
83 | 257 | for (n = 0; n < 256; n++) { |
84 | 256 | crc = table[0][n]; |
85 | 2.04k | for (k = 1; k < 8; k++) { |
86 | 1.79k | crc = table[0][crc & 0xff] ^ (crc >> 8); |
87 | 1.79k | table[k][n] = crc; |
88 | 1.79k | } |
89 | 256 | } |
90 | 1 | } |
91 | | |
92 | | /* This function is called once to initialize the CRC-64 table for use on a |
93 | | little-endian architecture. */ |
94 | | static void crc64_little_init(void) |
95 | 1 | { |
96 | 1 | crc64_init(crc64_little_table); |
97 | 1 | } |
98 | | |
99 | | /* Reverse the bytes in a 64-bit word. */ |
100 | | static inline uint64 rev8(uint64 a) |
101 | 0 | { |
102 | 0 | uint64 m; |
103 | |
|
104 | 0 | m = UINT64_C(0xff00ff00ff00ff); |
105 | 0 | a = ((a >> 8) & m) | (a & m) << 8; |
106 | 0 | m = UINT64_C(0xffff0000ffff); |
107 | 0 | a = ((a >> 16) & m) | (a & m) << 16; |
108 | 0 | return a >> 32 | a << 32; |
109 | 0 | } |
110 | | |
111 | | /* This function is called once to initialize the CRC-64 table for use on a |
112 | | big-endian architecture. */ |
113 | | static void crc64_big_init(void) |
114 | 0 | { |
115 | 0 | unsigned k, n; |
116 | |
|
117 | 0 | crc64_init(crc64_big_table); |
118 | 0 | for (k = 0; k < 8; k++) |
119 | 0 | for (n = 0; n < 256; n++) |
120 | 0 | crc64_big_table[k][n] = rev8(crc64_big_table[k][n]); |
121 | 0 | } |
122 | | |
123 | | /* Run the init() function exactly once. If pthread.h is not included, then |
124 | | this macro will use a simple static state variable for the purpose, which is |
125 | | not thread-safe. The init function must be of the type void init(void). */ |
126 | | #ifdef PTHREAD_ONCE_INIT |
127 | | # define ONCE(init) \ |
128 | | do { \ |
129 | | static pthread_once_t once = PTHREAD_ONCE_INIT; \ |
130 | | pthread_once(&once, init); \ |
131 | | } while (0) |
132 | | #else |
133 | | # define ONCE(init) \ |
134 | 90 | do { \ |
135 | 90 | static volatile int once = 1; \ |
136 | 90 | if (once) { \ |
137 | 1 | if (once++ == 1) { \ |
138 | 1 | init(); \ |
139 | 1 | once = 0; \ |
140 | 1 | } \ |
141 | 1 | else \ |
142 | 1 | while (once) \ |
143 | 0 | ; \ |
144 | 1 | } \ |
145 | 90 | } while (0) |
146 | | #endif |
147 | | |
148 | | /* Calculate a CRC-64 eight bytes at a time on a little-endian architecture. */ |
149 | | static inline uint64 crc64_little(uint64 crc, void *buf, size_t len) |
150 | 90 | { |
151 | 90 | unsigned char *next = buf; |
152 | | |
153 | 90 | ONCE(crc64_little_init); |
154 | 90 | crc = ~crc; |
155 | 90 | while (len && ((uintptr_t)next & 7) != 0) { |
156 | 0 | crc = crc64_little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); |
157 | 0 | len--; |
158 | 0 | } |
159 | 24.4k | while (len >= 8) { |
160 | 24.3k | crc ^= *(uint64 *)next; |
161 | 24.3k | crc = crc64_little_table[7][crc & 0xff] ^ |
162 | 24.3k | crc64_little_table[6][(crc >> 8) & 0xff] ^ |
163 | 24.3k | crc64_little_table[5][(crc >> 16) & 0xff] ^ |
164 | 24.3k | crc64_little_table[4][(crc >> 24) & 0xff] ^ |
165 | 24.3k | crc64_little_table[3][(crc >> 32) & 0xff] ^ |
166 | 24.3k | crc64_little_table[2][(crc >> 40) & 0xff] ^ |
167 | 24.3k | crc64_little_table[1][(crc >> 48) & 0xff] ^ |
168 | 24.3k | crc64_little_table[0][crc >> 56]; |
169 | 24.3k | next += 8; |
170 | 24.3k | len -= 8; |
171 | 24.3k | } |
172 | 343 | while (len) { |
173 | 253 | crc = crc64_little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); |
174 | 253 | len--; |
175 | 253 | } |
176 | 90 | return ~crc; |
177 | 90 | } |
178 | | |
179 | | /* Calculate a CRC-64 eight bytes at a time on a big-endian architecture. */ |
180 | | static inline uint64 crc64_big(uint64 crc, void *buf, size_t len) |
181 | 0 | { |
182 | 0 | unsigned char *next = buf; |
183 | |
|
184 | 0 | ONCE(crc64_big_init); |
185 | 0 | crc = ~rev8(crc); |
186 | 0 | while (len && ((uintptr_t)next & 7) != 0) { |
187 | 0 | crc = crc64_big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8); |
188 | 0 | len--; |
189 | 0 | } |
190 | 0 | while (len >= 8) { |
191 | 0 | crc ^= *(uint64 *)next; |
192 | 0 | crc = crc64_big_table[0][crc & 0xff] ^ |
193 | 0 | crc64_big_table[1][(crc >> 8) & 0xff] ^ |
194 | 0 | crc64_big_table[2][(crc >> 16) & 0xff] ^ |
195 | 0 | crc64_big_table[3][(crc >> 24) & 0xff] ^ |
196 | 0 | crc64_big_table[4][(crc >> 32) & 0xff] ^ |
197 | 0 | crc64_big_table[5][(crc >> 40) & 0xff] ^ |
198 | 0 | crc64_big_table[6][(crc >> 48) & 0xff] ^ |
199 | 0 | crc64_big_table[7][crc >> 56]; |
200 | 0 | next += 8; |
201 | 0 | len -= 8; |
202 | 0 | } |
203 | 0 | while (len) { |
204 | 0 | crc = crc64_big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8); |
205 | 0 | len--; |
206 | 0 | } |
207 | 0 | return ~rev8(crc); |
208 | 0 | } |
209 | | |
210 | | /* Return the CRC-64 of buf[0..len-1] with initial crc, processing eight bytes |
211 | | at a time. This selects one of two routines depending on the endianess of |
212 | | the architecture. A good optimizing compiler will determine the endianess |
213 | | at compile time if it can, and get rid of the unused code and table. If the |
214 | | endianess can be changed at run time, then this code will handle that as |
215 | | well, initializing and using two tables, if called upon to do so. */ |
216 | | |
217 | | static int littleendian = -1; |
218 | | |
219 | | EXTERNL uint64 |
220 | | NC_crc64(uint64 crc, void *buf, unsigned int len) |
221 | 90 | { |
222 | | /* Is this machine big vs little endian? */ |
223 | 90 | if(littleendian < 0) { |
224 | 1 | unsigned char* p = (void*)&littleendian; |
225 | 1 | littleendian = 1; |
226 | 1 | if(*p == 0) littleendian = 0; /* big endian */ |
227 | 1 | } |
228 | | |
229 | 90 | return littleendian ? crc64_little(crc, buf, (size_t)len) : |
230 | 90 | crc64_big(crc, buf, (size_t)len); |
231 | 90 | } |
232 | | |
233 | 0 | #define GF2_DIM 64 /* dimension of GF(2) vectors (length of CRC) */ |
234 | | |
235 | | static uint64 gf2_matrix_times(uint64 *mat, uint64 vec) |
236 | 0 | { |
237 | 0 | uint64 sum; |
238 | |
|
239 | 0 | sum = 0; |
240 | 0 | while (vec) { |
241 | 0 | if (vec & 1) |
242 | 0 | sum ^= *mat; |
243 | 0 | vec >>= 1; |
244 | 0 | mat++; |
245 | 0 | } |
246 | 0 | return sum; |
247 | 0 | } |
248 | | |
249 | | static void gf2_matrix_square(uint64 *square, uint64 *mat) |
250 | 0 | { |
251 | 0 | unsigned n; |
252 | |
|
253 | 0 | for (n = 0; n < GF2_DIM; n++) |
254 | 0 | square[n] = gf2_matrix_times(mat, mat[n]); |
255 | 0 | } |
256 | | |
257 | | /* Return the CRC-64 of two sequential blocks, where crc1 is the CRC-64 of the |
258 | | first block, crc2 is the CRC-64 of the second block, and len2 is the length |
259 | | of the second block. */ |
260 | | uint64 crc64_combine(uint64 crc1, uint64 crc2, uintmax_t len2) |
261 | 0 | { |
262 | 0 | unsigned n; |
263 | 0 | uint64 row; |
264 | 0 | uint64 even[GF2_DIM]; /* even-power-of-two zeros operator */ |
265 | 0 | uint64 odd[GF2_DIM]; /* odd-power-of-two zeros operator */ |
266 | | |
267 | | /* degenerate case */ |
268 | 0 | if (len2 == 0) |
269 | 0 | return crc1; |
270 | | |
271 | | /* put operator for one zero bit in odd */ |
272 | 0 | odd[0] = POLY; /* CRC-64 polynomial */ |
273 | 0 | row = 1; |
274 | 0 | for (n = 1; n < GF2_DIM; n++) { |
275 | 0 | odd[n] = row; |
276 | 0 | row <<= 1; |
277 | 0 | } |
278 | | |
279 | | /* put operator for two zero bits in even */ |
280 | 0 | gf2_matrix_square(even, odd); |
281 | | |
282 | | /* put operator for four zero bits in odd */ |
283 | 0 | gf2_matrix_square(odd, even); |
284 | | |
285 | | /* apply len2 zeros to crc1 (first square will put the operator for one |
286 | | zero byte, eight zero bits, in even) */ |
287 | 0 | do { |
288 | | /* apply zeros operator for this bit of len2 */ |
289 | 0 | gf2_matrix_square(even, odd); |
290 | 0 | if (len2 & 1) |
291 | 0 | crc1 = gf2_matrix_times(even, crc1); |
292 | 0 | len2 >>= 1; |
293 | | |
294 | | /* if no more bits set, then done */ |
295 | 0 | if (len2 == 0) |
296 | 0 | break; |
297 | | |
298 | | /* another iteration of the loop with odd and even swapped */ |
299 | 0 | gf2_matrix_square(odd, even); |
300 | 0 | if (len2 & 1) |
301 | 0 | crc1 = gf2_matrix_times(odd, crc1); |
302 | 0 | len2 >>= 1; |
303 | | |
304 | | /* if no more bits set, then done */ |
305 | 0 | } while (len2 != 0); |
306 | | |
307 | | /* return combined crc */ |
308 | 0 | crc1 ^= crc2; |
309 | 0 | return crc1; |
310 | 0 | } |
311 | | |
312 | | #if 0 |
313 | | /* Test crc64() on vector[0..len-1] which should have CRC-64 crc. Also test |
314 | | crc64_combine() on vector[] split in two. */ |
315 | | static void crc64_test(void *vector, size_t len, uint64 crc) |
316 | | { |
317 | | uint64 crc1, crc2; |
318 | | |
319 | | /* test crc64() */ |
320 | | crc1 = crc64(0, vector, len); |
321 | | if (crc1 ^ crc) |
322 | | printf("mismatch: %" PRIx64 ", should be %" PRIx64 "n", (ulong)crc1, (ulong)crc); |
323 | | |
324 | | /* test crc64_combine() */ |
325 | | crc1 = crc64(0, vector, (len + 1) >> 1); |
326 | | crc2 = crc64(0, vector + ((len + 1) >> 1), len >> 1); |
327 | | crc1 = crc64_combine(crc1, crc2, len >> 1); |
328 | | if (crc1 ^ crc) |
329 | | printf("mismatch: %" PRIx64 ", should be %" PRIx64 "n", (ulong)crc1, (ulong)crc); |
330 | | } |
331 | | |
332 | | /* Test vectors. */ |
333 | | #define TEST1 "123456789" |
334 | | #define TESTLEN1 9 |
335 | | #define TESTCRC1 UINT64_C(0x995dc9bbdf1939fa) |
336 | | #define TEST2 "This is a test of the emergency broadcast system." |
337 | | #define TESTLEN2 49 |
338 | | #define TESTCRC2 UINT64_C(0x27db187fc15bbc72) |
339 | | |
340 | | int main(void) |
341 | | { |
342 | | crc64_test(TEST1, TESTLEN1, TESTCRC1); |
343 | | crc64_test(TEST2, TESTLEN2, TESTCRC2); |
344 | | return 0; |
345 | | } |
346 | | #endif |