The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
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/*
29 * The clang static analyser does not inline acutest_check_(), which
30 * is variadic and large, so the analyser cannot see that TEST_ASSERT()
31 * aborts the test. The analyser then follows the path where the
32 * allocation failed into the rest of the test and reports arithmetic
33 * on the zeroed buffer. MEM() exits through a function that the
34 * analyser models as never returning, so MEM() wraps the talloc
35 * buffer inits below instead of TEST_ASSERT().
36 */
37
38#define TEST_SBUFF_LEN(_sbuff, _num) \
39do { \
40 size_t _len; \
41 _len = talloc_array_length((_sbuff)->buff); \
42 TEST_CHECK(_len == (size_t)_num); \
43 TEST_MSG("Expected length : %zu", (size_t)_num); \
44 TEST_MSG("Got length : %zu", _len); \
45} while(0)
46
47#define TEST_SBUFF_USED(_sbuff, _num) \
48do { \
49 size_t _len; \
50 _len = fr_sbuff_used(_sbuff); \
51 TEST_CHECK(_len == (size_t)_num); \
52 TEST_MSG("Expected length : %zu", (size_t)_num); \
53 TEST_MSG("Got length : %zu", _len); \
54} while(0)
55
56static void test_parse_init(void)
57{
58 char const in[] = "i am a test string";
59 fr_sbuff_t sbuff;
60
61 TEST_CASE("Parse init with size");
62 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
63
64 TEST_CHECK(sbuff.start == in);
65 TEST_CHECK(sbuff.p == in);
66 TEST_CHECK(sbuff.end == in + (sizeof(in) - 1));
67
68 TEST_CASE("Parse init with end");
69 fr_sbuff_init_in(&sbuff, in, in + strlen(in));
70
71 TEST_CHECK(sbuff.start == in);
72 TEST_CHECK(sbuff.p == in);
73 TEST_CHECK(sbuff.end == in + strlen(in));
74
75 TEST_CASE("Parse init with const end");
76 fr_sbuff_init_in(&sbuff, in, (char const *)(in + strlen(in)));
77
78 TEST_CHECK(sbuff.start == in);
79 TEST_CHECK(sbuff.p == in);
80 TEST_CHECK(sbuff.end == in + strlen(in));
81}
82
83static void test_is_char(void)
84{
85 char const in[] = "i am a test string";
86 fr_sbuff_t sbuff;
87 fr_sbuff_marker_t marker;
88
89 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
90 TEST_CHECK(fr_sbuff_is_char(&sbuff, 'i') == true);
91 TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'z'));
92
93 fr_sbuff_advance(&sbuff, 2);
94 TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'i'));
95 TEST_CHECK(fr_sbuff_is_char(&sbuff, 'a') == true);
96
97 fr_sbuff_advance(&sbuff, 15);
98 TEST_CHECK(fr_sbuff_is_char(&sbuff, 'g') == true);
99 fr_sbuff_marker(&marker, &sbuff);
100 TEST_CHECK(fr_sbuff_is_char(&marker, 'g') == true);
101
102 /*
103 * Ensure that after advancing the buffer past
104 * the end, the marker can still be correctly
105 * tested
106 */
107 fr_sbuff_advance(&sbuff, 1);
108 TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'g'));
109 TEST_CHECK(fr_sbuff_is_char(&marker, 'g') == true);
110}
111
112static void test_bstrncpy_exact(void)
113{
114 char const in[] = "i am a test string";
115 char const in_long[] = "i am a longer test string";
116 char out[18 + 1] = "";
117 char out_long[32 + 1] = "";
118 fr_sbuff_t sbuff;
119 fr_sbuff_err_t ret;
120
121 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
122
123 TEST_CASE("Copy 5 bytes to out");
124 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5);
126 TEST_CHECK_STRCMP(out, "i am ");
127 TEST_CHECK_STRCMP(sbuff.p, "a test string");
128
129 TEST_CASE("Copy 13 bytes to out");
130 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13);
132 TEST_CHECK_STRCMP(out, "a test string");
133 TEST_CHECK_STRCMP(sbuff.p, "");
134 TEST_CHECK(sbuff.p == sbuff.end);
135
136 TEST_CASE("Copy would overrun input");
137 out[0] = 'a';
138 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1);
140 TEST_CHECK(out[0] == '\0'); /* nothing left in out */
141 TEST_CHECK(sbuff.p == sbuff.end);
142
143 TEST_CASE("Copy would overrun input part way through");
144 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
145 fr_sbuff_advance(&sbuff, 12);
146 out[0] = 'a';
147 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 7);
149 TEST_CHECK(out[0] == '\0'); /* partial copy removed */
150 TEST_CHECK_STRCMP(sbuff.p, "string"); /* input not advanced */
151
152 TEST_CASE("Copy would overrun output");
153 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
154 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX);
156 TEST_CHECK(out[0] == '\0'); /* partial copy removed */
157 TEST_CHECK(sbuff.p == sbuff.start);
158
159 TEST_CASE("SIZE_MAX copies the whole input");
160 fr_sbuff_set_to_start(&sbuff);
161 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out_long, sizeof(out_long)), &sbuff, SIZE_MAX);
163 TEST_CHECK_STRCMP(out_long, in_long);
164 TEST_CHECK(sbuff.p == sbuff.end);
165
166 TEST_CASE("Zero length output buffer");
167 fr_sbuff_set_to_start(&sbuff);
168 out[0] = 'a';
169 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX);
171 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
172 TEST_CHECK(sbuff.p == sbuff.start);
173
174 TEST_CASE("Zero length size");
175 fr_sbuff_set_to_start(&sbuff);
176 out[0] = 'a';
177 ret = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, 0);
179 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
180 TEST_CHECK(sbuff.p == sbuff.start);
181}
182
183static void test_bstrncpy(void)
184{
185 char const in[] = "i am a test string";
186 char const in_long[] = "i am a longer test string";
187 char out[18 + 1] = "";
188 fr_sbuff_t sbuff;
189 fr_sbuff_err_t ret;
190 size_t len;
191
192 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
193
194 TEST_CASE("Copy 5 bytes to out");
195 ret = fr_sbuff_out_bstrncpy(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5);
198 TEST_CHECK_STRCMP(out, "i am ");
199 TEST_CHECK_STRCMP(sbuff.p, "a test string");
200
201 TEST_CASE("Copy 13 bytes to out");
202 ret = fr_sbuff_out_bstrncpy(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13);
204 TEST_CHECK_LEN(len, 13);
205 TEST_CHECK_STRCMP(out, "a test string");
206 TEST_CHECK_STRCMP(sbuff.p, "");
207 TEST_CHECK(sbuff.p == sbuff.end);
208
209 TEST_CASE("Copy would overrun input");
210 ret = fr_sbuff_out_bstrncpy(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1);
213 TEST_CHECK(sbuff.p == sbuff.end);
214
215 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
216 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
217
218 ret = fr_sbuff_out_bstrncpy(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX);
220 TEST_CHECK_LEN(len, 18);
221 TEST_CHECK_STRCMP(out, "i am a longer test");
222 TEST_CHECK_STRCMP(sbuff.p, " string"); /* Left at the first byte not copied */
223
224 TEST_CASE("Zero length output buffer");
225 fr_sbuff_set_to_start(&sbuff);
226 out[0] = 'a';
227 ret = fr_sbuff_out_bstrncpy(&len, &FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX);
230 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
231 TEST_CHECK(sbuff.p == sbuff.start);
232
233 TEST_CASE("Zero length size");
234 fr_sbuff_set_to_start(&sbuff);
235 out[0] = 'a';
236 ret = fr_sbuff_out_bstrncpy(&len, &FR_SBUFF_OUT(out, (size_t)1), &sbuff, 0);
239 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
240 TEST_CHECK(sbuff.p == sbuff.start);
241
242 TEST_CASE("Length may be NULL");
243 fr_sbuff_set_to_start(&sbuff);
244 ret = fr_sbuff_out_bstrncpy(NULL, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5);
246 TEST_CHECK_STRCMP(out, "i am ");
247}
248
250 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
251 ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
252 ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
253 ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true,
254 ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
255 ['z'] = true, [' '] = true
256};
257
259 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
260 ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
261 ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
262 ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = false,
263 ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
264 ['z'] = true, [' '] = true
265};
266
267static void test_bstrncpy_allowed(void)
268{
269 char const in[] = "i am a test string";
270 char const in_long[] = "i am a longer test string";
271 char out[18 + 1] = "";
272 fr_sbuff_t sbuff;
273 fr_sbuff_err_t ret;
274 size_t len;
275
276 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
277
278 /*
279 * Should behave identically to bstrncpy
280 * where there's no restrictions on char
281 * set.
282 */
283 TEST_CASE("Copy 5 bytes to out");
287 TEST_CHECK_STRCMP(out, "i am ");
288 TEST_CHECK_STRCMP(sbuff.p, "a test string");
289
290 TEST_CASE("Copy 13 bytes to out");
293 TEST_CHECK_LEN(len, 13);
294 TEST_CHECK_STRCMP(out, "a test string");
295 TEST_CHECK_STRCMP(sbuff.p, "");
296 TEST_CHECK(sbuff.p == sbuff.end);
297
298 TEST_CASE("Copy would overrun input");
302 TEST_CHECK(sbuff.p == sbuff.end);
303
304 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
305 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long));
306
307 ret = fr_sbuff_out_bstrncpy_allowed(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, allow_lowercase_and_space);
309 TEST_CHECK_LEN(len, 18);
310 TEST_CHECK_STRCMP(out, "i am a longer test");
311
312 TEST_CASE("Zero length output buffer");
313 fr_sbuff_set_to_start(&sbuff);
314 out[0] = 'a';
315 ret = fr_sbuff_out_bstrncpy_allowed(&len, &FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, allow_lowercase_and_space);
318 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
319 TEST_CHECK(sbuff.p == sbuff.start);
320
321 TEST_CASE("Zero length size");
322 fr_sbuff_set_to_start(&sbuff);
323 out[0] = 'a';
327 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
328 TEST_CHECK(sbuff.p == sbuff.start);
329
330 /*
331 * Check copy stops early
332 */
333 TEST_CASE("Copy until first t");
334 fr_sbuff_set_to_start(&sbuff);
335 ret = fr_sbuff_out_bstrncpy_allowed(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
338 TEST_CHECK_LEN(len, 14);
339 TEST_CHECK_STRCMP(out, "i am a longer ");
340
341 TEST_CASE("Copy until first t with length constraint (same len as token)");
342 fr_sbuff_set_to_start(&sbuff);
343 ret = fr_sbuff_out_bstrncpy_allowed(&len, &FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX,
346 TEST_CHECK_LEN(len, 14);
347 TEST_CHECK_STRCMP(out, "i am a longer ");
348
349 TEST_CASE("Copy until first t with length constraint (one shorter than token)");
350 fr_sbuff_set_to_start(&sbuff);
351 ret = fr_sbuff_out_bstrncpy_allowed(&len, &FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
354 TEST_CHECK_LEN(len, 13);
355 TEST_CHECK_STRCMP(out, "i am a longer");
356
357 TEST_CASE("Zero length token (should still be terminated)");
358 fr_sbuff_set_to_start(&sbuff);
359 ret = fr_sbuff_out_bstrncpy_allowed(&len, &FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
360 (bool[SBUFF_CHAR_CLASS]){});
364}
365
366static void test_bstrncpy_until(void)
367{
368 char const in[] = "i am a test string";
369 char const in_long[] = "i am a longer test string";
370 char out[18 + 1] = "";
371 fr_sbuff_t sbuff;
372 fr_sbuff_err_t ret;
373 size_t len;
374
375 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
376
377 /*
378 * Should behave identically to bstrncpy
379 * where there's no restrictions on char
380 * set.
381 */
382 TEST_CASE("Copy 5 bytes to out");
383 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, NULL);
386 TEST_CHECK_STRCMP(out, "i am ");
387 TEST_CHECK_STRCMP(sbuff.p, "a test string");
388
389 TEST_CASE("Copy 13 bytes to out");
390 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13, NULL, NULL);
392 TEST_CHECK_LEN(len, 13);
393 TEST_CHECK_STRCMP(out, "a test string");
394 TEST_CHECK_STRCMP(sbuff.p, "");
395 TEST_CHECK(sbuff.p == sbuff.end);
396
397 TEST_CASE("Copy would overrun input");
398 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1, NULL, NULL);
401 TEST_CHECK(sbuff.p == sbuff.end);
402
403 TEST_CASE("Check escapes");
404 fr_sbuff_set_to_start(&sbuff);
405 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
406 &FR_SBUFF_TERM("g"), &(fr_sbuff_unescape_rules_t){ .chr = 'n' });
408 TEST_CHECK_LEN(len, 18);
409 TEST_CHECK_STRCMP(out, "i am a test string");
410 TEST_CHECK_STRCMP(sbuff.p, "");
411
412 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
413 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
414
415 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, NULL);
417 TEST_CHECK_LEN(len, 18);
418 TEST_CHECK_STRCMP(out, "i am a longer test");
419
420 TEST_CASE("Zero length output buffer");
421 fr_sbuff_set_to_start(&sbuff);
422 out[0] = 'a';
423 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, NULL, NULL);
426 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
427 TEST_CHECK(sbuff.p == sbuff.start);
428
429 TEST_CASE("Zero length size");
430 fr_sbuff_set_to_start(&sbuff);
431 out[0] = 'a';
432 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 0, NULL, NULL);
435 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
436 TEST_CHECK(sbuff.p == sbuff.start);
437
438 /*
439 * Check copy stops early
440 */
441 TEST_CASE("Copy until first t");
442 fr_sbuff_set_to_start(&sbuff);
443 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
445 TEST_CHECK_LEN(len, 14);
446 TEST_CHECK_STRCMP(out, "i am a longer ");
447
448 TEST_CASE("Copy until first t with length constraint (same len as token)");
449 fr_sbuff_set_to_start(&sbuff);
450 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
452 TEST_CHECK_LEN(len, 14);
453 TEST_CHECK_STRCMP(out, "i am a longer ");
454
455 TEST_CASE("Copy until first t with length constraint (one shorter than token)");
456 fr_sbuff_set_to_start(&sbuff);
457 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
459 TEST_CHECK_LEN(len, 13);
460 TEST_CHECK_STRCMP(out, "i am a longer");
461
462 TEST_CASE("Zero length token (should still be terminated)");
463 fr_sbuff_set_to_start(&sbuff);
464 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("i"), NULL);
468}
469
470static void test_unescape_until(void)
471{
472 char const in[] = "i am a test string";
473 char const in_long[] = "i am a longer test string";
474 char const in_escapes[] = "i am a |t|est strin|g";
475 char const in_escapes_seq[] = "i |x|0am a |t|est strin|g|x20|040";
476 char out[18 + 1] = "";
477 char escape_out[20 + 1];
478
479 fr_sbuff_t sbuff;
480 fr_sbuff_err_t ret;
481 size_t len;
482
484 .chr = '\\'
485 };
486
487 fr_sbuff_unescape_rules_t pipe_rules = {
488 .chr = '|',
489 .subs = { ['g'] = 'g', ['|'] = '|' }
490 };
491
492 fr_sbuff_unescape_rules_t pipe_rules_sub = {
493 .chr = '|', .subs = { ['g'] = 'h', ['|'] = '|' }
494 };
495
496 fr_sbuff_unescape_rules_t pipe_rules_sub_hex = {
497 .chr = '|',
498 .subs = { ['g'] = 'h', ['|'] = '|' },
499 .do_hex = true
500 };
501
502 fr_sbuff_unescape_rules_t pipe_rules_sub_oct = {
503 .chr = '|',
504 .subs = { ['g'] = 'h', ['|'] = '|' },
505 .do_oct = true
506 };
507
508 fr_sbuff_unescape_rules_t pipe_rules_both = {
509 .chr = '|',
510 .subs = { ['g'] = 'h', ['|'] = '|' },
511 .do_hex = true,
512 .do_oct = true
513 };
514
515 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
516 /*
517 * Should behave identically to bstrncpy
518 * where there's no restrictions on char
519 * set.
520 */
521 TEST_CASE("Copy 5 bytes to out");
522 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, &rules);
525 TEST_CHECK_STRCMP(out, "i am ");
526 TEST_CHECK_STRCMP(sbuff.p, "a test string");
527
528 TEST_CASE("Copy 13 bytes to out");
529 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13, NULL, &rules);
531 TEST_CHECK_LEN(len, 13);
532 TEST_CHECK_STRCMP(out, "a test string");
533 TEST_CHECK_STRCMP(sbuff.p, "");
534 TEST_CHECK(sbuff.p == sbuff.end);
535
536 TEST_CASE("Copy would overrun input");
537 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1, NULL, &rules);
540 TEST_CHECK(sbuff.p == sbuff.end);
541
542 TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
543 fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
544
545 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, &rules);
547 TEST_CHECK_LEN(len, 18);
548 TEST_CHECK_STRCMP(out, "i am a longer test");
549 TEST_CHECK_STRCMP(sbuff.p, " string"); /* Left at the first byte not copied */
550
551 TEST_CASE("Zero length output buffer");
552 fr_sbuff_set_to_start(&sbuff);
553 out[0] = 'a';
554 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, NULL, &rules);
557 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
558 TEST_CHECK(sbuff.p == sbuff.start);
559
560 TEST_CASE("Zero length size");
561 fr_sbuff_set_to_start(&sbuff);
562 out[0] = 'a';
563 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 0, NULL, &rules);
566 TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
567 TEST_CHECK(sbuff.p == sbuff.start);
568
569 /*
570 * Check copy stops early
571 */
572 TEST_CASE("Copy until first t");
573 fr_sbuff_set_to_start(&sbuff);
574 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
575 &FR_SBUFF_TERM("t"), &rules);
577 TEST_CHECK_LEN(len, 14);
578 TEST_CHECK_STRCMP(out, "i am a longer ");
579
580 TEST_CASE("Copy until first t with length constraint (same len as token)");
581 fr_sbuff_set_to_start(&sbuff);
582 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX,
583 &FR_SBUFF_TERM("t"), &rules);
585 TEST_CHECK_LEN(len, 14);
586 TEST_CHECK_STRCMP(out, "i am a longer ");
587
588 TEST_CASE("Copy until first t with length constraint (one shorter than token)");
589 fr_sbuff_set_to_start(&sbuff);
590 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
591 &FR_SBUFF_TERM("t"), &rules);
593 TEST_CHECK_LEN(len, 13);
594 TEST_CHECK_STRCMP(out, "i am a longer");
595
596 TEST_CASE("Zero length token (should still be terminated)");
597 fr_sbuff_set_to_start(&sbuff);
598 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
599 &FR_SBUFF_TERM("i"), &rules);
603
604 /*
605 * Escapes and substitution
606 */
607 TEST_CASE("Escape with substitution to same char");
608 fr_sbuff_init_in(&sbuff, in_escapes, sizeof(in_escapes) - 1);
609 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX,
610 &FR_SBUFF_TERM("g"), &pipe_rules);
612 TEST_CHECK_LEN(len, 20);
613 TEST_CHECK_STRCMP(escape_out, "i am a |t|est string");
614 TEST_CHECK_STRCMP(sbuff.p, "");
615
616 TEST_CASE("Escape with substitution to different char");
617 fr_sbuff_init_in(&sbuff, in_escapes, sizeof(in_escapes) - 1);
618 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX,
619 &FR_SBUFF_TERM("g"), &pipe_rules_sub);
621 TEST_CHECK_LEN(len, 20);
622 TEST_CHECK_STRCMP(escape_out, "i am a |t|est strinh");
623 TEST_CHECK_STRCMP(sbuff.p, "");
624
625 {
626 char tmp_out[24 + 1];
627
628 TEST_CASE("Escape with hex substitutions (insufficient output space)");
629 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
630 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
631 &FR_SBUFF_TERM("g"), &pipe_rules_sub_hex);
633 TEST_CHECK_LEN(len, 24);
634 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh");
635 TEST_CHECK_STRCMP(sbuff.p, "|x20|040");
636 }
637
638 {
639 char tmp_out[25 + 1];
640
641 /*
642 * Space for the hex escape, but not the trailing
643 * octal sequence which is copied literally.
644 */
645 TEST_CASE("Escape with hex substitutions (sufficient output space for the hex escape)");
646 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
647 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
648 &FR_SBUFF_TERM("g"), &pipe_rules_sub_hex);
650 TEST_CHECK_LEN(len, 25);
651 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh ");
652 TEST_CHECK_STRCMP(sbuff.p, "|040");
653 }
654
655 {
656 char tmp_out[28 + 1];
657
658 TEST_CASE("Escape with oct substitutions (insufficient output space)");
659 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
660 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
661 &FR_SBUFF_TERM("g"), &pipe_rules_sub_oct);
663 TEST_CHECK_LEN(len, 28);
664 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh|x20");
665 TEST_CHECK_STRCMP(sbuff.p, "|040");
666 }
667
668 {
669 char tmp_out[29 + 1];
670
671 TEST_CASE("Escape with oct substitutions (sufficient output space)");
672 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
673 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
674 &FR_SBUFF_TERM("g"), &pipe_rules_sub_oct);
676 TEST_CHECK_LEN(len, 29);
677 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh|x20 ");
678 TEST_CHECK_STRCMP(sbuff.p, "");
679 }
680
681 {
682 char tmp_out[26 + 1];
683
684 TEST_CASE("Escape with hex and oct substitutions (sufficient output space)");
685 fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
686 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
687 &FR_SBUFF_TERM("g"), &pipe_rules_both);
689 TEST_CHECK_LEN(len, 26);
690 TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh ");
691 TEST_CHECK_STRCMP(sbuff.p, "");
692 }
693
694 {
695 char tmp_out[2 + 1];
696 char const in_escapes_collapse[] = "||";
697
698 TEST_CASE("Collapse double escapes");
699 fr_sbuff_init_in(&sbuff, in_escapes_collapse, sizeof(in_escapes_collapse) - 1);
700 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)),
701 &sbuff, SIZE_MAX, NULL, &pipe_rules);
704 TEST_CHECK_STRCMP(tmp_out, "|");
705 TEST_CHECK_STRCMP(sbuff.p, "");
706 }
707
708 {
709 char in_escapes_collapse[] = "||foo||";
710
711 TEST_CASE("Collapse double escapes overlapping");
712 fr_sbuff_init_in(&sbuff, in_escapes_collapse, sizeof(in_escapes_collapse) - 1);
713 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(in_escapes_collapse, sizeof(in_escapes_collapse)),
714 &sbuff, SIZE_MAX, NULL, &pipe_rules);
717 TEST_CHECK_STRCMP(in_escapes_collapse, "|foo|");
718 TEST_CHECK_STRCMP(sbuff.p, "");
719 }
720
721 {
722 char tmp_out[30 + 1];
723
724 fr_sbuff_unescape_rules_t double_quote_rules = {
725 .chr = '\\',
726 .subs = {
727 ['a'] = '\a',
728 ['b'] = '\b',
729 ['e'] = '\\',
730 ['n'] = '\n',
731 ['r'] = '\r',
732 ['t'] = '\t',
733 ['v'] = '\v',
734 ['\\'] = '\\',
735 ['"'] = '"' /* Quoting char */
736 },
737 .do_hex = true,
738 .do_oct = true
739 };
740
741 char const in_escapes_unit[] =
742 "0x01\\001"
743 "0x07\\007"
744 "0x0A\\n"
745 "0x0D\\r"
746 "\\\"\\\""
747 "0xb0"
748 "\\260\\xb0";
749
750 char const expected[] = {
751 '0', 'x', '0', '1', '\001',
752 '0', 'x', '0', '7', '\007',
753 '0', 'x', '0', 'A', '\n',
754 '0', 'x', '0', 'D', '\r',
755 '"', '"',
756 '0', 'x', 'b', '0',
757 '\260', '\xb0', '\0'
758 };
759
760 TEST_CASE("Check unit test test strings");
761 fr_sbuff_init_in(&sbuff, in_escapes_unit, sizeof(in_escapes_unit) - 1);
762 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
763 NULL, &double_quote_rules);
765 TEST_CHECK_LEN(len, 28);
766 TEST_CHECK_STRCMP(tmp_out, expected);
767 TEST_CHECK_STRCMP(sbuff.p, "");
768 }
769
770 /*
771 * Verify dynamic allocation
772 */
773 {
774 char *buff;
775 char const in_zero[] = "";
776
777 ret = fr_sbuff_out_aunescape_until(NULL, &buff, &len, &FR_SBUFF_IN(in_zero, sizeof(in_zero) - 1), SIZE_MAX,
778 NULL, &pipe_rules);
781 talloc_get_type_abort(buff, char);
782 TEST_CHECK_LEN(talloc_array_length(buff), 1);
784 }
785}
786
787/** Check that a hex or octal escape sequence cannot consume more input than "len" allows
788 *
789 * fr_sbuff_out_unescape_until() bounds the input it consumes with a marker placed
790 * "len" bytes ahead of the start. The escape sequences are the one place where
791 * the input is advanced by more than a byte at a time, so they are the one place
792 * where the marker can be stepped over.
793 *
794 * An escape sequence which does not fit within "len" is not a complete escape
795 * sequence, and is copied out as-is. That is the same thing which happens to an
796 * incomplete escape sequence at the end of the input.
797 */
799{
800 char out[24 + 1] = "";
801 fr_sbuff_t sbuff;
802 fr_sbuff_err_t ret;
803 size_t len;
804
805 char const in_hex[] = "|x41|x42|x43";
806 char const in_oct[] = "|101|102|103";
807
808 fr_sbuff_unescape_rules_t hex_rules = {
809 .chr = '|',
810 .subs = { ['|'] = '|' },
811 .do_hex = true
812 };
813
814 fr_sbuff_unescape_rules_t oct_rules = {
815 .chr = '|',
816 .subs = { ['|'] = '|' },
817 .do_oct = true
818 };
819
820 TEST_CASE("Hex, no limit");
821 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
822 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, &hex_rules);
825 TEST_CHECK_STRCMP(out, "ABC");
826 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 12);
827
828 TEST_CASE("Hex, limit falls on the end of an escape sequence");
829 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
830 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 4, NULL, &hex_rules);
834 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 4);
835
836 TEST_CASE("Hex, limit falls on the escape char");
837 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
838 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, &hex_rules);
841 TEST_CHECK_STRCMP(out, "A|");
842 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 5);
843
844 /*
845 * "|x" is within the limit, but neither hex digit is.
846 */
847 TEST_CASE("Hex, limit leaves no room for either hex digit");
848 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
849 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 6, NULL, &hex_rules);
852 TEST_CHECK_STRCMP(out, "A|x");
853 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 6);
854 TEST_MSG("consumed %zu bytes of input, was allowed 6", fr_sbuff_used(&sbuff));
855
856 /*
857 * "|x4" is within the limit, the second hex digit is not.
858 */
859 TEST_CASE("Hex, limit leaves room for only one hex digit");
860 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
861 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 7, NULL, &hex_rules);
864 TEST_CHECK_STRCMP(out, "A|x4");
865 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 7);
866 TEST_MSG("consumed %zu bytes of input, was allowed 7", fr_sbuff_used(&sbuff));
867
868 TEST_CASE("Octal, no limit");
869 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
870 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, &oct_rules);
873 TEST_CHECK_STRCMP(out, "ABC");
874 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 12);
875
876 TEST_CASE("Octal, limit falls on the end of an escape sequence");
877 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
878 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 4, NULL, &oct_rules);
882 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 4);
883
884 /*
885 * "|10" is within the limit, the third octal digit is not.
886 */
887 TEST_CASE("Octal, limit leaves room for only two octal digits");
888 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
889 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 7, NULL, &oct_rules);
892 TEST_CHECK_STRCMP(out, "A|10");
893 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 7);
894 TEST_MSG("consumed %zu bytes of input, was allowed 7", fr_sbuff_used(&sbuff));
895
896 /*
897 * Once the input has been stepped over, the "have we reached the
898 * end" check compares for equality, so it never matches again and
899 * the rest of the input is consumed too. Walk every limit and
900 * check that the input consumed never exceeds the limit.
901 */
902 TEST_CASE("No limit is ever exceeded, hex");
903 for (size_t i = 0; i <= sizeof(in_hex); i++) {
904 fr_sbuff_init_in(&sbuff, in_hex, sizeof(in_hex) - 1);
905 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, i, NULL, &hex_rules);
907 TEST_CHECK(fr_sbuff_used(&sbuff) <= i);
908 TEST_MSG("limit %zu consumed %zu bytes of input", i, fr_sbuff_used(&sbuff));
909 }
910
911 TEST_CASE("No limit is ever exceeded, octal");
912 for (size_t i = 0; i <= sizeof(in_oct); i++) {
913 fr_sbuff_init_in(&sbuff, in_oct, sizeof(in_oct) - 1);
914 ret = fr_sbuff_out_unescape_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, i, NULL, &oct_rules);
916 TEST_CHECK(fr_sbuff_used(&sbuff) <= i);
917 TEST_MSG("limit %zu consumed %zu bytes of input", i, fr_sbuff_used(&sbuff));
918 }
919}
920
922{
923 char const in[] = "foo, bar, baz```";
924 fr_sbuff_t sbuff;
925 fr_sbuff_err_t ret;
926 size_t len;
928 L(","),
929 L("```"),
930 L("bad"),
931 L("bar"),
932 L("boink"),
933 L("food"),
934 L("nyi")
935 );
936 char out[100] = "";
937
938 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
939
940 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
943 TEST_CHECK_STRCMP(out, "foo");
944
945 fr_sbuff_advance(&sbuff, 1);
946
947 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
951
952 fr_sbuff_advance(&sbuff, 4);
953
954 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
957 TEST_CHECK_STRCMP(out, " baz");
958}
959
960static void test_eof_terminal(void)
961{
962 char const in[] = "foo, bar";
963 fr_sbuff_t sbuff;
964 fr_sbuff_err_t ret;
965 size_t len;
967 L(""),
968 L(","),
969 );
971 L(",")
972 );
973 char out[100] = "";
974
975 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
976
977 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
980 TEST_CHECK_STRCMP(out, "foo");
981
982 fr_sbuff_advance(&sbuff, 1); /* Advance past comma */
983
984 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
987 TEST_CHECK_STRCMP(out, " bar");
988
989 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt_eof) == true);
990 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt) == false);
991}
992
993/*
994 * fr_sbuff_terminal_search() is called with a `p` that walks
995 * forward independently of in->p (see fr_sbuff_out_bstrncpy_until).
996 * `remaining` must be measured from `p`, not from `in->p` — otherwise
997 * memcmp() can read past in->end when matching a multi-byte terminal
998 * near the visible end of the buffer.
999 *
1000 * The underlying C string is "abc}xyz" but the sbuff is initialised
1001 * to only see the first 4 bytes ("abc}"). With a "}xyz" terminal,
1002 * the (buggy) code would happily compare 4 bytes from p='}' against
1003 * "}xyz" — reading the "xyz" tail past in->end and reporting a false
1004 * match. The fix clamps the compare to (in->end - p) = 1 byte, so
1005 * the partial-prefix match correctly falls through.
1006 */
1008{
1009 char const in[] = "abc}xyz";
1010 fr_sbuff_t sbuff;
1012 L("}xyz")
1013 );
1014 char out[16] = "";
1015 fr_sbuff_err_t ret;
1016 size_t len;
1017
1018 fr_sbuff_init_in(&sbuff, in, (size_t)4); /* Only "abc}" is visible */
1019
1020 ret = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
1022 TEST_CHECK_LEN(len, 4);
1023 TEST_CHECK_STRCMP(out, "abc}");
1024}
1025
1026/*
1027 * Regression: fr_sbuff_terminal_search() did the fast-path "idx[*p]"
1028 * dereference before checking whether the buffer was fully consumed.
1029 * When "p == in->end" (nothing left) "*p" is one byte past the usable
1030 * buffer. For a non-NUL-terminated sbuff whose end sits on an
1031 * allocation boundary that is a 1-byte over-read, which ASAN reports as
1032 * a heap-buffer-overflow.
1033 *
1034 * A heap buffer sized exactly to its contents (no trailing '\0') is used
1035 * so the byte past "end" lands in an ASAN redzone.
1036 */
1038{
1039 size_t const len = 4;
1040 char *buff = malloc(len); /* exactly len bytes, no NUL */
1041 fr_sbuff_t sbuff;
1043 L(","),
1044 L("}"),
1045 );
1046
1047 TEST_CHECK(buff != NULL);
1048 if (!buff) return;
1049 memcpy(buff, "abcd", len);
1050
1051 fr_sbuff_init_in(&sbuff, buff, len);
1052 fr_sbuff_advance(&sbuff, len); /* Consume everything: in->p == in->end */
1053
1054 /*
1055 * Must not read *p (one byte past the buffer). The sbuff is not
1056 * extendable and '\0' is not a terminal, so this is simply "not a
1057 * terminal".
1058 */
1059 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt) == false);
1060
1061 free(buff);
1062}
1063
1064static void test_terminal_merge(void)
1065{
1066 size_t i;
1068 L(""),
1069 L("\t"),
1070 L("\n"),
1071 L("\r"),
1072 L(" "),
1073 L("!"),
1074 L("%"),
1075 L("&"),
1076 L("*"),
1077 L("+"),
1078 L("-"),
1079 L("/"),
1080 L("<"),
1081 L("="),
1082 L(">"),
1083 L("^"),
1084 L("{"),
1085 L("|"),
1086 L("~")
1087 );
1089 L(""),
1090 L(")"),
1091 );
1092
1093 fr_sbuff_term_t expect =
1095 L(""),
1096 L("\t"),
1097 L("\n"),
1098 L("\r"),
1099 L(" "),
1100 L("!"),
1101 L("%"),
1102 L("&"),
1103 L(")"),
1104 L("*"),
1105 L("+"),
1106 L("-"),
1107 L("/"),
1108 L("<"),
1109 L("="),
1110 L(">"),
1111 L("^"),
1112 L("{"),
1113 L("|"),
1114 L("~")
1115 );
1116 fr_sbuff_term_t *result;
1117
1118 result = fr_sbuff_terminals_amerge(NULL, &a, &b);
1119 TEST_CHECK_LEN(result->len, expect.len);
1120
1121 for (i = 0; i < result->len; i++) {
1122 TEST_CHECK_STRCMP(result->elem[i].str, expect.elem[i].str);
1123 }
1124
1125 talloc_free(result);
1126}
1127
1128static void test_no_advance(void)
1129{
1130 char const *in = "i am a test string";
1131 char out[18 + 1] = "";
1132 fr_sbuff_t sbuff;
1133
1134 fr_sbuff_init_in(&sbuff, in, strlen(in));
1135
1136 TEST_CASE("Copy 5 bytes to out - no advance");
1137 TEST_CHECK(sbuff.p == sbuff.start);
1139 TEST_CHECK(strcmp(out, "i am ") == 0);
1140 TEST_CHECK(sbuff.p == sbuff.start);
1141}
1142
1143static void test_talloc_extend(void)
1144{
1145 fr_sbuff_t sbuff;
1147
1148 TEST_CASE("Initial allocation");
1149 MEM(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 32, 50));
1150 TEST_SBUFF_USED(&sbuff, 0);
1151 TEST_SBUFF_LEN(&sbuff, 33);
1152
1153 TEST_CASE("Trim to zero");
1154 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1155 TEST_SBUFF_USED(&sbuff, 0);
1156 TEST_SBUFF_LEN(&sbuff, 1);
1157
1158 TEST_CASE("Print string - Should realloc to init");
1159 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
1160 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "0123456789") == 0);
1161 TEST_SBUFF_USED(&sbuff, 10);
1162 TEST_SBUFF_LEN(&sbuff, 33);
1163
1164 TEST_CASE("Trim to strlen");
1165 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1166 TEST_SBUFF_LEN(&sbuff, 11);
1167
1168 TEST_CASE("Print string - Should realloc to init");
1169 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
1170 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789") == 0);
1171 TEST_SBUFF_USED(&sbuff, 20);
1172 TEST_SBUFF_LEN(&sbuff, 33);
1173
1174 TEST_CASE("Trim to strlen");
1175 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1176 TEST_SBUFF_LEN(&sbuff, 21);
1177
1178 TEST_CASE("Print string - Should realloc to double buffer len");
1179 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "012345678901234") == 15);
1180 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234") == 0);
1181 TEST_SBUFF_USED(&sbuff, 35);
1182 TEST_SBUFF_LEN(&sbuff, 41);
1183
1184 TEST_CASE("Print string - Should only add a single char, should not extend the buffer");
1185 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
1186 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234A") == 0);
1187 TEST_SBUFF_USED(&sbuff, 36);
1188 TEST_SBUFF_LEN(&sbuff, 41);
1189
1190 TEST_CASE("Print string - Use all available buffer data");
1191 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BCDE") == 4);
1192 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDE") == 0);
1193 TEST_SBUFF_USED(&sbuff, 40);
1194 TEST_SBUFF_LEN(&sbuff, 41);
1195
1196 TEST_CASE("Print string - Add single char, should trigger doubling constrained by max");
1197 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "F") == 1);
1198 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEF") == 0);
1199 TEST_SBUFF_USED(&sbuff, 41);
1200 TEST_SBUFF_LEN(&sbuff, 51);
1201
1202 TEST_CASE("Print string - Add data to take us up to max");
1203 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "GHIJKLMNO") == 9);
1204 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
1205 TEST_SBUFF_USED(&sbuff, 50);
1206 TEST_SBUFF_LEN(&sbuff, 51);
1207
1208 TEST_CASE("Print string - Add single char, should fail");
1209 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "P") == -1);
1210 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
1211 TEST_SBUFF_USED(&sbuff, 50);
1212 TEST_SBUFF_LEN(&sbuff, 51);
1213
1214 TEST_CASE("Trim to strlen (should be noop)");
1215 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
1216 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
1217 TEST_SBUFF_USED(&sbuff, 50);
1218 TEST_SBUFF_LEN(&sbuff, 51);
1219
1220 talloc_free(sbuff.buff);
1221}
1222
1224{
1225 fr_sbuff_t sbuff;
1227
1228 TEST_CASE("Initial allocation");
1229 MEM(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 0, 50));
1230 TEST_SBUFF_USED(&sbuff, 0);
1231 TEST_SBUFF_LEN(&sbuff, 1);
1232
1233 TEST_CASE("Print string - Should alloc one byte");
1234 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
1235 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "A") == 0);
1236 TEST_SBUFF_USED(&sbuff, 1);
1237 TEST_SBUFF_LEN(&sbuff, 2);
1238
1239 TEST_CASE("Print string - Should alloc two bytes");
1240 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BC") == 2);
1241 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABC") == 0);
1242 TEST_SBUFF_USED(&sbuff, 3);
1243 TEST_SBUFF_LEN(&sbuff, 4);
1244
1245 TEST_CASE("Print string - Should alloc three bytes");
1246 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "D") == 1);
1247 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABCD") == 0);
1248 TEST_SBUFF_USED(&sbuff, 4);
1249 TEST_SBUFF_LEN(&sbuff, 7);
1250
1251 talloc_free(sbuff.buff);
1252}
1253
1255{
1256 fr_sbuff_t sbuff_0, sbuff_1;
1258
1259 TEST_CASE("Initial allocation");
1260 MEM(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50));
1261 TEST_SBUFF_USED(&sbuff_0, 0);
1262 TEST_SBUFF_LEN(&sbuff_0, 1);
1263
1264 sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0);
1265 TEST_CASE("Check sbuff_1 has extend fields set");
1266 TEST_CHECK(sbuff_0.extend == sbuff_1.extend);
1267 TEST_CHECK(sbuff_0.uctx == sbuff_1.uctx);
1268 TEST_CHECK(sbuff_1.parent == &sbuff_0);
1269 TEST_SBUFF_USED(&sbuff_1, 0);
1270 TEST_SBUFF_LEN(&sbuff_1, 1);
1271
1272 TEST_CASE("Print string - Should alloc one byte");
1273 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "A") == 1);
1274 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff_1), "A") == 0);
1275 TEST_SBUFF_USED(&sbuff_0, 1);
1276 TEST_SBUFF_LEN(&sbuff_0, 2);
1277 TEST_SBUFF_USED(&sbuff_1, 1);
1278 TEST_SBUFF_LEN(&sbuff_1, 2);
1279
1280 TEST_CHECK(sbuff_0.start == sbuff_1.start);
1281 TEST_CHECK(sbuff_0.end == sbuff_1.end);
1282 TEST_CHECK(sbuff_0.p == sbuff_1.p);
1283
1284 talloc_free(sbuff_0.buff);
1285}
1286
1288{
1289 fr_sbuff_t sbuff_0, sbuff_1;
1290 fr_sbuff_marker_t marker_0, marker_1;
1292
1293 TEST_CASE("Initial allocation");
1294 MEM(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50));
1295 TEST_SBUFF_USED(&sbuff_0, 0);
1296 TEST_SBUFF_LEN(&sbuff_0, 1);
1297
1298 TEST_CASE("Print string - Should alloc one byte");
1299 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "A") == 1);
1300 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "A");
1301 TEST_SBUFF_USED(&sbuff_0, 1);
1302 TEST_SBUFF_LEN(&sbuff_0, 2);
1303
1304 fr_sbuff_marker(&marker_0, &sbuff_0);
1305 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1306
1307 TEST_CASE("Print string - Ensure marker is updated");
1308 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "B") == 1);
1309 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "AB");
1310 TEST_SBUFF_USED(&sbuff_0, 2);
1311 TEST_SBUFF_LEN(&sbuff_0, 3);
1312 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1313
1314 TEST_CASE("Print string - Copy sbuff");
1315 sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0); /* Dup sbuff_0 */
1316 TEST_CHECK(sbuff_0.p == sbuff_1.start);
1317 fr_sbuff_marker(&marker_1, &sbuff_1);
1318
1319 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1320 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1321 TEST_CHECK(sbuff_0.p == sbuff_1.start);
1322
1323 TEST_CASE("Print string - Trigger re-alloc, ensure all pointers are updated");
1324 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "C") == 1);
1325 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_1), "C");
1326 TEST_CHECK(sbuff_0.buff == sbuff_1.buff);
1327 TEST_CHECK(sbuff_0.p == sbuff_1.start + 1);
1328 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1329 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1330 TEST_SBUFF_USED(&sbuff_0, 3);
1331 TEST_SBUFF_LEN(&sbuff_0, 5);
1332
1333 talloc_free(sbuff_0.buff);
1334}
1335
1337{
1338 fr_sbuff_t sbuff;
1340
1341 TEST_CASE("Intermix shift and extend");
1342 MEM(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 4, 8));
1343 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123") == 4);
1344 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "5678") == 4);
1345 TEST_CHECK(fr_sbuff_shift(&sbuff, 4, false) == 4);
1346 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "AAAA") == 4);
1347 TEST_CHECK(fr_sbuff_shift(&sbuff, 8, false) == 8);
1348 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BBBBBBBB") == 8);
1349
1350 talloc_free(sbuff.buff);
1351}
1352
1353/*
1354 * fr_sbuff_used() counts from the sbuff's start pointer to its current
1355 * position. fr_sbuff_used_total() adds the "shifted" counter, which
1356 * records how much data has been moved out of the buffer and discarded.
1357 *
1358 * The two come apart as soon as anything is shifted, and which one a
1359 * caller wants is not obvious, so pin the behaviour down here.
1360 */
1362{
1363 fr_sbuff_t sbuff;
1365
1366 TEST_CASE("Before any shift, used() and used_total() agree");
1367 MEM(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 8, 32));
1368 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "01234567") == 8);
1369
1370 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 8);
1372 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 0);
1373
1374 TEST_CASE("A shift moves data out of the buffer");
1375 TEST_CHECK_LEN(fr_sbuff_shift(&sbuff, 4, false), 4);
1376
1377 /*
1378 * This sbuff's start pointer is the beginning of the buffer, so the
1379 * shift cannot move it back any further. fr_sbuff_shift() adds the
1380 * whole shift to "shifted" instead (sbuff.c).
1381 *
1382 * The result is that used() forgets the shifted data and
1383 * used_total() does not. used_total() is therefore the count which
1384 * survives a shift.
1385 */
1386 TEST_CASE("used() forgets the shifted data");
1387 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 4);
1388
1389 TEST_CASE("used_total() remembers it, and is unchanged by the shift");
1391 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 4);
1392
1393 TEST_CASE("used_total() stays whole across a second shift");
1394 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "89AB") == 4);
1396 TEST_CHECK_LEN(fr_sbuff_shift(&sbuff, 4, false), 4);
1398 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 8);
1399
1400 /*
1401 * The sum is the point: whatever the shift did, the two halves of
1402 * the count still add up to everything ever written.
1403 */
1404 TEST_CASE("used() plus shifted() equals used_total()");
1406 fr_sbuff_used_total(&sbuff));
1407
1408 talloc_free(sbuff.buff);
1409}
1410
1411/*
1412 * fr_sbuff_marker_update_end() places a marker at the point where a
1413 * budget of "max" bytes runs out, and is called again after every buffer
1414 * extension. It reads the budget already spent with
1415 * fr_sbuff_used_total(), which is the count that survives a shift.
1416 */
1417static void test_marker_update_end(void)
1418{
1419 fr_sbuff_t sbuff;
1421 char buff[16];
1422
1423 TEST_CASE("SIZE_MAX means no constraint, so the marker sits at the end");
1424 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1425 fr_sbuff_marker(&end, &sbuff);
1426 TEST_CHECK(fr_sbuff_marker_update_end(&end, SIZE_MAX) == fr_sbuff_end(&sbuff));
1427 fr_sbuff_marker_release(&end);
1428
1429 TEST_CASE("A budget smaller than the buffer stops the marker short");
1430 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1431 fr_sbuff_marker(&end, &sbuff);
1432 TEST_CHECK(fr_sbuff_marker_update_end(&end, 4) == fr_sbuff_current(&sbuff) + 4);
1433 fr_sbuff_marker_release(&end);
1434
1435 TEST_CASE("Data already consumed comes off the budget");
1436 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1437 TEST_CHECK(fr_sbuff_advance(&sbuff, 3) > 0);
1438 fr_sbuff_marker(&end, &sbuff);
1440
1441 /*
1442 * Three of the six are already spent, so three are left.
1443 */
1444 TEST_CHECK(fr_sbuff_marker_update_end(&end, 6) == fr_sbuff_current(&sbuff) + 3);
1445 fr_sbuff_marker_release(&end);
1446
1447 TEST_CASE("A budget which exactly matches what was consumed leaves nothing");
1448 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1449 TEST_CHECK(fr_sbuff_advance(&sbuff, 4) > 0);
1450 fr_sbuff_marker(&end, &sbuff);
1451 TEST_CHECK(fr_sbuff_marker_update_end(&end, 4) == fr_sbuff_current(&sbuff));
1452 fr_sbuff_marker_release(&end);
1453
1454 /*
1455 * "max - used" is computed in size_t, so an overspent budget does
1456 * not go negative. Without a clamp it wraps to an enormous number,
1457 * which compares greater than fr_sbuff_remaining(), and the marker
1458 * lands on the end of the buffer: the budget is overspent, and the
1459 * marker then permits everything which is left.
1460 *
1461 * fr_sbuff_marker_update_end() checks for that case, so the marker
1462 * stays at the current position and allows nothing more.
1463 */
1464 TEST_CASE("An overspent budget allows nothing more");
1465 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1466 TEST_CHECK(fr_sbuff_advance(&sbuff, 6) > 0);
1467 fr_sbuff_marker(&end, &sbuff);
1469 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1470 TEST_MSG("marker is %zu past the current position, expected 0",
1471 (size_t) (fr_sbuff_current(&end) - fr_sbuff_current(&sbuff)));
1472 TEST_CHECK(fr_sbuff_current(&end) != fr_sbuff_end(&sbuff));
1473 fr_sbuff_marker_release(&end);
1474
1475 TEST_CASE("A budget overspent by a single byte allows nothing more");
1476 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1477 TEST_CHECK(fr_sbuff_advance(&sbuff, 4) > 0);
1478 fr_sbuff_marker(&end, &sbuff);
1479 TEST_CHECK(fr_sbuff_marker_update_end(&end, 3) == fr_sbuff_current(&sbuff));
1480 fr_sbuff_marker_release(&end);
1481
1482 TEST_CASE("A zero budget allows nothing, whether or not anything was used");
1483 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1484 fr_sbuff_marker(&end, &sbuff);
1485 TEST_CHECK(fr_sbuff_marker_update_end(&end, 0) == fr_sbuff_current(&sbuff));
1486 fr_sbuff_marker_release(&end);
1487
1488 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1489 TEST_CHECK(fr_sbuff_advance(&sbuff, 5) > 0);
1490 fr_sbuff_marker(&end, &sbuff);
1491 TEST_CHECK(fr_sbuff_marker_update_end(&end, 0) == fr_sbuff_current(&sbuff));
1492 fr_sbuff_marker_release(&end);
1493
1494 /*
1495 * fr_sbuff_out_unescape_until() calls fr_sbuff_marker_update_end()
1496 * once per extension, with the same budget every time. Repeated
1497 * calls on an overspent budget must keep giving the same answer.
1498 */
1499 TEST_CASE("Repeated updates on an overspent budget stay put");
1500 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1501 TEST_CHECK(fr_sbuff_advance(&sbuff, 7) > 0);
1502 fr_sbuff_marker(&end, &sbuff);
1503 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1504 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1505 TEST_CHECK(fr_sbuff_marker_update_end(&end, 2) == fr_sbuff_current(&sbuff));
1506 fr_sbuff_marker_release(&end);
1507
1508 /*
1509 * Nothing is left in the buffer, so "allow nothing more" and "allow
1510 * everything which is left" are the same position. The overspend
1511 * is invisible here, which is why the cases above use a buffer with
1512 * data still in it.
1513 */
1514 TEST_CASE("An overspent budget at the end of the buffer");
1515 fr_sbuff_init_in(&sbuff, "0123456789", 10);
1516 TEST_CHECK(fr_sbuff_advance(&sbuff, 10) > 0);
1517 fr_sbuff_marker(&end, &sbuff);
1519 TEST_CHECK(fr_sbuff_marker_update_end(&end, 4) == fr_sbuff_current(&sbuff));
1520 TEST_CHECK(fr_sbuff_current(&end) == fr_sbuff_end(&sbuff));
1521 fr_sbuff_marker_release(&end);
1522
1523 /*
1524 * Keep the compiler honest about buff being used.
1525 */
1526 fr_sbuff_init_out(&sbuff, buff, sizeof(buff));
1527 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "x") == 1);
1528}
1529
1530/** Check that the budget is measured with fr_sbuff_used_total(), not fr_sbuff_used()
1531 *
1532 * fr_sbuff_shift() moves data out of the buffer and adds the amount which
1533 * fr_sbuff_start() can no longer account for to fr_sbuff_shifted(). So
1534 * fr_sbuff_used_total() does not change across a shift, while fr_sbuff_used()
1535 * shrinks. See test_shift_used_accounting().
1536 *
1537 * fr_sbuff_out_unescape_until() re-runs fr_sbuff_marker_update_end() after every
1538 * extension, and an extension is exactly when a shift happens. Measuring the
1539 * budget with fr_sbuff_used() would therefore reset the budget on every shift,
1540 * and the operation could copy more than it was allowed to.
1541 *
1542 * The budget below is larger than fr_sbuff_used() but smaller than
1543 * fr_sbuff_used_total(), so the two accessors give different answers.
1544 */
1546{
1547 fr_sbuff_t sbuff;
1550 size_t used, used_total;
1551
1552 /*
1553 * The buffer has to have room left in it. fr_sbuff_remaining() is
1554 * what an unclamped overspend gets compared against, so with a full
1555 * buffer "allow nothing more" and "allow everything left" are the
1556 * same position, and the overspend cannot be seen.
1557 */
1558 TEST_CASE("Set up a buffer which has shifted, and which has room left");
1559 MEM(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 16, 32));
1560 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "01234567") == 8);
1561 TEST_CHECK_LEN(fr_sbuff_shift(&sbuff, 4, true), 4);
1562
1563 used = fr_sbuff_used(&sbuff);
1564 used_total = fr_sbuff_used_total(&sbuff);
1565
1566 TEST_CHECK_LEN(used, 4);
1567 TEST_CHECK_LEN(fr_sbuff_shifted(&sbuff), 4);
1568 TEST_CHECK_LEN(used_total, 8);
1569 TEST_CHECK(fr_sbuff_remaining(&sbuff) > 0);
1570
1571 /*
1572 * A budget of 6 is more than the 4 which fr_sbuff_used() reports,
1573 * and less than the 8 which fr_sbuff_used_total() reports.
1574 */
1575 TEST_CASE("A budget between used and used_total is already overspent");
1576 fr_sbuff_marker(&end, &sbuff);
1577 TEST_CHECK(fr_sbuff_marker_update_end(&end, 6) == fr_sbuff_current(&sbuff));
1578 TEST_MSG("marker is %zu past the current position, expected 0. "
1579 "Measuring the budget with fr_sbuff_used() would give 2, "
1580 "and not clamping the overspend would give %zu",
1581 (size_t) (fr_sbuff_current(&end) - fr_sbuff_current(&sbuff)),
1582 fr_sbuff_remaining(&sbuff));
1583 fr_sbuff_marker_release(&end);
1584
1585 /*
1586 * A budget larger than used_total still has room in it, and the
1587 * room left is measured from used_total, not from used.
1588 */
1589 TEST_CASE("A budget larger than used_total leaves used_total's worth of room");
1590 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "89") == 2);
1591 used_total = fr_sbuff_used_total(&sbuff);
1592 TEST_CHECK_LEN(fr_sbuff_used(&sbuff), 6);
1593 TEST_CHECK_LEN(used_total, 10);
1594 TEST_CHECK(fr_sbuff_remaining(&sbuff) > 1);
1595
1596 fr_sbuff_marker(&end, &sbuff);
1597 TEST_CHECK(fr_sbuff_marker_update_end(&end, used_total + 1) == fr_sbuff_current(&sbuff) + 1);
1598 TEST_MSG("marker is %zu past the current position, expected 1. "
1599 "Measuring the budget with fr_sbuff_used() would give 5",
1600 (size_t) (fr_sbuff_current(&end) - fr_sbuff_current(&sbuff)));
1601 fr_sbuff_marker_release(&end);
1602
1603 talloc_free(sbuff.buff);
1604}
1605
1606static void test_file_extend(void)
1607{
1608 fr_sbuff_t sbuff;
1609 fr_sbuff_t our_sbuff, child_sbuff;
1611 FILE *fp;
1612 char buff[5];
1613 char out[24] = "not written"; /* Not empty, so the checks for "" test the copy */
1614 char fbuff[24];
1615 const char PATTERN[] = "xyzzy";
1616#define PATTERN_LEN (sizeof(PATTERN) - 1)
1617 char *post_ws;
1618 ssize_t slen;
1619 size_t len;
1620
1621 static_assert(sizeof(buff) >= PATTERN_LEN, "Buffer must be sufficiently large to hold the pattern");
1622 static_assert((sizeof(fbuff) % sizeof(buff)) > 0, "sizeof buff must not be a multiple of fbuff");
1623 static_assert((sizeof(fbuff) % sizeof(buff)) < PATTERN_LEN, "remainder of sizeof(fbuff)/sizeof(buff) must be less than sizeof pattern");
1624
1625 TEST_CASE("Initialization");
1626 memset(fbuff, ' ', sizeof(fbuff));
1627 memcpy(fbuff + sizeof(fbuff) - PATTERN_LEN, PATTERN, PATTERN_LEN);
1628
1629 fp = fmemopen(fbuff, sizeof(fbuff), "r");
1630#ifdef __clang_analyzer__
1631 if (fp == NULL) return;
1632#endif
1633
1634 TEST_CHECK(fp != NULL);
1635 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1636 our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1637
1638 TEST_CASE("Advance past whitespace, which will require shift/extend");
1639 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&our_sbuff, SIZE_MAX, NULL), sizeof(fbuff) - PATTERN_LEN);
1640 TEST_CASE("Verify extend on unused child buffer");
1641 child_sbuff = FR_SBUFF(&our_sbuff);
1642 slen = fr_sbuff_extend_file(NULL, &child_sbuff, 0);
1643 TEST_CHECK_SLEN(slen, sizeof(fbuff) % PATTERN_LEN);
1644 TEST_CASE("Verify that we passed all and only whitespace");
1645 TEST_CHECK_RET(fr_sbuff_out_abstrncpy(NULL, &post_ws, NULL, &our_sbuff, 24), FR_SBUFF_OK);
1646 TEST_CHECK_STRCMP(post_ws, PATTERN);
1647 talloc_free(post_ws);
1648 TEST_CASE("Verify parent buffer end");
1649 TEST_CHECK(sbuff.end == our_sbuff.end);
1650
1651 TEST_CASE("Verify that we do not read shifted buffer past eof");
1652 TEST_CHECK_RET(fr_sbuff_out_bstrncpy(&len, &FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX), FR_SBUFF_OK);
1653 TEST_CHECK_LEN(len, 0);
1654 TEST_CHECK_RET(fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX), FR_SBUFF_OK);
1656 TEST_CHECK_RET(fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, NULL, NULL), FR_SBUFF_OK);
1657 TEST_CHECK_LEN(len, 0);
1659 TEST_CHECK_LEN(len, 0);
1660
1661 fclose(fp);
1662
1663 TEST_CASE("Verify fr_sbuff_out_bstrncpy_until() extends from file properly");
1664 fp = fmemopen(fbuff, sizeof(fbuff), "r");
1665#ifdef __clang_analyzer__
1666 if (fp == NULL) return;
1667#endif
1668
1669 TEST_CHECK(fp != NULL);
1670 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1671 our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1672 TEST_CHECK_RET(fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, &FR_SBUFF_TERM("x"), NULL), FR_SBUFF_OK);
1673 TEST_CHECK_LEN(len, sizeof(fbuff) - PATTERN_LEN);
1674
1675 fclose(fp);
1676}
1677
1678static void test_file_extend_max(void)
1679{
1680 fr_sbuff_t sbuff;
1682 FILE *fp;
1683 char buff[16];
1684 char fbuff[] = " xyzzy";
1685 char *post_ws;
1686 size_t len;
1687
1688 TEST_CASE("Initialization");
1689 fp = fmemopen(fbuff, sizeof(fbuff) - 1, "r");
1690#ifdef __clang_analyzer__
1691 if (fp == NULL) return;
1692#endif
1693 TEST_CHECK(fp != NULL);
1694 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, sizeof(fbuff) - 8) == &sbuff);
1695
1696 TEST_CASE("Confirm that max stops us from seeing xyzzy");
1697 TEST_CHECK_SLEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), sizeof(fbuff) - 8);
1698 TEST_CHECK_RET(fr_sbuff_out_abstrncpy(NULL, &post_ws, &len, &sbuff, 24), FR_SBUFF_OK);
1699 TEST_CHECK_LEN(len, 0);
1700 TEST_CHECK_STRCMP(post_ws, "");
1701 talloc_free(post_ws);
1702 fclose(fp);
1703}
1704
1705/** A read error on the file backing an sbuff is reported, not treated as the end of the input
1706 *
1707 * Reading from a stream opened for writing fails, which sets the stream's
1708 * error indicator without reaching EOF.
1709 */
1710static void test_file_extend_error(void)
1711{
1712 fr_sbuff_t sbuff;
1714 FILE *fp;
1715 char buff[16];
1716 char out[16] = "not written"; /* Not empty, so the checks for "" test the copy */
1717 size_t len = 1;
1718
1719 TEST_CASE("Initialization");
1720 fp = fopen("/dev/null", "w");
1721#ifdef __clang_analyzer__
1722 if (fp == NULL) return;
1723#endif
1724 TEST_CHECK(fp != NULL);
1725 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, SIZE_MAX) == &sbuff);
1726
1727 TEST_CASE("Copy reports the read error");
1729 TEST_CHECK_LEN(len, 0);
1731
1732 TEST_CASE("Copy until reports the read error");
1733 TEST_CHECK_RET(fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
1734 &FR_SBUFF_TERM(","), NULL), FR_SBUFF_ERR_EXTEND);
1735 TEST_CHECK_LEN(len, 0);
1736
1737 TEST_CASE("Exact copy reports the read error");
1739
1740 fclose(fp);
1741}
1742
1743static void test_out_int(void)
1744{
1745 fr_sbuff_t sbuff;
1746 int8_t i8 = 0;
1747
1748 TEST_CASE("In range, input advanced past the number");
1749 fr_sbuff_init_in(&sbuff, "-12,", 4);
1751 TEST_CHECK(i8 == -12);
1752 TEST_CHECK_STRCMP(sbuff.p, ",");
1753
1754 TEST_CASE("Overflow, out is the maximum and input is not advanced");
1755 fr_sbuff_init_in(&sbuff, "300", 3);
1757 TEST_CHECK(i8 == INT8_MAX);
1758 TEST_CHECK(sbuff.p == sbuff.start);
1759
1760 TEST_CASE("Underflow, out is the minimum");
1761 fr_sbuff_init_in(&sbuff, "-300", 4);
1763 TEST_CHECK(i8 == INT8_MIN);
1764 TEST_CHECK(sbuff.p == sbuff.start);
1765
1766 TEST_CASE("Not a number");
1767 fr_sbuff_init_in(&sbuff, "xyz", 3);
1769 TEST_CHECK(sbuff.p == sbuff.start);
1770
1771 TEST_CASE("Empty input");
1772 fr_sbuff_init_in(&sbuff, "", 0);
1774}
1775
1776static void test_out_uint(void)
1777{
1778 fr_sbuff_t sbuff;
1779 uint8_t u8 = 0;
1780
1781 TEST_CASE("In range");
1782 fr_sbuff_init_in(&sbuff, "255,", 4);
1784 TEST_CHECK(u8 == 255);
1785 TEST_CHECK_STRCMP(sbuff.p, ",");
1786
1787 TEST_CASE("Leading minus is underflow");
1788 fr_sbuff_init_in(&sbuff, "-1", 2);
1790 TEST_CHECK(sbuff.p == sbuff.start);
1791
1792 TEST_CASE("Hex, more digits than the type can hold is trailing data");
1793 fr_sbuff_init_in(&sbuff, "1234", 4);
1794 TEST_CHECK_RET(fr_sbuff_out_uint8_hex(&u8, &sbuff, true), FR_SBUFF_ERR_TRAILING);
1795 TEST_CHECK(u8 == UINT8_MAX);
1796 TEST_CHECK(sbuff.p == sbuff.start);
1797
1798 TEST_CASE("Hex, trailing digits allowed, input advanced past the two consumed");
1799 fr_sbuff_init_in(&sbuff, "1234", 4);
1800 TEST_CHECK_RET(fr_sbuff_out_uint8_hex(&u8, &sbuff, false), FR_SBUFF_OK);
1801 TEST_CHECK(u8 == 0x12);
1802 TEST_CHECK_STRCMP(sbuff.p, "34");
1803}
1804
1805static void test_out_float(void)
1806{
1807 fr_sbuff_t sbuff;
1808 float f = 0;
1809
1810 TEST_CASE("In range");
1811 fr_sbuff_init_in(&sbuff, "1.5,", 4);
1813 TEST_CHECK(f == 1.5f);
1814 TEST_CHECK_STRCMP(sbuff.p, ",");
1815
1816 TEST_CASE("Not a number");
1817 fr_sbuff_init_in(&sbuff, "xyz", 3);
1819 TEST_CHECK(sbuff.p == sbuff.start);
1820
1821 TEST_CASE("Empty input");
1822 fr_sbuff_init_in(&sbuff, "", 0);
1824}
1825
1826static void test_out_bool(void)
1827{
1828 fr_sbuff_t sbuff;
1829 bool b = false;
1830
1831 TEST_CASE("True, input advanced past the value");
1832 fr_sbuff_init_in(&sbuff, "yes,", 4);
1834 TEST_CHECK(b == true);
1835 TEST_CHECK_STRCMP(sbuff.p, ",");
1836
1837 TEST_CASE("False");
1838 fr_sbuff_init_in(&sbuff, "False", 5);
1840 TEST_CHECK(b == false);
1841 TEST_CHECK(sbuff.p == sbuff.end);
1842
1843 TEST_CASE("Not a truth value, out is false and input is not advanced");
1844 b = true;
1845 fr_sbuff_init_in(&sbuff, "maybe", 5);
1847 TEST_CHECK(b == false);
1848 TEST_CHECK(sbuff.p == sbuff.start);
1849
1850 TEST_CASE("Empty input");
1851 fr_sbuff_init_in(&sbuff, "", 0);
1853}
1854
1855static void test_adv_past_str(void)
1856{
1857 fr_sbuff_t sbuff;
1858 char const in[] = "i am a test string";
1859
1860 TEST_CASE("Check for token at beginning of string");
1861 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1862 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 6);
1863 TEST_CHECK_STRCMP(sbuff.p, " test string");
1864
1865 TEST_CASE("Check for token not at beginning of string");
1866 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1867 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, " am a", SIZE_MAX), 0);
1868 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1869
1870 TEST_CASE("Check for token larger than the string");
1871 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1872 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string ", SIZE_MAX), 0);
1873 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1874
1875 TEST_CASE("Check for token with zero length string");
1876 fr_sbuff_init_in(&sbuff, in, 0);
1877 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 0);
1878
1879 TEST_CASE("Check for token that is the string");
1880 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1881 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string", SIZE_MAX), 18);
1882 TEST_CHECK_STRCMP(sbuff.p, "");
1883 TEST_CHECK(sbuff.p == sbuff.end);
1884}
1885
1886static void test_adv_past_strcase(void)
1887{
1888 fr_sbuff_t sbuff;
1889 char const in[] = "i am a test string";
1890
1891 TEST_CASE("Check for token at beginning of string");
1892 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1893 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 6);
1894 TEST_CHECK_STRCMP(sbuff.p, " test string");
1895
1896 TEST_CASE("Check for token not at beginning of string");
1897 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1898 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, " AM a", SIZE_MAX), 0);
1899 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1900
1901 TEST_CASE("Check for token larger than the string");
1902 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1903 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string ", SIZE_MAX), 0);
1904 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1905
1906 TEST_CASE("Check for token with zero length string");
1907 fr_sbuff_init_in(&sbuff, in, 0);
1908 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 0);
1909
1910 TEST_CASE("Check for token that is the string");
1911 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1912 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string", SIZE_MAX), 18);
1913 TEST_CHECK_STRCMP(sbuff.p, "");
1914 TEST_CHECK(sbuff.p == sbuff.end);
1915}
1916
1918{
1919 fr_sbuff_t sbuff;
1920 char const in[] = " i am a test string";
1921 char const in_ns[] = "i am a test string";
1922 char const in_ws[] = " ";
1923
1924 TEST_CASE("Check for token at beginning of string");
1925 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1926 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1927 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1928
1929 TEST_CASE("Check for token not at beginning of string");
1930 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1931 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1932 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1933
1934 TEST_CASE("Check for token with zero length string");
1935 fr_sbuff_init_in(&sbuff, in, 0);
1936 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1937
1938 TEST_CASE("Check for token that is the string");
1939 fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1940 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1941
1942 TEST_CASE("Length constraint with token match");
1943 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1944 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 2);
1945 TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1946
1947 TEST_CASE("Length constraint without token match");
1948 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1949 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 0);
1950 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1951}
1952
1953static void test_adv_past_allowed(void)
1954{
1955 fr_sbuff_t sbuff;
1956 char const in[] = " i am a test string";
1957 char const in_ns[] = "i am a test string";
1958 char const in_ws[] = " ";
1959
1960 TEST_CASE("Check for token at beginning of string");
1961 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1962 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 5);
1963 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1964
1965 TEST_CASE("Check for token not at beginning of string");
1966 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1967 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1968 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1969
1970 TEST_CASE("Check for token with zero length string");
1971 fr_sbuff_init_in(&sbuff, in, 0);
1972 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1973 TEST_CHECK(sbuff.p == sbuff.start);
1974
1975 TEST_CASE("Check for token at the end of the string");
1976 fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1977 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 5);
1978 TEST_CHECK(sbuff.p == sbuff.end);
1979
1980 TEST_CASE("Length constraint with token match");
1981 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1982 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 2);
1983 TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1984
1985 TEST_CASE("Length constraint with token match");
1986 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1987 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1988 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1989}
1990
1991static void test_adv_until(void)
1992{
1993 fr_sbuff_t sbuff;
1994 char const in[] = " abcdefgh ijklmnopp";
1995
1996 TEST_CASE("Check for token at beginning of string");
1997 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1998 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM(" "), '\0'), 0);
1999 TEST_CHECK_STRCMP(sbuff.p, " abcdefgh ijklmnopp");
2000
2001 TEST_CASE("Check for token not at beginning of string");
2002 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2003 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 1);
2004 TEST_CHECK_STRCMP(sbuff.p, "abcdefgh ijklmnopp");
2005
2006 TEST_CASE("Check for token with zero length string");
2007 fr_sbuff_init_in(&sbuff, in, 0);
2008 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 0);
2009 TEST_CHECK(sbuff.p == sbuff.start);
2010
2011 TEST_CASE("Check for token that is not in the string");
2012 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2013 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("|"), '\0'), 19);
2014 TEST_CHECK(sbuff.p == sbuff.end);
2015
2016 TEST_CASE("Check escapes");
2017 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2018 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("p"), 'o'), 18);
2019 TEST_CHECK_STRCMP(sbuff.p, "p");
2020
2021 TEST_CASE("Check for token that is not in the string with length constraint");
2022 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2023 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, 5, &FR_SBUFF_TERM("|"), '\0'), 5);
2024 TEST_CHECK(sbuff.p == (sbuff.start + 5));
2025}
2026
2027static void test_adv_to_utf8(void)
2028{
2029 fr_sbuff_t sbuff;
2030 char const in[] = "🥺🥺🥺🥺🍪😀";
2031 char *p;
2032
2033 TEST_CASE("Check for token at beginning of string");
2034 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2035 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🥺");
2036 TEST_CHECK(p == sbuff.p);
2037 TEST_CHECK_STRCMP(sbuff.p, "🥺🥺🥺🥺🍪😀");
2038
2039 TEST_CASE("Check for token not at beginning of string");
2040 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2041 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
2042 TEST_CHECK(p == (sbuff.start + (sizeof("🥺🥺🥺🥺") - 1)));
2043 TEST_CHECK_STRCMP(p, "🍪😀");
2044
2045 TEST_CASE("Check for token with zero length string");
2046 fr_sbuff_init_in(&sbuff, in, 0);
2047 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
2048 TEST_CHECK(p == NULL);
2049 TEST_CHECK(sbuff.start == sbuff.p);
2050
2051 TEST_CASE("Check for token at the end of the string");
2052 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2053 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "😀");
2054 TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
2055
2056 TEST_CASE("Check for token not in the string");
2057 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2058 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍆 ");
2059 TEST_CHECK(p == NULL);
2060
2061 TEST_CASE("Check for token at the end of the string within len constraints");
2062 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2063 p = fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 1), "😀");
2064 TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
2065
2066 TEST_CASE("Check for token at the end of the string outside len constraints #1");
2067 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2068 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 2), "😀"));
2069 TEST_CHECK(sbuff.p == sbuff.start);
2070
2071 TEST_CASE("Check for token at the end of the string outside len constraints #2");
2072 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2073 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 3), "😀"));
2074 TEST_CHECK(sbuff.p == sbuff.start);
2075
2076 TEST_CASE("Check for token at the end of the string outside len constraints #3");
2077 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2078 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 4), "😀"));
2079 TEST_CHECK(sbuff.p == sbuff.start);
2080
2081 TEST_CASE("Check for token at the end of the string outside len constraints #4");
2082 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2083 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 5), "😀"));
2084 TEST_CHECK(sbuff.p == sbuff.start);
2085}
2086
2087static void test_adv_to_chr(void)
2088{
2089 fr_sbuff_t sbuff;
2090 char const in[] = "AAAAbC";
2091 char *p;
2092
2093 TEST_CASE("Check for token at beginning of string");
2094 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2095 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'A');
2096 TEST_CHECK(p == sbuff.p);
2097 TEST_CHECK_STRCMP(sbuff.p, "AAAAbC");
2098
2099 TEST_CASE("Check for token not at beginning of string");
2100 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2101 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b');
2102 TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
2103 TEST_CHECK_STRCMP(p, "bC");
2104
2105 TEST_CASE("Check for token with zero length string");
2106 fr_sbuff_init_in(&sbuff, in, 0);
2107 TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b'));
2108 TEST_CHECK(sbuff.start == sbuff.p);
2109
2110 TEST_CASE("Check for token at the end of the string");
2111 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2112 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'C');
2113 TEST_CHECK(p == sbuff.start + (sizeof("AAAAb") - 1));
2114
2115 TEST_CASE("Check for token not in the string");
2116 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2117 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'D');
2118 TEST_CHECK(p == NULL);
2119
2120 TEST_CASE("Check for token not at beginning of string within length constraints");
2121 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2122 p = fr_sbuff_adv_to_chr(&sbuff, 5, 'b');
2123 TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
2124 TEST_CHECK_STRCMP(p, "bC");
2125
2126 TEST_CASE("Check for token not at beginning of string outside length constraints");
2127 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2128 TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, 4, 'b'));
2129 TEST_CHECK(sbuff.p == sbuff.start);
2130}
2131
2132static void test_adv_to_str(void)
2133{
2134 fr_sbuff_t sbuff;
2135 char const in[] = "i am a test string";
2136 char *p;
2137
2138 TEST_CASE("Check for token at beginning of string");
2139 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2140 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test", SIZE_MAX);
2141 TEST_CHECK(sbuff.p == p);
2142 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
2143
2144 TEST_CASE("Check for token not at beginning of string");
2145 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2146 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "test", SIZE_MAX);
2147 TEST_CHECK(sbuff.p == p);
2148 TEST_CHECK_STRCMP(sbuff.p, "test string");
2149
2150 TEST_CASE("Check for token at the end of string");
2151 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2152 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "ing", SIZE_MAX);
2153 TEST_CHECK(sbuff.p == p);
2154 TEST_CHECK_STRCMP(sbuff.p, "ing");
2155
2156 TEST_CASE("Check for token larger than the string");
2157 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2158 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string ", SIZE_MAX);
2159 TEST_CHECK(sbuff.p == sbuff.start);
2160 TEST_CHECK(p == NULL);
2161
2162 TEST_CASE("Check for token shorter than string, not in the string");
2163 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2164 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "ng ", SIZE_MAX);
2165 TEST_CHECK(sbuff.p == sbuff.start);
2166 TEST_CHECK(p == NULL);
2167
2168 TEST_CASE("Check for token with zero length string");
2169 fr_sbuff_init_in(&sbuff, in, 0);
2170 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a", SIZE_MAX);
2171 TEST_CHECK(sbuff.p == sbuff.start);
2172 TEST_CHECK(p == NULL);
2173
2174 TEST_CASE("Check for token that is the string");
2175 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2176 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string", SIZE_MAX);
2177 TEST_CHECK(sbuff.p == sbuff.start);
2178 TEST_CHECK(sbuff.p == p);
2179 TEST_CHECK_STRCMP(p, "i am a test string");
2180
2181 TEST_CASE("Check for token not at beginning of string within length constraints");
2182 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2183 p = fr_sbuff_adv_to_str(&sbuff, 11, "test", SIZE_MAX);
2184 TEST_CHECK(sbuff.p == p);
2185 TEST_CHECK_STRCMP(sbuff.p, "test string");
2186
2187 TEST_CASE("Check for token not at beginning of string outside length constraints");
2188 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2189 TEST_CHECK(!fr_sbuff_adv_to_str(&sbuff, 10, "test", SIZE_MAX));
2190 TEST_CHECK(sbuff.p == sbuff.start);
2191}
2192
2193static void test_adv_to_strcase(void)
2194{
2195 fr_sbuff_t sbuff;
2196 char const in[] = "i am a test string";
2197 char *p;
2198
2199 TEST_CASE("Check for token at beginning of string");
2200 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2201 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a TEST", SIZE_MAX);
2202 TEST_CHECK(sbuff.p == p);
2203 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
2204
2205 TEST_CASE("Check for token not at beginning of string");
2206 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2207 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "tEst", SIZE_MAX);
2208 TEST_CHECK(sbuff.p == p);
2209 TEST_CHECK_STRCMP(sbuff.p, "test string");
2210
2211 TEST_CASE("Check for token at the end of string");
2212 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2213 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "Ing", SIZE_MAX);
2214 TEST_CHECK(sbuff.p == p);
2215 TEST_CHECK_STRCMP(sbuff.p, "ing");
2216
2217 TEST_CASE("Check for token larger than the string");
2218 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2219 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i aM a tEst stRIng ", SIZE_MAX);
2220 TEST_CHECK(sbuff.p == sbuff.start);
2221 TEST_CHECK(p == NULL);
2222
2223 TEST_CASE("Check for token shorter than string, not in the string");
2224 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2225 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "nG ", SIZE_MAX);
2226 TEST_CHECK(sbuff.p == sbuff.start);
2227 TEST_CHECK(p == NULL);
2228
2229 TEST_CASE("Check for token with zero length string");
2230 fr_sbuff_init_in(&sbuff, in, 0);
2231 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a", SIZE_MAX);
2232 TEST_CHECK(sbuff.p == sbuff.start);
2233 TEST_CHECK(p == NULL);
2234
2235 TEST_CASE("Check for token that is the string");
2236 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2237 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a teST stRIng", SIZE_MAX);
2238 TEST_CHECK(sbuff.p == sbuff.start);
2239 TEST_CHECK(sbuff.p == p);
2240 TEST_CHECK_STRCMP(p, "i am a test string");
2241
2242 TEST_CASE("Check for token not at beginning of string within length constraints");
2243 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2244 p = fr_sbuff_adv_to_strcase(&sbuff, 11, "tEst", SIZE_MAX);
2245 TEST_CHECK(sbuff.p == p);
2246 TEST_CHECK_STRCMP(sbuff.p, "test string");
2247
2248 TEST_CASE("Check for token not at beginning of string outside length constraints");
2249 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2250 TEST_CHECK(!fr_sbuff_adv_to_strcase(&sbuff, 10, "tEst", SIZE_MAX));
2251 TEST_CHECK(sbuff.p == sbuff.start);
2252}
2253
2254static void test_next_if_char(void)
2255{
2256 fr_sbuff_t sbuff;
2257 char const in[] = "i ";
2258
2259 TEST_CASE("Check for advancement on match");
2260 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2261 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == true);
2262 TEST_CHECK_STRCMP(sbuff.p, " ");
2263
2264 TEST_CASE("Check for non-advancement on non-match");
2265 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == false);
2266 TEST_CHECK_STRCMP(sbuff.p, " ");
2267
2268 TEST_CASE("Check for advancement at end");
2269 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == true);
2270 TEST_CHECK_STRCMP(sbuff.p, "");
2271
2272 TEST_CASE("Check we can't advance off the end of the buffer");
2273 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == false);
2274 TEST_CHECK_STRCMP(sbuff.p, "");
2275}
2276
2277static void test_next_unless_char(void)
2278{
2279 fr_sbuff_t sbuff;
2280 char const in[] = "i ";
2281
2282 TEST_CASE("Check for advancement on non-match");
2283 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
2284 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == true);
2285 TEST_CHECK_STRCMP(sbuff.p, " ");
2286
2287 TEST_CASE("Check for non-advancement on match");
2288 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == false);
2289 TEST_CHECK_STRCMP(sbuff.p, " ");
2290
2291 TEST_CASE("Check for advancement at end");
2292 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == true);
2293 TEST_CHECK_STRCMP(sbuff.p, "");
2294
2295 TEST_CASE("Check we can't advance off the end of the buffer");
2296 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == false);
2297 TEST_CHECK_STRCMP(sbuff.p, "");
2298}
2299
2301 /*
2302 * Basic tests
2303 */
2304 { "fr_sbuff_init", test_parse_init },
2305 { "fr_sbuff_is_char", test_is_char },
2306 { "fr_sbuff_out_bstrncpy_exact", test_bstrncpy_exact },
2307 { "fr_sbuff_out_bstrncpy", test_bstrncpy },
2308 { "fr_sbuff_out_bstrncpy_allowed", test_bstrncpy_allowed },
2309 { "fr_sbuff_out_bstrncpy_until", test_bstrncpy_until },
2310 { "multi-char terminals", test_unescape_multi_char_terminals },
2311 { "fr_sbuff_out_unescape_until", test_unescape_until },
2312 { "fr_sbuff_out_unescape_until_escape_bounds", test_unescape_until_escape_bounds },
2313 { "fr_sbuff_terminal_eof", test_eof_terminal },
2314 { "terminal search past visible end", test_terminal_search_past_visible_end },
2315 { "terminal search at end no overrun", test_terminal_search_at_end_no_overrun },
2316 { "terminal merge", test_terminal_merge },
2317
2318 /*
2319 * Extending buffer
2320 */
2321 { "fr_sbuff_talloc_extend", test_talloc_extend },
2322 { "fr_sbuff_talloc_extend_init_zero", test_talloc_extend_init_zero },
2323 { "fr_sbuff_talloc_extend_multi_level", test_talloc_extend_multi_level },
2324 { "fr_sbuff_talloc_extend_with_marker", test_talloc_extend_with_marker },
2325 { "fr_sbuff_talloc_extend_with_shift", test_talloc_extend_with_shift},
2326 { "fr_sbuff_shift_used_accounting", test_shift_used_accounting},
2327 { "fr_sbuff_marker_update_end", test_marker_update_end},
2328 { "fr_sbuff_marker_update_end_after_shift", test_marker_update_end_after_shift},
2329 { "fr_sbuff_file_extend", test_file_extend },
2330 { "fr_sbuff_file_extend_max", test_file_extend_max },
2331 { "fr_sbuff_file_extend_error", test_file_extend_error },
2332
2333 /*
2334 * Typed parsing
2335 */
2336 { "fr_sbuff_out_int", test_out_int },
2337 { "fr_sbuff_out_uint", test_out_uint },
2338 { "fr_sbuff_out_float", test_out_float },
2339 { "fr_sbuff_out_bool", test_out_bool },
2340
2341 { "fr_sbuff_no_advance", test_no_advance },
2342
2343 /*
2344 * Token skipping
2345 */
2346 { "fr_sbuff_adv_past_str", test_adv_past_str },
2347 { "fr_sbuff_adv_past_strcase", test_adv_past_strcase },
2348 { "fr_sbuff_adv_past_whitespace", test_adv_past_whitespace },
2349 { "fr_sbuff_adv_past_allowed", test_adv_past_allowed },
2350 { "fr_sbuff_adv_until", test_adv_until },
2351
2352 /*
2353 * Token searching
2354 */
2355 { "fr_sbuff_adv_to_utf8", test_adv_to_utf8 },
2356 { "fr_sbuff_adv_to_chr", test_adv_to_chr },
2357 { "fr_sbuff_adv_to_str", test_adv_to_str },
2358 { "fr_sbuff_adv_to_strcase", test_adv_to_strcase },
2359
2360 /*
2361 * Advancement
2362 */
2363 { "fr_sbuff_next_if_char", test_next_if_char },
2364 { "fr_sbuff_next_unless_char", test_next_unless_char },
2365
2367};
#define TEST_CHECK(cond)
Definition acutest.h:101
#define TEST_CASE(name)
Definition acutest.h:203
#define TEST_TERMINATOR
Definition acutest.h:77
#define TEST_MSG(...)
Definition acutest.h:234
Common initialisation for acutest binaries.
#define TEST_CHECK_SLEN(_got, _exp)
#define TEST_CHECK_LEN(_got, _exp)
#define TEST_CHECK_RET(_got, _exp)
#define TEST_CHECK_STRCMP(_got, _exp)
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define MEM(x)
Definition debug.h:38
free(array)
talloc_free(hp)
fr_sbuff_err_t fr_sbuff_out_bstrncpy_exact(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
#define SBUFF_CHAR_CLASS
long int ssize_t
fr_sbuff_err_t fr_sbuff_out_bstrncpy_allowed(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max, bool const allowed[static SBUFF_CHAR_CLASS])
fr_sbuff_err_t
@ FR_SBUFF_ERR_INPUT_EMPTY
@ FR_SBUFF_ERR_TRAILING
@ FR_SBUFF_ERR_NO_SPACE
@ FR_SBUFF_ERR_EXTEND
@ FR_SBUFF_ERR_UNDERFLOW
@ FR_SBUFF_ERR_OVERFLOW
@ FR_SBUFF_ERR_INPUT_SHORT
@ FR_SBUFF_ERR_NOT_FOUND
@ FR_SBUFF_OK
fr_sbuff_err_t fr_sbuff_out_bstrncpy_until(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
unsigned char uint8_t
fr_sbuff_err_t fr_sbuff_out_unescape_until(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
#define UINT8_MAX
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:1936
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:444
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:1529
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:2070
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:2151
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:2204
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:282
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:1871
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:212
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:2115
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2311
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:1902
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:2268
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:2011
fr_sbuff_err_t fr_sbuff_out_bstrncpy(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max)
Copy as many bytes as possible from a sbuff to a sbuff.
Definition sbuff.c:755
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:675
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:2247
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_shifted(_sbuff_or_marker)
#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:161
#define fr_sbuff_current(_sbuff_or_marker)
char chr
Character at the start of an escape sequence.
Definition sbuff.h:212
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:191
#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:171
#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_out(_out, _in)
#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:172
#define FR_SBUFF_TERM(_str)
Initialise a terminal structure with a single string.
Definition sbuff.h:179
Set of terminal elements.
File sbuff extension structure.
Definition sbuff.h:149
Talloc sbuff extension structure.
Definition sbuff.h:138
Set of parsing rules for *unescape_until functions.
static void test_out_int(void)
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:47
static void test_adv_until(void)
static void test_out_bool(void)
static void test_out_uint(void)
static void test_is_char(void)
Definition sbuff_tests.c:83
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_file_extend_error(void)
A read error on the file backing an sbuff is reported, not treated as the end of the input.
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:38
static void test_file_extend(void)
static void test_out_float(void)
static void test_terminal_merge(void)
static void test_parse_init(void)
Definition sbuff_tests.c:56
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 fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061