crcsync change: better detection of final block.
[ccan] / ccan / crcsync / test / run.c
1 #include "crcsync/crcsync.h"
2 #include "../crcsync.c"
3 #include "tap/tap.h"
4 #include <stdlib.h>
5 #include <stdbool.h>
6 #include <string.h>
7
8 /* FIXME: ccanize. */
9 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
10
11 struct result {
12         enum {
13                 LITERAL, BLOCK
14         } type;
15         /* Block number, or length of literal. */
16         size_t val;
17 };
18
19 static inline size_t num_blocks(size_t len, size_t block_size)
20 {
21         return (len + block_size - 1) / block_size;
22 }
23
24 static void check_finalized_result(size_t curr_literal,
25                                    const struct result results[],
26                                    size_t num_results,
27                                    size_t *curr_result)
28 {
29         if (curr_literal == 0)
30                 return;
31         ok1(*curr_result < num_results);
32         ok1(results[*curr_result].type == LITERAL);
33         ok1(results[*curr_result].val == curr_literal);
34         (*curr_result)++;
35 }
36
37 static void check_result(long result,
38                          size_t *curr_literal,
39                          const struct result results[], size_t num_results,
40                          size_t *curr_result)
41 {
42         /* We append multiple literals into one. */
43         if (result >= 0) {
44                 *curr_literal += result;
45                 return;
46         }
47
48         /* Check outstanding literals. */
49         if (*curr_literal) {
50                 check_finalized_result(*curr_literal, results, num_results,
51                                        curr_result);
52                 *curr_literal = 0;
53         }
54
55         ok1(*curr_result < num_results);
56         ok1(results[*curr_result].type == BLOCK);
57         ok1(results[*curr_result].val == -result - 1);
58         (*curr_result)++;
59 }
60
61 /* Start with buffer1 and sync to buffer2. */
62 static void test_sync(const char *buffer1, size_t len1,
63                       const char *buffer2, size_t len2,
64                       size_t block_size,
65                       const struct result results[], size_t num_results)
66 {
67         struct crc_context *ctx;
68         size_t used, ret, i, curr_literal, finalsize;
69         long result;
70         uint32_t crcs[num_blocks(len1, block_size)];
71
72         crc_of_blocks(buffer1, len1, block_size, 32, crcs);
73
74         finalsize = len1 % block_size ?: block_size;
75
76         /* Normal method. */
77         ctx = crc_context_new(block_size, 32, crcs, ARRAY_SIZE(crcs),
78                               finalsize);
79
80         curr_literal = 0;
81         for (used = 0, i = 0; used < len2; used += ret) {
82                 ret = crc_read_block(ctx, &result, buffer2+used, len2-used);
83                 check_result(result, &curr_literal, results, num_results, &i);
84         }
85
86         while ((result = crc_read_flush(ctx)) != 0)
87                 check_result(result, &curr_literal, results, num_results, &i);
88
89         check_finalized_result(curr_literal, results, num_results, &i);
90         
91         /* We must have achieved everything we expected. */
92         ok1(i == num_results);
93         crc_context_free(ctx);
94
95         /* Byte-at-a-time method. */
96         ctx = crc_context_new(block_size, 32, crcs, ARRAY_SIZE(crcs),
97                               finalsize);
98
99         curr_literal = 0;
100         for (used = 0, i = 0; used < len2; used += ret) {
101                 ret = crc_read_block(ctx, &result, buffer2+used, 1);
102
103                 check_result(result, &curr_literal, results, num_results, &i);
104         }
105
106         while ((result = crc_read_flush(ctx)) != 0)
107                 check_result(result, &curr_literal, results, num_results, &i);
108
109         check_finalized_result(curr_literal, results, num_results, &i);
110         
111         /* We must have achieved everything we expected. */
112         ok1(i == num_results);
113         crc_context_free(ctx);
114 }
115
116 int main(int argc, char *argv[])
117 {
118         char *buffer1, *buffer2;
119         unsigned int i;
120         uint32_t crcs1[12], crcs2[12];
121
122         plan_tests(1454);
123
124         buffer1 = calloc(1024, 1);
125         buffer2 = calloc(1024, 1);
126
127         /* Truncated end block test. */
128         crcs1[11] = 0xdeadbeef;
129         crc_of_blocks(buffer1, 1024, 100, 32, crcs1);
130         ok1(crcs1[11] == 0xdeadbeef);
131         crc_of_blocks(buffer2, 1024, 100, 32, crcs2);
132         ok1(memcmp(crcs1, crcs2, sizeof(crcs1[0])*11) == 0);
133
134         /* Fill with non-zero pattern, retest. */
135         for (i = 0; i < 1024; i++)
136                 buffer1[i] = buffer2[i] = i + i/128;
137
138         crcs1[11] = 0xdeadbeef;
139         crc_of_blocks(buffer1, 1024, 100, 32, crcs1);
140         ok1(crcs1[11] == 0xdeadbeef);
141         crc_of_blocks(buffer2, 1024, 100, 32, crcs2);
142         ok1(memcmp(crcs1, crcs2, sizeof(crcs1[0])*11) == 0);
143
144         /* Check that it correctly masks bits. */
145         crc_of_blocks(buffer1, 1024, 128, 32, crcs1);
146         crc_of_blocks(buffer2, 1024, 128, 8, crcs2);
147         for (i = 0; i < 1024/128; i++)
148                 ok1(crcs2[i] == (crcs1[i] & 0xFF));
149
150         /* Now test the "exact match" "round blocks" case. */
151         {
152                 struct result res[] = {
153                         { BLOCK, 0 },
154                         { BLOCK, 1 },
155                         { BLOCK, 2 },
156                         { BLOCK, 3 },
157                         { BLOCK, 4 },
158                         { BLOCK, 5 },
159                         { BLOCK, 6 },
160                         { BLOCK, 7 } };
161                 test_sync(buffer1, 1024, buffer2, 1024, 128,
162                           res, ARRAY_SIZE(res));
163         }
164
165         /* Now test the "exact match" with end block case. */
166         {
167                 struct result res[] = {
168                         { BLOCK, 0 },
169                         { BLOCK, 1 },
170                         { BLOCK, 2 },
171                         { BLOCK, 3 },
172                         { BLOCK, 4 },
173                         { BLOCK, 5 },
174                         { BLOCK, 6 },
175                         { BLOCK, 7 },
176                         { BLOCK, 8 },
177                         { BLOCK, 9 },
178                         { BLOCK, 10 } };
179                 test_sync(buffer1, 1024, buffer2, 1024, 100,
180                           res, ARRAY_SIZE(res));
181         }
182
183         /* Now test the "one byte append" "round blocks" case. */
184         {
185                 struct result res[] = {
186                         { BLOCK, 0 },
187                         { BLOCK, 1 },
188                         { BLOCK, 2 },
189                         { BLOCK, 3 },
190                         { BLOCK, 4 },
191                         { BLOCK, 5 },
192                         { BLOCK, 6 },
193                         { LITERAL, 1 } };
194                 test_sync(buffer1, 1024-128, buffer2, 1024-127, 128,
195                           res, ARRAY_SIZE(res));
196         }
197
198         /* Now test the "one byte append" with end block case. */
199         {
200                 struct result res[] = {
201                         { BLOCK, 0 },
202                         { BLOCK, 1 },
203                         { BLOCK, 2 },
204                         { BLOCK, 3 },
205                         { BLOCK, 4 },
206                         { BLOCK, 5 },
207                         { BLOCK, 6 },
208                         { BLOCK, 7 },
209                         { BLOCK, 8 },
210                         { BLOCK, 9 },
211                         { BLOCK, 10 },
212                         { LITERAL, 1 } };
213                 test_sync(buffer1, 1023, buffer2, 1024, 100,
214                           res, ARRAY_SIZE(res));
215         }
216
217         /* Now try changing one block at a time, check we get right results. */
218         for (i = 0; i < 1024/128; i++) {
219                 unsigned int j;
220                 struct result res[8];
221
222                 /* Mess with block. */
223                 memcpy(buffer2, buffer1, 1024);
224                 buffer2[i * 128]++;
225
226                 for (j = 0; j < ARRAY_SIZE(res); j++) {
227                         if (j == i) {
228                                 res[j].type = LITERAL;
229                                 res[j].val = 128;
230                         } else {
231                                 res[j].type = BLOCK;
232                                 res[j].val = j;
233                         }
234                 }
235
236                 test_sync(buffer1, 1024, buffer2, 1024, 128,
237                           res, ARRAY_SIZE(res));
238         }
239
240         /* Now try shrinking one block at a time, check we get right results. */
241         for (i = 0; i < 1024/128; i++) {
242                 unsigned int j;
243                 struct result res[8];
244
245                 /* Shrink block. */
246                 memcpy(buffer2, buffer1, i * 128 + 64);
247                 memcpy(buffer2 + i * 128 + 64, buffer1 + i * 128 + 65,
248                        1024 - (i * 128 + 65));
249
250                 for (j = 0; j < ARRAY_SIZE(res); j++) {
251                         if (j == i) {
252                                 res[j].type = LITERAL;
253                                 res[j].val = 127;
254                         } else {
255                                 res[j].type = BLOCK;
256                                 res[j].val = j;
257                         }
258                 }
259
260                 test_sync(buffer1, 1024, buffer2, 1023, 128,
261                           res, ARRAY_SIZE(res));
262         }
263
264         /* Now try shrinking one block at a time, check we get right results. */
265         for (i = 0; i < 1024/128; i++) {
266                 unsigned int j;
267                 struct result res[8];
268
269                 /* Shrink block. */
270                 memcpy(buffer2, buffer1, i * 128 + 64);
271                 memcpy(buffer2 + i * 128 + 64, buffer1 + i * 128 + 65,
272                        1024 - (i * 128 + 65));
273
274                 for (j = 0; j < ARRAY_SIZE(res); j++) {
275                         if (j == i) {
276                                 res[j].type = LITERAL;
277                                 res[j].val = 127;
278                         } else {
279                                 res[j].type = BLOCK;
280                                 res[j].val = j;
281                         }
282                 }
283
284                 test_sync(buffer1, 1024, buffer2, 1023, 128,
285                           res, ARRAY_SIZE(res));
286         }
287
288         return exit_status();
289 }