The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
sbuff_tests.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Tests for a generic string buffer structure for string printing and parsing
18 *
19 * @file src/lib/util/test//sbuff_tests.c
20 *
21 * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 */
23#include "acutest_common_init.h"
24#include "acutest_helpers.h"
25
26#include <freeradius-devel/util/sbuff.h>
27
28#define TEST_SBUFF_LEN(_sbuff, _num) \
29do { \
30 size_t _len; \
31 _len = talloc_array_length((_sbuff)->buff); \
32 TEST_CHECK(_len == (size_t)_num); \
33 TEST_MSG("Expected length : %zu", (size_t)_num); \
34 TEST_MSG("Got length : %zu", _len); \
35} while(0)
36
37#define TEST_SBUFF_USED(_sbuff, _num) \
38do { \
39 size_t _len; \
40 _len = fr_sbuff_used(_sbuff); \
41 TEST_CHECK(_len == (size_t)_num); \
42 TEST_MSG("Expected length : %zu", (size_t)_num); \
43 TEST_MSG("Got length : %zu", _len); \
44} while(0)
45
46static void test_parse_init(void)
47{
48 char const in[] = "i am a test string";
49 fr_sbuff_t sbuff;
50
51 TEST_CASE("Parse init with size");
52 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
53
54 TEST_CHECK(sbuff.start == in);
55 TEST_CHECK(sbuff.p == in);
56 TEST_CHECK(sbuff.end == in + (sizeof(in) - 1));
57
58 TEST_CASE("Parse init with end");
59 fr_sbuff_init_in(&sbuff, in, in + strlen(in));
60
61 TEST_CHECK(sbuff.start == in);
62 TEST_CHECK(sbuff.p == in);
63 TEST_CHECK(sbuff.end == in + strlen(in));
64
65 TEST_CASE("Parse init with const end");
66 fr_sbuff_init_in(&sbuff, in, (char const *)(in + strlen(in)));
67
68 TEST_CHECK(sbuff.start == in);
69 TEST_CHECK(sbuff.p == in);
70 TEST_CHECK(sbuff.end == in + strlen(in));
71}
72
73static void test_is_char(void)
74{
75 char const in[] = "i am a test string";
76 fr_sbuff_t sbuff;
77 fr_sbuff_marker_t marker;
78
79 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
80 TEST_CHECK(fr_sbuff_is_char(&sbuff, 'i'));
81 TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'z'));
82
83 fr_sbuff_advance(&sbuff, 2);
84 TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'i'));
85 TEST_CHECK(fr_sbuff_is_char(&sbuff, 'a'));
86
87 fr_sbuff_advance(&sbuff, 15);
88 TEST_CHECK(fr_sbuff_is_char(&sbuff, 'g'));
89 fr_sbuff_marker(&marker, &sbuff);
90 TEST_CHECK(fr_sbuff_is_char(&marker, 'g'));
91
92 /*
93 * Ensure that after advancing the buffer past
94 * the end, the marker can still be correctly
95 * tested
96 */
97 fr_sbuff_advance(&sbuff, 1);
98 TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'g'));
99 TEST_CHECK(fr_sbuff_is_char(&marker, 'g'));
100}
101
102static void test_bstrncpy_exact(void)
103{
104 char const in[] = "i am a test string";
105 char const in_long[] = "i am a longer test string";
106 char out[18 + 1] = "";
107 fr_sbuff_t sbuff;
108 ssize_t slen;
109
110 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
111
112 TEST_CASE("Copy 5 bytes to out");
113 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5);
114 TEST_CHECK_SLEN_RETURN(slen, 5);
115 TEST_CHECK_STRCMP(out, "i am ");
116 TEST_CHECK_STRCMP(sbuff.p, "a test string");
117
118 TEST_CASE("Copy 13 bytes to out");
119 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13);
120 TEST_CHECK_SLEN(slen, 13);
121 TEST_CHECK_STRCMP(out, "a test string");
122 TEST_CHECK_STRCMP(sbuff.p, "");
123 TEST_CHECK(sbuff.p == sbuff.end);
124
125 TEST_CASE("Copy would overrun input");
126 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1);
127 TEST_CHECK_SLEN(slen, 0);
128 TEST_CHECK(sbuff.p == sbuff.end);
129
130 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
131 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
132
133 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX);
134 TEST_CHECK_SLEN(slen, -1);
135 TEST_CHECK(sbuff.p == sbuff.start);
136
137 TEST_CASE("Zero length output buffer");
138 fr_sbuff_set_to_start(&sbuff);
139 out[0] = 'a';
140 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX);
141 TEST_CHECK_SLEN(slen, -1);
142 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
143 TEST_CHECK(sbuff.p == sbuff.start);
144
145 TEST_CASE("Zero length size");
146 fr_sbuff_set_to_start(&sbuff);
147 out[0] = 'a';
148 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, 0);
149 TEST_CHECK_SLEN(slen, 0);
150 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
151 TEST_CHECK(sbuff.p == sbuff.start);
152}
153
154static void test_bstrncpy(void)
155{
156 char const in[] = "i am a test string";
157 char const in_long[] = "i am a longer test string";
158 char out[18 + 1] = "";
159 fr_sbuff_t sbuff;
160 ssize_t slen;
161
162 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
163
164 TEST_CASE("Copy 5 bytes to out");
165 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5);
166 TEST_CHECK_SLEN_RETURN(slen, 5);
167 TEST_CHECK_STRCMP(out, "i am ");
168 TEST_CHECK_STRCMP(sbuff.p, "a test string");
169
170 TEST_CASE("Copy 13 bytes to out");
171 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13);
172 TEST_CHECK_SLEN(slen, 13);
173 TEST_CHECK_STRCMP(out, "a test string");
174 TEST_CHECK_STRCMP(sbuff.p, "");
175 TEST_CHECK(sbuff.p == sbuff.end);
176
177 TEST_CASE("Copy would overrun input");
178 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1);
179 TEST_CHECK_SLEN(slen, 0);
180 TEST_CHECK(sbuff.p == sbuff.end);
181
182 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
183 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
184
185 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX);
186 TEST_CHECK_SLEN(slen, 18);
187 TEST_CHECK_STRCMP(out, "i am a longer test");
188
189 TEST_CASE("Zero length output buffer");
190 fr_sbuff_set_to_start(&sbuff);
191 out[0] = 'a';
192 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX);
193 TEST_CHECK_SLEN(slen, 0);
194 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
195 TEST_CHECK(sbuff.p == sbuff.start);
196
197 TEST_CASE("Zero length size");
198 fr_sbuff_set_to_start(&sbuff);
199 out[0] = 'a';
200 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, 0);
201 TEST_CHECK_SLEN(slen, 0);
202 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
203 TEST_CHECK(sbuff.p == sbuff.start);
204}
205
207 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
208 ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
209 ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
210 ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true,
211 ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
212 ['z'] = true, [' '] = true
213};
214
216 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
217 ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
218 ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
219 ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = false,
220 ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
221 ['z'] = true, [' '] = true
222};
223
224static void test_bstrncpy_allowed(void)
225{
226 char const in[] = "i am a test string";
227 char const in_long[] = "i am a longer test string";
228 char out[18 + 1] = "";
229 fr_sbuff_t sbuff;
230 ssize_t slen;
231
232 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
233
234 /*
235 * Should behave identically to bstrncpy
236 * where there's no restrictions on char
237 * set.
238 */
239 TEST_CASE("Copy 5 bytes to out");
241 TEST_CHECK_SLEN_RETURN(slen, 5);
242 TEST_CHECK_STRCMP(out, "i am ");
243 TEST_CHECK_STRCMP(sbuff.p, "a test string");
244
245 TEST_CASE("Copy 13 bytes to out");
247 TEST_CHECK_SLEN(slen, 13);
248 TEST_CHECK_STRCMP(out, "a test string");
249 TEST_CHECK_STRCMP(sbuff.p, "");
250 TEST_CHECK(sbuff.p == sbuff.end);
251
252 TEST_CASE("Copy would overrun input");
254 TEST_CHECK_SLEN(slen, 0);
255 TEST_CHECK(sbuff.p == sbuff.end);
256
257 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
258 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long));
259
260 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, allow_lowercase_and_space);
261 TEST_CHECK_SLEN(slen, 18);
262 TEST_CHECK_STRCMP(out, "i am a longer test");
263
264 TEST_CASE("Zero length output buffer");
265 fr_sbuff_set_to_start(&sbuff);
266 out[0] = 'a';
267 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, allow_lowercase_and_space);
268 TEST_CHECK_SLEN(slen, 0);
269 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
270 TEST_CHECK(sbuff.p == sbuff.start);
271
272 TEST_CASE("Zero length size");
273 fr_sbuff_set_to_start(&sbuff);
274 out[0] = 'a';
275 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, allow_lowercase_and_space);
276 TEST_CHECK_SLEN(slen, 0);
277 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
278 TEST_CHECK(sbuff.p == sbuff.start);
279
280 /*
281 * Check copy stops early
282 */
283 TEST_CASE("Copy until first t");
284 fr_sbuff_set_to_start(&sbuff);
285 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
287 TEST_CHECK_SLEN(slen, 14);
288 TEST_CHECK_STRCMP(out, "i am a longer ");
289
290 TEST_CASE("Copy until first t with length constraint (same len as token)");
291 fr_sbuff_set_to_start(&sbuff);
292 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX,
294 TEST_CHECK_SLEN(slen, 14);
295 TEST_CHECK_STRCMP(out, "i am a longer ");
296
297 TEST_CASE("Copy until first t with length constraint (one shorter than token)");
298 fr_sbuff_set_to_start(&sbuff);
299 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
301 TEST_CHECK_SLEN(slen, 13);
302 TEST_CHECK_STRCMP(out, "i am a longer");
303
304 TEST_CASE("Zero length token (should still be terminated)");
305 fr_sbuff_set_to_start(&sbuff);
306 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
307 (bool[SBUFF_CHAR_CLASS]){});
308 TEST_CHECK_SLEN(slen, 0);
310}
311
312static void test_bstrncpy_until(void)
313{
314 char const in[] = "i am a test string";
315 char const in_long[] = "i am a longer test string";
316 char out[18 + 1];
317 fr_sbuff_t sbuff;
318 ssize_t slen = 0;
319
320 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
321
322 /*
323 * Should behave identically to bstrncpy
324 * where there's no restrictions on char
325 * set.
326 */
327 TEST_CASE("Copy 5 bytes to out");
328 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, NULL);
329 TEST_CHECK_SLEN_RETURN(slen, 5);
330 TEST_CHECK_STRCMP(out, "i am ");
331 TEST_CHECK_STRCMP(sbuff.p, "a test string");
332
333 TEST_CASE("Copy 13 bytes to out");
334 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13, NULL, NULL);
335 TEST_CHECK_SLEN(slen, 13);
336 TEST_CHECK_STRCMP(out, "a test string");
337 TEST_CHECK_STRCMP(sbuff.p, "");
338 TEST_CHECK(sbuff.p == sbuff.end);
339
340 TEST_CASE("Copy would overrun input");
341 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1, NULL, NULL);
342 TEST_CHECK_SLEN(slen, 0);
343 TEST_CHECK(sbuff.p == sbuff.end);
344
345 TEST_CASE("Check escapes");
346 fr_sbuff_set_to_start(&sbuff);
347 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
348 &FR_SBUFF_TERM("g"), &(fr_sbuff_unescape_rules_t){ .chr = 'n' });
349 TEST_CHECK_SLEN(slen, 18);
350 TEST_CHECK_STRCMP(out, "i am a test string");
351 TEST_CHECK_STRCMP(sbuff.p, "");
352
353 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
354 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
355
356 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, NULL);
357 TEST_CHECK_SLEN(slen, 18);
358 TEST_CHECK_STRCMP(out, "i am a longer test");
359
360 TEST_CASE("Zero length output buffer");
361 fr_sbuff_set_to_start(&sbuff);
362 out[0] = 'a';
363 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, NULL, NULL);
364 TEST_CHECK_SLEN(slen, 0);
365 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
366 TEST_CHECK(sbuff.p == sbuff.start);
367
368 TEST_CASE("Zero length size");
369 fr_sbuff_set_to_start(&sbuff);
370 out[0] = 'a';
371 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 0, NULL, NULL);
372 TEST_CHECK_SLEN(slen, 0);
373 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
374 TEST_CHECK(sbuff.p == sbuff.start);
375
376 /*
377 * Check copy stops early
378 */
379 TEST_CASE("Copy until first t");
380 fr_sbuff_set_to_start(&sbuff);
381 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
382 TEST_CHECK_SLEN(slen, 14);
383 TEST_CHECK_STRCMP(out, "i am a longer ");
384
385 TEST_CASE("Copy until first t with length constraint (same len as token)");
386 fr_sbuff_set_to_start(&sbuff);
387 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
388 TEST_CHECK_SLEN(slen, 14);
389 TEST_CHECK_STRCMP(out, "i am a longer ");
390
391 TEST_CASE("Copy until first t with length constraint (one shorter than token)");
392 fr_sbuff_set_to_start(&sbuff);
393 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
394 TEST_CHECK_SLEN(slen, 13);
395 TEST_CHECK_STRCMP(out, "i am a longer");
396
397 TEST_CASE("Zero length token (should still be terminated)");
398 fr_sbuff_set_to_start(&sbuff);
399 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("i"), NULL);
400 TEST_CHECK_SLEN(slen, 0);
402}
403
404static void test_unescape_until(void)
405{
406 char const in[] = "i am a test string";
407 char const in_long[] = "i am a longer test string";
408 char const in_escapes[] = "i am a |t|est strin|g";
409 char const in_escapes_seq[] = "i |x|0am a |t|est strin|g|x20|040";
410 char out[18 + 1] = "";
411 char escape_out[20 + 1];
412
413 fr_sbuff_t sbuff;
414 ssize_t slen;
415
417 .chr = '\\'
418 };
419
420 fr_sbuff_unescape_rules_t pipe_rules = {
421 .chr = '|',
422 .subs = { ['g'] = 'g', ['|'] = '|' }
423 };
424
425 fr_sbuff_unescape_rules_t pipe_rules_sub = {
426 .chr = '|', .subs = { ['g'] = 'h', ['|'] = '|' }
427 };
428
429 fr_sbuff_unescape_rules_t pipe_rules_sub_hex = {
430 .chr = '|',
431 .subs = { ['g'] = 'h', ['|'] = '|' },
432 .do_hex = true
433 };
434
435 fr_sbuff_unescape_rules_t pipe_rules_sub_oct = {
436 .chr = '|',
437 .subs = { ['g'] = 'h', ['|'] = '|' },
438 .do_oct = true
439 };
440
441 fr_sbuff_unescape_rules_t pipe_rules_both = {
442 .chr = '|',
443 .subs = { ['g'] = 'h', ['|'] = '|' },
444 .do_hex = true,
445 .do_oct = true
446 };
447
448 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
449 /*
450 * Should behave identically to bstrncpy
451 * where there's no restrictions on char
452 * set.
453 */
454 TEST_CASE("Copy 5 bytes to out");
455 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, &rules);
456 TEST_CHECK_SLEN_RETURN(slen, 5);
457 TEST_CHECK_STRCMP(out, "i am ");
458 TEST_CHECK_STRCMP(sbuff.p, "a test string");
459
460 TEST_CASE("Copy 13 bytes to out");
461 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13, NULL, &rules);
462 TEST_CHECK_SLEN(slen, 13);
463 TEST_CHECK_STRCMP(out, "a test string");
464 TEST_CHECK_STRCMP(sbuff.p, "");
465 TEST_CHECK(sbuff.p == sbuff.end);
466
467 TEST_CASE("Copy would overrun input");
468 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1, NULL, &rules);
469 TEST_CHECK_SLEN(slen, 0);
470 TEST_CHECK(sbuff.p == sbuff.end);
471
472 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
473 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
474
475 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, &rules);
476 TEST_CHECK_SLEN(slen, 18);
477 TEST_CHECK_STRCMP(out, "i am a longer test");
478
479 TEST_CASE("Zero length output buffer");
480 fr_sbuff_set_to_start(&sbuff);
481 out[0] = 'a';
482 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, NULL, &rules);
483 TEST_CHECK_SLEN(slen, 0);
484 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
485 TEST_CHECK(sbuff.p == sbuff.start);
486
487 TEST_CASE("Zero length size");
488 fr_sbuff_set_to_start(&sbuff);
489 out[0] = 'a';
490 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 0, NULL, &rules);
491 TEST_CHECK_SLEN(slen, 0);
492 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
493 TEST_CHECK(sbuff.p == sbuff.start);
494
495 /*
496 * Check copy stops early
497 */
498 TEST_CASE("Copy until first t");
499 fr_sbuff_set_to_start(&sbuff);
500 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
501 &FR_SBUFF_TERM("t"), &rules);
502 TEST_CHECK_SLEN(slen, 14);
503 TEST_CHECK_STRCMP(out, "i am a longer ");
504
505 TEST_CASE("Copy until first t with length constraint (same len as token)");
506 fr_sbuff_set_to_start(&sbuff);
507 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX,
508 &FR_SBUFF_TERM("t"), &rules);
509 TEST_CHECK_SLEN(slen, 14);
510 TEST_CHECK_STRCMP(out, "i am a longer ");
511
512 TEST_CASE("Copy until first t with length constraint (one shorter than token)");
513 fr_sbuff_set_to_start(&sbuff);
514 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
515 &FR_SBUFF_TERM("t"), &rules);
516 TEST_CHECK_SLEN(slen, 13);
517 TEST_CHECK_STRCMP(out, "i am a longer");
518
519 TEST_CASE("Zero length token (should still be terminated)");
520 fr_sbuff_set_to_start(&sbuff);
521 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
522 &FR_SBUFF_TERM("i"), &rules);
523 TEST_CHECK_SLEN(slen, 0);
525
526 /*
527 * Escapes and substitution
528 */
529 TEST_CASE("Escape with substitution to same char");
530 fr_sbuff_init_in(&sbuff, in_escapes, sizeof(in_escapes) - 1);
531 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX,
532 &FR_SBUFF_TERM("g"), &pipe_rules);
533 TEST_CHECK_SLEN_RETURN(slen, 20);
534 TEST_CHECK_STRCMP(escape_out, "i am a |t|est string");
535 TEST_CHECK_STRCMP(sbuff.p, "");
536
537 TEST_CASE("Escape with substitution to different char");
538 fr_sbuff_init_in(&sbuff, in_escapes, sizeof(in_escapes) - 1);
539 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX,
540 &FR_SBUFF_TERM("g"), &pipe_rules_sub);
541 TEST_CHECK_SLEN(slen, 20);
542 TEST_CHECK_STRCMP(escape_out, "i am a |t|est strinh");
543 TEST_CHECK_STRCMP(sbuff.p, "");
544
545 {
546 char tmp_out[24 + 1];
547
548 TEST_CASE("Escape with hex substitutions (insufficient output space)");
549 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
550 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
551 &FR_SBUFF_TERM("g"), &pipe_rules_sub_hex);
552 TEST_CHECK_SLEN_RETURN(slen, 24);
553 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh");
554 TEST_CHECK_STRCMP(sbuff.p, "|x20|040");
555 }
556
557 {
558 char tmp_out[25 + 1];
559
560 TEST_CASE("Escape with hex substitutions (sufficient output space)");
561 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
562 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
563 &FR_SBUFF_TERM("g"), &pipe_rules_sub_hex);
564 TEST_CHECK_SLEN(slen, 25);
565 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh ");
566 TEST_CHECK_STRCMP(sbuff.p, "|040");
567 }
568
569 {
570 char tmp_out[28 + 1];
571
572 TEST_CASE("Escape with oct substitutions (insufficient output space)");
573 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
574 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
575 &FR_SBUFF_TERM("g"), &pipe_rules_sub_oct);
576 TEST_CHECK_SLEN(slen, 28);
577 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh|x20");
578 TEST_CHECK_STRCMP(sbuff.p, "|040");
579 }
580
581 {
582 char tmp_out[29 + 1];
583
584 TEST_CASE("Escape with oct substitutions (sufficient output space)");
585 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
586 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
587 &FR_SBUFF_TERM("g"), &pipe_rules_sub_oct);
588 TEST_CHECK_SLEN(slen, 29);
589 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh|x20 ");
590 TEST_CHECK_STRCMP(sbuff.p, "");
591 }
592
593 {
594 char tmp_out[26 + 1];
595
596 TEST_CASE("Escape with hex and oct substitutions (sufficient output space)");
597 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
598 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
599 &FR_SBUFF_TERM("g"), &pipe_rules_both);
600 TEST_CHECK_SLEN(slen, 26);
601 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh ");
602 TEST_CHECK_STRCMP(sbuff.p, "");
603 }
604
605 {
606 char tmp_out[2 + 1];
607 char const in_escapes_collapse[] = "||";
608
609 TEST_CASE("Collapse double escapes");
610 fr_sbuff_init_in(&sbuff, in_escapes_collapse, sizeof(in_escapes_collapse) - 1);
611 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)),
612 &sbuff, SIZE_MAX, NULL, &pipe_rules);
613 TEST_CHECK_SLEN(slen, 1);
614 TEST_CHECK_STRCMP(tmp_out, "|");
615 TEST_CHECK_STRCMP(sbuff.p, "");
616 }
617
618 {
619 char in_escapes_collapse[] = "||foo||";
620
621 TEST_CASE("Collapse double escapes overlapping");
622 fr_sbuff_init_in(&sbuff, in_escapes_collapse, sizeof(in_escapes_collapse) - 1);
623 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(in_escapes_collapse, sizeof(in_escapes_collapse)),
624 &sbuff, SIZE_MAX, NULL, &pipe_rules);
625 TEST_CHECK_SLEN(slen, 5);
626 TEST_CHECK_STRCMP(in_escapes_collapse, "|foo|");
627 TEST_CHECK_STRCMP(sbuff.p, "");
628 }
629
630 {
631 char tmp_out[30 + 1];
632
633 fr_sbuff_unescape_rules_t double_quote_rules = {
634 .chr = '\\',
635 .subs = {
636 ['a'] = '\a',
637 ['b'] = '\b',
638 ['e'] = '\\',
639 ['n'] = '\n',
640 ['r'] = '\r',
641 ['t'] = '\t',
642 ['v'] = '\v',
643 ['\\'] = '\\',
644 ['"'] = '"' /* Quoting char */
645 },
646 .do_hex = true,
647 .do_oct = true
648 };
649
650 char const in_escapes_unit[] =
651 "0x01\\001"
652 "0x07\\007"
653 "0x0A\\n"
654 "0x0D\\r"
655 "\\\"\\\""
656 "0xb0"
657 "\\260\\xb0";
658
659 char const expected[] = {
660 '0', 'x', '0', '1', '\001',
661 '0', 'x', '0', '7', '\007',
662 '0', 'x', '0', 'A', '\n',
663 '0', 'x', '0', 'D', '\r',
664 '"', '"',
665 '0', 'x', 'b', '0',
666 '\260', '\xb0', '\0'
667 };
668
669 TEST_CASE("Check unit test test strings");
670 fr_sbuff_init_in(&sbuff, in_escapes_unit, sizeof(in_escapes_unit) - 1);
671 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
672 NULL, &double_quote_rules);
673 TEST_CHECK_SLEN(slen, 28);
674 TEST_CHECK_STRCMP(tmp_out, expected);
675 TEST_CHECK_STRCMP(sbuff.p, "");
676 }
677
678 /*
679 * Verify dynamic allocation
680 */
681 {
682 char *buff;
683 size_t len;
684 char const in_zero[] = "";
685
686 len = fr_sbuff_out_aunescape_until(NULL, &buff, &FR_SBUFF_IN(in_zero, sizeof(in_zero) - 1), SIZE_MAX,
687 NULL, &pipe_rules);
688 TEST_CHECK_SLEN(len, 0);
689 talloc_get_type_abort(buff, char);
690 TEST_CHECK_SLEN(talloc_array_length(buff), 1);
692 }
693}
694
695/** Check that a hex or octal escape sequence cannot consume more input than "len" allows
696 *
697 * fr_sbuff_out_unescape_until() bounds the input it consumes with a marker placed
698 * "len" bytes ahead of the start. The escape sequences are the one place where
699 * the input is advanced by more than a byte at a time, so they are the one place
700 * where the marker can be stepped over.
701 *
702 * An escape sequence which does not fit within "len" is not a complete escape
703 * sequence, and is copied out as-is. That is the same thing which happens to an
704 * incomplete escape sequence at the end of the input.
705 */
707{
708 char out[24 + 1] = "";
709 fr_sbuff_t sbuff;
710 ssize_t slen;
711
712 char const in_hex[] = "|x41|x42|x43";
713 char const in_oct[] = "|101|102|103";
714
715 fr_sbuff_unescape_rules_t hex_rules = {
716 .chr = '|',
717 .subs = { ['|'] = '|' },
718 .do_hex = true
719 };
720
721 fr_sbuff_unescape_rules_t oct_rules = {
722 .chr = '|',
723 .subs = { ['|'] = '|' },
724 .do_oct = true
725 };
726
727 TEST_CASE("Hex, no limit");
728 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
729 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, &hex_rules);
730 TEST_CHECK_SLEN(slen, 3);
731 TEST_CHECK_STRCMP(out, "ABC");
732 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 12);
733
734 TEST_CASE("Hex, limit falls on the end of an escape sequence");
735 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
736 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 4, NULL, &hex_rules);
737 TEST_CHECK_SLEN(slen, 1);
739 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 4);
740
741 TEST_CASE("Hex, limit falls on the escape char");
742 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
743 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, &hex_rules);
744 TEST_CHECK_SLEN(slen, 2);
745 TEST_CHECK_STRCMP(out, "A|");
746 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 5);
747
748 /*
749 * "|x" is within the limit, but neither hex digit is.
750 */
751 TEST_CASE("Hex, limit leaves no room for either hex digit");
752 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
753 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 6, NULL, &hex_rules);
754 TEST_CHECK_SLEN(slen, 3);
755 TEST_CHECK_STRCMP(out, "A|x");
756 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 6);
757 TEST_MSG("consumed %zu bytes of input, was allowed 6", fr_sbuff_used(&sbuff));
758
759 /*
760 * "|x4" is within the limit, the second hex digit is not.
761 */
762 TEST_CASE("Hex, limit leaves room for only one hex digit");
763 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
764 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 7, NULL, &hex_rules);
765 TEST_CHECK_SLEN(slen, 4);
766 TEST_CHECK_STRCMP(out, "A|x4");
767 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 7);
768 TEST_MSG("consumed %zu bytes of input, was allowed 7", fr_sbuff_used(&sbuff));
769
770 TEST_CASE("Octal, no limit");
771 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
772 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, &oct_rules);
773 TEST_CHECK_SLEN(slen, 3);
774 TEST_CHECK_STRCMP(out, "ABC");
775 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 12);
776
777 TEST_CASE("Octal, limit falls on the end of an escape sequence");
778 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
779 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 4, NULL, &oct_rules);
780 TEST_CHECK_SLEN(slen, 1);
782 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 4);
783
784 /*
785 * "|10" is within the limit, the third octal digit is not.
786 */
787 TEST_CASE("Octal, limit leaves room for only two octal digits");
788 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
789 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 7, NULL, &oct_rules);
790 TEST_CHECK_SLEN(slen, 4);
791 TEST_CHECK_STRCMP(out, "A|10");
792 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 7);
793 TEST_MSG("consumed %zu bytes of input, was allowed 7", fr_sbuff_used(&sbuff));
794
795 /*
796 * Once the input has been stepped over, the "have we reached the
797 * end" check compares for equality, so it never matches again and
798 * the rest of the input is consumed too. Walk every limit and
799 * check that the input consumed never exceeds the limit.
800 */
801 TEST_CASE("No limit is ever exceeded, hex");
802 for (size_t i = 0; i <= sizeof(in_hex); i++) {
803 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
804 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, i, NULL, &hex_rules);
805 TEST_CHECK(slen >= 0);
806 TEST_CHECK(fr_sbuff_used(&sbuff) <= i);
807 TEST_MSG("limit %zu consumed %zu bytes of input", i, fr_sbuff_used(&sbuff));
808 }
809
810 TEST_CASE("No limit is ever exceeded, octal");
811 for (size_t i = 0; i <= sizeof(in_oct); i++) {
812 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
813 slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, i, NULL, &oct_rules);
814 TEST_CHECK(slen >= 0);
815 TEST_CHECK(fr_sbuff_used(&sbuff) <= i);
816 TEST_MSG("limit %zu consumed %zu bytes of input", i, fr_sbuff_used(&sbuff));
817 }
818}
819
821{
822 char const in[] = "foo, bar, baz```";
823 fr_sbuff_t sbuff;
824 ssize_t slen = 0;
826 L(","),
827 L("```"),
828 L("bad"),
829 L("bar"),
830 L("boink"),
831 L("food"),
832 L("nyi")
833 );
834 char out[100];
835
836 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
837
838 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
839 TEST_CHECK_SLEN_RETURN(slen, 3);
840 TEST_CHECK_STRCMP(out, "foo");
841
842 fr_sbuff_advance(&sbuff, 1);
843
844 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
845 TEST_CHECK(slen == 1);
847
848 fr_sbuff_advance(&sbuff, 4);
849
850 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
851 TEST_CHECK(slen == 4);
852 TEST_CHECK_STRCMP(out, " baz");
853}
854
855static void test_eof_terminal(void)
856{
857 char const in[] = "foo, bar";
858 fr_sbuff_t sbuff;
859 ssize_t slen = 0;
861 L(""),
862 L(","),
863 );
865 L(",")
866 );
867 char out[100];
868
869 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
870
871 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
872 TEST_CHECK_SLEN_RETURN(slen, 3);
873 TEST_CHECK_STRCMP(out, "foo");
874
875 fr_sbuff_advance(&sbuff, 1); /* Advance past comma */
876
877 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
878 TEST_CHECK_SLEN_RETURN(slen, 4);
879 TEST_CHECK_STRCMP(out, " bar");
880
881 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt_eof) == true);
882 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt) == false);
883}
884
885/*
886 * fr_sbuff_terminal_search() is called with a `p` that walks
887 * forward independently of in->p (see fr_sbuff_out_bstrncpy_until).
888 * `remaining` must be measured from `p`, not from `in->p` — otherwise
889 * memcmp() can read past in->end when matching a multi-byte terminal
890 * near the visible end of the buffer.
891 *
892 * The underlying C string is "abc}xyz" but the sbuff is initialised
893 * to only see the first 4 bytes ("abc}"). With a "}xyz" terminal,
894 * the (buggy) code would happily compare 4 bytes from p='}' against
895 * "}xyz" — reading the "xyz" tail past in->end and reporting a false
896 * match. The fix clamps the compare to (in->end - p) = 1 byte, so
897 * the partial-prefix match correctly falls through.
898 */
900{
901 char const in[] = "abc}xyz";
902 fr_sbuff_t sbuff;
904 L("}xyz")
905 );
906 char out[16] = "";
907 ssize_t slen;
908
909 fr_sbuff_init_in(&sbuff, in, (size_t)4); /* Only "abc}" is visible */
910
911 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
912
913 TEST_CHECK_SLEN(slen, 4);
914 TEST_CHECK_STRCMP(out, "abc}");
915}
916
917/*
918 * Regression: fr_sbuff_terminal_search() did the fast-path "idx[*p]"
919 * dereference before checking whether the buffer was fully consumed.
920 * When "p == in->end" (nothing left) "*p" is one byte past the usable
921 * buffer. For a non-NUL-terminated sbuff whose end sits on an
922 * allocation boundary that is a 1-byte over-read, which ASAN reports as
923 * a heap-buffer-overflow.
924 *
925 * A heap buffer sized exactly to its contents (no trailing '\0') is used
926 * so the byte past "end" lands in an ASAN redzone.
927 */
929{
930 size_t const len = 4;
931 char *buff = malloc(len); /* exactly len bytes, no NUL */
932 fr_sbuff_t sbuff;
934 L(","),
935 L("}"),
936 );
937
938 TEST_CHECK(buff != NULL);
939 if (!buff) return;
940 memcpy(buff, "abcd", len);
941
942 fr_sbuff_init_in(&sbuff, buff, len);
943 fr_sbuff_advance(&sbuff, len); /* Consume everything: in->p == in->end */
944
945 /*
946 * Must not read *p (one byte past the buffer). The sbuff is not
947 * extendable and '\0' is not a terminal, so this is simply "not a
948 * terminal".
949 */
950 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt) == false);
951
952 free(buff);
953}
954
955static void test_terminal_merge(void)
956{
957 size_t i;
959 L(""),
960 L("\t"),
961 L("\n"),
962 L("\r"),
963 L(" "),
964 L("!"),
965 L("%"),
966 L("&"),
967 L("*"),
968 L("+"),
969 L("-"),
970 L("/"),
971 L("<"),
972 L("="),
973 L(">"),
974 L("^"),
975 L("{"),
976 L("|"),
977 L("~")
978 );
980 L(""),
981 L(")"),
982 );
983
984 fr_sbuff_term_t expect =
986 L(""),
987 L("\t"),
988 L("\n"),
989 L("\r"),
990 L(" "),
991 L("!"),
992 L("%"),
993 L("&"),
994 L(")"),
995 L("*"),
996 L("+"),
997 L("-"),
998 L("/"),
999 L("<"),
1000 L("="),
1001 L(">"),
1002 L("^"),
1003 L("{"),
1004 L("|"),
1005 L("~")
1006 );
1007 fr_sbuff_term_t *result;
1008
1009 result = fr_sbuff_terminals_amerge(NULL, &a, &b);
1010 TEST_CHECK_LEN(result->len, expect.len);
1011
1012 for (i = 0; i < result->len; i++) {
1013 TEST_CHECK_STRCMP(result->elem[i].str, expect.elem[i].str);
1014 }
1015
1016 talloc_free(result);
1017}
1018
1019static void test_no_advance(void)
1020{
1021 char const *in = "i am a test string";
1022 char out[18 + 1] = "";
1023 fr_sbuff_t sbuff;
1024 ssize_t slen;
1025
1026 fr_sbuff_init_in(&sbuff, in, strlen(in));
1027
1028 TEST_CASE("Copy 5 bytes to out - no advance");
1029 TEST_CHECK(sbuff.p == sbuff.start);
1030 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &FR_SBUFF(&sbuff), 5);
1031 TEST_CHECK_SLEN_RETURN(slen, 5);
1032 TEST_CHECK(strcmp(out, "i am ") == 0);
1033 TEST_CHECK(sbuff.p == sbuff.start);
1034}
1035
1036static void test_talloc_extend(void)
1037{
1038 fr_sbuff_t sbuff;
1040
1041 TEST_CASE("Initial allocation");
1042 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 32, 50) == &sbuff);
1043 TEST_SBUFF_USED(&sbuff, 0);
1044 TEST_SBUFF_LEN(&sbuff, 33);
1045
1046 TEST_CASE("Trim to zero");
1047 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1048 TEST_SBUFF_USED(&sbuff, 0);
1049 TEST_SBUFF_LEN(&sbuff, 1);
1050
1051 TEST_CASE("Print string - Should realloc to init");
1052 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
1053 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "0123456789") == 0);
1054 TEST_SBUFF_USED(&sbuff, 10);
1055 TEST_SBUFF_LEN(&sbuff, 33);
1056
1057 TEST_CASE("Trim to strlen");
1058 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1059 TEST_SBUFF_LEN(&sbuff, 11);
1060
1061 TEST_CASE("Print string - Should realloc to init");
1062 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
1063 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789") == 0);
1064 TEST_SBUFF_USED(&sbuff, 20);
1065 TEST_SBUFF_LEN(&sbuff, 33);
1066
1067 TEST_CASE("Trim to strlen");
1068 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1069 TEST_SBUFF_LEN(&sbuff, 21);
1070
1071 TEST_CASE("Print string - Should realloc to double buffer len");
1072 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "012345678901234") == 15);
1073 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234") == 0);
1074 TEST_SBUFF_USED(&sbuff, 35);
1075 TEST_SBUFF_LEN(&sbuff, 41);
1076
1077 TEST_CASE("Print string - Should only add a single char, should not extend the buffer");
1078 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
1079 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234A") == 0);
1080 TEST_SBUFF_USED(&sbuff, 36);
1081 TEST_SBUFF_LEN(&sbuff, 41);
1082
1083 TEST_CASE("Print string - Use all available buffer data");
1084 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BCDE") == 4);
1085 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDE") == 0);
1086 TEST_SBUFF_USED(&sbuff, 40);
1087 TEST_SBUFF_LEN(&sbuff, 41);
1088
1089 TEST_CASE("Print string - Add single char, should trigger doubling constrained by max");
1090 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "F") == 1);
1091 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEF") == 0);
1092 TEST_SBUFF_USED(&sbuff, 41);
1093 TEST_SBUFF_LEN(&sbuff, 51);
1094
1095 TEST_CASE("Print string - Add data to take us up to max");
1096 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "GHIJKLMNO") == 9);
1097 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
1098 TEST_SBUFF_USED(&sbuff, 50);
1099 TEST_SBUFF_LEN(&sbuff, 51);
1100
1101 TEST_CASE("Print string - Add single char, should fail");
1102 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "P") == -1);
1103 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
1104 TEST_SBUFF_USED(&sbuff, 50);
1105 TEST_SBUFF_LEN(&sbuff, 51);
1106
1107 TEST_CASE("Trim to strlen (should be noop)");
1108 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1109 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
1110 TEST_SBUFF_USED(&sbuff, 50);
1111 TEST_SBUFF_LEN(&sbuff, 51);
1112
1113 talloc_free(sbuff.buff);
1114}
1115
1117{
1118 fr_sbuff_t sbuff;
1120
1121 TEST_CASE("Initial allocation");
1122 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 0, 50) == &sbuff);
1123 TEST_SBUFF_USED(&sbuff, 0);
1124 TEST_SBUFF_LEN(&sbuff, 1);
1125
1126 TEST_CASE("Print string - Should alloc one byte");
1127 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
1128 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "A") == 0);
1129 TEST_SBUFF_USED(&sbuff, 1);
1130 TEST_SBUFF_LEN(&sbuff, 2);
1131
1132 TEST_CASE("Print string - Should alloc two bytes");
1133 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BC") == 2);
1134 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABC") == 0);
1135 TEST_SBUFF_USED(&sbuff, 3);
1136 TEST_SBUFF_LEN(&sbuff, 4);
1137
1138 TEST_CASE("Print string - Should alloc three bytes");
1139 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "D") == 1);
1140 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABCD") == 0);
1141 TEST_SBUFF_USED(&sbuff, 4);
1142 TEST_SBUFF_LEN(&sbuff, 7);
1143
1144 talloc_free(sbuff.buff);
1145}
1146
1148{
1149 fr_sbuff_t sbuff_0, sbuff_1;
1151
1152 TEST_CASE("Initial allocation");
1153 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
1154 TEST_SBUFF_USED(&sbuff_0, 0);
1155 TEST_SBUFF_LEN(&sbuff_0, 1);
1156
1157 sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0);
1158 TEST_CASE("Check sbuff_1 has extend fields set");
1159 TEST_CHECK(sbuff_0.extend == sbuff_1.extend);
1160 TEST_CHECK(sbuff_0.uctx == sbuff_1.uctx);
1161 TEST_CHECK(sbuff_1.parent == &sbuff_0);
1162 TEST_SBUFF_USED(&sbuff_1, 0);
1163 TEST_SBUFF_LEN(&sbuff_1, 1);
1164
1165 TEST_CASE("Print string - Should alloc one byte");
1166 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "A") == 1);
1167 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff_1), "A") == 0);
1168 TEST_SBUFF_USED(&sbuff_0, 1);
1169 TEST_SBUFF_LEN(&sbuff_0, 2);
1170 TEST_SBUFF_USED(&sbuff_1, 1);
1171 TEST_SBUFF_LEN(&sbuff_1, 2);
1172
1173 TEST_CHECK(sbuff_0.start == sbuff_1.start);
1174 TEST_CHECK(sbuff_0.end == sbuff_1.end);
1175 TEST_CHECK(sbuff_0.p == sbuff_1.p);
1176
1177 talloc_free(sbuff_0.buff);
1178}
1179
1181{
1182 fr_sbuff_t sbuff_0, sbuff_1;
1183 fr_sbuff_marker_t marker_0, marker_1;
1185
1186 TEST_CASE("Initial allocation");
1187 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
1188 TEST_SBUFF_USED(&sbuff_0, 0);
1189 TEST_SBUFF_LEN(&sbuff_0, 1);
1190
1191 TEST_CASE("Print string - Should alloc one byte");
1192 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "A") == 1);
1193 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "A");
1194 TEST_SBUFF_USED(&sbuff_0, 1);
1195 TEST_SBUFF_LEN(&sbuff_0, 2);
1196
1197 fr_sbuff_marker(&marker_0, &sbuff_0);
1198 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1199
1200 TEST_CASE("Print string - Ensure marker is updated");
1201 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "B") == 1);
1202 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "AB");
1203 TEST_SBUFF_USED(&sbuff_0, 2);
1204 TEST_SBUFF_LEN(&sbuff_0, 3);
1205 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1206
1207 TEST_CASE("Print string - Copy sbuff");
1208 sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0); /* Dup sbuff_0 */
1209 TEST_CHECK(sbuff_0.p == sbuff_1.start);
1210 fr_sbuff_marker(&marker_1, &sbuff_1);
1211
1212 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1213 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1214 TEST_CHECK(sbuff_0.p == sbuff_1.start);
1215
1216 TEST_CASE("Print string - Trigger re-alloc, ensure all pointers are updated");
1217 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "C") == 1);
1218 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_1), "C");
1219 TEST_CHECK(sbuff_0.buff == sbuff_1.buff);
1220 TEST_CHECK(sbuff_0.p == sbuff_1.start + 1);
1221 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1222 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1223 TEST_SBUFF_USED(&sbuff_0, 3);
1224 TEST_SBUFF_LEN(&sbuff_0, 5);
1225
1226 talloc_free(sbuff_0.buff);
1227}
1228
1230{
1231 fr_sbuff_t sbuff;
1233
1234 TEST_CASE("Intermix shift and extend");
1235 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 4, 8) == &sbuff);
1236 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123") == 4);
1237 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "5678") == 4);
1238 TEST_CHECK(fr_sbuff_shift(&sbuff, 4, false) == 4);
1239 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "AAAA") == 4);
1240 TEST_CHECK(fr_sbuff_shift(&sbuff, 8, false) == 8);
1241 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BBBBBBBB") == 8);
1242
1243 talloc_free(sbuff.buff);
1244}
1245
1246/*
1247 * fr_sbuff_used() counts from the sbuff's start pointer to its current
1248 * position. fr_sbuff_used_total() adds the "shifted" counter, which
1249 * records how much data has been moved out of the buffer and discarded.
1250 *
1251 * The two come apart as soon as anything is shifted, and which one a
1252 * caller wants is not obvious, so pin the behaviour down here.
1253 */
1255{
1256 fr_sbuff_t sbuff;
1258
1259 TEST_CASE("Before any shift, used() and used_total() agree");
1260 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 8, 32) == &sbuff);
1261 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "01234567") == 8);
1262
1263 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 8);
1265 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 0);
1266
1267 TEST_CASE("A shift moves data out of the buffer");
1268 TEST_CHECK_LEN(fr_sbuff_shift(&sbuff, 4, false), 4);
1269
1270 /*
1271 * This sbuff's start pointer is the beginning of the buffer, so the
1272 * shift cannot move it back any further. fr_sbuff_shift() adds the
1273 * whole shift to "shifted" instead (sbuff.c).
1274 *
1275 * The result is that used() forgets the shifted data and
1276 * used_total() does not. used_total() is therefore the count which
1277 * survives a shift.
1278 */
1279 TEST_CASE("used() forgets the shifted data");
1280 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 4);
1281
1282 TEST_CASE("used_total() remembers it, and is unchanged by the shift");
1284 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 4);
1285
1286 TEST_CASE("used_total() stays whole across a second shift");
1287 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "89AB") == 4);
1289 TEST_CHECK_LEN(fr_sbuff_shift(&sbuff, 4, false), 4);
1291 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 8);
1292
1293 /*
1294 * The sum is the point: whatever the shift did, the two halves of
1295 * the count still add up to everything ever written.
1296 */
1297 TEST_CASE("used() plus shifted() equals used_total()");
1299 fr_sbuff_used_total(&sbuff));
1300
1301 talloc_free(sbuff.buff);
1302}
1303
1304/*
1305 * fr_sbuff_marker_update_end() places a marker at the point where a
1306 * budget of "max" bytes runs out, and is called again after every buffer
1307 * extension. It reads the budget already spent with
1308 * fr_sbuff_used_total(), which is the count that survives a shift.
1309 */
1310static void test_marker_update_end(void)
1311{
1312 fr_sbuff_t sbuff;
1314 char buff[16];
1315
1316 TEST_CASE("SIZE_MAX means no constraint, so the marker sits at the end");
1317 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1318 fr_sbuff_marker(&end, &sbuff);
1319 TEST_CHECK(fr_sbuff_marker_update_end(&end, SIZE_MAX) == fr_sbuff_end(&sbuff));
1320 fr_sbuff_marker_release(&end);
1321
1322 TEST_CASE("A budget smaller than the buffer stops the marker short");
1323 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1324 fr_sbuff_marker(&end, &sbuff);
1325 TEST_CHECK(fr_sbuff_marker_update_end(&end, 4) == fr_sbuff_current(&sbuff) + 4);
1326 fr_sbuff_marker_release(&end);
1327
1328 TEST_CASE("Data already consumed comes off the budget");
1329 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1330 TEST_CHECK(fr_sbuff_advance(&sbuff, 3) > 0);
1331 fr_sbuff_marker(&end, &sbuff);
1333
1334 /*
1335 * Three of the six are already spent, so three are left.
1336 */
1337 TEST_CHECK(fr_sbuff_marker_update_end(&end, 6) == fr_sbuff_current(&sbuff) + 3);
1338 fr_sbuff_marker_release(&end);
1339
1340 TEST_CASE("A budget which exactly matches what was consumed leaves nothing");
1341 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1342 TEST_CHECK(fr_sbuff_advance(&sbuff, 4) > 0);
1343 fr_sbuff_marker(&end, &sbuff);
1344 TEST_CHECK(fr_sbuff_marker_update_end(&end, 4) == fr_sbuff_current(&sbuff));
1345 fr_sbuff_marker_release(&end);
1346
1347 /*
1348 * "max - used" is computed in size_t, so an overspent budget does
1349 * not go negative. Without a clamp it wraps to an enormous number,
1350 * which compares greater than fr_sbuff_remaining(), and the marker
1351 * lands on the end of the buffer: the budget is overspent, and the
1352 * marker then permits everything which is left.
1353 *
1354 * fr_sbuff_marker_update_end() checks for that case, so the marker
1355 * stays at the current position and allows nothing more.
1356 */
1357 TEST_CASE("An overspent budget allows nothing more");
1358 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1359 TEST_CHECK(fr_sbuff_advance(&sbuff, 6) > 0);
1360 fr_sbuff_marker(&end, &sbuff);
1362 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1363 TEST_MSG("marker is %zu past the current position, expected 0",
1364 (size_t) (fr_sbuff_current(&end) - fr_sbuff_current(&sbuff)));
1365 TEST_CHECK(fr_sbuff_current(&end) != fr_sbuff_end(&sbuff));
1366 fr_sbuff_marker_release(&end);
1367
1368 TEST_CASE("A budget overspent by a single byte allows nothing more");
1369 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1370 TEST_CHECK(fr_sbuff_advance(&sbuff, 4) > 0);
1371 fr_sbuff_marker(&end, &sbuff);
1372 TEST_CHECK(fr_sbuff_marker_update_end(&end, 3) == fr_sbuff_current(&sbuff));
1373 fr_sbuff_marker_release(&end);
1374
1375 TEST_CASE("A zero budget allows nothing, whether or not anything was used");
1376 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1377 fr_sbuff_marker(&end, &sbuff);
1378 TEST_CHECK(fr_sbuff_marker_update_end(&end, 0) == fr_sbuff_current(&sbuff));
1379 fr_sbuff_marker_release(&end);
1380
1381 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1382 TEST_CHECK(fr_sbuff_advance(&sbuff, 5) > 0);
1383 fr_sbuff_marker(&end, &sbuff);
1384 TEST_CHECK(fr_sbuff_marker_update_end(&end, 0) == fr_sbuff_current(&sbuff));
1385 fr_sbuff_marker_release(&end);
1386
1387 /*
1388 * fr_sbuff_out_unescape_until() calls fr_sbuff_marker_update_end()
1389 * once per extension, with the same budget every time. Repeated
1390 * calls on an overspent budget must keep giving the same answer.
1391 */
1392 TEST_CASE("Repeated updates on an overspent budget stay put");
1393 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1394 TEST_CHECK(fr_sbuff_advance(&sbuff, 7) > 0);
1395 fr_sbuff_marker(&end, &sbuff);
1396 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1397 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1398 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1399 fr_sbuff_marker_release(&end);
1400
1401 /*
1402 * Nothing is left in the buffer, so "allow nothing more" and "allow
1403 * everything which is left" are the same position. The overspend
1404 * is invisible here, which is why the cases above use a buffer with
1405 * data still in it.
1406 */
1407 TEST_CASE("An overspent budget at the end of the buffer");
1408 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1409 TEST_CHECK(fr_sbuff_advance(&sbuff, 10) > 0);
1410 fr_sbuff_marker(&end, &sbuff);
1412 TEST_CHECK(fr_sbuff_marker_update_end(&end, 4) == fr_sbuff_current(&sbuff));
1413 TEST_CHECK(fr_sbuff_current(&end) == fr_sbuff_end(&sbuff));
1414 fr_sbuff_marker_release(&end);
1415
1416 /*
1417 * Keep the compiler honest about buff being used.
1418 */
1419 fr_sbuff_init_out(&sbuff, buff, sizeof(buff));
1420 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "x") == 1);
1421}
1422
1423/** Check that the budget is measured with fr_sbuff_used_total(), not fr_sbuff_used()
1424 *
1425 * fr_sbuff_shift() moves data out of the buffer and adds the amount which
1426 * fr_sbuff_start() can no longer account for to fr_sbuff_shifted(). So
1427 * fr_sbuff_used_total() does not change across a shift, while fr_sbuff_used()
1428 * shrinks. See test_shift_used_accounting().
1429 *
1430 * fr_sbuff_out_unescape_until() re-runs fr_sbuff_marker_update_end() after every
1431 * extension, and an extension is exactly when a shift happens. Measuring the
1432 * budget with fr_sbuff_used() would therefore reset the budget on every shift,
1433 * and the operation could copy more than it was allowed to.
1434 *
1435 * The budget below is larger than fr_sbuff_used() but smaller than
1436 * fr_sbuff_used_total(), so the two accessors give different answers.
1437 */
1439{
1440 fr_sbuff_t sbuff;
1443 size_t used, used_total;
1444
1445 /*
1446 * The buffer has to have room left in it. fr_sbuff_remaining() is
1447 * what an unclamped overspend gets compared against, so with a full
1448 * buffer "allow nothing more" and "allow everything left" are the
1449 * same position, and the overspend cannot be seen.
1450 */
1451 TEST_CASE("Set up a buffer which has shifted, and which has room left");
1452 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 16, 32) == &sbuff);
1453 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "01234567") == 8);
1454 TEST_CHECK_LEN(fr_sbuff_shift(&sbuff, 4, true), 4);
1455
1456 used = fr_sbuff_used(&sbuff);
1457 used_total = fr_sbuff_used_total(&sbuff);
1458
1459 TEST_CHECK_LEN(used, 4);
1460 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 4);
1461 TEST_CHECK_LEN(used_total, 8);
1462 TEST_CHECK(fr_sbuff_remaining(&sbuff) > 0);
1463
1464 /*
1465 * A budget of 6 is more than the 4 which fr_sbuff_used() reports,
1466 * and less than the 8 which fr_sbuff_used_total() reports.
1467 */
1468 TEST_CASE("A budget between used and used_total is already overspent");
1469 fr_sbuff_marker(&end, &sbuff);
1470 TEST_CHECK(fr_sbuff_marker_update_end(&end, 6) == fr_sbuff_current(&sbuff));
1471 TEST_MSG("marker is %zu past the current position, expected 0. "
1472 "Measuring the budget with fr_sbuff_used() would give 2, "
1473 "and not clamping the overspend would give %zu",
1474 (size_t) (fr_sbuff_current(&end) - fr_sbuff_current(&sbuff)),
1475 fr_sbuff_remaining(&sbuff));
1476 fr_sbuff_marker_release(&end);
1477
1478 /*
1479 * A budget larger than used_total still has room in it, and the
1480 * room left is measured from used_total, not from used.
1481 */
1482 TEST_CASE("A budget larger than used_total leaves used_total's worth of room");
1483 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "89") == 2);
1484 used_total = fr_sbuff_used_total(&sbuff);
1485 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 6);
1486 TEST_CHECK_LEN(used_total, 10);
1487 TEST_CHECK(fr_sbuff_remaining(&sbuff) > 1);
1488
1489 fr_sbuff_marker(&end, &sbuff);
1490 TEST_CHECK(fr_sbuff_marker_update_end(&end, used_total + 1) == fr_sbuff_current(&sbuff) + 1);
1491 TEST_MSG("marker is %zu past the current position, expected 1. "
1492 "Measuring the budget with fr_sbuff_used() would give 5",
1493 (size_t) (fr_sbuff_current(&end) - fr_sbuff_current(&sbuff)));
1494 fr_sbuff_marker_release(&end);
1495
1496 talloc_free(sbuff.buff);
1497}
1498
1499static void test_file_extend(void)
1500{
1501 fr_sbuff_t sbuff;
1502 fr_sbuff_t our_sbuff, child_sbuff;
1504 FILE *fp;
1505 char buff[5];
1506 char out[24];
1507 char fbuff[24];
1508 const char PATTERN[] = "xyzzy";
1509#define PATTERN_LEN (sizeof(PATTERN) - 1)
1510 char *post_ws;
1511 ssize_t slen;
1512
1513 static_assert(sizeof(buff) >= PATTERN_LEN, "Buffer must be sufficiently large to hold the pattern");
1514 static_assert((sizeof(fbuff) % sizeof(buff)) > 0, "sizeof buff must not be a multiple of fbuff");
1515 static_assert((sizeof(fbuff) % sizeof(buff)) < PATTERN_LEN, "remainder of sizeof(fbuff)/sizeof(buff) must be less than sizeof pattern");
1516
1517 TEST_CASE("Initialization");
1518 memset(fbuff, ' ', sizeof(fbuff));
1519 memcpy(fbuff + sizeof(fbuff) - PATTERN_LEN, PATTERN, PATTERN_LEN);
1520
1521 fp = fmemopen(fbuff, sizeof(fbuff), "r");
1522#ifdef __clang_analyzer__
1523 if (fp == NULL) return;
1524#endif
1525
1526 TEST_CHECK(fp != NULL);
1527 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1528 our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1529
1530 TEST_CASE("Advance past whitespace, which will require shift/extend");
1531 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&our_sbuff, SIZE_MAX, NULL), sizeof(fbuff) - PATTERN_LEN);
1532 TEST_CASE("Verify extend on unused child buffer");
1533 child_sbuff = FR_SBUFF(&our_sbuff);
1534 slen = fr_sbuff_extend_file(NULL, &child_sbuff, 0);
1535 TEST_CHECK_SLEN(slen, sizeof(fbuff) % PATTERN_LEN);
1536 TEST_CASE("Verify that we passed all and only whitespace");
1537 (void) fr_sbuff_out_abstrncpy(NULL, &post_ws, &our_sbuff, 24);
1538 TEST_CHECK_STRCMP(post_ws, PATTERN);
1539 talloc_free(post_ws);
1540 TEST_CASE("Verify parent buffer end");
1541 TEST_CHECK(sbuff.end == our_sbuff.end);
1542
1543 TEST_CASE("Verify that we do not read shifted buffer past eof");
1544 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1545 TEST_CHECK_SLEN(slen, 0);
1546 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1547 TEST_CHECK_SLEN(slen, 0);
1548 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, NULL, NULL);
1549 TEST_CHECK_SLEN(slen, 0);
1550 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, allow_lowercase_and_space);
1551 TEST_CHECK_SLEN(slen, 0);
1552
1553 fclose(fp);
1554
1555 TEST_CASE("Verify fr_sbuff_out_bstrncpy_until() extends from file properly");
1556 fp = fmemopen(fbuff, sizeof(fbuff), "r");
1557#ifdef __clang_analyzer__
1558 if (fp == NULL) return;
1559#endif
1560
1561 TEST_CHECK(fp != NULL);
1562 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1563 our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1564 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, &FR_SBUFF_TERM("x"), NULL);
1565 TEST_CHECK_SLEN(slen, sizeof(fbuff) - PATTERN_LEN);
1566
1567 fclose(fp);
1568}
1569
1570static void test_file_extend_max(void)
1571{
1572 fr_sbuff_t sbuff;
1574 FILE *fp;
1575 char buff[16];
1576 char fbuff[] = " xyzzy";
1577 char *post_ws;
1578
1579 TEST_CASE("Initialization");
1580 fp = fmemopen(fbuff, sizeof(fbuff) - 1, "r");
1581#ifdef __clang_analyzer__
1582 if (fp == NULL) return;
1583#endif
1584 TEST_CHECK(fp != NULL);
1585 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, sizeof(fbuff) - 8) == &sbuff);
1586
1587 TEST_CASE("Confirm that max stops us from seeing xyzzy");
1588 TEST_CHECK_SLEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), sizeof(fbuff) - 8);
1589 TEST_CHECK_SLEN(fr_sbuff_out_abstrncpy(NULL, &post_ws, &sbuff, 24), 0);
1590 TEST_CHECK_STRCMP(post_ws, "");
1591 talloc_free(post_ws);
1592 fclose(fp);
1593}
1594
1595static void test_adv_past_str(void)
1596{
1597 fr_sbuff_t sbuff;
1598 char const in[] = "i am a test string";
1599
1600 TEST_CASE("Check for token at beginning of string");
1601 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1602 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 6);
1603 TEST_CHECK_STRCMP(sbuff.p, " test string");
1604
1605 TEST_CASE("Check for token not at beginning of string");
1606 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1607 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, " am a", SIZE_MAX), 0);
1608 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1609
1610 TEST_CASE("Check for token larger than the string");
1611 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1612 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string ", SIZE_MAX), 0);
1613 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1614
1615 TEST_CASE("Check for token with zero length string");
1616 fr_sbuff_init_in(&sbuff, in, 0);
1617 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 0);
1618
1619 TEST_CASE("Check for token that is the string");
1620 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1621 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string", SIZE_MAX), 18);
1622 TEST_CHECK_STRCMP(sbuff.p, "");
1623 TEST_CHECK(sbuff.p == sbuff.end);
1624}
1625
1626static void test_adv_past_strcase(void)
1627{
1628 fr_sbuff_t sbuff;
1629 char const in[] = "i am a test string";
1630
1631 TEST_CASE("Check for token at beginning of string");
1632 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1633 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 6);
1634 TEST_CHECK_STRCMP(sbuff.p, " test string");
1635
1636 TEST_CASE("Check for token not at beginning of string");
1637 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1638 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, " AM a", SIZE_MAX), 0);
1639 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1640
1641 TEST_CASE("Check for token larger than the string");
1642 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1643 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string ", SIZE_MAX), 0);
1644 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1645
1646 TEST_CASE("Check for token with zero length string");
1647 fr_sbuff_init_in(&sbuff, in, 0);
1648 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 0);
1649
1650 TEST_CASE("Check for token that is the string");
1651 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1652 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string", SIZE_MAX), 18);
1653 TEST_CHECK_STRCMP(sbuff.p, "");
1654 TEST_CHECK(sbuff.p == sbuff.end);
1655}
1656
1658{
1659 fr_sbuff_t sbuff;
1660 char const in[] = " i am a test string";
1661 char const in_ns[] = "i am a test string";
1662 char const in_ws[] = " ";
1663
1664 TEST_CASE("Check for token at beginning of string");
1665 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1666 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1667 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1668
1669 TEST_CASE("Check for token not at beginning of string");
1670 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1671 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1672 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1673
1674 TEST_CASE("Check for token with zero length string");
1675 fr_sbuff_init_in(&sbuff, in, 0);
1676 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1677
1678 TEST_CASE("Check for token that is the string");
1679 fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1680 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1681
1682 TEST_CASE("Length constraint with token match");
1683 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1684 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 2);
1685 TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1686
1687 TEST_CASE("Length constraint without token match");
1688 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1689 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 0);
1690 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1691}
1692
1693static void test_adv_past_allowed(void)
1694{
1695 fr_sbuff_t sbuff;
1696 char const in[] = " i am a test string";
1697 char const in_ns[] = "i am a test string";
1698 char const in_ws[] = " ";
1699
1700 TEST_CASE("Check for token at beginning of string");
1701 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1702 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 5);
1703 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1704
1705 TEST_CASE("Check for token not at beginning of string");
1706 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1707 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1708 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1709
1710 TEST_CASE("Check for token with zero length string");
1711 fr_sbuff_init_in(&sbuff, in, 0);
1712 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1713 TEST_CHECK(sbuff.p == sbuff.start);
1714
1715 TEST_CASE("Check for token at the end of the string");
1716 fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1717 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 5);
1718 TEST_CHECK(sbuff.p == sbuff.end);
1719
1720 TEST_CASE("Length constraint with token match");
1721 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1722 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 2);
1723 TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1724
1725 TEST_CASE("Length constraint with token match");
1726 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1727 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1728 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1729}
1730
1731static void test_adv_until(void)
1732{
1733 fr_sbuff_t sbuff;
1734 char const in[] = " abcdefgh ijklmnopp";
1735
1736 TEST_CASE("Check for token at beginning of string");
1737 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1738 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM(" "), '\0'), 0);
1739 TEST_CHECK_STRCMP(sbuff.p, " abcdefgh ijklmnopp");
1740
1741 TEST_CASE("Check for token not at beginning of string");
1742 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1743 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 1);
1744 TEST_CHECK_STRCMP(sbuff.p, "abcdefgh ijklmnopp");
1745
1746 TEST_CASE("Check for token with zero length string");
1747 fr_sbuff_init_in(&sbuff, in, 0);
1748 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 0);
1749 TEST_CHECK(sbuff.p == sbuff.start);
1750
1751 TEST_CASE("Check for token that is not in the string");
1752 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1753 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("|"), '\0'), 19);
1754 TEST_CHECK(sbuff.p == sbuff.end);
1755
1756 TEST_CASE("Check escapes");
1757 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1758 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("p"), 'o'), 18);
1759 TEST_CHECK_STRCMP(sbuff.p, "p");
1760
1761 TEST_CASE("Check for token that is not in the string with length constraint");
1762 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1763 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, 5, &FR_SBUFF_TERM("|"), '\0'), 5);
1764 TEST_CHECK(sbuff.p == (sbuff.start + 5));
1765}
1766
1767static void test_adv_to_utf8(void)
1768{
1769 fr_sbuff_t sbuff;
1770 char const in[] = "🥺🥺🥺🥺🍪😀";
1771 char *p;
1772
1773 TEST_CASE("Check for token at beginning of string");
1774 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1775 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🥺");
1776 TEST_CHECK(p == sbuff.p);
1777 TEST_CHECK_STRCMP(sbuff.p, "🥺🥺🥺🥺🍪😀");
1778
1779 TEST_CASE("Check for token not at beginning of string");
1780 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1781 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1782 TEST_CHECK(p == (sbuff.start + (sizeof("🥺🥺🥺🥺") - 1)));
1783 TEST_CHECK_STRCMP(p, "🍪😀");
1784
1785 TEST_CASE("Check for token with zero length string");
1786 fr_sbuff_init_in(&sbuff, in, 0);
1787 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1788 TEST_CHECK(p == NULL);
1789 TEST_CHECK(sbuff.start == sbuff.p);
1790
1791 TEST_CASE("Check for token at the end of the string");
1792 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1793 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "😀");
1794 TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1795
1796 TEST_CASE("Check for token not in the string");
1797 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1798 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍆 ");
1799 TEST_CHECK(p == NULL);
1800
1801 TEST_CASE("Check for token at the end of the string within len constraints");
1802 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1803 p = fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 1), "😀");
1804 TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1805
1806 TEST_CASE("Check for token at the end of the string outside len constraints #1");
1807 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1808 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 2), "😀"));
1809 TEST_CHECK(sbuff.p == sbuff.start);
1810
1811 TEST_CASE("Check for token at the end of the string outside len constraints #2");
1812 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1813 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 3), "😀"));
1814 TEST_CHECK(sbuff.p == sbuff.start);
1815
1816 TEST_CASE("Check for token at the end of the string outside len constraints #3");
1817 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1818 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 4), "😀"));
1819 TEST_CHECK(sbuff.p == sbuff.start);
1820
1821 TEST_CASE("Check for token at the end of the string outside len constraints #4");
1822 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1823 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 5), "😀"));
1824 TEST_CHECK(sbuff.p == sbuff.start);
1825}
1826
1827static void test_adv_to_chr(void)
1828{
1829 fr_sbuff_t sbuff;
1830 char const in[] = "AAAAbC";
1831 char *p;
1832
1833 TEST_CASE("Check for token at beginning of string");
1834 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1835 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'A');
1836 TEST_CHECK(p == sbuff.p);
1837 TEST_CHECK_STRCMP(sbuff.p, "AAAAbC");
1838
1839 TEST_CASE("Check for token not at beginning of string");
1840 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1841 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b');
1842 TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1843 TEST_CHECK_STRCMP(p, "bC");
1844
1845 TEST_CASE("Check for token with zero length string");
1846 fr_sbuff_init_in(&sbuff, in, 0);
1847 TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b'));
1848 TEST_CHECK(sbuff.start == sbuff.p);
1849
1850 TEST_CASE("Check for token at the end of the string");
1851 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1852 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'C');
1853 TEST_CHECK(p == sbuff.start + (sizeof("AAAAb") - 1));
1854
1855 TEST_CASE("Check for token not in the string");
1856 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1857 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'D');
1858 TEST_CHECK(p == NULL);
1859
1860 TEST_CASE("Check for token not at beginning of string within length constraints");
1861 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1862 p = fr_sbuff_adv_to_chr(&sbuff, 5, 'b');
1863 TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1864 TEST_CHECK_STRCMP(p, "bC");
1865
1866 TEST_CASE("Check for token not at beginning of string outside length constraints");
1867 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1868 TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, 4, 'b'));
1869 TEST_CHECK(sbuff.p == sbuff.start);
1870}
1871
1872static void test_adv_to_str(void)
1873{
1874 fr_sbuff_t sbuff;
1875 char const in[] = "i am a test string";
1876 char *p;
1877
1878 TEST_CASE("Check for token at beginning of string");
1879 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1880 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test", SIZE_MAX);
1881 TEST_CHECK(sbuff.p == p);
1882 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1883
1884 TEST_CASE("Check for token not at beginning of string");
1885 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1886 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "test", SIZE_MAX);
1887 TEST_CHECK(sbuff.p == p);
1888 TEST_CHECK_STRCMP(sbuff.p, "test string");
1889
1890 TEST_CASE("Check for token at the end of string");
1891 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1892 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "ing", SIZE_MAX);
1893 TEST_CHECK(sbuff.p == p);
1894 TEST_CHECK_STRCMP(sbuff.p, "ing");
1895
1896 TEST_CASE("Check for token larger than the string");
1897 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1898 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string ", SIZE_MAX);
1899 TEST_CHECK(sbuff.p == sbuff.start);
1900 TEST_CHECK(p == NULL);
1901
1902 TEST_CASE("Check for token shorter than string, not in the string");
1903 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1904 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "ng ", SIZE_MAX);
1905 TEST_CHECK(sbuff.p == sbuff.start);
1906 TEST_CHECK(p == NULL);
1907
1908 TEST_CASE("Check for token with zero length string");
1909 fr_sbuff_init_in(&sbuff, in, 0);
1910 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a", SIZE_MAX);
1911 TEST_CHECK(sbuff.p == sbuff.start);
1912 TEST_CHECK(p == NULL);
1913
1914 TEST_CASE("Check for token that is the string");
1915 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1916 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string", SIZE_MAX);
1917 TEST_CHECK(sbuff.p == sbuff.start);
1918 TEST_CHECK(sbuff.p == p);
1919 TEST_CHECK_STRCMP(p, "i am a test string");
1920
1921 TEST_CASE("Check for token not at beginning of string within length constraints");
1922 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1923 p = fr_sbuff_adv_to_str(&sbuff, 11, "test", SIZE_MAX);
1924 TEST_CHECK(sbuff.p == p);
1925 TEST_CHECK_STRCMP(sbuff.p, "test string");
1926
1927 TEST_CASE("Check for token not at beginning of string outside length constraints");
1928 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1929 TEST_CHECK(!fr_sbuff_adv_to_str(&sbuff, 10, "test", SIZE_MAX));
1930 TEST_CHECK(sbuff.p == sbuff.start);
1931}
1932
1933static void test_adv_to_strcase(void)
1934{
1935 fr_sbuff_t sbuff;
1936 char const in[] = "i am a test string";
1937 char *p;
1938
1939 TEST_CASE("Check for token at beginning of string");
1940 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1941 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a TEST", SIZE_MAX);
1942 TEST_CHECK(sbuff.p == p);
1943 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1944
1945 TEST_CASE("Check for token not at beginning of string");
1946 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1947 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "tEst", SIZE_MAX);
1948 TEST_CHECK(sbuff.p == p);
1949 TEST_CHECK_STRCMP(sbuff.p, "test string");
1950
1951 TEST_CASE("Check for token at the end of string");
1952 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1953 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "Ing", SIZE_MAX);
1954 TEST_CHECK(sbuff.p == p);
1955 TEST_CHECK_STRCMP(sbuff.p, "ing");
1956
1957 TEST_CASE("Check for token larger than the string");
1958 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1959 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i aM a tEst stRIng ", SIZE_MAX);
1960 TEST_CHECK(sbuff.p == sbuff.start);
1961 TEST_CHECK(p == NULL);
1962
1963 TEST_CASE("Check for token shorter than string, not in the string");
1964 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1965 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "nG ", SIZE_MAX);
1966 TEST_CHECK(sbuff.p == sbuff.start);
1967 TEST_CHECK(p == NULL);
1968
1969 TEST_CASE("Check for token with zero length string");
1970 fr_sbuff_init_in(&sbuff, in, 0);
1971 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a", SIZE_MAX);
1972 TEST_CHECK(sbuff.p == sbuff.start);
1973 TEST_CHECK(p == NULL);
1974
1975 TEST_CASE("Check for token that is the string");
1976 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1977 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a teST stRIng", SIZE_MAX);
1978 TEST_CHECK(sbuff.p == sbuff.start);
1979 TEST_CHECK(sbuff.p == p);
1980 TEST_CHECK_STRCMP(p, "i am a test string");
1981
1982 TEST_CASE("Check for token not at beginning of string within length constraints");
1983 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1984 p = fr_sbuff_adv_to_strcase(&sbuff, 11, "tEst", SIZE_MAX);
1985 TEST_CHECK(sbuff.p == p);
1986 TEST_CHECK_STRCMP(sbuff.p, "test string");
1987
1988 TEST_CASE("Check for token not at beginning of string outside length constraints");
1989 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1990 TEST_CHECK(!fr_sbuff_adv_to_strcase(&sbuff, 10, "tEst", SIZE_MAX));
1991 TEST_CHECK(sbuff.p == sbuff.start);
1992}
1993
1994static void test_next_if_char(void)
1995{
1996 fr_sbuff_t sbuff;
1997 char const in[] = "i ";
1998
1999 TEST_CASE("Check for advancement on match");
2000 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2001 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == true);
2002 TEST_CHECK_STRCMP(sbuff.p, " ");
2003
2004 TEST_CASE("Check for non-advancement on non-match");
2005 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == false);
2006 TEST_CHECK_STRCMP(sbuff.p, " ");
2007
2008 TEST_CASE("Check for advancement at end");
2009 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == true);
2010 TEST_CHECK_STRCMP(sbuff.p, "");
2011
2012 TEST_CASE("Check we can't advance off the end of the buffer");
2013 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == false);
2014 TEST_CHECK_STRCMP(sbuff.p, "");
2015}
2016
2017static void test_next_unless_char(void)
2018{
2019 fr_sbuff_t sbuff;
2020 char const in[] = "i ";
2021
2022 TEST_CASE("Check for advancement on non-match");
2023 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2024 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == true);
2025 TEST_CHECK_STRCMP(sbuff.p, " ");
2026
2027 TEST_CASE("Check for non-advancement on match");
2028 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == false);
2029 TEST_CHECK_STRCMP(sbuff.p, " ");
2030
2031 TEST_CASE("Check for advancement at end");
2032 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == true);
2033 TEST_CHECK_STRCMP(sbuff.p, "");
2034
2035 TEST_CASE("Check we can't advance off the end of the buffer");
2036 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == false);
2037 TEST_CHECK_STRCMP(sbuff.p, "");
2038}
2039
2041 /*
2042 * Basic tests
2043 */
2044 { "fr_sbuff_init", test_parse_init },
2045 { "fr_sbuff_is_char", test_is_char },
2046 { "fr_sbuff_out_bstrncpy_exact", test_bstrncpy_exact },
2047 { "fr_sbuff_out_bstrncpy", test_bstrncpy },
2048 { "fr_sbuff_out_bstrncpy_allowed", test_bstrncpy_allowed },
2049 { "fr_sbuff_out_bstrncpy_until", test_bstrncpy_until },
2050 { "multi-char terminals", test_unescape_multi_char_terminals },
2051 { "fr_sbuff_out_unescape_until", test_unescape_until },
2052 { "fr_sbuff_out_unescape_until_escape_bounds", test_unescape_until_escape_bounds },
2053 { "fr_sbuff_terminal_eof", test_eof_terminal },
2054 { "terminal search past visible end", test_terminal_search_past_visible_end },
2055 { "terminal search at end no overrun", test_terminal_search_at_end_no_overrun },
2056 { "terminal merge", test_terminal_merge },
2057
2058 /*
2059 * Extending buffer
2060 */
2061 { "fr_sbuff_talloc_extend", test_talloc_extend },
2062 { "fr_sbuff_talloc_extend_init_zero", test_talloc_extend_init_zero },
2063 { "fr_sbuff_talloc_extend_multi_level", test_talloc_extend_multi_level },
2064 { "fr_sbuff_talloc_extend_with_marker", test_talloc_extend_with_marker },
2065 { "fr_sbuff_talloc_extend_with_shift", test_talloc_extend_with_shift},
2066 { "fr_sbuff_shift_used_accounting", test_shift_used_accounting},
2067 { "fr_sbuff_marker_update_end", test_marker_update_end},
2068 { "fr_sbuff_marker_update_end_after_shift", test_marker_update_end_after_shift},
2069 { "fr_sbuff_file_extend", test_file_extend },
2070 { "fr_sbuff_file_extend_max", test_file_extend_max },
2071
2072 { "fr_sbuff_no_advance", test_no_advance },
2073
2074 /*
2075 * Token skipping
2076 */
2077 { "fr_sbuff_adv_past_str", test_adv_past_str },
2078 { "fr_sbuff_adv_past_strcase", test_adv_past_strcase },
2079 { "fr_sbuff_adv_past_whitespace", test_adv_past_whitespace },
2080 { "fr_sbuff_adv_past_allowed", test_adv_past_allowed },
2081 { "fr_sbuff_adv_until", test_adv_until },
2082
2083 /*
2084 * Token searching
2085 */
2086 { "fr_sbuff_adv_to_utf8", test_adv_to_utf8 },
2087 { "fr_sbuff_adv_to_chr", test_adv_to_chr },
2088 { "fr_sbuff_adv_to_str", test_adv_to_str },
2089 { "fr_sbuff_adv_to_strcase", test_adv_to_strcase },
2090
2091 /*
2092 * Advancement
2093 */
2094 { "fr_sbuff_next_if_char", test_next_if_char },
2095 { "fr_sbuff_next_unless_char", test_next_unless_char },
2096
2098};
#define TEST_CHECK(cond)
Definition acutest.h:87
#define TEST_CASE(name)
Definition acutest.h:186
#define TEST_TERMINATOR
Definition acutest.h:64
#define TEST_MSG(...)
Definition acutest.h:217
Common initialisation for acutest binaries.
#define TEST_CHECK_SLEN(_got, _exp)
#define TEST_CHECK_SLEN_RETURN(_got, _exp)
#define TEST_CHECK_LEN(_got, _exp)
#define TEST_CHECK_STRCMP(_got, _exp)
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
static fr_slen_t in
Definition dict.h:906
free(array)
talloc_free(hp)
#define malloc
size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
long int ssize_t
ssize_t fr_sbuff_out_bstrncpy_exact(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, bool const allowed[static SBUFF_CHAR_CLASS])
static size_t used
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static SBUFF_CHAR_CLASS], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition sbuff.c:1883
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len)
Trim a talloced sbuff to the minimum length required to represent the contained string.
Definition sbuff.c:433
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1476
char * fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, size_t len, char const *chr)
Wind position to first instance of specified multibyte utf8 char.
Definition sbuff.c:2017
char * fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len)
Wind position to the first instance of the specified needle.
Definition sbuff.c:2098
char * fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len)
Wind position to the first instance of the specified needle.
Definition sbuff.c:2151
size_t fr_sbuff_extend_file(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension)
Refresh the buffer with more data from the file.
Definition sbuff.c:271
size_t fr_sbuff_adv_past_str(fr_sbuff_t *sbuff, char const *needle, size_t needle_len)
Return true and advance past the end of the needle if needle occurs next in the sbuff.
Definition sbuff.c:1818
size_t fr_sbuff_shift(fr_sbuff_t *sbuff, size_t shift, bool move_end)
Shift the contents of the sbuff, returning the number of bytes we managed to shift.
Definition sbuff.c:201
char * fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c)
Wind position to first instance of specified char.
Definition sbuff.c:2062
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2258
size_t fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t needle_len)
Return true and advance past the end of the needle if needle occurs next in the sbuff.
Definition sbuff.c:1849
bool fr_sbuff_next_unless_char(fr_sbuff_t *sbuff, char c)
Return true and advance if the next char does not match.
Definition sbuff.c:2215
size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, fr_sbuff_term_t const *tt, char escape_chr)
Wind position until we hit a character in the terminal set.
Definition sbuff.c:1958
size_t fr_sbuff_out_bstrncpy(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
Copy as many bytes as possible from a sbuff to a sbuff.
Definition sbuff.c:735
fr_sbuff_term_t * fr_sbuff_terminals_amerge(TALLOC_CTX *ctx, fr_sbuff_term_t const *a, fr_sbuff_term_t const *b)
Merge two sets of terminal strings.
Definition sbuff.c:657
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2194
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_shifted(_sbuff_or_marker)
#define SBUFF_CHAR_CLASS
Definition sbuff.h:203
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_BIND_CURRENT(_sbuff_or_marker)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
char const * str
Terminal string.
Definition sbuff.h:160
#define fr_sbuff_current(_sbuff_or_marker)
char chr
Character at the start of an escape sequence.
Definition sbuff.h:211
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:190
#define fr_sbuff_used_total(_sbuff_or_marker)
#define fr_sbuff_init_out(_out, _start, _len_or_end)
size_t len
Length of the list.
Definition sbuff.h:170
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define fr_sbuff_end(_sbuff_or_marker)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_init_in(_out, _start, _len_or_end)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
fr_sbuff_term_elem_t * elem
A sorted list of terminal strings.
Definition sbuff.h:171
#define FR_SBUFF_TERM(_str)
Initialise a terminal structure with a single string.
Definition sbuff.h:178
Set of terminal elements.
File sbuff extension structure.
Definition sbuff.h:148
Talloc sbuff extension structure.
Definition sbuff.h:137
Set of parsing rules for *unescape_until functions.
static void test_adv_past_allowed(void)
static void test_terminal_search_past_visible_end(void)
TEST_LIST
static void test_no_advance(void)
static void test_marker_update_end(void)
static void test_file_extend_max(void)
static void test_talloc_extend_multi_level(void)
static void test_adv_past_strcase(void)
static void test_shift_used_accounting(void)
static void test_adv_to_strcase(void)
static void test_talloc_extend(void)
static void test_bstrncpy_until(void)
static void test_bstrncpy_allowed(void)
static bool allow_lowercase_and_space_no_t[SBUFF_CHAR_CLASS]
#define TEST_SBUFF_USED(_sbuff, _num)
Definition sbuff_tests.c:37
static void test_adv_until(void)
static void test_is_char(void)
Definition sbuff_tests.c:73
static void test_bstrncpy(void)
static void test_bstrncpy_exact(void)
static void test_adv_to_utf8(void)
static void test_marker_update_end_after_shift(void)
Check that the budget is measured with fr_sbuff_used_total(), not fr_sbuff_used()
static void test_adv_to_chr(void)
static void test_eof_terminal(void)
static void test_next_unless_char(void)
static void test_adv_past_whitespace(void)
static void test_next_if_char(void)
static void test_terminal_search_at_end_no_overrun(void)
static void test_unescape_until(void)
static void test_adv_past_str(void)
static void test_talloc_extend_with_shift(void)
static void test_unescape_multi_char_terminals(void)
#define TEST_SBUFF_LEN(_sbuff, _num)
Definition sbuff_tests.c:28
static void test_file_extend(void)
static void test_terminal_merge(void)
static void test_parse_init(void)
Definition sbuff_tests.c:46
static bool allow_lowercase_and_space[SBUFF_CHAR_CLASS]
static void test_adv_to_str(void)
static void test_talloc_extend_init_zero(void)
static void test_talloc_extend_with_marker(void)
#define PATTERN_LEN
static void test_unescape_until_escape_bounds(void)
Check that a hex or octal escape sequence cannot consume more input than "len" allows.
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
static size_t char ** out
Definition value.h:1062