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  TEST_CHECK(fp != NULL);
1060  TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1061  our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1062 
1063  TEST_CASE("Advance past whitespace, which will require shift/extend");
1064  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&our_sbuff, SIZE_MAX, NULL), sizeof(fbuff) - PATTERN_LEN);
1065  TEST_CASE("Verify extend on unused child buffer");
1066  child_sbuff = FR_SBUFF(&our_sbuff);
1067  slen = fr_sbuff_extend_file(&child_sbuff, 0);
1068  TEST_CHECK_SLEN(slen, sizeof(fbuff) % PATTERN_LEN);
1069  TEST_CASE("Verify that we passed all and only whitespace");
1070  (void) fr_sbuff_out_abstrncpy(NULL, &post_ws, &our_sbuff, 24);
1071  TEST_CHECK_STRCMP(post_ws, PATTERN);
1072  talloc_free(post_ws);
1073  TEST_CASE("Verify parent buffer end");
1074  TEST_CHECK(sbuff.end == our_sbuff.end);
1075 
1076  TEST_CASE("Verify that we do not read shifted buffer past eof");
1077  slen = fr_sbuff_out_bstrncpy(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1078  TEST_CHECK_SLEN(slen, 0);
1079  slen = fr_sbuff_out_bstrncpy_exact(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX);
1080  TEST_CHECK_SLEN(slen, 0);
1081  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, NULL, NULL);
1082  TEST_CHECK_SLEN(slen, 0);
1083  slen = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, allow_lowercase_and_space);
1084  TEST_CHECK_SLEN(slen, 0);
1085 
1086  fclose(fp);
1087 
1088  TEST_CASE("Verify fr_sbuff_out_bstrncpy_until() extends from file properly");
1089  fp = fmemopen(fbuff, sizeof(fbuff), "r");
1090  TEST_CHECK(fp != NULL);
1091  TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, 128) == &sbuff);
1092  our_sbuff = FR_SBUFF_BIND_CURRENT(&sbuff);
1093  slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(out, sizeof(out)), &our_sbuff, SIZE_MAX, &FR_SBUFF_TERM("x"), NULL);
1094  TEST_CHECK_SLEN(slen, sizeof(fbuff) - PATTERN_LEN);
1095 
1096  fclose(fp);
1097 }
1098 
1099 static void test_file_extend_max(void)
1100 {
1101  fr_sbuff_t sbuff;
1102  fr_sbuff_uctx_file_t fctx;
1103  FILE *fp;
1104  char buff[16];
1105  char fbuff[] = " xyzzy";
1106  char *post_ws;
1107 
1108  TEST_CASE("Initialization");
1109  fp = fmemopen(fbuff, sizeof(fbuff) - 1, "r");
1110  TEST_CHECK(fp != NULL);
1111  TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx, buff, sizeof(buff), fp, sizeof(fbuff) - 8) == &sbuff);
1112 
1113  TEST_CASE("Confirm that max stops us from seeing xyzzy");
1114  TEST_CHECK_SLEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), sizeof(fbuff) - 8);
1115  TEST_CHECK_SLEN(fr_sbuff_out_abstrncpy(NULL, &post_ws, &sbuff, 24), 0);
1116  TEST_CHECK_STRCMP(post_ws, "");
1117  talloc_free(post_ws);
1118  fclose(fp);
1119 }
1120 
1121 static void test_adv_past_str(void)
1122 {
1123  fr_sbuff_t sbuff;
1124  char const in[] = "i am a test string";
1125 
1126  TEST_CASE("Check for token at beginning of string");
1127  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1128  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 6);
1129  TEST_CHECK_STRCMP(sbuff.p, " test string");
1130 
1131  TEST_CASE("Check for token not at beginning of string");
1132  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1133  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, " am a", SIZE_MAX), 0);
1134  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1135 
1136  TEST_CASE("Check for token larger than the string");
1137  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1138  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string ", SIZE_MAX), 0);
1139  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1140 
1141  TEST_CASE("Check for token with zero length string");
1142  fr_sbuff_init_in(&sbuff, in, 0);
1143  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a", SIZE_MAX), 0);
1144 
1145  TEST_CASE("Check for token that is the string");
1146  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1147  TEST_CHECK_LEN(fr_sbuff_adv_past_str(&sbuff, "i am a test string", SIZE_MAX), 18);
1148  TEST_CHECK_STRCMP(sbuff.p, "");
1149  TEST_CHECK(sbuff.p == sbuff.end);
1150 }
1151 
1152 static void test_adv_past_strcase(void)
1153 {
1154  fr_sbuff_t sbuff;
1155  char const in[] = "i am a test string";
1156 
1157  TEST_CASE("Check for token at beginning of string");
1158  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1159  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 6);
1160  TEST_CHECK_STRCMP(sbuff.p, " test string");
1161 
1162  TEST_CASE("Check for token not at beginning of string");
1163  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1164  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, " AM a", SIZE_MAX), 0);
1165  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1166 
1167  TEST_CASE("Check for token larger than the string");
1168  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1169  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string ", SIZE_MAX), 0);
1170  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1171 
1172  TEST_CASE("Check for token with zero length string");
1173  fr_sbuff_init_in(&sbuff, in, 0);
1174  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a", SIZE_MAX), 0);
1175 
1176  TEST_CASE("Check for token that is the string");
1177  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1178  TEST_CHECK_LEN(fr_sbuff_adv_past_strcase(&sbuff, "i AM a TEST string", SIZE_MAX), 18);
1179  TEST_CHECK_STRCMP(sbuff.p, "");
1180  TEST_CHECK(sbuff.p == sbuff.end);
1181 }
1182 
1183 static void test_adv_past_whitespace(void)
1184 {
1185  fr_sbuff_t sbuff;
1186  char const in[] = " i am a test string";
1187  char const in_ns[] = "i am a test string";
1188  char const in_ws[] = " ";
1189 
1190  TEST_CASE("Check for token at beginning of string");
1191  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1192  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1193  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1194 
1195  TEST_CASE("Check for token not at beginning of string");
1196  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1197  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1198  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1199 
1200  TEST_CASE("Check for token with zero length string");
1201  fr_sbuff_init_in(&sbuff, in, 0);
1202  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 0);
1203 
1204  TEST_CASE("Check for token that is the string");
1205  fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1206  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL), 5);
1207 
1208  TEST_CASE("Length constraint with token match");
1209  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1210  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 2);
1211  TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1212 
1213  TEST_CASE("Length constraint without token match");
1214  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1215  TEST_CHECK_LEN(fr_sbuff_adv_past_whitespace(&sbuff, 2, NULL), 0);
1216  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1217 }
1218 
1219 static void test_adv_past_allowed(void)
1220 {
1221  fr_sbuff_t sbuff;
1222  char const in[] = " i am a test string";
1223  char const in_ns[] = "i am a test string";
1224  char const in_ws[] = " ";
1225 
1226  TEST_CASE("Check for token at beginning of string");
1227  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1228  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 5);
1229  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1230 
1231  TEST_CASE("Check for token not at beginning of string");
1232  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1233  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 0);
1234  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1235 
1236  TEST_CASE("Check for token with zero length string");
1237  fr_sbuff_init_in(&sbuff, in, 0);
1238  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 0);
1239  TEST_CHECK(sbuff.p == sbuff.start);
1240 
1241  TEST_CASE("Check for token at the end of the string");
1242  fr_sbuff_init_in(&sbuff, in_ws, sizeof(in_ws) - 1);
1243  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 5);
1244  TEST_CHECK(sbuff.p == sbuff.end);
1245 
1246  TEST_CASE("Length constraint with token match");
1247  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1248  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 2);
1249  TEST_CHECK_STRCMP(sbuff.p, " i am a test string");
1250 
1251  TEST_CASE("Length constraint with token match");
1252  fr_sbuff_init_in(&sbuff, in_ns, sizeof(in_ns) - 1);
1253  TEST_CHECK_LEN(fr_sbuff_adv_past_allowed(&sbuff, 2, (bool[UINT8_MAX + 1]){ [' '] = true }, NULL), 0);
1254  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1255 }
1256 
1257 static void test_adv_until(void)
1258 {
1259  fr_sbuff_t sbuff;
1260  char const in[] = " abcdefgh ijklmnopp";
1261 
1262  TEST_CASE("Check for token at beginning of string");
1263  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1264  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM(" "), '\0'), 0);
1265  TEST_CHECK_STRCMP(sbuff.p, " abcdefgh ijklmnopp");
1266 
1267  TEST_CASE("Check for token not at beginning of string");
1268  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1269  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 1);
1270  TEST_CHECK_STRCMP(sbuff.p, "abcdefgh ijklmnopp");
1271 
1272  TEST_CASE("Check for token with zero length string");
1273  fr_sbuff_init_in(&sbuff, in, 0);
1274  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("a"), '\0'), 0);
1275  TEST_CHECK(sbuff.p == sbuff.start);
1276 
1277  TEST_CASE("Check for token that is not in the string");
1278  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1279  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("|"), '\0'), 19);
1280  TEST_CHECK(sbuff.p == sbuff.end);
1281 
1282  TEST_CASE("Check escapes");
1283  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1284  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, SIZE_MAX, &FR_SBUFF_TERM("p"), 'o'), 18);
1285  TEST_CHECK_STRCMP(sbuff.p, "p");
1286 
1287  TEST_CASE("Check for token that is not in the string with length constraint");
1288  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1289  TEST_CHECK_LEN(fr_sbuff_adv_until(&sbuff, 5, &FR_SBUFF_TERM("|"), '\0'), 5);
1290  TEST_CHECK(sbuff.p == (sbuff.start + 5));
1291 }
1292 
1293 static void test_adv_to_utf8(void)
1294 {
1295  fr_sbuff_t sbuff;
1296  char const in[] = "🥺🥺🥺🥺🍪😀";
1297  char *p;
1298 
1299  TEST_CASE("Check for token at beginning of string");
1300  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1301  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🥺");
1302  TEST_CHECK(p == sbuff.p);
1303  TEST_CHECK_STRCMP(sbuff.p, "🥺🥺🥺🥺🍪😀");
1304 
1305  TEST_CASE("Check for token not at beginning of string");
1306  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1307  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1308  TEST_CHECK(p == (sbuff.start + (sizeof("🥺🥺🥺🥺") - 1)));
1309  TEST_CHECK_STRCMP(p, "🍪😀");
1310 
1311  TEST_CASE("Check for token with zero length string");
1312  fr_sbuff_init_in(&sbuff, in, 0);
1313  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍪");
1314  TEST_CHECK(p == NULL);
1315  TEST_CHECK(sbuff.start == sbuff.p);
1316 
1317  TEST_CASE("Check for token at the end of the string");
1318  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1319  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "😀");
1320  TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1321 
1322  TEST_CASE("Check for token not in the string");
1323  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1324  p = fr_sbuff_adv_to_chr_utf8(&sbuff, SIZE_MAX, "🍆 ");
1325  TEST_CHECK(p == NULL);
1326 
1327  TEST_CASE("Check for token at the end of the string within len constraints");
1328  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1329  p = fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 1), "😀");
1330  TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
1331 
1332  TEST_CASE("Check for token at the end of the string outside len constraints #1");
1333  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1334  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 2), "😀"));
1335  TEST_CHECK(sbuff.p == sbuff.start);
1336 
1337  TEST_CASE("Check for token at the end of the string outside len constraints #2");
1338  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1339  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 3), "😀"));
1340  TEST_CHECK(sbuff.p == sbuff.start);
1341 
1342  TEST_CASE("Check for token at the end of the string outside len constraints #3");
1343  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1344  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 4), "😀"));
1345  TEST_CHECK(sbuff.p == sbuff.start);
1346 
1347  TEST_CASE("Check for token at the end of the string outside len constraints #4");
1348  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1349  TEST_CHECK(!fr_sbuff_adv_to_chr_utf8(&sbuff, (sizeof("🥺🥺🥺🥺🍪😀") - 5), "😀"));
1350  TEST_CHECK(sbuff.p == sbuff.start);
1351 }
1352 
1353 static void test_adv_to_chr(void)
1354 {
1355  fr_sbuff_t sbuff;
1356  char const in[] = "AAAAbC";
1357  char *p;
1358 
1359  TEST_CASE("Check for token at beginning of string");
1360  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1361  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'A');
1362  TEST_CHECK(p == sbuff.p);
1363  TEST_CHECK_STRCMP(sbuff.p, "AAAAbC");
1364 
1365  TEST_CASE("Check for token not at beginning of string");
1366  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1367  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b');
1368  TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1369  TEST_CHECK_STRCMP(p, "bC");
1370 
1371  TEST_CASE("Check for token with zero length string");
1372  fr_sbuff_init_in(&sbuff, in, 0);
1373  TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'b'));
1374  TEST_CHECK(sbuff.start == sbuff.p);
1375 
1376  TEST_CASE("Check for token at the end of the string");
1377  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1378  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'C');
1379  TEST_CHECK(p == sbuff.start + (sizeof("AAAAb") - 1));
1380 
1381  TEST_CASE("Check for token not in the string");
1382  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1383  p = fr_sbuff_adv_to_chr(&sbuff, SIZE_MAX, 'D');
1384  TEST_CHECK(p == NULL);
1385 
1386  TEST_CASE("Check for token not at beginning of string within length constraints");
1387  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1388  p = fr_sbuff_adv_to_chr(&sbuff, 5, 'b');
1389  TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
1390  TEST_CHECK_STRCMP(p, "bC");
1391 
1392  TEST_CASE("Check for token not at beginning of string outside length constraints");
1393  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1394  TEST_CHECK(!fr_sbuff_adv_to_chr(&sbuff, 4, 'b'));
1395  TEST_CHECK(sbuff.p == sbuff.start);
1396 }
1397 
1398 static void test_adv_to_str(void)
1399 {
1400  fr_sbuff_t sbuff;
1401  char const in[] = "i am a test string";
1402  char *p;
1403 
1404  TEST_CASE("Check for token at beginning of string");
1405  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1406  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test", SIZE_MAX);
1407  TEST_CHECK(sbuff.p == p);
1408  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1409 
1410  TEST_CASE("Check for token not at beginning of string");
1411  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1412  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "test", SIZE_MAX);
1413  TEST_CHECK(sbuff.p == p);
1414  TEST_CHECK_STRCMP(sbuff.p, "test string");
1415 
1416  TEST_CASE("Check for token at the end of string");
1417  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1418  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "ing", SIZE_MAX);
1419  TEST_CHECK(sbuff.p == p);
1420  TEST_CHECK_STRCMP(sbuff.p, "ing");
1421 
1422  TEST_CASE("Check for token larger than the string");
1423  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1424  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string ", SIZE_MAX);
1425  TEST_CHECK(sbuff.p == sbuff.start);
1426  TEST_CHECK(p == NULL);
1427 
1428  TEST_CASE("Check for token shorter than string, not in the string");
1429  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1430  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "ng ", SIZE_MAX);
1431  TEST_CHECK(sbuff.p == sbuff.start);
1432  TEST_CHECK(p == NULL);
1433 
1434  TEST_CASE("Check for token with zero length string");
1435  fr_sbuff_init_in(&sbuff, in, 0);
1436  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a", SIZE_MAX);
1437  TEST_CHECK(sbuff.p == sbuff.start);
1438  TEST_CHECK(p == NULL);
1439 
1440  TEST_CASE("Check for token that is the string");
1441  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1442  p = fr_sbuff_adv_to_str(&sbuff, SIZE_MAX, "i am a test string", SIZE_MAX);
1443  TEST_CHECK(sbuff.p == sbuff.start);
1444  TEST_CHECK(sbuff.p == p);
1445  TEST_CHECK_STRCMP(p, "i am a test string");
1446 
1447  TEST_CASE("Check for token not at beginning of string within length constraints");
1448  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1449  p = fr_sbuff_adv_to_str(&sbuff, 11, "test", SIZE_MAX);
1450  TEST_CHECK(sbuff.p == p);
1451  TEST_CHECK_STRCMP(sbuff.p, "test string");
1452 
1453  TEST_CASE("Check for token not at beginning of string outside length constraints");
1454  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1455  TEST_CHECK(!fr_sbuff_adv_to_str(&sbuff, 10, "test", SIZE_MAX));
1456  TEST_CHECK(sbuff.p == sbuff.start);
1457 }
1458 
1459 static void test_adv_to_strcase(void)
1460 {
1461  fr_sbuff_t sbuff;
1462  char const in[] = "i am a test string";
1463  char *p;
1464 
1465  TEST_CASE("Check for token at beginning of string");
1466  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1467  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a TEST", SIZE_MAX);
1468  TEST_CHECK(sbuff.p == p);
1469  TEST_CHECK_STRCMP(sbuff.p, "i am a test string");
1470 
1471  TEST_CASE("Check for token not at beginning of string");
1472  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1473  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "tEst", SIZE_MAX);
1474  TEST_CHECK(sbuff.p == p);
1475  TEST_CHECK_STRCMP(sbuff.p, "test string");
1476 
1477  TEST_CASE("Check for token at the end of string");
1478  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1479  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "Ing", SIZE_MAX);
1480  TEST_CHECK(sbuff.p == p);
1481  TEST_CHECK_STRCMP(sbuff.p, "ing");
1482 
1483  TEST_CASE("Check for token larger than the string");
1484  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1485  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i aM a tEst stRIng ", SIZE_MAX);
1486  TEST_CHECK(sbuff.p == sbuff.start);
1487  TEST_CHECK(p == NULL);
1488 
1489  TEST_CASE("Check for token shorter than string, not in the string");
1490  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1491  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "nG ", SIZE_MAX);
1492  TEST_CHECK(sbuff.p == sbuff.start);
1493  TEST_CHECK(p == NULL);
1494 
1495  TEST_CASE("Check for token with zero length string");
1496  fr_sbuff_init_in(&sbuff, in, 0);
1497  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a", SIZE_MAX);
1498  TEST_CHECK(sbuff.p == sbuff.start);
1499  TEST_CHECK(p == NULL);
1500 
1501  TEST_CASE("Check for token that is the string");
1502  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1503  p = fr_sbuff_adv_to_strcase(&sbuff, SIZE_MAX, "i AM a teST stRIng", SIZE_MAX);
1504  TEST_CHECK(sbuff.p == sbuff.start);
1505  TEST_CHECK(sbuff.p == p);
1506  TEST_CHECK_STRCMP(p, "i am a test string");
1507 
1508  TEST_CASE("Check for token not at beginning of string within length constraints");
1509  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1510  p = fr_sbuff_adv_to_strcase(&sbuff, 11, "tEst", SIZE_MAX);
1511  TEST_CHECK(sbuff.p == p);
1512  TEST_CHECK_STRCMP(sbuff.p, "test string");
1513 
1514  TEST_CASE("Check for token not at beginning of string outside length constraints");
1515  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1516  TEST_CHECK(!fr_sbuff_adv_to_strcase(&sbuff, 10, "tEst", SIZE_MAX));
1517  TEST_CHECK(sbuff.p == sbuff.start);
1518 }
1519 
1520 static void test_next_if_char(void)
1521 {
1522  fr_sbuff_t sbuff;
1523  char const in[] = "i ";
1524 
1525  TEST_CASE("Check for advancement on match");
1526  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1527  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == true);
1528  TEST_CHECK_STRCMP(sbuff.p, " ");
1529 
1530  TEST_CASE("Check for non-advancement on non-match");
1531  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == false);
1532  TEST_CHECK_STRCMP(sbuff.p, " ");
1533 
1534  TEST_CASE("Check for advancement at end");
1535  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == true);
1536  TEST_CHECK_STRCMP(sbuff.p, "");
1537 
1538  TEST_CASE("Check we can't advance off the end of the buffer");
1539  TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == false);
1540  TEST_CHECK_STRCMP(sbuff.p, "");
1541 }
1542 
1543 static void test_next_unless_char(void)
1544 {
1545  fr_sbuff_t sbuff;
1546  char const in[] = "i ";
1547 
1548  TEST_CASE("Check for advancement on non-match");
1549  fr_sbuff_init_in(&sbuff, in, sizeof(in) - 1);
1550  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == true);
1551  TEST_CHECK_STRCMP(sbuff.p, " ");
1552 
1553  TEST_CASE("Check for non-advancement on match");
1554  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == false);
1555  TEST_CHECK_STRCMP(sbuff.p, " ");
1556 
1557  TEST_CASE("Check for advancement at end");
1558  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == true);
1559  TEST_CHECK_STRCMP(sbuff.p, "");
1560 
1561  TEST_CASE("Check we can't advance off the end of the buffer");
1562  TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == false);
1563  TEST_CHECK_STRCMP(sbuff.p, "");
1564 }
1565 
1567  /*
1568  * Basic tests
1569  */
1570  { "fr_sbuff_init", test_parse_init },
1571  { "fr_sbuff_is_char", test_is_char },
1572  { "fr_sbuff_out_bstrncpy_exact", test_bstrncpy_exact },
1573  { "fr_sbuff_out_bstrncpy", test_bstrncpy },
1574  { "fr_sbuff_out_bstrncpy_allowed", test_bstrncpy_allowed },
1575  { "fr_sbuff_out_bstrncpy_until", test_bstrncpy_until },
1576  { "multi-char terminals", test_unescape_multi_char_terminals },
1577  { "fr_sbuff_out_unescape_until", test_unescape_until },
1578  { "fr_sbuff_terminal_eof", test_eof_terminal },
1579  { "terminal merge", test_terminal_merge },
1580 
1581  /*
1582  * Extending buffer
1583  */
1584  { "fr_sbuff_talloc_extend", test_talloc_extend },
1585  { "fr_sbuff_talloc_extend_init_zero", test_talloc_extend_init_zero },
1586  { "fr_sbuff_talloc_extend_multi_level", test_talloc_extend_multi_level },
1587  { "fr_sbuff_talloc_extend_with_marker", test_talloc_extend_with_marker },
1588  { "fr_sbuff_file_extend", test_file_extend },
1589  { "fr_sbuff_file_extend_max", test_file_extend_max },
1590 
1591  { "fr_sbuff_no_advance", test_no_advance },
1592 
1593  /*
1594  * Token skipping
1595  */
1596  { "fr_sbuff_adv_past_str", test_adv_past_str },
1597  { "fr_sbuff_adv_past_strcase", test_adv_past_strcase },
1598  { "fr_sbuff_adv_past_whitespace", test_adv_past_whitespace },
1599  { "fr_sbuff_adv_past_allowed", test_adv_past_allowed },
1600  { "fr_sbuff_adv_until", test_adv_until },
1601 
1602  /*
1603  * Token searching
1604  */
1605  { "fr_sbuff_adv_to_utf8", test_adv_to_utf8 },
1606  { "fr_sbuff_adv_to_chr", test_adv_to_chr },
1607  { "fr_sbuff_adv_to_str", test_adv_to_str },
1608  { "fr_sbuff_adv_to_strcase", test_adv_to_strcase },
1609 
1610  /*
1611  * Advancement
1612  */
1613  { "fr_sbuff_next_if_char", test_next_if_char },
1614  { "fr_sbuff_next_unless_char", test_next_unless_char },
1615 
1616  { NULL }
1617 };
#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:645
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:399
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:1419
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:1736
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:1870
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:1951
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:613
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:1915
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:257
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:1671
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:2004
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition: sbuff.c:2111
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:1702
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:2068
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:1811
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:691
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:2047
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:1219
TEST_LIST
Definition: sbuff_tests.c:1566
static void test_no_advance(void)
Definition: sbuff_tests.c:826
static void test_file_extend_max(void)
Definition: sbuff_tests.c:1099
static void test_talloc_extend_multi_level(void)
Definition: sbuff_tests.c:954
static void test_adv_past_strcase(void)
Definition: sbuff_tests.c:1152
static void test_adv_to_strcase(void)
Definition: sbuff_tests.c:1459
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:1257
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:1293
static void test_adv_to_chr(void)
Definition: sbuff_tests.c:1353
static void test_eof_terminal(void)
Definition: sbuff_tests.c:732
static void test_next_unless_char(void)
Definition: sbuff_tests.c:1543
static void test_adv_past_whitespace(void)
Definition: sbuff_tests.c:1183
static void test_next_if_char(void)
Definition: sbuff_tests.c:1520
static void test_unescape_until(void)
Definition: sbuff_tests.c:406
static void test_adv_past_str(void)
Definition: sbuff_tests.c:1121
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:1398
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:984