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