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.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, -7);
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, -25);
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
696{
697 char const in[] = "foo, bar, baz```";
698 fr_sbuff_t sbuff;
699 ssize_t slen = 0;
701 L(","),
702 L("```"),
703 L("bad"),
704 L("bar"),
705 L("boink"),
706 L("food"),
707 L("nyi")
708 );
709 char out[100];
710
711 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
712
713 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
714 TEST_CHECK_SLEN_RETURN(slen, 3);
715 TEST_CHECK_STRCMP(out, "foo");
716
717 fr_sbuff_advance(&sbuff, 1);
718
719 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
720 TEST_CHECK(slen == 1);
722
723 fr_sbuff_advance(&sbuff, 4);
724
725 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
726 TEST_CHECK(slen == 4);
727 TEST_CHECK_STRCMP(out, " baz");
728}
729
730static void test_eof_terminal(void)
731{
732 char const in[] = "foo, bar";
733 fr_sbuff_t sbuff;
734 ssize_t slen = 0;
736 L(""),
737 L(","),
738 );
740 L(",")
741 );
742 char out[100];
743
744 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
745
746 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
747 TEST_CHECK_SLEN_RETURN(slen, 3);
748 TEST_CHECK_STRCMP(out, "foo");
749
750 fr_sbuff_advance(&sbuff, 1); /* Advance past comma */
751
752 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
753 TEST_CHECK_SLEN_RETURN(slen, 4);
754 TEST_CHECK_STRCMP(out, " bar");
755
756 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt_eof) == true);
757 TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt) == false);
758}
759
760/*
761 * fr_sbuff_terminal_search() is called with a `p` that walks
762 * forward independently of in->p (see fr_sbuff_out_bstrncpy_until).
763 * `remaining` must be measured from `p`, not from `in->p` — otherwise
764 * memcmp() can read past in->end when matching a multi-byte terminal
765 * near the visible end of the buffer.
766 *
767 * The underlying C string is "abc}xyz" but the sbuff is initialised
768 * to only see the first 4 bytes ("abc}"). With a "}xyz" terminal,
769 * the (buggy) code would happily compare 4 bytes from p='}' against
770 * "}xyz" — reading the "xyz" tail past in->end and reporting a false
771 * match. The fix clamps the compare to (in->end - p) = 1 byte, so
772 * the partial-prefix match correctly falls through.
773 */
775{
776 char const in[] = "abc}xyz";
777 fr_sbuff_t sbuff;
779 L("}xyz")
780 );
781 char out[16];
782 ssize_t slen;
783
784 fr_sbuff_init_in(&sbuff, in, (size_t)4); /* Only "abc}" is visible */
785
786 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
787
788 TEST_CHECK_SLEN(slen, 4);
789 TEST_CHECK_STRCMP(out, "abc}");
790}
791
792static void test_terminal_merge(void)
793{
794 size_t i;
796 L(""),
797 L("\t"),
798 L("\n"),
799 L("\r"),
800 L(" "),
801 L("!"),
802 L("%"),
803 L("&"),
804 L("*"),
805 L("+"),
806 L("-"),
807 L("/"),
808 L("<"),
809 L("="),
810 L(">"),
811 L("^"),
812 L("{"),
813 L("|"),
814 L("~")
815 );
817 L(""),
818 L(")"),
819 );
820
821 fr_sbuff_term_t expect =
823 L(""),
824 L("\t"),
825 L("\n"),
826 L("\r"),
827 L(" "),
828 L("!"),
829 L("%"),
830 L("&"),
831 L(")"),
832 L("*"),
833 L("+"),
834 L("-"),
835 L("/"),
836 L("<"),
837 L("="),
838 L(">"),
839 L("^"),
840 L("{"),
841 L("|"),
842 L("~")
843 );
844 fr_sbuff_term_t *result;
845
846 result = fr_sbuff_terminals_amerge(NULL, &a, &b);
847 TEST_CHECK_LEN(result->len, expect.len);
848
849 for (i = 0; i < result->len; i++) {
850 TEST_CHECK_STRCMP(result->elem[i].str, expect.elem[i].str);
851 }
852
853 talloc_free(result);
854}
855
856static void test_no_advance(void)
857{
858 char const *in = "i am a test string";
859 char out[18 + 1] = "";
860 fr_sbuff_t sbuff;
861 ssize_t slen;
862
863 fr_sbuff_init_in(&sbuff, in, strlen(in));
864
865 TEST_CASE("Copy 5 bytes to out - no advance");
866 TEST_CHECK(sbuff.p == sbuff.start);
867 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &FR_SBUFF(&sbuff), 5);
868 TEST_CHECK_SLEN_RETURN(slen, 5);
869 TEST_CHECK(strcmp(out, "i am ") == 0);
870 TEST_CHECK(sbuff.p == sbuff.start);
871}
872
873static void test_talloc_extend(void)
874{
875 fr_sbuff_t sbuff;
877
878 TEST_CASE("Initial allocation");
879 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 32, 50) == &sbuff);
880 TEST_SBUFF_USED(&sbuff, 0);
881 TEST_SBUFF_LEN(&sbuff, 33);
882
883 TEST_CASE("Trim to zero");
884 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
885 TEST_SBUFF_USED(&sbuff, 0);
886 TEST_SBUFF_LEN(&sbuff, 1);
887
888 TEST_CASE("Print string - Should realloc to init");
889 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
890 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "0123456789") == 0);
891 TEST_SBUFF_USED(&sbuff, 10);
892 TEST_SBUFF_LEN(&sbuff, 33);
893
894 TEST_CASE("Trim to strlen");
895 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
896 TEST_SBUFF_LEN(&sbuff, 11);
897
898 TEST_CASE("Print string - Should realloc to init");
899 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
900 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789") == 0);
901 TEST_SBUFF_USED(&sbuff, 20);
902 TEST_SBUFF_LEN(&sbuff, 33);
903
904 TEST_CASE("Trim to strlen");
905 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
906 TEST_SBUFF_LEN(&sbuff, 21);
907
908 TEST_CASE("Print string - Should realloc to double buffer len");
909 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "012345678901234") == 15);
910 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234") == 0);
911 TEST_SBUFF_USED(&sbuff, 35);
912 TEST_SBUFF_LEN(&sbuff, 41);
913
914 TEST_CASE("Print string - Should only add a single char, should not extend the buffer");
915 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
916 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234A") == 0);
917 TEST_SBUFF_USED(&sbuff, 36);
918 TEST_SBUFF_LEN(&sbuff, 41);
919
920 TEST_CASE("Print string - Use all available buffer data");
921 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BCDE") == 4);
922 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDE") == 0);
923 TEST_SBUFF_USED(&sbuff, 40);
924 TEST_SBUFF_LEN(&sbuff, 41);
925
926 TEST_CASE("Print string - Add single char, should trigger doubling constrained by max");
927 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "F") == 1);
928 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEF") == 0);
929 TEST_SBUFF_USED(&sbuff, 41);
930 TEST_SBUFF_LEN(&sbuff, 51);
931
932 TEST_CASE("Print string - Add data to take us up to max");
933 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "GHIJKLMNO") == 9);
934 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
935 TEST_SBUFF_USED(&sbuff, 50);
936 TEST_SBUFF_LEN(&sbuff, 51);
937
938 TEST_CASE("Print string - Add single char, should fail");
939 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "P") == -1);
940 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
941 TEST_SBUFF_USED(&sbuff, 50);
942 TEST_SBUFF_LEN(&sbuff, 51);
943
944 TEST_CASE("Trim to strlen (should be noop)");
945 TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
946 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
947 TEST_SBUFF_USED(&sbuff, 50);
948 TEST_SBUFF_LEN(&sbuff, 51);
949
950 talloc_free(sbuff.buff);
951}
952
954{
955 fr_sbuff_t sbuff;
957
958 TEST_CASE("Initial allocation");
959 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 0, 50) == &sbuff);
960 TEST_SBUFF_USED(&sbuff, 0);
961 TEST_SBUFF_LEN(&sbuff, 1);
962
963 TEST_CASE("Print string - Should alloc one byte");
964 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
965 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "A") == 0);
966 TEST_SBUFF_USED(&sbuff, 1);
967 TEST_SBUFF_LEN(&sbuff, 2);
968
969 TEST_CASE("Print string - Should alloc two bytes");
970 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BC") == 2);
971 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABC") == 0);
972 TEST_SBUFF_USED(&sbuff, 3);
973 TEST_SBUFF_LEN(&sbuff, 4);
974
975 TEST_CASE("Print string - Should alloc three bytes");
976 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "D") == 1);
977 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABCD") == 0);
978 TEST_SBUFF_USED(&sbuff, 4);
979 TEST_SBUFF_LEN(&sbuff, 7);
980
981 talloc_free(sbuff.buff);
982}
983
985{
986 fr_sbuff_t sbuff_0, sbuff_1;
988
989 TEST_CASE("Initial allocation");
990 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
991 TEST_SBUFF_USED(&sbuff_0, 0);
992 TEST_SBUFF_LEN(&sbuff_0, 1);
993
994 sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0);
995 TEST_CASE("Check sbuff_1 has extend fields set");
996 TEST_CHECK(sbuff_0.extend == sbuff_1.extend);
997 TEST_CHECK(sbuff_0.uctx == sbuff_1.uctx);
998 TEST_CHECK(sbuff_1.parent == &sbuff_0);
999 TEST_SBUFF_USED(&sbuff_1, 0);
1000 TEST_SBUFF_LEN(&sbuff_1, 1);
1001
1002 TEST_CASE("Print string - Should alloc one byte");
1003 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "A") == 1);
1004 TEST_CHECK(strcmp(fr_sbuff_start(&sbuff_1), "A") == 0);
1005 TEST_SBUFF_USED(&sbuff_0, 1);
1006 TEST_SBUFF_LEN(&sbuff_0, 2);
1007 TEST_SBUFF_USED(&sbuff_1, 1);
1008 TEST_SBUFF_LEN(&sbuff_1, 2);
1009
1010 TEST_CHECK(sbuff_0.start == sbuff_1.start);
1011 TEST_CHECK(sbuff_0.end == sbuff_1.end);
1012 TEST_CHECK(sbuff_0.p == sbuff_1.p);
1013
1014 talloc_free(sbuff_0.buff);
1015}
1016
1018{
1019 fr_sbuff_t sbuff_0, sbuff_1;
1020 fr_sbuff_marker_t marker_0, marker_1;
1022
1023 TEST_CASE("Initial allocation");
1024 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
1025 TEST_SBUFF_USED(&sbuff_0, 0);
1026 TEST_SBUFF_LEN(&sbuff_0, 1);
1027
1028 TEST_CASE("Print string - Should alloc one byte");
1029 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "A") == 1);
1030 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "A");
1031 TEST_SBUFF_USED(&sbuff_0, 1);
1032 TEST_SBUFF_LEN(&sbuff_0, 2);
1033
1034 fr_sbuff_marker(&marker_0, &sbuff_0);
1035 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1036
1037 TEST_CASE("Print string - Ensure marker is updated");
1038 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "B") == 1);
1039 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "AB");
1040 TEST_SBUFF_USED(&sbuff_0, 2);
1041 TEST_SBUFF_LEN(&sbuff_0, 3);
1042 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1043
1044 TEST_CASE("Print string - Copy sbuff");
1045 sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0); /* Dup sbuff_0 */
1046 TEST_CHECK(sbuff_0.p == sbuff_1.start);
1047 fr_sbuff_marker(&marker_1, &sbuff_1);
1048
1049 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1050 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1051 TEST_CHECK(sbuff_0.p == sbuff_1.start);
1052
1053 TEST_CASE("Print string - Trigger re-alloc, ensure all pointers are updated");
1054 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "C") == 1);
1055 TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_1), "C");
1056 TEST_CHECK(sbuff_0.buff == sbuff_1.buff);
1057 TEST_CHECK(sbuff_0.p == sbuff_1.start + 1);
1058 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1059 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1060 TEST_SBUFF_USED(&sbuff_0, 3);
1061 TEST_SBUFF_LEN(&sbuff_0, 5);
1062
1063 talloc_free(sbuff_0.buff);
1064}
1065
1067{
1068 fr_sbuff_t sbuff;
1070
1071 TEST_CASE("Intermix shift and extend");
1072 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 4, 8) == &sbuff);
1073 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123") == 4);
1074 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "5678") == 4);
1075 TEST_CHECK(fr_sbuff_shift(&sbuff, 4, false) == 4);
1076 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "AAAA") == 4);
1077 TEST_CHECK(fr_sbuff_shift(&sbuff, 8, false) == 8);
1078 TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BBBBBBBB") == 8);
1079
1080 talloc_free(sbuff.buff);
1081}
1082
1083static void test_file_extend(void)
1084{
1085 fr_sbuff_t sbuff;
1086 fr_sbuff_t our_sbuff, child_sbuff;
1088 FILE *fp;
1089 char buff[5];
1090 char out[24];
1091 char fbuff[24];
1092 const char PATTERN[] = "xyzzy";
1093#define PATTERN_LEN (sizeof(PATTERN) - 1)
1094 char *post_ws;
1095 ssize_t slen;
1096
1097 static_assert(sizeof(buff) >= PATTERN_LEN, "Buffer must be sufficiently large to hold the pattern");
1098 static_assert((sizeof(fbuff) % sizeof(buff)) > 0, "sizeof buff must not be a multiple of fbuff");
1099 static_assert((sizeof(fbuff) % sizeof(buff)) < PATTERN_LEN, "remainder of sizeof(fbuff)/sizeof(buff) must be less than sizeof pattern");
1100
1101 TEST_CASE("Initialization");
1102 memset(fbuff, ' ', sizeof(fbuff));
1103 memcpy(fbuff + sizeof(fbuff) - PATTERN_LEN, PATTERN, PATTERN_LEN);
1104
1105 fp = fmemopen(fbuff, sizeof(fbuff), "r");
1106#ifdef __clang_analyzer__
1107 if (fp == NULL) return;
1108#endif
1109
1110 TEST_CHECK(fp != NULL);
1111 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1112 our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1113
1114 TEST_CASE("Advance past whitespace, which will require shift/extend");
1115 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&our_sbuff, SIZE_MAX, NULL), sizeof(fbuff) - PATTERN_LEN);
1116 TEST_CASE("Verify extend on unused child buffer");
1117 child_sbuff = FR_SBUFF(&our_sbuff);
1118 slen = fr_sbuff_extend_file(NULL, &child_sbuff, 0);
1119 TEST_CHECK_SLEN(slen, sizeof(fbuff) % PATTERN_LEN);
1120 TEST_CASE("Verify that we passed all and only whitespace");
1121 (void) fr_sbuff_out_abstrncpy(NULL, &post_ws, &our_sbuff, 24);
1122 TEST_CHECK_STRCMP(post_ws, PATTERN);
1123 talloc_free(post_ws);
1124 TEST_CASE("Verify parent buffer end");
1125 TEST_CHECK(sbuff.end == our_sbuff.end);
1126
1127 TEST_CASE("Verify that we do not read shifted buffer past eof");
1128 slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1129 TEST_CHECK_SLEN(slen, 0);
1130 slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1131 TEST_CHECK_SLEN(slen, 0);
1132 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, NULL, NULL);
1133 TEST_CHECK_SLEN(slen, 0);
1134 slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, allow_lowercase_and_space);
1135 TEST_CHECK_SLEN(slen, 0);
1136
1137 fclose(fp);
1138
1139 TEST_CASE("Verify fr_sbuff_out_bstrncpy_until() extends from file properly");
1140 fp = fmemopen(fbuff, sizeof(fbuff), "r");
1141#ifdef __clang_analyzer__
1142 if (fp == NULL) return;
1143#endif
1144
1145 TEST_CHECK(fp != NULL);
1146 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1147 our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1148 slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, &FR_SBUFF_TERM("x"), NULL);
1149 TEST_CHECK_SLEN(slen, sizeof(fbuff) - PATTERN_LEN);
1150
1151 fclose(fp);
1152}
1153
1154static void test_file_extend_max(void)
1155{
1156 fr_sbuff_t sbuff;
1158 FILE *fp;
1159 char buff[16];
1160 char fbuff[] = " xyzzy";
1161 char *post_ws;
1162
1163 TEST_CASE("Initialization");
1164 fp = fmemopen(fbuff, sizeof(fbuff) - 1, "r");
1165#ifdef __clang_analyzer__
1166 if (fp == NULL) return;
1167#endif
1168 TEST_CHECK(fp != NULL);
1169 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, sizeof(fbuff) - 8) == &sbuff);
1170
1171 TEST_CASE("Confirm that max stops us from seeing xyzzy");
1172 TEST_CHECK_SLEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), sizeof(fbuff) - 8);
1173 TEST_CHECK_SLEN(fr_sbuff_out_abstrncpy(NULL, &post_ws, &sbuff, 24), 0);
1174 TEST_CHECK_STRCMP(post_ws, "");
1175 talloc_free(post_ws);
1176 fclose(fp);
1177}
1178
1179static void test_adv_past_str(void)
1180{
1181 fr_sbuff_t sbuff;
1182 char const in[] = "i am a test string";
1183
1184 TEST_CASE("Check for token at beginning of string");
1185 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1186 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 6);
1187 TEST_CHECK_STRCMP(sbuff.p, " test string");
1188
1189 TEST_CASE("Check for token not at beginning of string");
1190 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1191 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, " am a", SIZE_MAX), 0);
1192 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1193
1194 TEST_CASE("Check for token larger than the string");
1195 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1196 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string ", SIZE_MAX), 0);
1197 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1198
1199 TEST_CASE("Check for token with zero length string");
1200 fr_sbuff_init_in(&sbuff, in, 0);
1201 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 0);
1202
1203 TEST_CASE("Check for token that is the string");
1204 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1205 TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string", SIZE_MAX), 18);
1206 TEST_CHECK_STRCMP(sbuff.p, "");
1207 TEST_CHECK(sbuff.p == sbuff.end);
1208}
1209
1210static void test_adv_past_strcase(void)
1211{
1212 fr_sbuff_t sbuff;
1213 char const in[] = "i am a test string";
1214
1215 TEST_CASE("Check for token at beginning of string");
1216 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1217 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 6);
1218 TEST_CHECK_STRCMP(sbuff.p, " test string");
1219
1220 TEST_CASE("Check for token not at beginning of string");
1221 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1222 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, " AM a", SIZE_MAX), 0);
1223 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1224
1225 TEST_CASE("Check for token larger than the string");
1226 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1227 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string ", SIZE_MAX), 0);
1228 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1229
1230 TEST_CASE("Check for token with zero length string");
1231 fr_sbuff_init_in(&sbuff, in, 0);
1232 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 0);
1233
1234 TEST_CASE("Check for token that is the string");
1235 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1236 TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string", SIZE_MAX), 18);
1237 TEST_CHECK_STRCMP(sbuff.p, "");
1238 TEST_CHECK(sbuff.p == sbuff.end);
1239}
1240
1242{
1243 fr_sbuff_t sbuff;
1244 char const in[] = " i am a test string";
1245 char const in_ns[] = "i am a test string";
1246 char const in_ws[] = " ";
1247
1248 TEST_CASE("Check for token at beginning of string");
1249 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1250 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1251 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1252
1253 TEST_CASE("Check for token not at beginning of string");
1254 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1255 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1256 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1257
1258 TEST_CASE("Check for token with zero length string");
1259 fr_sbuff_init_in(&sbuff, in, 0);
1260 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1261
1262 TEST_CASE("Check for token that is the string");
1263 fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1264 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1265
1266 TEST_CASE("Length constraint with token match");
1267 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1268 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 2);
1269 TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1270
1271 TEST_CASE("Length constraint without token match");
1272 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1273 TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 0);
1274 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1275}
1276
1277static void test_adv_past_allowed(void)
1278{
1279 fr_sbuff_t sbuff;
1280 char const in[] = " i am a test string";
1281 char const in_ns[] = "i am a test string";
1282 char const in_ws[] = " ";
1283
1284 TEST_CASE("Check for token at beginning of string");
1285 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1286 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 5);
1287 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1288
1289 TEST_CASE("Check for token not at beginning of string");
1290 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1291 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1292 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1293
1294 TEST_CASE("Check for token with zero length string");
1295 fr_sbuff_init_in(&sbuff, in, 0);
1296 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1297 TEST_CHECK(sbuff.p == sbuff.start);
1298
1299 TEST_CASE("Check for token at the end of the string");
1300 fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1301 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 5);
1302 TEST_CHECK(sbuff.p == sbuff.end);
1303
1304 TEST_CASE("Length constraint with token match");
1305 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1306 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 2);
1307 TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1308
1309 TEST_CASE("Length constraint with token match");
1310 fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1311 TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[SBUFF_CHAR_CLASS]){ [' '] = true }, NULL), 0);
1312 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1313}
1314
1315static void test_adv_until(void)
1316{
1317 fr_sbuff_t sbuff;
1318 char const in[] = " abcdefgh ijklmnopp";
1319
1320 TEST_CASE("Check for token at beginning of string");
1321 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1322 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM(" "), '\0'), 0);
1323 TEST_CHECK_STRCMP(sbuff.p, " abcdefgh ijklmnopp");
1324
1325 TEST_CASE("Check for token not at beginning of string");
1326 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1327 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 1);
1328 TEST_CHECK_STRCMP(sbuff.p, "abcdefgh ijklmnopp");
1329
1330 TEST_CASE("Check for token with zero length string");
1331 fr_sbuff_init_in(&sbuff, in, 0);
1332 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 0);
1333 TEST_CHECK(sbuff.p == sbuff.start);
1334
1335 TEST_CASE("Check for token that is not in the string");
1336 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1337 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("|"), '\0'), 19);
1338 TEST_CHECK(sbuff.p == sbuff.end);
1339
1340 TEST_CASE("Check escapes");
1341 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1342 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("p"), 'o'), 18);
1343 TEST_CHECK_STRCMP(sbuff.p, "p");
1344
1345 TEST_CASE("Check for token that is not in the string with length constraint");
1346 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1347 TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, 5, &FR_SBUFF_TERM("|"), '\0'), 5);
1348 TEST_CHECK(sbuff.p == (sbuff.start + 5));
1349}
1350
1351static void test_adv_to_utf8(void)
1352{
1353 fr_sbuff_t sbuff;
1354 char const in[] = "🥺🥺🥺🥺🍪😀";
1355 char *p;
1356
1357 TEST_CASE("Check for token at beginning of string");
1358 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1359 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🥺");
1360 TEST_CHECK(p == sbuff.p);
1361 TEST_CHECK_STRCMP(sbuff.p, "🥺🥺🥺🥺🍪😀");
1362
1363 TEST_CASE("Check for token not at beginning of string");
1364 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1365 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1366 TEST_CHECK(p == (sbuff.start + (sizeof("🥺🥺🥺🥺") - 1)));
1367 TEST_CHECK_STRCMP(p, "🍪😀");
1368
1369 TEST_CASE("Check for token with zero length string");
1370 fr_sbuff_init_in(&sbuff, in, 0);
1371 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1372 TEST_CHECK(p == NULL);
1373 TEST_CHECK(sbuff.start == sbuff.p);
1374
1375 TEST_CASE("Check for token at the end of the string");
1376 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1377 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "😀");
1378 TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1379
1380 TEST_CASE("Check for token not in the string");
1381 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1382 p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍆 ");
1383 TEST_CHECK(p == NULL);
1384
1385 TEST_CASE("Check for token at the end of the string within len constraints");
1386 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1387 p = fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 1), "😀");
1388 TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1389
1390 TEST_CASE("Check for token at the end of the string outside len constraints #1");
1391 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1392 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 2), "😀"));
1393 TEST_CHECK(sbuff.p == sbuff.start);
1394
1395 TEST_CASE("Check for token at the end of the string outside len constraints #2");
1396 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1397 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 3), "😀"));
1398 TEST_CHECK(sbuff.p == sbuff.start);
1399
1400 TEST_CASE("Check for token at the end of the string outside len constraints #3");
1401 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1402 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 4), "😀"));
1403 TEST_CHECK(sbuff.p == sbuff.start);
1404
1405 TEST_CASE("Check for token at the end of the string outside len constraints #4");
1406 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1407 TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 5), "😀"));
1408 TEST_CHECK(sbuff.p == sbuff.start);
1409}
1410
1411static void test_adv_to_chr(void)
1412{
1413 fr_sbuff_t sbuff;
1414 char const in[] = "AAAAbC";
1415 char *p;
1416
1417 TEST_CASE("Check for token at beginning of string");
1418 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1419 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'A');
1420 TEST_CHECK(p == sbuff.p);
1421 TEST_CHECK_STRCMP(sbuff.p, "AAAAbC");
1422
1423 TEST_CASE("Check for token not at beginning of string");
1424 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1425 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b');
1426 TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1427 TEST_CHECK_STRCMP(p, "bC");
1428
1429 TEST_CASE("Check for token with zero length string");
1430 fr_sbuff_init_in(&sbuff, in, 0);
1431 TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b'));
1432 TEST_CHECK(sbuff.start == sbuff.p);
1433
1434 TEST_CASE("Check for token at the end of the string");
1435 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1436 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'C');
1437 TEST_CHECK(p == sbuff.start + (sizeof("AAAAb") - 1));
1438
1439 TEST_CASE("Check for token not in the string");
1440 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1441 p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'D');
1442 TEST_CHECK(p == NULL);
1443
1444 TEST_CASE("Check for token not at beginning of string within length constraints");
1445 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1446 p = fr_sbuff_adv_to_chr(&sbuff, 5, 'b');
1447 TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1448 TEST_CHECK_STRCMP(p, "bC");
1449
1450 TEST_CASE("Check for token not at beginning of string outside length constraints");
1451 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1452 TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, 4, 'b'));
1453 TEST_CHECK(sbuff.p == sbuff.start);
1454}
1455
1456static void test_adv_to_str(void)
1457{
1458 fr_sbuff_t sbuff;
1459 char const in[] = "i am a test string";
1460 char *p;
1461
1462 TEST_CASE("Check for token at beginning of string");
1463 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1464 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test", SIZE_MAX);
1465 TEST_CHECK(sbuff.p == p);
1466 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1467
1468 TEST_CASE("Check for token not at beginning of string");
1469 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1470 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "test", SIZE_MAX);
1471 TEST_CHECK(sbuff.p == p);
1472 TEST_CHECK_STRCMP(sbuff.p, "test string");
1473
1474 TEST_CASE("Check for token at the end of string");
1475 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1476 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "ing", SIZE_MAX);
1477 TEST_CHECK(sbuff.p == p);
1478 TEST_CHECK_STRCMP(sbuff.p, "ing");
1479
1480 TEST_CASE("Check for token larger than the string");
1481 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1482 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string ", SIZE_MAX);
1483 TEST_CHECK(sbuff.p == sbuff.start);
1484 TEST_CHECK(p == NULL);
1485
1486 TEST_CASE("Check for token shorter than string, not in the string");
1487 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1488 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "ng ", SIZE_MAX);
1489 TEST_CHECK(sbuff.p == sbuff.start);
1490 TEST_CHECK(p == NULL);
1491
1492 TEST_CASE("Check for token with zero length string");
1493 fr_sbuff_init_in(&sbuff, in, 0);
1494 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a", SIZE_MAX);
1495 TEST_CHECK(sbuff.p == sbuff.start);
1496 TEST_CHECK(p == NULL);
1497
1498 TEST_CASE("Check for token that is the string");
1499 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1500 p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string", SIZE_MAX);
1501 TEST_CHECK(sbuff.p == sbuff.start);
1502 TEST_CHECK(sbuff.p == p);
1503 TEST_CHECK_STRCMP(p, "i am a test string");
1504
1505 TEST_CASE("Check for token not at beginning of string within length constraints");
1506 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1507 p = fr_sbuff_adv_to_str(&sbuff, 11, "test", SIZE_MAX);
1508 TEST_CHECK(sbuff.p == p);
1509 TEST_CHECK_STRCMP(sbuff.p, "test string");
1510
1511 TEST_CASE("Check for token not at beginning of string outside length constraints");
1512 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1513 TEST_CHECK(!fr_sbuff_adv_to_str(&sbuff, 10, "test", SIZE_MAX));
1514 TEST_CHECK(sbuff.p == sbuff.start);
1515}
1516
1517static void test_adv_to_strcase(void)
1518{
1519 fr_sbuff_t sbuff;
1520 char const in[] = "i am a test string";
1521 char *p;
1522
1523 TEST_CASE("Check for token at beginning of string");
1524 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1525 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a TEST", SIZE_MAX);
1526 TEST_CHECK(sbuff.p == p);
1527 TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1528
1529 TEST_CASE("Check for token not at beginning of string");
1530 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1531 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "tEst", SIZE_MAX);
1532 TEST_CHECK(sbuff.p == p);
1533 TEST_CHECK_STRCMP(sbuff.p, "test string");
1534
1535 TEST_CASE("Check for token at the end of string");
1536 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1537 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "Ing", SIZE_MAX);
1538 TEST_CHECK(sbuff.p == p);
1539 TEST_CHECK_STRCMP(sbuff.p, "ing");
1540
1541 TEST_CASE("Check for token larger than the string");
1542 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1543 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i aM a tEst stRIng ", SIZE_MAX);
1544 TEST_CHECK(sbuff.p == sbuff.start);
1545 TEST_CHECK(p == NULL);
1546
1547 TEST_CASE("Check for token shorter than string, not in the string");
1548 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1549 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "nG ", SIZE_MAX);
1550 TEST_CHECK(sbuff.p == sbuff.start);
1551 TEST_CHECK(p == NULL);
1552
1553 TEST_CASE("Check for token with zero length string");
1554 fr_sbuff_init_in(&sbuff, in, 0);
1555 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a", SIZE_MAX);
1556 TEST_CHECK(sbuff.p == sbuff.start);
1557 TEST_CHECK(p == NULL);
1558
1559 TEST_CASE("Check for token that is the string");
1560 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1561 p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a teST stRIng", SIZE_MAX);
1562 TEST_CHECK(sbuff.p == sbuff.start);
1563 TEST_CHECK(sbuff.p == p);
1564 TEST_CHECK_STRCMP(p, "i am a test string");
1565
1566 TEST_CASE("Check for token not at beginning of string within length constraints");
1567 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1568 p = fr_sbuff_adv_to_strcase(&sbuff, 11, "tEst", SIZE_MAX);
1569 TEST_CHECK(sbuff.p == p);
1570 TEST_CHECK_STRCMP(sbuff.p, "test string");
1571
1572 TEST_CASE("Check for token not at beginning of string outside length constraints");
1573 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1574 TEST_CHECK(!fr_sbuff_adv_to_strcase(&sbuff, 10, "tEst", SIZE_MAX));
1575 TEST_CHECK(sbuff.p == sbuff.start);
1576}
1577
1578static void test_next_if_char(void)
1579{
1580 fr_sbuff_t sbuff;
1581 char const in[] = "i ";
1582
1583 TEST_CASE("Check for advancement on match");
1584 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1585 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == true);
1586 TEST_CHECK_STRCMP(sbuff.p, " ");
1587
1588 TEST_CASE("Check for non-advancement on non-match");
1589 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == false);
1590 TEST_CHECK_STRCMP(sbuff.p, " ");
1591
1592 TEST_CASE("Check for advancement at end");
1593 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == true);
1594 TEST_CHECK_STRCMP(sbuff.p, "");
1595
1596 TEST_CASE("Check we can't advance off the end of the buffer");
1597 TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == false);
1598 TEST_CHECK_STRCMP(sbuff.p, "");
1599}
1600
1601static void test_next_unless_char(void)
1602{
1603 fr_sbuff_t sbuff;
1604 char const in[] = "i ";
1605
1606 TEST_CASE("Check for advancement on non-match");
1607 fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1608 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == true);
1609 TEST_CHECK_STRCMP(sbuff.p, " ");
1610
1611 TEST_CASE("Check for non-advancement on match");
1612 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == false);
1613 TEST_CHECK_STRCMP(sbuff.p, " ");
1614
1615 TEST_CASE("Check for advancement at end");
1616 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == true);
1617 TEST_CHECK_STRCMP(sbuff.p, "");
1618
1619 TEST_CASE("Check we can't advance off the end of the buffer");
1620 TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == false);
1621 TEST_CHECK_STRCMP(sbuff.p, "");
1622}
1623
1625 /*
1626 * Basic tests
1627 */
1628 { "fr_sbuff_init", test_parse_init },
1629 { "fr_sbuff_is_char", test_is_char },
1630 { "fr_sbuff_out_bstrncpy_exact", test_bstrncpy_exact },
1631 { "fr_sbuff_out_bstrncpy", test_bstrncpy },
1632 { "fr_sbuff_out_bstrncpy_allowed", test_bstrncpy_allowed },
1633 { "fr_sbuff_out_bstrncpy_until", test_bstrncpy_until },
1634 { "multi-char terminals", test_unescape_multi_char_terminals },
1635 { "fr_sbuff_out_unescape_until", test_unescape_until },
1636 { "fr_sbuff_terminal_eof", test_eof_terminal },
1637 { "terminal search past visible end", test_terminal_search_past_visible_end },
1638 { "terminal merge", test_terminal_merge },
1639
1640 /*
1641 * Extending buffer
1642 */
1643 { "fr_sbuff_talloc_extend", test_talloc_extend },
1644 { "fr_sbuff_talloc_extend_init_zero", test_talloc_extend_init_zero },
1645 { "fr_sbuff_talloc_extend_multi_level", test_talloc_extend_multi_level },
1646 { "fr_sbuff_talloc_extend_with_marker", test_talloc_extend_with_marker },
1647 { "fr_sbuff_talloc_extend_with_shift", test_talloc_extend_with_shift},
1648 { "fr_sbuff_file_extend", test_file_extend },
1649 { "fr_sbuff_file_extend_max", test_file_extend_max },
1650
1651 { "fr_sbuff_no_advance", test_no_advance },
1652
1653 /*
1654 * Token skipping
1655 */
1656 { "fr_sbuff_adv_past_str", test_adv_past_str },
1657 { "fr_sbuff_adv_past_strcase", test_adv_past_strcase },
1658 { "fr_sbuff_adv_past_whitespace", test_adv_past_whitespace },
1659 { "fr_sbuff_adv_past_allowed", test_adv_past_allowed },
1660 { "fr_sbuff_adv_until", test_adv_until },
1661
1662 /*
1663 * Token searching
1664 */
1665 { "fr_sbuff_adv_to_utf8", test_adv_to_utf8 },
1666 { "fr_sbuff_adv_to_chr", test_adv_to_chr },
1667 { "fr_sbuff_adv_to_str", test_adv_to_str },
1668 { "fr_sbuff_adv_to_strcase", test_adv_to_strcase },
1669
1670 /*
1671 * Advancement
1672 */
1673 { "fr_sbuff_next_if_char", test_next_if_char },
1674 { "fr_sbuff_next_unless_char", test_next_unless_char },
1675
1677};
#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_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:882
talloc_free(hp)
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])
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:1865
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:1469
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:1999
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:2080
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:2133
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:1800
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:2044
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2240
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:1831
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:2197
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:1940
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:733
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:655
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:2176
#define fr_sbuff_start(_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
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
size_t len
Length of the list.
Definition sbuff.h:170
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#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_OUT(_start, _len_or_end)
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_file_extend_max(void)
static void test_talloc_extend_multi_level(void)
static void test_adv_past_strcase(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_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_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 char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
static size_t char ** out
Definition value.h:1030