Coverage Report

Created: 2023-05-28 06:42

/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