The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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/sbuff_tests.c
20  *
21  * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22  */
23 #include <freeradius-devel/util/acutest.h>
24 #include <freeradius-devel/util/acutest_helpers.h>
25 
26 #include "sbuff.h"
27 
28 //#include <gperftools/profiler.h>
29 
30 #define TEST_SBUFF_LEN(_sbuff, _num) \
31 do { \
32  size_t _len; \
33  _len = talloc_array_length((_sbuff)->buff); \
34  TEST_CHECK(_len == (size_t)_num); \
35  TEST_MSG("Expected length : %zu", (size_t)_num); \
36  TEST_MSG("Got length : %zu", _len); \
37 } while(0)
38 
39 #define TEST_SBUFF_USED(_sbuff, _num) \
40 do { \
41  size_t _len; \
42  _len = fr_sbuff_used(_sbuff); \
43  TEST_CHECK(_len == (size_t)_num); \
44  TEST_MSG("Expected length : %zu", (size_t)_num); \
45  TEST_MSG("Got length : %zu", _len); \
46 } while(0)
47 
48 static void test_parse_init(void)
49 {
50  char const in[] = "i am a test string";
51  fr_sbuff_t sbuff;
52 
53  TEST_CASE("Parse init with size");
54  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
55 
56  TEST_CHECK(sbuff.start == in);
57  TEST_CHECK(sbuff.p == in);
58  TEST_CHECK(sbuff.end == in + (sizeof(in) - 1));
59 
60  TEST_CASE("Parse init with end");
61  fr_sbuff_init_in(&sbuff, in, in + strlen(in));
62 
63  TEST_CHECK(sbuff.start == in);
64  TEST_CHECK(sbuff.p == in);
65  TEST_CHECK(sbuff.end == in + strlen(in));
66 
67  TEST_CASE("Parse init with const end");
68  fr_sbuff_init_in(&sbuff, in, (char const *)(in + strlen(in)));
69 
70  TEST_CHECK(sbuff.start == in);
71  TEST_CHECK(sbuff.p == in);
72  TEST_CHECK(sbuff.end == in + strlen(in));
73 }
74 
75 static void test_is_char(void)
76 {
77  char const in[] = "i am a test string";
78  fr_sbuff_t sbuff;
79  fr_sbuff_marker_t marker;
80 
81  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
82  TEST_CHECK(fr_sbuff_is_char(&sbuff, 'i'));
83  TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'z'));
84 
85  fr_sbuff_advance(&sbuff, 2);
86  TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'i'));
87  TEST_CHECK(fr_sbuff_is_char(&sbuff, 'a'));
88 
89  fr_sbuff_advance(&sbuff, 15);
90  TEST_CHECK(fr_sbuff_is_char(&sbuff, 'g'));
91  fr_sbuff_marker(&marker, &sbuff);
92  TEST_CHECK(fr_sbuff_is_char(&marker, 'g'));
93 
94  /*
95  * Ensure that after advancing the buffer past
96  * the end, the marker can still be correctly
97  * tested
98  */
99  fr_sbuff_advance(&sbuff, 1);
100  TEST_CHECK(!fr_sbuff_is_char(&sbuff, 'g'));
101  TEST_CHECK(fr_sbuff_is_char(&marker, 'g'));
102 }
103 
104 static void test_bstrncpy_exact(void)
105 {
106  char const in[] = "i am a test string";
107  char const in_long[] = "i am a longer test string";
108  char out[18 + 1] = "";
109  fr_sbuff_t sbuff;
110  ssize_t slen;
111 
112  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
113 
114  TEST_CASE("Copy 5 bytes to out");
115  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5);
116  TEST_CHECK_SLEN_RETURN(slen, 5);
117  TEST_CHECK_STRCMP(out, "i am ");
118  TEST_CHECK_STRCMP(sbuff.p, "a test string");
119 
120  TEST_CASE("Copy 13 bytes to out");
121  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13);
122  TEST_CHECK_SLEN(slen, 13);
123  TEST_CHECK_STRCMP(out, "a test string");
124  TEST_CHECK_STRCMP(sbuff.p, "");
125  TEST_CHECK(sbuff.p == sbuff.end);
126 
127  TEST_CASE("Copy would overrun input");
128  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1);
129  TEST_CHECK_SLEN(slen, 0);
130  TEST_CHECK(sbuff.p == sbuff.end);
131 
132  TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
133  fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
134 
135  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX);
136  TEST_CHECK_SLEN(slen, -7);
137  TEST_CHECK(sbuff.p == sbuff.start);
138 
139  TEST_CASE("Zero length output buffer");
140  fr_sbuff_set_to_start(&sbuff);
141  out[0] = 'a';
142  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX);
143  TEST_CHECK_SLEN(slen, -25);
144  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
145  TEST_CHECK(sbuff.p == sbuff.start);
146 
147  TEST_CASE("Zero length size");
148  fr_sbuff_set_to_start(&sbuff);
149  out[0] = 'a';
150  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, 0);
151  TEST_CHECK_SLEN(slen, 0);
152  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
153  TEST_CHECK(sbuff.p == sbuff.start);
154 }
155 
156 static void test_bstrncpy(void)
157 {
158  char const in[] = "i am a test string";
159  char const in_long[] = "i am a longer test string";
160  char out[18 + 1] = "";
161  fr_sbuff_t sbuff;
162  ssize_t slen;
163 
164  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
165 
166  TEST_CASE("Copy 5 bytes to out");
167  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5);
168  TEST_CHECK_SLEN_RETURN(slen, 5);
169  TEST_CHECK_STRCMP(out, "i am ");
170  TEST_CHECK_STRCMP(sbuff.p, "a test string");
171 
172  TEST_CASE("Copy 13 bytes to out");
173  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13);
174  TEST_CHECK_SLEN(slen, 13);
175  TEST_CHECK_STRCMP(out, "a test string");
176  TEST_CHECK_STRCMP(sbuff.p, "");
177  TEST_CHECK(sbuff.p == sbuff.end);
178 
179  TEST_CASE("Copy would overrun input");
180  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1);
181  TEST_CHECK_SLEN(slen, 0);
182  TEST_CHECK(sbuff.p == sbuff.end);
183 
184  TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
185  fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
186 
187  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX);
188  TEST_CHECK_SLEN(slen, 18);
189  TEST_CHECK_STRCMP(out, "i am a longer test");
190 
191  TEST_CASE("Zero length output buffer");
192  fr_sbuff_set_to_start(&sbuff);
193  out[0] = 'a';
194  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX);
195  TEST_CHECK_SLEN(slen, 0);
196  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
197  TEST_CHECK(sbuff.p == sbuff.start);
198 
199  TEST_CASE("Zero length size");
200  fr_sbuff_set_to_start(&sbuff);
201  out[0] = 'a';
202  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, 0);
203  TEST_CHECK_SLEN(slen, 0);
204  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
205  TEST_CHECK(sbuff.p == sbuff.start);
206 }
207 
209  ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
210  ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
211  ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
212  ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true,
213  ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
214  ['z'] = true, [' '] = true
215 };
216 
218  ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
219  ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
220  ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
221  ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = false,
222  ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
223  ['z'] = true, [' '] = true
224 };
225 
226 static void test_bstrncpy_allowed(void)
227 {
228  char const in[] = "i am a test string";
229  char const in_long[] = "i am a longer test string";
230  char out[18 + 1] = "";
231  fr_sbuff_t sbuff;
232  ssize_t slen;
233 
234  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
235 
236  /*
237  * Should behave identically to bstrncpy
238  * where there's no restrictions on char
239  * set.
240  */
241  TEST_CASE("Copy 5 bytes to out");
243  TEST_CHECK_SLEN_RETURN(slen, 5);
244  TEST_CHECK_STRCMP(out, "i am ");
245  TEST_CHECK_STRCMP(sbuff.p, "a test string");
246 
247  TEST_CASE("Copy 13 bytes to out");
249  TEST_CHECK_SLEN(slen, 13);
250  TEST_CHECK_STRCMP(out, "a test string");
251  TEST_CHECK_STRCMP(sbuff.p, "");
252  TEST_CHECK(sbuff.p == sbuff.end);
253 
254  TEST_CASE("Copy would overrun input");
256  TEST_CHECK_SLEN(slen, 0);
257  TEST_CHECK(sbuff.p == sbuff.end);
258 
259  TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
260  fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long));
261 
262  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, allow_lowercase_and_space);
263  TEST_CHECK_SLEN(slen, 18);
264  TEST_CHECK_STRCMP(out, "i am a longer test");
265 
266  TEST_CASE("Zero length output buffer");
267  fr_sbuff_set_to_start(&sbuff);
268  out[0] = 'a';
269  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, allow_lowercase_and_space);
270  TEST_CHECK_SLEN(slen, 0);
271  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
272  TEST_CHECK(sbuff.p == sbuff.start);
273 
274  TEST_CASE("Zero length size");
275  fr_sbuff_set_to_start(&sbuff);
276  out[0] = 'a';
277  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, allow_lowercase_and_space);
278  TEST_CHECK_SLEN(slen, 0);
279  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
280  TEST_CHECK(sbuff.p == sbuff.start);
281 
282  /*
283  * Check copy stops early
284  */
285  TEST_CASE("Copy until first t");
286  fr_sbuff_set_to_start(&sbuff);
287  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
289  TEST_CHECK_SLEN(slen, 14);
290  TEST_CHECK_STRCMP(out, "i am a longer ");
291 
292  TEST_CASE("Copy until first t with length constraint (same len as token)");
293  fr_sbuff_set_to_start(&sbuff);
294  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX,
296  TEST_CHECK_SLEN(slen, 14);
297  TEST_CHECK_STRCMP(out, "i am a longer ");
298 
299  TEST_CASE("Copy until first t with length constraint (one shorter than token)");
300  fr_sbuff_set_to_start(&sbuff);
301  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
303  TEST_CHECK_SLEN(slen, 13);
304  TEST_CHECK_STRCMP(out, "i am a longer");
305 
306  TEST_CASE("Zero length token (should still be terminated)");
307  fr_sbuff_set_to_start(&sbuff);
308  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
309  (bool[UINT8_MAX + 1]){});
310  TEST_CHECK_SLEN(slen, 0);
311  TEST_CHECK_STRCMP(out, "");
312 }
313 
314 static void test_bstrncpy_until(void)
315 {
316  char const in[] = "i am a test string";
317  char const in_long[] = "i am a longer test string";
318  char out[18 + 1];
319  fr_sbuff_t sbuff;
320  ssize_t slen;
321 
322  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
323 
324  /*
325  * Should behave identically to bstrncpy
326  * where there's no restrictions on char
327  * set.
328  */
329  TEST_CASE("Copy 5 bytes to out");
330  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, NULL);
331  TEST_CHECK_SLEN_RETURN(slen, 5);
332  TEST_CHECK_STRCMP(out, "i am ");
333  TEST_CHECK_STRCMP(sbuff.p, "a test string");
334 
335  TEST_CASE("Copy 13 bytes to out");
336  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13, NULL, NULL);
337  TEST_CHECK_SLEN(slen, 13);
338  TEST_CHECK_STRCMP(out, "a test string");
339  TEST_CHECK_STRCMP(sbuff.p, "");
340  TEST_CHECK(sbuff.p == sbuff.end);
341 
342  TEST_CASE("Copy would overrun input");
343  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1, NULL, NULL);
344  TEST_CHECK_SLEN(slen, 0);
345  TEST_CHECK(sbuff.p == sbuff.end);
346 
347  TEST_CASE("Check escapes");
348  fr_sbuff_set_to_start(&sbuff);
349  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
350  &FR_SBUFF_TERM("g"), &(fr_sbuff_unescape_rules_t){ .chr = 'n' });
351  TEST_CHECK_SLEN(slen, 18);
352  TEST_CHECK_STRCMP(out, "i am a test string");
353  TEST_CHECK_STRCMP(sbuff.p, "");
354 
355  TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
356  fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
357 
358  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, NULL);
359  TEST_CHECK_SLEN(slen, 18);
360  TEST_CHECK_STRCMP(out, "i am a longer test");
361 
362  TEST_CASE("Zero length output buffer");
363  fr_sbuff_set_to_start(&sbuff);
364  out[0] = 'a';
365  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, NULL, NULL);
366  TEST_CHECK_SLEN(slen, 0);
367  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
368  TEST_CHECK(sbuff.p == sbuff.start);
369 
370  TEST_CASE("Zero length size");
371  fr_sbuff_set_to_start(&sbuff);
372  out[0] = 'a';
373  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 0, NULL, NULL);
374  TEST_CHECK_SLEN(slen, 0);
375  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
376  TEST_CHECK(sbuff.p == sbuff.start);
377 
378  /*
379  * Check copy stops early
380  */
381  TEST_CASE("Copy until first t");
382  fr_sbuff_set_to_start(&sbuff);
383  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
384  TEST_CHECK_SLEN(slen, 14);
385  TEST_CHECK_STRCMP(out, "i am a longer ");
386 
387  TEST_CASE("Copy until first t with length constraint (same len as token)");
388  fr_sbuff_set_to_start(&sbuff);
389  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
390  TEST_CHECK_SLEN(slen, 14);
391  TEST_CHECK_STRCMP(out, "i am a longer ");
392 
393  TEST_CASE("Copy until first t with length constraint (one shorter than token)");
394  fr_sbuff_set_to_start(&sbuff);
395  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("t"), NULL);
396  TEST_CHECK_SLEN(slen, 13);
397  TEST_CHECK_STRCMP(out, "i am a longer");
398 
399  TEST_CASE("Zero length token (should still be terminated)");
400  fr_sbuff_set_to_start(&sbuff);
401  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX, &FR_SBUFF_TERM("i"), NULL);
402  TEST_CHECK_SLEN(slen, 0);
403  TEST_CHECK_STRCMP(out, "");
404 }
405 
406 static void test_unescape_until(void)
407 {
408  char const in[] = "i am a test string";
409  char const in_long[] = "i am a longer test string";
410  char const in_escapes[] = "i am a |t|est strin|g";
411  char const in_escapes_seq[] = "i |x|0am a |t|est strin|g|x20|040";
412  char out[18 + 1] = "";
413  char escape_out[20 + 1];
414 
415  fr_sbuff_t sbuff;
416  ssize_t slen;
417 
418  fr_sbuff_unescape_rules_t rules = {
419  .chr = '\\'
420  };
421 
422  fr_sbuff_unescape_rules_t pipe_rules = {
423  .chr = '|',
424  .subs = { ['g'] = 'g', ['|'] = '|' }
425  };
426 
427  fr_sbuff_unescape_rules_t pipe_rules_sub = {
428  .chr = '|', .subs = { ['g'] = 'h', ['|'] = '|' }
429  };
430 
431  fr_sbuff_unescape_rules_t pipe_rules_sub_hex = {
432  .chr = '|',
433  .subs = { ['g'] = 'h', ['|'] = '|' },
434  .do_hex = true
435  };
436 
437  fr_sbuff_unescape_rules_t pipe_rules_sub_oct = {
438  .chr = '|',
439  .subs = { ['g'] = 'h', ['|'] = '|' },
440  .do_oct = true
441  };
442 
443  fr_sbuff_unescape_rules_t pipe_rules_both = {
444  .chr = '|',
445  .subs = { ['g'] = 'h', ['|'] = '|' },
446  .do_hex = true,
447  .do_oct = true
448  };
449 
450  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
451  /*
452  * Should behave identically to bstrncpy
453  * where there's no restrictions on char
454  * set.
455  */
456  TEST_CASE("Copy 5 bytes to out");
457  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 5, NULL, &rules);
458  TEST_CHECK_SLEN_RETURN(slen, 5);
459  TEST_CHECK_STRCMP(out, "i am ");
460  TEST_CHECK_STRCMP(sbuff.p, "a test string");
461 
462  TEST_CASE("Copy 13 bytes to out");
463  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 13, NULL, &rules);
464  TEST_CHECK_SLEN(slen, 13);
465  TEST_CHECK_STRCMP(out, "a test string");
466  TEST_CHECK_STRCMP(sbuff.p, "");
467  TEST_CHECK(sbuff.p == sbuff.end);
468 
469  TEST_CASE("Copy would overrun input");
470  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 1, NULL, &rules);
471  TEST_CHECK_SLEN(slen, 0);
472  TEST_CHECK(sbuff.p == sbuff.end);
473 
474  TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
475  fr_sbuff_init_in(&sbuff, in_long, sizeof(in_long) - 1);
476 
477  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, NULL, &rules);
478  TEST_CHECK_SLEN(slen, 18);
479  TEST_CHECK_STRCMP(out, "i am a longer test");
480 
481  TEST_CASE("Zero length output buffer");
482  fr_sbuff_set_to_start(&sbuff);
483  out[0] = 'a';
484  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, (size_t)1), &sbuff, SIZE_MAX, NULL, &rules);
485  TEST_CHECK_SLEN(slen, 0);
486  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
487  TEST_CHECK(sbuff.p == sbuff.start);
488 
489  TEST_CASE("Zero length size");
490  fr_sbuff_set_to_start(&sbuff);
491  out[0] = 'a';
492  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, 0, NULL, &rules);
493  TEST_CHECK_SLEN(slen, 0);
494  TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
495  TEST_CHECK(sbuff.p == sbuff.start);
496 
497  /*
498  * Check copy stops early
499  */
500  TEST_CASE("Copy until first t");
501  fr_sbuff_set_to_start(&sbuff);
502  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX,
503  &FR_SBUFF_TERM("t"), &rules);
504  TEST_CHECK_SLEN(slen, 14);
505  TEST_CHECK_STRCMP(out, "i am a longer ");
506 
507  TEST_CASE("Copy until first t with length constraint (same len as token)");
508  fr_sbuff_set_to_start(&sbuff);
509  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, 15), &sbuff, SIZE_MAX,
510  &FR_SBUFF_TERM("t"), &rules);
511  TEST_CHECK_SLEN(slen, 14);
512  TEST_CHECK_STRCMP(out, "i am a longer ");
513 
514  TEST_CASE("Copy until first t with length constraint (one shorter than token)");
515  fr_sbuff_set_to_start(&sbuff);
516  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
517  &FR_SBUFF_TERM("t"), &rules);
518  TEST_CHECK_SLEN(slen, 13);
519  TEST_CHECK_STRCMP(out, "i am a longer");
520 
521  TEST_CASE("Zero length token (should still be terminated)");
522  fr_sbuff_set_to_start(&sbuff);
523  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(out, 14), &sbuff, SIZE_MAX,
524  &FR_SBUFF_TERM("i"), &rules);
525  TEST_CHECK_SLEN(slen, 0);
526  TEST_CHECK_STRCMP(out, "");
527 
528  /*
529  * Escapes and substitution
530  */
531  TEST_CASE("Escape with substitution to same char");
532  fr_sbuff_init_in(&sbuff, in_escapes, sizeof(in_escapes) - 1);
533  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX,
534  &FR_SBUFF_TERM("g"), &pipe_rules);
535  TEST_CHECK_SLEN_RETURN(slen, 20);
536  TEST_CHECK_STRCMP(escape_out, "i am a |t|est string");
537  TEST_CHECK_STRCMP(sbuff.p, "");
538 
539  TEST_CASE("Escape with substitution to different char");
540  fr_sbuff_init_in(&sbuff, in_escapes, sizeof(in_escapes) - 1);
541  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX,
542  &FR_SBUFF_TERM("g"), &pipe_rules_sub);
543  TEST_CHECK_SLEN(slen, 20);
544  TEST_CHECK_STRCMP(escape_out, "i am a |t|est strinh");
545  TEST_CHECK_STRCMP(sbuff.p, "");
546 
547  {
548  char tmp_out[24 + 1];
549 
550  TEST_CASE("Escape with hex substitutions (insufficient output space)");
551  fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
552  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
553  &FR_SBUFF_TERM("g"), &pipe_rules_sub_hex);
554  TEST_CHECK_SLEN_RETURN(slen, 24);
555  TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh");
556  TEST_CHECK_STRCMP(sbuff.p, "|x20|040");
557  }
558 
559  {
560  char tmp_out[25 + 1];
561 
562  TEST_CASE("Escape with hex substitutions (sufficient output space)");
563  fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
564  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
565  &FR_SBUFF_TERM("g"), &pipe_rules_sub_hex);
566  TEST_CHECK_SLEN(slen, 25);
567  TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh ");
568  TEST_CHECK_STRCMP(sbuff.p, "|040");
569  }
570 
571  {
572  char tmp_out[28 + 1];
573 
574  TEST_CASE("Escape with oct substitutions (insufficient output space)");
575  fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
576  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
577  &FR_SBUFF_TERM("g"), &pipe_rules_sub_oct);
578  TEST_CHECK_SLEN(slen, 28);
579  TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh|x20");
580  TEST_CHECK_STRCMP(sbuff.p, "|040");
581  }
582 
583  {
584  char tmp_out[29 + 1];
585 
586  TEST_CASE("Escape with oct substitutions (sufficient output space)");
587  fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
588  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
589  &FR_SBUFF_TERM("g"), &pipe_rules_sub_oct);
590  TEST_CHECK_SLEN(slen, 29);
591  TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh|x20 ");
592  TEST_CHECK_STRCMP(sbuff.p, "");
593  }
594 
595  {
596  char tmp_out[26 + 1];
597 
598  TEST_CASE("Escape with hex and oct substitutions (sufficient output space)");
599  fr_sbuff_init_in(&sbuff, in_escapes_seq, sizeof(in_escapes_seq) - 1);
600  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
601  &FR_SBUFF_TERM("g"), &pipe_rules_both);
602  TEST_CHECK_SLEN(slen, 26);
603  TEST_CHECK_STRCMP(tmp_out, "i |x|0am a |t|est strinh ");
604  TEST_CHECK_STRCMP(sbuff.p, "");
605  }
606 
607  {
608  char tmp_out[2 + 1];
609  char const in_escapes_collapse[] = "||";
610 
611  TEST_CASE("Collapse double escapes");
612  fr_sbuff_init_in(&sbuff, in_escapes_collapse, sizeof(in_escapes_collapse) - 1);
613  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)),
614  &sbuff, SIZE_MAX, NULL, &pipe_rules);
615  TEST_CHECK_SLEN(slen, 1);
616  TEST_CHECK_STRCMP(tmp_out, "|");
617  TEST_CHECK_STRCMP(sbuff.p, "");
618  }
619 
620  {
621  char in_escapes_collapse[] = "||foo||";
622 
623  TEST_CASE("Collapse double escapes overlapping");
624  fr_sbuff_init_in(&sbuff, in_escapes_collapse, sizeof(in_escapes_collapse) - 1);
625  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(in_escapes_collapse, sizeof(in_escapes_collapse)),
626  &sbuff, SIZE_MAX, NULL, &pipe_rules);
627  TEST_CHECK_SLEN(slen, 5);
628  TEST_CHECK_STRCMP(in_escapes_collapse, "|foo|");
629  TEST_CHECK_STRCMP(sbuff.p, "");
630  }
631 
632  {
633  char tmp_out[30 + 1];
634 
635  fr_sbuff_unescape_rules_t double_quote_rules = {
636  .chr = '\\',
637  .subs = {
638  ['a'] = '\a',
639  ['b'] = '\b',
640  ['e'] = '\\',
641  ['n'] = '\n',
642  ['r'] = '\r',
643  ['t'] = '\t',
644  ['v'] = '\v',
645  ['\\'] = '\\',
646  ['"'] = '"' /* Quoting char */
647  },
648  .do_hex = true,
649  .do_oct = true
650  };
651 
652  char const in_escapes_unit[] =
653  "0x01\\001"
654  "0x07\\007"
655  "0x0A\\n"
656  "0x0D\\r"
657  "\\\"\\\""
658  "0xb0"
659  "\\260\\xb0";
660 
661  char const expected[] = {
662  '0', 'x', '0', '1', '\001',
663  '0', 'x', '0', '7', '\007',
664  '0', 'x', '0', 'A', '\n',
665  '0', 'x', '0', 'D', '\r',
666  '"', '"',
667  '0', 'x', 'b', '0',
668  '\260', '\xb0', '\0'
669  };
670 
671  TEST_CASE("Check unit test test strings");
672  fr_sbuff_init_in(&sbuff, in_escapes_unit, sizeof(in_escapes_unit) - 1);
673  slen = fr_sbuff_out_unescape_until(&FR_SBUFF_OUT(tmp_out, sizeof(tmp_out)), &sbuff, SIZE_MAX,
674  NULL, &double_quote_rules);
675  TEST_CHECK_SLEN(slen, 28);
676  TEST_CHECK_STRCMP(tmp_out, expected);
677  TEST_CHECK_STRCMP(sbuff.p, "");
678  }
679 
680  /*
681  * Verify dynamic allocation
682  */
683  {
684  char *buff;
685  size_t len;
686  char const in_zero[] = "";
687 
688  len = fr_sbuff_out_aunescape_until(NULL, &buff, &FR_SBUFF_IN(in_zero, sizeof(in_zero) - 1), SIZE_MAX,
689  NULL, &pipe_rules);
690  TEST_CHECK_SLEN(len, 0);
691  talloc_get_type_abort(buff, char);
692  TEST_CHECK_SLEN(talloc_array_length(buff), 1);
693  talloc_free(buff);
694  }
695 }
696 
698 {
699  char const in[] = "foo, bar, baz```";
700  fr_sbuff_t sbuff;
701  ssize_t slen;
703  L(","),
704  L("```"),
705  L("bad"),
706  L("bar"),
707  L("boink"),
708  L("food"),
709  L("nyi")
710  );
711  char out[100];
712 
713  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
714 
715  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
716  TEST_CHECK_SLEN_RETURN(slen, 3);
717  TEST_CHECK_STRCMP(out, "foo");
718 
719  fr_sbuff_advance(&sbuff, 1);
720 
721  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
722  TEST_CHECK(slen == 1);
723  TEST_CHECK_STRCMP(out, " ");
724 
725  fr_sbuff_advance(&sbuff, 4);
726 
727  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt, NULL);
728  TEST_CHECK(slen == 4);
729  TEST_CHECK_STRCMP(out, " baz");
730 }
731 
732 static void test_eof_terminal(void)
733 {
734  char const in[] = "foo, bar";
735  fr_sbuff_t sbuff;
736  ssize_t slen;
738  L(""),
739  L(","),
740  );
742  L(",")
743  );
744  char out[100];
745 
746  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
747 
748  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
749  TEST_CHECK_SLEN_RETURN(slen, 3);
750  TEST_CHECK_STRCMP(out, "foo");
751 
752  fr_sbuff_advance(&sbuff, 1); /* Advance past comma */
753 
754  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &sbuff, SIZE_MAX, &tt_eof, NULL);
755  TEST_CHECK_SLEN_RETURN(slen, 4);
756  TEST_CHECK_STRCMP(out, " bar");
757 
758  TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt_eof) == true);
759  TEST_CHECK(fr_sbuff_is_terminal(&sbuff, &tt) == false);
760 }
761 
762 static void test_terminal_merge(void)
763 {
764  size_t i;
766  L(""),
767  L("\t"),
768  L("\n"),
769  L("\r"),
770  L(" "),
771  L("!"),
772  L("%"),
773  L("&"),
774  L("*"),
775  L("+"),
776  L("-"),
777  L("/"),
778  L("<"),
779  L("="),
780  L(">"),
781  L("^"),
782  L("{"),
783  L("|"),
784  L("~")
785  );
787  L(""),
788  L(")"),
789  );
790 
791  fr_sbuff_term_t expect =
793  L(""),
794  L("\t"),
795  L("\n"),
796  L("\r"),
797  L(" "),
798  L("!"),
799  L("%"),
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  );
814  fr_sbuff_term_t *result;
815 
816  result = fr_sbuff_terminals_amerge(NULL, &a, &b);
817  TEST_CHECK_LEN(result->len, expect.len);
818 
819  for (i = 0; i < result->len; i++) {
820  TEST_CHECK_STRCMP(result->elem[i].str, expect.elem[i].str);
821  }
822 
823  talloc_free(result);
824 }
825 
826 static void test_no_advance(void)
827 {
828  char const *in = "i am a test string";
829  char out[18 + 1] = "";
830  fr_sbuff_t sbuff;
831  ssize_t slen;
832 
833  fr_sbuff_init_in(&sbuff, in, strlen(in));
834 
835  TEST_CASE("Copy 5 bytes to out - no advance");
836  TEST_CHECK(sbuff.p == sbuff.start);
837  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &FR_SBUFF(&sbuff), 5);
838  TEST_CHECK_SLEN_RETURN(slen, 5);
839  TEST_CHECK(strcmp(out, "i am ") == 0);
840  TEST_CHECK(sbuff.p == sbuff.start);
841 }
842 
843 static void test_talloc_extend(void)
844 {
845  fr_sbuff_t sbuff;
847 
848  TEST_CASE("Initial allocation");
849  TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 32, 50) == &sbuff);
850  TEST_SBUFF_USED(&sbuff, 0);
851  TEST_SBUFF_LEN(&sbuff, 33);
852 
853  TEST_CASE("Trim to zero");
854  TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
855  TEST_SBUFF_USED(&sbuff, 0);
856  TEST_SBUFF_LEN(&sbuff, 1);
857 
858  TEST_CASE("Print string - Should realloc to init");
859  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
860  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "0123456789") == 0);
861  TEST_SBUFF_USED(&sbuff, 10);
862  TEST_SBUFF_LEN(&sbuff, 33);
863 
864  TEST_CASE("Trim to strlen");
865  TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
866  TEST_SBUFF_LEN(&sbuff, 11);
867 
868  TEST_CASE("Print string - Should realloc to init");
869  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "0123456789") == 10);
870  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789") == 0);
871  TEST_SBUFF_USED(&sbuff, 20);
872  TEST_SBUFF_LEN(&sbuff, 33);
873 
874  TEST_CASE("Trim to strlen");
875  TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
876  TEST_SBUFF_LEN(&sbuff, 21);
877 
878  TEST_CASE("Print string - Should realloc to double buffer len");
879  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "012345678901234") == 15);
880  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234") == 0);
881  TEST_SBUFF_USED(&sbuff, 35);
882  TEST_SBUFF_LEN(&sbuff, 41);
883 
884  TEST_CASE("Print string - Should only add a single char, should not extend the buffer");
885  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
886  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234A") == 0);
887  TEST_SBUFF_USED(&sbuff, 36);
888  TEST_SBUFF_LEN(&sbuff, 41);
889 
890  TEST_CASE("Print string - Use all available buffer data");
891  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BCDE") == 4);
892  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDE") == 0);
893  TEST_SBUFF_USED(&sbuff, 40);
894  TEST_SBUFF_LEN(&sbuff, 41);
895 
896  TEST_CASE("Print string - Add single char, should trigger doubling constrained by max");
897  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "F") == 1);
898  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEF") == 0);
899  TEST_SBUFF_USED(&sbuff, 41);
900  TEST_SBUFF_LEN(&sbuff, 51);
901 
902  TEST_CASE("Print string - Add data to take us up to max");
903  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "GHIJKLMNO") == 9);
904  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
905  TEST_SBUFF_USED(&sbuff, 50);
906  TEST_SBUFF_LEN(&sbuff, 51);
907 
908  TEST_CASE("Print string - Add single char, should fail");
909  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "P") == -1);
910  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
911  TEST_SBUFF_USED(&sbuff, 50);
912  TEST_SBUFF_LEN(&sbuff, 51);
913 
914  TEST_CASE("Trim to strlen (should be noop)");
915  TEST_CHECK(fr_sbuff_trim_talloc(&sbuff, SIZE_MAX) == 0);
916  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "01234567890123456789012345678901234ABCDEFGHIJKLMNO") == 0);
917  TEST_SBUFF_USED(&sbuff, 50);
918  TEST_SBUFF_LEN(&sbuff, 51);
919 
920  talloc_free(sbuff.buff);
921 }
922 
924 {
925  fr_sbuff_t sbuff;
927 
928  TEST_CASE("Initial allocation");
929  TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 0, 50) == &sbuff);
930  TEST_SBUFF_USED(&sbuff, 0);
931  TEST_SBUFF_LEN(&sbuff, 1);
932 
933  TEST_CASE("Print string - Should alloc one byte");
934  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "A") == 1);
935  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "A") == 0);
936  TEST_SBUFF_USED(&sbuff, 1);
937  TEST_SBUFF_LEN(&sbuff, 2);
938 
939  TEST_CASE("Print string - Should alloc two bytes");
940  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "BC") == 2);
941  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABC") == 0);
942  TEST_SBUFF_USED(&sbuff, 3);
943  TEST_SBUFF_LEN(&sbuff, 4);
944 
945  TEST_CASE("Print string - Should alloc three bytes");
946  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff, "D") == 1);
947  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff), "ABCD") == 0);
948  TEST_SBUFF_USED(&sbuff, 4);
949  TEST_SBUFF_LEN(&sbuff, 7);
950 
951  talloc_free(sbuff.buff);
952 }
953 
955 {
956  fr_sbuff_t sbuff_0, sbuff_1;
958 
959  TEST_CASE("Initial allocation");
960  TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
961  TEST_SBUFF_USED(&sbuff_0, 0);
962  TEST_SBUFF_LEN(&sbuff_0, 1);
963 
964  sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0);
965  TEST_CASE("Check sbuff_1 has extend fields set");
966  TEST_CHECK(sbuff_0.extend == sbuff_1.extend);
967  TEST_CHECK(sbuff_0.uctx == sbuff_1.uctx);
968  TEST_CHECK(sbuff_1.parent == &sbuff_0);
969  TEST_SBUFF_USED(&sbuff_1, 0);
970  TEST_SBUFF_LEN(&sbuff_1, 1);
971 
972  TEST_CASE("Print string - Should alloc one byte");
973  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "A") == 1);
974  TEST_CHECK(strcmp(fr_sbuff_start(&sbuff_1), "A") == 0);
975  TEST_SBUFF_USED(&sbuff_0, 1);
976  TEST_SBUFF_LEN(&sbuff_0, 2);
977  TEST_SBUFF_USED(&sbuff_1, 1);
978  TEST_SBUFF_LEN(&sbuff_1, 2);
979 
980  TEST_CHECK(sbuff_0.start == sbuff_1.start);
981  TEST_CHECK(sbuff_0.end == sbuff_1.end);
982  TEST_CHECK(sbuff_0.p == sbuff_1.p);
983 
984  talloc_free(sbuff_0.buff);
985 }
986 
988 {
989  fr_sbuff_t sbuff_0, sbuff_1;
990  fr_sbuff_marker_t marker_0, marker_1;
992 
993  TEST_CASE("Initial allocation");
994  TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
995  TEST_SBUFF_USED(&sbuff_0, 0);
996  TEST_SBUFF_LEN(&sbuff_0, 1);
997 
998  TEST_CASE("Print string - Should alloc one byte");
999  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "A") == 1);
1000  TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "A");
1001  TEST_SBUFF_USED(&sbuff_0, 1);
1002  TEST_SBUFF_LEN(&sbuff_0, 2);
1003 
1004  fr_sbuff_marker(&marker_0, &sbuff_0);
1005  TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1006 
1007  TEST_CASE("Print string - Ensure marker is updated");
1008  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_0, "B") == 1);
1009  TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_0), "AB");
1010  TEST_SBUFF_USED(&sbuff_0, 2);
1011  TEST_SBUFF_LEN(&sbuff_0, 3);
1012  TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1013 
1014  TEST_CASE("Print string - Copy sbuff");
1015  sbuff_1 = FR_SBUFF_BIND_CURRENT(&sbuff_0); /* Dup sbuff_0 */
1016  TEST_CHECK(sbuff_0.p == sbuff_1.start);
1017  fr_sbuff_marker(&marker_1, &sbuff_1);
1018 
1019  TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1020  TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1021  TEST_CHECK(sbuff_0.p == sbuff_1.start);
1022 
1023  TEST_CASE("Print string - Trigger re-alloc, ensure all pointers are updated");
1024  TEST_CHECK(fr_sbuff_in_strcpy(&sbuff_1, "C") == 1);
1025  TEST_CHECK_STRCMP(fr_sbuff_start(&sbuff_1), "C");
1026  TEST_CHECK(sbuff_0.buff == sbuff_1.buff);
1027  TEST_CHECK(sbuff_0.p == sbuff_1.start + 1);
1028  TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1029  TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1030  TEST_SBUFF_USED(&sbuff_0, 3);
1031  TEST_SBUFF_LEN(&sbuff_0, 5);
1032 
1033  talloc_free(sbuff_0.buff);
1034 }
1035 
1036 static void test_file_extend(void)
1037 {
1038  fr_sbuff_t sbuff;
1039  fr_sbuff_t our_sbuff, child_sbuff;
1040  fr_sbuff_uctx_file_t fctx;
1041  FILE *fp;
1042  char buff[5];
1043  char out[24];
1044  char fbuff[24];
1045  const char PATTERN[] = "xyzzy";
1046 #define PATTERN_LEN (sizeof(PATTERN) - 1)
1047  char *post_ws;
1048  ssize_t slen;
1049 
1050  static_assert(sizeof(buff) >= PATTERN_LEN, "Buffer must be sufficiently large to hold the pattern");
1051  static_assert((sizeof(fbuff) % sizeof(buff)) > 0, "sizeof buff must not be a multiple of fbuff");
1052  static_assert((sizeof(fbuff) % sizeof(buff)) < PATTERN_LEN, "remainder of sizeof(fbuff)/sizeof(buff) must be less than sizeof pattern");
1053 
1054  TEST_CASE("Initialization");
1055  memset(fbuff, ' ', sizeof(fbuff));
1056  memcpy(fbuff + sizeof(fbuff) - PATTERN_LEN, PATTERN, PATTERN_LEN);
1057 
1058  fp = fmemopen(fbuff, sizeof(fbuff), "r");
1059 #ifdef __clang_analyzer__
1060  if (fp == NULL) return;
1061 #endif
1062 
1063  TEST_CHECK(fp != NULL);
1064  TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1065  our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1066 
1067  TEST_CASE("Advance past whitespace, which will require shift/extend");
1068  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&our_sbuff, SIZE_MAX, NULL), sizeof(fbuff) - PATTERN_LEN);
1069  TEST_CASE("Verify extend on unused child buffer");
1070  child_sbuff = FR_SBUFF(&our_sbuff);
1071  slen = fr_sbuff_extend_file(&child_sbuff, 0);
1072  TEST_CHECK_SLEN(slen, sizeof(fbuff) % PATTERN_LEN);
1073  TEST_CASE("Verify that we passed all and only whitespace");
1074  (void) fr_sbuff_out_abstrncpy(NULL, &post_ws, &our_sbuff, 24);
1075  TEST_CHECK_STRCMP(post_ws, PATTERN);
1076  talloc_free(post_ws);
1077  TEST_CASE("Verify parent buffer end");
1078  TEST_CHECK(sbuff.end == our_sbuff.end);
1079 
1080  TEST_CASE("Verify that we do not read shifted buffer past eof");
1081  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1082  TEST_CHECK_SLEN(slen, 0);
1083  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1084  TEST_CHECK_SLEN(slen, 0);
1085  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, NULL, NULL);
1086  TEST_CHECK_SLEN(slen, 0);
1087  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, allow_lowercase_and_space);
1088  TEST_CHECK_SLEN(slen, 0);
1089 
1090  fclose(fp);
1091 
1092  TEST_CASE("Verify fr_sbuff_out_bstrncpy_until() extends from file properly");
1093  fp = fmemopen(fbuff, sizeof(fbuff), "r");
1094 #ifdef __clang_analyzer__
1095  if (fp == NULL) return;
1096 #endif
1097 
1098  TEST_CHECK(fp != NULL);
1099  TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1100  our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1101  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, &FR_SBUFF_TERM("x"), NULL);
1102  TEST_CHECK_SLEN(slen, sizeof(fbuff) - PATTERN_LEN);
1103 
1104  fclose(fp);
1105 }
1106 
1107 static void test_file_extend_max(void)
1108 {
1109  fr_sbuff_t sbuff;
1110  fr_sbuff_uctx_file_t fctx;
1111  FILE *fp;
1112  char buff[16];
1113  char fbuff[] = " xyzzy";
1114  char *post_ws;
1115 
1116  TEST_CASE("Initialization");
1117  fp = fmemopen(fbuff, sizeof(fbuff) - 1, "r");
1118 #ifdef __clang_analyzer__
1119  if (fp == NULL) return;
1120 #endif
1121  TEST_CHECK(fp != NULL);
1122  TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, sizeof(fbuff) - 8) == &sbuff);
1123 
1124  TEST_CASE("Confirm that max stops us from seeing xyzzy");
1125  TEST_CHECK_SLEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), sizeof(fbuff) - 8);
1126  TEST_CHECK_SLEN(fr_sbuff_out_abstrncpy(NULL, &post_ws, &sbuff, 24), 0);
1127  TEST_CHECK_STRCMP(post_ws, "");
1128  talloc_free(post_ws);
1129  fclose(fp);
1130 }
1131 
1132 static void test_adv_past_str(void)
1133 {
1134  fr_sbuff_t sbuff;
1135  char const in[] = "i am a test string";
1136 
1137  TEST_CASE("Check for token at beginning of string");
1138  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1139  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 6);
1140  TEST_CHECK_STRCMP(sbuff.p, " test string");
1141 
1142  TEST_CASE("Check for token not at beginning of string");
1143  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1144  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, " am a", SIZE_MAX), 0);
1145  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1146 
1147  TEST_CASE("Check for token larger than the string");
1148  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1149  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string ", SIZE_MAX), 0);
1150  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1151 
1152  TEST_CASE("Check for token with zero length string");
1153  fr_sbuff_init_in(&sbuff, in, 0);
1154  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 0);
1155 
1156  TEST_CASE("Check for token that is the string");
1157  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1158  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string", SIZE_MAX), 18);
1159  TEST_CHECK_STRCMP(sbuff.p, "");
1160  TEST_CHECK(sbuff.p == sbuff.end);
1161 }
1162 
1163 static void test_adv_past_strcase(void)
1164 {
1165  fr_sbuff_t sbuff;
1166  char const in[] = "i am a test string";
1167 
1168  TEST_CASE("Check for token at beginning of string");
1169  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1170  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 6);
1171  TEST_CHECK_STRCMP(sbuff.p, " test string");
1172 
1173  TEST_CASE("Check for token not at beginning of string");
1174  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1175  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, " AM a", SIZE_MAX), 0);
1176  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1177 
1178  TEST_CASE("Check for token larger than the string");
1179  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1180  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string ", SIZE_MAX), 0);
1181  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1182 
1183  TEST_CASE("Check for token with zero length string");
1184  fr_sbuff_init_in(&sbuff, in, 0);
1185  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 0);
1186 
1187  TEST_CASE("Check for token that is the string");
1188  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1189  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string", SIZE_MAX), 18);
1190  TEST_CHECK_STRCMP(sbuff.p, "");
1191  TEST_CHECK(sbuff.p == sbuff.end);
1192 }
1193 
1194 static void test_adv_past_whitespace(void)
1195 {
1196  fr_sbuff_t sbuff;
1197  char const in[] = " i am a test string";
1198  char const in_ns[] = "i am a test string";
1199  char const in_ws[] = " ";
1200 
1201  TEST_CASE("Check for token at beginning of string");
1202  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1203  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1204  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1205 
1206  TEST_CASE("Check for token not at beginning of string");
1207  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1208  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1209  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1210 
1211  TEST_CASE("Check for token with zero length string");
1212  fr_sbuff_init_in(&sbuff, in, 0);
1213  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1214 
1215  TEST_CASE("Check for token that is the string");
1216  fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1217  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1218 
1219  TEST_CASE("Length constraint with token match");
1220  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1221  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 2);
1222  TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1223 
1224  TEST_CASE("Length constraint without token match");
1225  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1226  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 0);
1227  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1228 }
1229 
1230 static void test_adv_past_allowed(void)
1231 {
1232  fr_sbuff_t sbuff;
1233  char const in[] = " i am a test string";
1234  char const in_ns[] = "i am a test string";
1235  char const in_ws[] = " ";
1236 
1237  TEST_CASE("Check for token at beginning of string");
1238  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1239  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 5);
1240  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1241 
1242  TEST_CASE("Check for token not at beginning of string");
1243  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1244  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 0);
1245  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1246 
1247  TEST_CASE("Check for token with zero length string");
1248  fr_sbuff_init_in(&sbuff, in, 0);
1249  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 0);
1250  TEST_CHECK(sbuff.p == sbuff.start);
1251 
1252  TEST_CASE("Check for token at the end of the string");
1253  fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1254  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 5);
1255  TEST_CHECK(sbuff.p == sbuff.end);
1256 
1257  TEST_CASE("Length constraint with token match");
1258  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1259  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 2);
1260  TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1261 
1262  TEST_CASE("Length constraint with token match");
1263  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1264  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 0);
1265  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1266 }
1267 
1268 static void test_adv_until(void)
1269 {
1270  fr_sbuff_t sbuff;
1271  char const in[] = " abcdefgh ijklmnopp";
1272 
1273  TEST_CASE("Check for token at beginning of string");
1274  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1275  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM(" "), '\0'), 0);
1276  TEST_CHECK_STRCMP(sbuff.p, " abcdefgh ijklmnopp");
1277 
1278  TEST_CASE("Check for token not at beginning of string");
1279  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1280  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 1);
1281  TEST_CHECK_STRCMP(sbuff.p, "abcdefgh ijklmnopp");
1282 
1283  TEST_CASE("Check for token with zero length string");
1284  fr_sbuff_init_in(&sbuff, in, 0);
1285  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 0);
1286  TEST_CHECK(sbuff.p == sbuff.start);
1287 
1288  TEST_CASE("Check for token that is not in the string");
1289  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1290  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("|"), '\0'), 19);
1291  TEST_CHECK(sbuff.p == sbuff.end);
1292 
1293  TEST_CASE("Check escapes");
1294  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1295  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("p"), 'o'), 18);
1296  TEST_CHECK_STRCMP(sbuff.p, "p");
1297 
1298  TEST_CASE("Check for token that is not in the string with length constraint");
1299  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1300  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, 5, &FR_SBUFF_TERM("|"), '\0'), 5);
1301  TEST_CHECK(sbuff.p == (sbuff.start + 5));
1302 }
1303 
1304 static void test_adv_to_utf8(void)
1305 {
1306  fr_sbuff_t sbuff;
1307  char const in[] = "🥺🥺🥺🥺🍪😀";
1308  char *p;
1309 
1310  TEST_CASE("Check for token at beginning of string");
1311  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1312  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🥺");
1313  TEST_CHECK(p == sbuff.p);
1314  TEST_CHECK_STRCMP(sbuff.p, "🥺🥺🥺🥺🍪😀");
1315 
1316  TEST_CASE("Check for token not at beginning of string");
1317  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1318  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1319  TEST_CHECK(p == (sbuff.start + (sizeof("🥺🥺🥺🥺") - 1)));
1320  TEST_CHECK_STRCMP(p, "🍪😀");
1321 
1322  TEST_CASE("Check for token with zero length string");
1323  fr_sbuff_init_in(&sbuff, in, 0);
1324  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1325  TEST_CHECK(p == NULL);
1326  TEST_CHECK(sbuff.start == sbuff.p);
1327 
1328  TEST_CASE("Check for token at the end of the string");
1329  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1330  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "😀");
1331  TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1332 
1333  TEST_CASE("Check for token not in the string");
1334  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1335  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍆 ");
1336  TEST_CHECK(p == NULL);
1337 
1338  TEST_CASE("Check for token at the end of the string within len constraints");
1339  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1340  p = fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 1), "😀");
1341  TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1342 
1343  TEST_CASE("Check for token at the end of the string outside len constraints #1");
1344  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1345  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 2), "😀"));
1346  TEST_CHECK(sbuff.p == sbuff.start);
1347 
1348  TEST_CASE("Check for token at the end of the string outside len constraints #2");
1349  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1350  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 3), "😀"));
1351  TEST_CHECK(sbuff.p == sbuff.start);
1352 
1353  TEST_CASE("Check for token at the end of the string outside len constraints #3");
1354  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1355  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 4), "😀"));
1356  TEST_CHECK(sbuff.p == sbuff.start);
1357 
1358  TEST_CASE("Check for token at the end of the string outside len constraints #4");
1359  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1360  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 5), "😀"));
1361  TEST_CHECK(sbuff.p == sbuff.start);
1362 }
1363 
1364 static void test_adv_to_chr(void)
1365 {
1366  fr_sbuff_t sbuff;
1367  char const in[] = "AAAAbC";
1368  char *p;
1369 
1370  TEST_CASE("Check for token at beginning of string");
1371  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1372  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'A');
1373  TEST_CHECK(p == sbuff.p);
1374  TEST_CHECK_STRCMP(sbuff.p, "AAAAbC");
1375 
1376  TEST_CASE("Check for token not at beginning of string");
1377  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1378  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b');
1379  TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1380  TEST_CHECK_STRCMP(p, "bC");
1381 
1382  TEST_CASE("Check for token with zero length string");
1383  fr_sbuff_init_in(&sbuff, in, 0);
1384  TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b'));
1385  TEST_CHECK(sbuff.start == sbuff.p);
1386 
1387  TEST_CASE("Check for token at the end of the string");
1388  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1389  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'C');
1390  TEST_CHECK(p == sbuff.start + (sizeof("AAAAb") - 1));
1391 
1392  TEST_CASE("Check for token not in the string");
1393  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1394  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'D');
1395  TEST_CHECK(p == NULL);
1396 
1397  TEST_CASE("Check for token not at beginning of string within length constraints");
1398  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1399  p = fr_sbuff_adv_to_chr(&sbuff, 5, 'b');
1400  TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1401  TEST_CHECK_STRCMP(p, "bC");
1402 
1403  TEST_CASE("Check for token not at beginning of string outside length constraints");
1404  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1405  TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, 4, 'b'));
1406  TEST_CHECK(sbuff.p == sbuff.start);
1407 }
1408 
1409 static void test_adv_to_str(void)
1410 {
1411  fr_sbuff_t sbuff;
1412  char const in[] = "i am a test string";
1413  char *p;
1414 
1415  TEST_CASE("Check for token at beginning of string");
1416  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1417  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test", SIZE_MAX);
1418  TEST_CHECK(sbuff.p == p);
1419  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1420 
1421  TEST_CASE("Check for token not at beginning of string");
1422  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1423  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "test", SIZE_MAX);
1424  TEST_CHECK(sbuff.p == p);
1425  TEST_CHECK_STRCMP(sbuff.p, "test string");
1426 
1427  TEST_CASE("Check for token at the end of string");
1428  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1429  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "ing", SIZE_MAX);
1430  TEST_CHECK(sbuff.p == p);
1431  TEST_CHECK_STRCMP(sbuff.p, "ing");
1432 
1433  TEST_CASE("Check for token larger than the string");
1434  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1435  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string ", SIZE_MAX);
1436  TEST_CHECK(sbuff.p == sbuff.start);
1437  TEST_CHECK(p == NULL);
1438 
1439  TEST_CASE("Check for token shorter than string, not in the string");
1440  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1441  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "ng ", SIZE_MAX);
1442  TEST_CHECK(sbuff.p == sbuff.start);
1443  TEST_CHECK(p == NULL);
1444 
1445  TEST_CASE("Check for token with zero length string");
1446  fr_sbuff_init_in(&sbuff, in, 0);
1447  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a", SIZE_MAX);
1448  TEST_CHECK(sbuff.p == sbuff.start);
1449  TEST_CHECK(p == NULL);
1450 
1451  TEST_CASE("Check for token that is the string");
1452  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1453  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string", SIZE_MAX);
1454  TEST_CHECK(sbuff.p == sbuff.start);
1455  TEST_CHECK(sbuff.p == p);
1456  TEST_CHECK_STRCMP(p, "i am a test string");
1457 
1458  TEST_CASE("Check for token not at beginning of string within length constraints");
1459  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1460  p = fr_sbuff_adv_to_str(&sbuff, 11, "test", SIZE_MAX);
1461  TEST_CHECK(sbuff.p == p);
1462  TEST_CHECK_STRCMP(sbuff.p, "test string");
1463 
1464  TEST_CASE("Check for token not at beginning of string outside length constraints");
1465  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1466  TEST_CHECK(!fr_sbuff_adv_to_str(&sbuff, 10, "test", SIZE_MAX));
1467  TEST_CHECK(sbuff.p == sbuff.start);
1468 }
1469 
1470 static void test_adv_to_strcase(void)
1471 {
1472  fr_sbuff_t sbuff;
1473  char const in[] = "i am a test string";
1474  char *p;
1475 
1476  TEST_CASE("Check for token at beginning of string");
1477  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1478  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a TEST", SIZE_MAX);
1479  TEST_CHECK(sbuff.p == p);
1480  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1481 
1482  TEST_CASE("Check for token not at beginning of string");
1483  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1484  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "tEst", SIZE_MAX);
1485  TEST_CHECK(sbuff.p == p);
1486  TEST_CHECK_STRCMP(sbuff.p, "test string");
1487 
1488  TEST_CASE("Check for token at the end of string");
1489  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1490  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "Ing", SIZE_MAX);
1491  TEST_CHECK(sbuff.p == p);
1492  TEST_CHECK_STRCMP(sbuff.p, "ing");
1493 
1494  TEST_CASE("Check for token larger than the string");
1495  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1496  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i aM a tEst stRIng ", SIZE_MAX);
1497  TEST_CHECK(sbuff.p == sbuff.start);
1498  TEST_CHECK(p == NULL);
1499 
1500  TEST_CASE("Check for token shorter than string, not in the string");
1501  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1502  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "nG ", SIZE_MAX);
1503  TEST_CHECK(sbuff.p == sbuff.start);
1504  TEST_CHECK(p == NULL);
1505 
1506  TEST_CASE("Check for token with zero length string");
1507  fr_sbuff_init_in(&sbuff, in, 0);
1508  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a", SIZE_MAX);
1509  TEST_CHECK(sbuff.p == sbuff.start);
1510  TEST_CHECK(p == NULL);
1511 
1512  TEST_CASE("Check for token that is the string");
1513  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1514  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a teST stRIng", SIZE_MAX);
1515  TEST_CHECK(sbuff.p == sbuff.start);
1516  TEST_CHECK(sbuff.p == p);
1517  TEST_CHECK_STRCMP(p, "i am a test string");
1518 
1519  TEST_CASE("Check for token not at beginning of string within length constraints");
1520  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1521  p = fr_sbuff_adv_to_strcase(&sbuff, 11, "tEst", SIZE_MAX);
1522  TEST_CHECK(sbuff.p == p);
1523  TEST_CHECK_STRCMP(sbuff.p, "test string");
1524 
1525  TEST_CASE("Check for token not at beginning of string outside length constraints");
1526  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1527  TEST_CHECK(!fr_sbuff_adv_to_strcase(&sbuff, 10, "tEst", SIZE_MAX));
1528  TEST_CHECK(sbuff.p == sbuff.start);
1529 }
1530 
1531 static void test_next_if_char(void)
1532 {
1533  fr_sbuff_t sbuff;
1534  char const in[] = "i ";
1535 
1536  TEST_CASE("Check for advancement on match");
1537  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1538  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == true);
1539  TEST_CHECK_STRCMP(sbuff.p, " ");
1540 
1541  TEST_CASE("Check for non-advancement on non-match");
1542  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == false);
1543  TEST_CHECK_STRCMP(sbuff.p, " ");
1544 
1545  TEST_CASE("Check for advancement at end");
1546  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == true);
1547  TEST_CHECK_STRCMP(sbuff.p, "");
1548 
1549  TEST_CASE("Check we can't advance off the end of the buffer");
1550  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == false);
1551  TEST_CHECK_STRCMP(sbuff.p, "");
1552 }
1553 
1554 static void test_next_unless_char(void)
1555 {
1556  fr_sbuff_t sbuff;
1557  char const in[] = "i ";
1558 
1559  TEST_CASE("Check for advancement on non-match");
1560  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1561  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == true);
1562  TEST_CHECK_STRCMP(sbuff.p, " ");
1563 
1564  TEST_CASE("Check for non-advancement on match");
1565  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == false);
1566  TEST_CHECK_STRCMP(sbuff.p, " ");
1567 
1568  TEST_CASE("Check for advancement at end");
1569  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == true);
1570  TEST_CHECK_STRCMP(sbuff.p, "");
1571 
1572  TEST_CASE("Check we can't advance off the end of the buffer");
1573  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == false);
1574  TEST_CHECK_STRCMP(sbuff.p, "");
1575 }
1576 
1578  /*
1579  * Basic tests
1580  */
1581  { "fr_sbuff_init", test_parse_init },
1582  { "fr_sbuff_is_char", test_is_char },
1583  { "fr_sbuff_out_bstrncpy_exact", test_bstrncpy_exact },
1584  { "fr_sbuff_out_bstrncpy", test_bstrncpy },
1585  { "fr_sbuff_out_bstrncpy_allowed", test_bstrncpy_allowed },
1586  { "fr_sbuff_out_bstrncpy_until", test_bstrncpy_until },
1587  { "multi-char terminals", test_unescape_multi_char_terminals },
1588  { "fr_sbuff_out_unescape_until", test_unescape_until },
1589  { "fr_sbuff_terminal_eof", test_eof_terminal },
1590  { "terminal merge", test_terminal_merge },
1591 
1592  /*
1593  * Extending buffer
1594  */
1595  { "fr_sbuff_talloc_extend", test_talloc_extend },
1596  { "fr_sbuff_talloc_extend_init_zero", test_talloc_extend_init_zero },
1597  { "fr_sbuff_talloc_extend_multi_level", test_talloc_extend_multi_level },
1598  { "fr_sbuff_talloc_extend_with_marker", test_talloc_extend_with_marker },
1599  { "fr_sbuff_file_extend", test_file_extend },
1600  { "fr_sbuff_file_extend_max", test_file_extend_max },
1601 
1602  { "fr_sbuff_no_advance", test_no_advance },
1603 
1604  /*
1605  * Token skipping
1606  */
1607  { "fr_sbuff_adv_past_str", test_adv_past_str },
1608  { "fr_sbuff_adv_past_strcase", test_adv_past_strcase },
1609  { "fr_sbuff_adv_past_whitespace", test_adv_past_whitespace },
1610  { "fr_sbuff_adv_past_allowed", test_adv_past_allowed },
1611  { "fr_sbuff_adv_until", test_adv_until },
1612 
1613  /*
1614  * Token searching
1615  */
1616  { "fr_sbuff_adv_to_utf8", test_adv_to_utf8 },
1617  { "fr_sbuff_adv_to_chr", test_adv_to_chr },
1618  { "fr_sbuff_adv_to_str", test_adv_to_str },
1619  { "fr_sbuff_adv_to_strcase", test_adv_to_strcase },
1620 
1621  /*
1622  * Advancement
1623  */
1624  { "fr_sbuff_next_if_char", test_next_if_char },
1625  { "fr_sbuff_next_unless_char", test_next_unless_char },
1626 
1627  { NULL }
1628 };
#define TEST_CHECK(cond)
Definition: acutest.h:85
#define TEST_CASE(name)
Definition: acutest.h:184
#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:207
#define static_assert
For systems with an old version libc, define static_assert.
Definition: build.h:35
static fr_slen_t in
Definition: dict.h:821
talloc_free(reap)
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)
Definition: merged_model.c:177
long int ssize_t
Definition: merged_model.c:24
ssize_t fr_sbuff_out_bstrncpy_exact(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
Definition: merged_model.c:142
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)
Definition: merged_model.c:166
char * p
Definition: merged_model.c:38
#define UINT8_MAX
Definition: merged_model.c:32
size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, bool const allowed[static UINT8_MAX+1])
Definition: merged_model.c:151
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:407
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:1432
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static UINT8_MAX+1], fr_sbuff_term_t const *tt)
Wind position past characters in the allowed set.
Definition: sbuff.c:1755
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:1889
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:1970
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:622
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:1934
size_t fr_sbuff_extend_file(fr_sbuff_t *sbuff, size_t extension)
Refresh the buffer with more data from the file.
Definition: sbuff.c:265
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:1690
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:2023
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition: sbuff.c:2130
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:1721
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:2087
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:1830
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:700
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:2066
A generic buffer structure for string printing and parsing strings.
#define fr_sbuff_start(_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:137
char chr
Character at the start of an escape sequence.
Definition: sbuff.h:179
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition: sbuff.h:167
size_t len
Length of the list.
Definition: sbuff.h:147
#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:148
#define FR_SBUFF_TERM(_str)
Initialise a terminal structure with a single string.
Definition: sbuff.h:155
Set of terminal elements.
Definition: merged_model.c:161
File sbuff extension structure.
Definition: sbuff.h:125
Talloc sbuff extension structure.
Definition: sbuff.h:114
Set of parsing rules for *unescape_until functions.
Definition: merged_model.c:163
static void test_adv_past_allowed(void)
Definition: sbuff_tests.c:1230
TEST_LIST
Definition: sbuff_tests.c:1577
static void test_no_advance(void)
Definition: sbuff_tests.c:826
static void test_file_extend_max(void)
Definition: sbuff_tests.c:1107
static void test_talloc_extend_multi_level(void)
Definition: sbuff_tests.c:954
static void test_adv_past_strcase(void)
Definition: sbuff_tests.c:1163
static void test_adv_to_strcase(void)
Definition: sbuff_tests.c:1470
static void test_talloc_extend(void)
Definition: sbuff_tests.c:843
static void test_bstrncpy_until(void)
Definition: sbuff_tests.c:314
static void test_bstrncpy_allowed(void)
Definition: sbuff_tests.c:226
#define TEST_SBUFF_USED(_sbuff, _num)
Definition: sbuff_tests.c:39
static void test_adv_until(void)
Definition: sbuff_tests.c:1268
static bool allow_lowercase_and_space_no_t[UINT8_MAX+1]
Definition: sbuff_tests.c:217
static void test_is_char(void)
Definition: sbuff_tests.c:75
static void test_bstrncpy(void)
Definition: sbuff_tests.c:156
static void test_bstrncpy_exact(void)
Definition: sbuff_tests.c:104
static void test_adv_to_utf8(void)
Definition: sbuff_tests.c:1304
static void test_adv_to_chr(void)
Definition: sbuff_tests.c:1364
static void test_eof_terminal(void)
Definition: sbuff_tests.c:732
static void test_next_unless_char(void)
Definition: sbuff_tests.c:1554
static void test_adv_past_whitespace(void)
Definition: sbuff_tests.c:1194
static void test_next_if_char(void)
Definition: sbuff_tests.c:1531
static void test_unescape_until(void)
Definition: sbuff_tests.c:406
static void test_adv_past_str(void)
Definition: sbuff_tests.c:1132
static bool allow_lowercase_and_space[UINT8_MAX+1]
Definition: sbuff_tests.c:208
static void test_unescape_multi_char_terminals(void)
Definition: sbuff_tests.c:697
#define TEST_SBUFF_LEN(_sbuff, _num)
Definition: sbuff_tests.c:30
static void test_file_extend(void)
Definition: sbuff_tests.c:1036
static void test_terminal_merge(void)
Definition: sbuff_tests.c:762
static void test_parse_init(void)
Definition: sbuff_tests.c:48
static void test_adv_to_str(void)
Definition: sbuff_tests.c:1409
static void test_talloc_extend_init_zero(void)
Definition: sbuff_tests.c:923
static void test_talloc_extend_with_marker(void)
Definition: sbuff_tests.c:987
#define PATTERN_LEN
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
static size_t char ** out
Definition: value.h:997