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
|
/* Licensed under LGPLv2.1+ - see LICENSE file for details */
#include "crcsync.h"
#include <ccan/crc/crc.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
/* FIXME: That 64-bit CRC takes a while to warm the lower bits. Do
* some quantitative tests and replace it? Meanwhile, use upper bits. */
static uint64_t mask_of(unsigned int crcbits)
{
return -1ULL << (64 - crcbits);
}
void crc_of_blocks(const void *data, size_t len, unsigned int block_size,
unsigned int crcbits, uint64_t crc[])
{
unsigned int i;
const uint8_t *buf = data;
uint64_t crcmask = mask_of(crcbits);
for (i = 0; len >= block_size; i++) {
crc[i] = (crc64_iso(0, buf, block_size) & crcmask);
buf += block_size;
len -= block_size;
}
if (len)
crc[i] = (crc64_iso(0, buf, len) & crcmask);
}
struct crc_context {
size_t block_size;
uint64_t crcmask;
/* Saved old buffer bytes (block_size bytes). */
void *buffer;
size_t buffer_start, buffer_end;
/* Progress so far. */
uint64_t running_crc;
size_t literal_bytes;
size_t total_bytes;
int have_match;
/* Final block is special (if a different size) */
size_t tail_size;
uint64_t tail_crc;
/* Uncrc tab. */
uint64_t uncrc_tab[256];
/* This doesn't count the last CRC. */
unsigned int num_crcs;
uint64_t crc[];
};
/* Calculate the how the crc changes when we take a give char out of the
* crc'd area. */
static void init_uncrc_tab(uint64_t uncrc_tab[], unsigned int wsize)
{
unsigned int i;
uint64_t part_crc;
uint8_t buffer[wsize];
/* Calculate crc(buffer+1, wsize-1) once, since it doesn't change. */
memset(buffer, 0, wsize);
part_crc = crc64_iso(0, buffer+1, wsize-1);
for (i = 0; i < 256; i++) {
buffer[0] = i;
uncrc_tab[i] = (crc64_iso(0, buffer, wsize) ^ part_crc);
}
}
struct crc_context *crc_context_new(size_t block_size, unsigned crcbits,
const uint64_t crc[], unsigned num_crcs,
size_t tail_size)
{
struct crc_context *ctx;
assert(num_crcs > 0);
assert(block_size > 0);
assert(tail_size < block_size);
ctx = malloc(sizeof(*ctx) + sizeof(crc[0])*num_crcs);
if (ctx) {
ctx->block_size = block_size;
ctx->tail_size = tail_size;
if (tail_size)
ctx->tail_crc = crc[--num_crcs];
ctx->crcmask = mask_of(crcbits);
ctx->num_crcs = num_crcs;
memcpy(ctx->crc, crc, sizeof(crc[0])*num_crcs);
ctx->buffer_end = 0;
ctx->buffer_start = 0;
ctx->running_crc = 0;
ctx->literal_bytes = 0;
ctx->total_bytes = 0;
ctx->have_match = -1;
init_uncrc_tab(ctx->uncrc_tab, block_size);
ctx->buffer = malloc(block_size);
if (!ctx->buffer) {
free(ctx);
ctx = NULL;
}
}
return ctx;
}
/* Return -1 or index into matching crc. */
static int crc_matches(const struct crc_context *ctx)
{
unsigned int i;
if (ctx->literal_bytes < ctx->block_size)
return -1;
for (i = 0; i < ctx->num_crcs; i++)
if ((ctx->running_crc & ctx->crcmask) == ctx->crc[i])
return i;
return -1;
}
static bool tail_matches(const struct crc_context *ctx)
{
if (ctx->literal_bytes != ctx->tail_size)
return false;
return (ctx->running_crc & ctx->crcmask) == ctx->tail_crc;
}
static uint64_t crc_add_byte(uint64_t crc, uint8_t newbyte)
{
return crc64_iso(crc, &newbyte, 1);
}
static uint64_t crc_remove_byte(uint64_t crc, uint8_t oldbyte,
const uint64_t uncrc_tab[])
{
return crc ^ uncrc_tab[oldbyte];
}
static uint64_t crc_roll(uint64_t crc, uint8_t oldbyte, uint8_t newbyte,
const uint64_t uncrc_tab[])
{
return crc_add_byte(crc_remove_byte(crc, oldbyte, uncrc_tab), newbyte);
}
static size_t buffer_size(const struct crc_context *ctx)
{
return ctx->buffer_end - ctx->buffer_start;
}
size_t crc_read_block(struct crc_context *ctx, long *result,
const void *buf, size_t buflen)
{
size_t consumed = 0, len;
int crcmatch = -1;
const uint8_t *old, *p = buf;
/* Simple optimization, if we found a match last time. */
if (ctx->have_match >= 0) {
crcmatch = ctx->have_match;
goto have_match;
}
/* old is the trailing edge of the checksum window. */
if (buffer_size(ctx) >= ctx->block_size)
old = (uint8_t *)ctx->buffer + ctx->buffer_start;
else
old = NULL;
while (ctx->literal_bytes < ctx->block_size
|| (crcmatch = crc_matches(ctx)) < 0) {
if (consumed == buflen)
break;
/* Increment these now in case we hit goto: below. */
ctx->literal_bytes++;
ctx->total_bytes++;
consumed++;
/* Update crc. */
if (old) {
ctx->running_crc = crc_roll(ctx->running_crc,
*old, *p,
ctx->uncrc_tab);
old++;
/* End of stored buffer? Start on data they gave us. */
if (old == (uint8_t *)ctx->buffer + ctx->buffer_end)
old = buf;
} else {
ctx->running_crc = crc_add_byte(ctx->running_crc, *p);
if (p == (uint8_t *)buf + ctx->block_size - 1)
old = buf;
/* We don't roll this csum, we only look for it after
* a block match. It's simpler and faster. */
if (tail_matches(ctx)) {
crcmatch = ctx->num_crcs;
goto have_match;
}
}
p++;
}
if (crcmatch >= 0) {
/* We have a match! */
if (ctx->literal_bytes > ctx->block_size) {
/* Output literal first. */
*result = ctx->literal_bytes - ctx->block_size;
ctx->literal_bytes = ctx->block_size;
/* Remember for next time! */
ctx->have_match = crcmatch;
} else {
have_match:
*result = -crcmatch-1;
if (crcmatch == ctx->num_crcs)
assert(ctx->literal_bytes == ctx->tail_size);
else
assert(ctx->literal_bytes == ctx->block_size);
ctx->literal_bytes = 0;
ctx->have_match = -1;
ctx->running_crc = 0;
/* Nothing more in the buffer. */
ctx->buffer_start = ctx->buffer_end = 0;
}
} else {
/* Output literal if it's more than 1 block ago. */
if (ctx->literal_bytes > ctx->block_size) {
*result = ctx->literal_bytes - ctx->block_size;
ctx->literal_bytes -= *result;
/* Advance buffer. */
if (*result >= buffer_size(ctx))
ctx->buffer_start = ctx->buffer_end = 0;
else
ctx->buffer_start += *result;
} else
*result = 0;
/* Now save any literal bytes we'll need in future. */
len = ctx->literal_bytes - buffer_size(ctx);
/* Move down old data if we don't have room. */
if (ctx->buffer_end + len > ctx->block_size) {
memmove(ctx->buffer,
(uint8_t *)ctx->buffer + ctx->buffer_start,
buffer_size(ctx));
ctx->buffer_end -= ctx->buffer_start;
ctx->buffer_start = 0;
}
/* Copy len bytes from tail of buffer. */
memcpy((uint8_t *)ctx->buffer + ctx->buffer_end,
(const uint8_t *)buf + buflen - len, len);
ctx->buffer_end += len;
assert(buffer_size(ctx) <= ctx->block_size);
}
return consumed;
}
long crc_read_flush(struct crc_context *ctx)
{
long ret;
/* We might have ended right on a matched block. */
if (ctx->have_match != -1) {
ctx->literal_bytes -= ctx->block_size;
assert(ctx->literal_bytes == 0);
ret = -ctx->have_match-1;
ctx->have_match = -1;
ctx->running_crc = 0;
/* Nothing more in the buffer. */
ctx->buffer_start = ctx->buffer_end;
return ret;
}
/* The rest is just a literal. */
ret = buffer_size(ctx);
assert(ctx->literal_bytes == ret);
ctx->buffer_start = ctx->buffer_end = 0;
ctx->literal_bytes = 0;
return ret;
}
/**
* crc_context_free - free a context returned from crc_context_new.
* @ctx: the context returned from crc_context_new, or NULL.
*/
void crc_context_free(struct crc_context *ctx)
{
free(ctx->buffer);
free(ctx);
}
|