-
Notifications
You must be signed in to change notification settings - Fork 47
/
crcall.c
377 lines (351 loc) · 12.5 KB
/
crcall.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/* crcall.c -- Generate CRC and test code for every model read from stdin
* Copyright (C) 2020 Mark Adler
* For conditions of distribution and use, see copyright notice in crcany.c.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include "model.h"
#include "crc.h"
#include "crcgen.h"
// Define INTMAX_BITS.
#ifndef INTMAX_BITS
# if INTMAX_MAX == 2147483647
# define INTMAX_BITS 32
# elif INTMAX_MAX == 9223372036854775807
# define INTMAX_BITS 64
# else
# error Unexpected maximum integer size
# endif
#endif
// printf() directive to print a uintmax_t in hexadecimal (e.g. "llx" or "jx").
#define X PRIxMAX
// A strcpy() that returns a pointer to the terminating null, like stpcpy().
static char *strcpytail(char *dst, char const *src) {
size_t i = 0;
for (;;) {
int ch = src[i];
dst[i] = ch;
if (ch == 0)
return dst + i;
i++;
}
}
// A strncmp() that ignores case, like POSIX strncasecmp().
static int strncmpi(char const *s1, char const *s2, size_t n) {
unsigned char const *a = (unsigned char const *)s1,
*b = (unsigned char const *)s2;
for (size_t i = 0; i < n; i++) {
int diff = tolower(a[i]) - tolower(b[i]);
if (diff != 0)
return diff;
if (a[i] == 0)
break;
}
return 0;
}
// Generate test code for model and name. Append the include for the header
// file for this model to defs, test code for each function for this model to
// test, a unified interface to the word-wise function of this model to allc,
// and a table of names, widths, and function pointers to allh. The test code
// computes the CRC of "123456789" (nine bytes), and compares that to the
// provided check value. If the check value does not match the computed CRC,
// then the generated code prints an error to stderr.
static int test_gen(model_t *model, char *name,
FILE *defs, FILE *test, FILE *allc, FILE *allh) {
// Write test and all code for bit-wise function.
fprintf(defs,
"#include \"%s.h\"\n", name);
fprintf(test,
"\n"
" // %s\n"
" init = %s_bit(0, NULL, 0);\n"
" blot = init | ~((((uintmax_t)1 << (%d - 1)) << 1) - 1);\n"
" if (%s_bit(blot, \"123456789\", 9) != %#"X")\n"
" fputs(\"bit-wise mismatch for %s\\n\", stderr), err++;\n"
" crc = %s_bit(blot, data + 1, sizeof(data) - 1);\n",
name, name, model->width, name, model->check, name, name);
fprintf(allc,
"\n"
"#include \"%s.h\"\n"
"uintmax_t %s(uintmax_t crc, void const *mem, size_t len) {\n"
" return %s_word(crc, mem, len);\n"
"}\n", name, name, name);
fprintf(allh,
" {\"%s\", \"", model->name);
for (char *p = name + 3; *p; p++)
if (isalnum(*p))
putc(*p, allh);
fprintf(allh,
"\", %u, %s},\n", model->width, name);
// Write test code for small number of bits function.
if (model->ref)
fprintf(test,
" if (%s_bit(blot, \"\\xda\", 1) !=\n"
" %s_rem(%s_rem(blot, 0xda, 3), 0x1b, 5))\n"
" fputs(\"small bits mismatch for %s\\n\", stderr), err++;\n",
name, name, name, name);
else
fprintf(test,
" if (%s_bit(blot, \"\\xda\", 1) !=\n"
" %s_rem(%s_rem(blot, 0xda, 3), 0xd0, 5))\n"
" fputs(\"small bits mismatch for %s\\n\", stderr), err++;\n",
name, name, name, name);
// Write test code for byte-wise function.
fprintf(test,
" if (%s_byte(0, NULL, 0) != init ||\n"
" %s_byte(blot, \"123456789\", 9) != %#"X" ||\n"
" %s_byte(blot, data + 1, sizeof(data) - 1) != crc)\n"
" fputs(\"byte-wise mismatch for %s\\n\", stderr), err++;\n",
name, name, model->check, name, name);
// Write test code for word-wise function.
fprintf(test,
" if (%s_word(0, NULL, 0) != init ||\n"
" %s_word(blot, \"123456789\", 9) != %#"X" ||\n"
" %s_word(blot, data + 1, sizeof(data) - 1) != crc)\n"
" fputs(\"word-wise mismatch for %s\\n\", stderr), err++;\n",
name, name, model->check, name, name);
// Write test code for combination function.
fprintf(test,
" if (%s_comb(\n"
" %s_byte(init, data + 1, cut - 1),\n"
" %s_byte(init, data + cut, 23), 23) != crc)\n"
" fputs(\"combination mismatch for %s\\n\", stderr), err++;\n",
name, name, name, name);
return 0;
}
// Make a base name for the CRC routines and source files, making use of the
// name in the model description. All names start with "crc*", where "*" is
// replaced by the number of bits in the CRC. The returned name is allocated
// space, or NULL if there was an error. This transformation is tuned to the
// names that appear in the RevEng CRC catalogue.
static char *crc_name(model_t *model) {
char *id = model->name;
char *name = malloc(8 + strlen(id));
if (name == NULL)
return NULL;
char *next = strcpytail(name, "crc");
next += sprintf(next, "%u", model->width);
if (strncmpi(id, "crc", 3) == 0) {
id += 3;
if (*id == '-')
id++;
while (*id >= '0' && *id <= '9')
id++;
if (*id == '/')
id++;
}
if (*id) {
char *suff = next;
do {
if (isalnum(*id)) {
if (next == suff && isdigit(*id))
*next++ = '_';
*next++ = tolower(*id);
}
else if (*id == '-')
*next++ = '_';
} while (*++id);
}
*next = 0;
return name;
}
// Create the src directory if necessary, and create src/name.h and src/name.c
// source files for writing, returning their handles in *head and *code
// respectively. If head or code is NULL, then the respective file is not
// created. If a file by either of those names already exists, then an error is
// returned. A failure to create the directory or writable files will return
// true, with no open handles and *head and *code containing NULL. If the
// problem was a source file that already existed, then create_source() will
// return 2. Otherwise it will return 1 on error, 0 on success.
static int create_source(char *src, char *name, FILE **head, FILE **code) {
// For error return.
if (head != NULL)
*head = NULL;
if (code != NULL)
*code = NULL;
// Create the src directory if it does not exist.
int ret = mkdir(src, 0755);
if (ret && errno != EEXIST)
return 1;
// Construct the path for the source files, leaving suff pointing to the
// position for the 'h' or 'c'.
char path[strlen(src) + 1 + strlen(name) + 2 + 1];
char *suff = strcpytail(path, src);
*suff++ = '/';
suff = strcpytail(suff, name);
*suff++ = '.';
suff[1] = 0;
// Create header file.
if (head != NULL) {
*suff = 'h';
*head = fopen(path, "wx");
if (*head == NULL)
return errno == EEXIST ? 2 : 1;
}
// Create code file.
if (code != NULL) {
*suff = 'c';
*code = fopen(path, "wx");
if (*code == NULL) {
int err = errno;
if (head != NULL) {
fclose(*head);
*head = NULL;
*suff = 'h';
unlink(path);
}
return err == EEXIST ? 2 : 1;
}
}
// All good -- return handles for header and code.
return 0;
}
// Subdirectory for source files.
#define SRC "src"
// Read CRC models from stdin, one per line, and generate C tables and routines
// to compute each one. Each CRC goes into it's own .h and .c source files in
// the "src" subdirectory of the current directory.
int main(int argc, char **argv) {
// Determine endianess of this machine (for testing on this machine, we
// need to match its endianess).
unsigned little = 1;
little = *((unsigned char *)(&little));
int bits = INTMAX_BITS;
// Process option for generated code word bits.
for (int i = 1; i < argc; i++)
if (argv[i][0] == '-')
for (char *opt = argv[i] + 1; *opt; opt++)
switch (*opt) {
case '4':
bits = 32;
break;
case 'h':
fputs("usage: crcall [-4] < crc-defs\n"
" -4 for four-byte words\n", stderr);
return 0;
default:
fprintf(stderr, "unknown option: %c\n", *opt);
return 1;
}
else {
fputs("must precede options with a dash\n", stderr);
return 1;
}
// Create test source files.
FILE *defs, *test, *allc, *allh;
if (create_source(SRC, "test_src", &defs, &test) ||
create_source(SRC, "allcrcs", &allh, &allc)) {
fputs("could not create test code files -- aborting\n", stderr);
return 1;
}
fputs(
"#include <stdio.h>\n"
"#include <stdlib.h>\n"
"#include <stdint.h>\n"
"#include <time.h>\n"
"#include \"test_src.h\"\n"
"\n"
"int main(void) {\n"
" unsigned char data[31];\n"
" {\n"
" unsigned max = (unsigned)RAND_MAX + 1;\n"
" int shft = 0;\n"
" do {\n"
" max >>= 1;\n"
" shft++;\n"
" } while (max > 256);\n"
" srand(time(NULL));\n"
" for (int i = 0; i < 997; i++)\n"
" (void)rand();\n"
" size_t n = sizeof(data);\n"
" do {\n"
" data[--n] = rand() >> shft;\n"
" } while (n);\n"
" }\n"
" uintmax_t init, blot, crc;\n"
" size_t cut = sizeof(data) - 23;\n"
" int err = 0;\n", test);
fputs(
"#include <stdint.h>\n", allc);
fputs(
"\n"
"typedef uintmax_t (*crc_f)(uintmax_t, void const *, size_t);\n"
"\n"
"struct {\n"
" char const *name;\n"
" char const *match;\n"
" unsigned short width;\n"
" crc_f func;\n"
"} const all[] = {\n", allh);
// Read each line from stdin, process the CRC description.
char *line = NULL;
size_t size;
ptrdiff_t len;
while ((len = getcleanline(&line, &size, stdin)) != -1) {
if (len == 0)
continue;
model_t model;
// Read the model.
model.name = NULL;
int ret = read_model(&model, line, 0);
if (ret == 2) {
fputs("out of memory -- aborting\n", stderr);
break;
}
else if (ret == 1)
fprintf(stderr, "%s is an unusable model -- skipping\n",
model.name);
else if (model.width > bits)
fprintf(stderr, "%s is too wide (%u bits) -- skipping\n",
model.name, model.width);
else {
// Convert the parameters for calculation, and fill in the tables
// that are independent of endianess and word length.
process_model(&model);
crc_table_combine(&model);
crc_table_bytewise(&model);
// Generate the routine name prefix for this model.
char *name = crc_name(&model);
if (name == NULL) {
fputs("out of memory -- aborting\n", stderr);
break;
}
// Generate the code.
FILE *head, *code;
int ret = create_source(SRC, name, &head, &code);
if (ret)
fprintf(stderr, "%s/%s.[ch] %s -- skipping\n", SRC, name,
errno == 1 ? "create error" : "exists");
else {
crc_gen(&model, name, little, bits, head, code);
test_gen(&model, name, defs, test, allc, allh);
fclose(code);
fclose(head);
}
free(name);
}
free(model.name);
}
free(line);
fputs(
" {\"\", \"\", 0, NULL}\n"
"};\n", allh);
fclose(allh);
fclose(allc);
fputs(
"\n"
" // done\n"
" fputs(err ? \"** verification failed\\n\" :\n"
" \"-- all good\\n\", stderr);\n"
" return 0;\n"
"}\n", test);
fclose(test);
fclose(defs);
return 0;
}