23 #include <freeradius-devel/util/acutest.h>
24 #include <freeradius-devel/util/acutest_helpers.h>
30 #define TEST_SBUFF_LEN(_sbuff, _num) \
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); \
39 #define TEST_SBUFF_USED(_sbuff, _num) \
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); \
50 char const in[] =
"i am a test string";
77 char const in[] =
"i am a test string";
91 fr_sbuff_marker(&marker, &sbuff);
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] =
"";
132 TEST_CASE(
"Copy would overrun output (and SIZE_MAX special value)");
140 fr_sbuff_set_to_start(&sbuff);
148 fr_sbuff_set_to_start(&sbuff);
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] =
"";
184 TEST_CASE(
"Copy would overrun output (and SIZE_MAX special value)");
192 fr_sbuff_set_to_start(&sbuff);
200 fr_sbuff_set_to_start(&sbuff);
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
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
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] =
"";
259 TEST_CASE(
"Copy would overrun output (and SIZE_MAX special value)");
267 fr_sbuff_set_to_start(&sbuff);
275 fr_sbuff_set_to_start(&sbuff);
286 fr_sbuff_set_to_start(&sbuff);
292 TEST_CASE(
"Copy until first t with length constraint (same len as token)");
293 fr_sbuff_set_to_start(&sbuff);
299 TEST_CASE(
"Copy until first t with length constraint (one shorter than token)");
300 fr_sbuff_set_to_start(&sbuff);
306 TEST_CASE(
"Zero length token (should still be terminated)");
307 fr_sbuff_set_to_start(&sbuff);
316 char const in[] =
"i am a test string";
317 char const in_long[] =
"i am a longer test string";
348 fr_sbuff_set_to_start(&sbuff);
355 TEST_CASE(
"Copy would overrun output (and SIZE_MAX special value)");
363 fr_sbuff_set_to_start(&sbuff);
371 fr_sbuff_set_to_start(&sbuff);
382 fr_sbuff_set_to_start(&sbuff);
387 TEST_CASE(
"Copy until first t with length constraint (same len as token)");
388 fr_sbuff_set_to_start(&sbuff);
393 TEST_CASE(
"Copy until first t with length constraint (one shorter than token)");
394 fr_sbuff_set_to_start(&sbuff);
399 TEST_CASE(
"Zero length token (should still be terminated)");
400 fr_sbuff_set_to_start(&sbuff);
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];
424 .subs = { [
'g'] =
'g', [
'|'] =
'|' }
428 .
chr =
'|', .subs = { [
'g'] =
'h', [
'|'] =
'|' }
433 .subs = { [
'g'] =
'h', [
'|'] =
'|' },
439 .subs = { [
'g'] =
'h', [
'|'] =
'|' },
445 .subs = { [
'g'] =
'h', [
'|'] =
'|' },
474 TEST_CASE(
"Copy would overrun output (and SIZE_MAX special value)");
482 fr_sbuff_set_to_start(&sbuff);
490 fr_sbuff_set_to_start(&sbuff);
501 fr_sbuff_set_to_start(&sbuff);
507 TEST_CASE(
"Copy until first t with length constraint (same len as token)");
508 fr_sbuff_set_to_start(&sbuff);
514 TEST_CASE(
"Copy until first t with length constraint (one shorter than token)");
515 fr_sbuff_set_to_start(&sbuff);
521 TEST_CASE(
"Zero length token (should still be terminated)");
522 fr_sbuff_set_to_start(&sbuff);
531 TEST_CASE(
"Escape with substitution to same char");
539 TEST_CASE(
"Escape with substitution to different char");
548 char tmp_out[24 + 1];
550 TEST_CASE(
"Escape with hex substitutions (insufficient output space)");
560 char tmp_out[25 + 1];
562 TEST_CASE(
"Escape with hex substitutions (sufficient output space)");
572 char tmp_out[28 + 1];
574 TEST_CASE(
"Escape with oct substitutions (insufficient output space)");
584 char tmp_out[29 + 1];
586 TEST_CASE(
"Escape with oct substitutions (sufficient output space)");
596 char tmp_out[26 + 1];
598 TEST_CASE(
"Escape with hex and oct substitutions (sufficient output space)");
609 char const in_escapes_collapse[] =
"||";
612 fr_sbuff_init_in(&sbuff, in_escapes_collapse,
sizeof(in_escapes_collapse) - 1);
614 &sbuff, SIZE_MAX, NULL, &pipe_rules);
621 char in_escapes_collapse[] =
"||foo||";
623 TEST_CASE(
"Collapse double escapes overlapping");
624 fr_sbuff_init_in(&sbuff, in_escapes_collapse,
sizeof(in_escapes_collapse) - 1);
626 &sbuff, SIZE_MAX, NULL, &pipe_rules);
633 char tmp_out[30 + 1];
652 char const in_escapes_unit[] =
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',
671 TEST_CASE(
"Check unit test test strings");
674 NULL, &double_quote_rules);
686 char const in_zero[] =
"";
688 len = fr_sbuff_out_aunescape_until(NULL, &
buff, &
FR_SBUFF_IN(in_zero,
sizeof(in_zero) - 1), SIZE_MAX,
691 talloc_get_type_abort(
buff,
char);
699 char const in[] =
"foo, bar, baz```";
734 char const in[] =
"foo, bar";
819 for (i = 0; i < result->
len; i++) {
828 char const *
in =
"i am a test string";
829 char out[18 + 1] =
"";
835 TEST_CASE(
"Copy 5 bytes to out - no advance");
849 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 32, 50) == &sbuff);
858 TEST_CASE(
"Print string - Should realloc to init");
868 TEST_CASE(
"Print string - Should realloc to init");
878 TEST_CASE(
"Print string - Should realloc to double buffer len");
884 TEST_CASE(
"Print string - Should only add a single char, should not extend the buffer");
890 TEST_CASE(
"Print string - Use all available buffer data");
896 TEST_CASE(
"Print string - Add single char, should trigger doubling constrained by max");
902 TEST_CASE(
"Print string - Add data to take us up to max");
908 TEST_CASE(
"Print string - Add single char, should fail");
914 TEST_CASE(
"Trim to strlen (should be noop)");
929 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff, &tctx, 0, 50) == &sbuff);
933 TEST_CASE(
"Print string - Should alloc one byte");
939 TEST_CASE(
"Print string - Should alloc two bytes");
945 TEST_CASE(
"Print string - Should alloc three bytes");
960 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
965 TEST_CASE(
"Check sbuff_1 has extend fields set");
972 TEST_CASE(
"Print string - Should alloc one byte");
994 TEST_CHECK(fr_sbuff_init_talloc(NULL, &sbuff_0, &tctx, 0, 50) == &sbuff_0);
998 TEST_CASE(
"Print string - Should alloc one byte");
1004 fr_sbuff_marker(&marker_0, &sbuff_0);
1005 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1007 TEST_CASE(
"Print string - Ensure marker is updated");
1012 TEST_CHECK((marker_0.p - sbuff_0.start) == 1);
1017 fr_sbuff_marker(&marker_1, &sbuff_1);
1019 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1020 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1023 TEST_CASE(
"Print string - Trigger re-alloc, ensure all pointers are updated");
1028 TEST_CHECK((marker_1.p - sbuff_1.start) == 0);
1029 TEST_CHECK((marker_1.p - sbuff_0.start) == 2);
1045 const char PATTERN[] =
"xyzzy";
1046 #define PATTERN_LEN (sizeof(PATTERN) - 1)
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");
1055 memset(fbuff,
' ',
sizeof(fbuff));
1058 fp = fmemopen(fbuff,
sizeof(fbuff),
"r");
1059 #ifdef __clang_analyzer__
1060 if (fp == NULL)
return;
1064 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx,
buff,
sizeof(
buff), fp, 128) == &sbuff);
1067 TEST_CASE(
"Advance past whitespace, which will require shift/extend");
1069 TEST_CASE(
"Verify extend on unused child buffer");
1070 child_sbuff =
FR_SBUFF(&our_sbuff);
1073 TEST_CASE(
"Verify that we passed all and only whitespace");
1074 (void) fr_sbuff_out_abstrncpy(NULL, &post_ws, &our_sbuff, 24);
1080 TEST_CASE(
"Verify that we do not read shifted buffer past eof");
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;
1099 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx,
buff,
sizeof(
buff), fp, 128) == &sbuff);
1113 char fbuff[] =
" xyzzy";
1117 fp = fmemopen(fbuff,
sizeof(fbuff) - 1,
"r");
1118 #ifdef __clang_analyzer__
1119 if (fp == NULL)
return;
1122 TEST_CHECK(fr_sbuff_init_file(&sbuff, &fctx,
buff,
sizeof(
buff), fp,
sizeof(fbuff) - 8) == &sbuff);
1124 TEST_CASE(
"Confirm that max stops us from seeing xyzzy");
1126 TEST_CHECK_SLEN(fr_sbuff_out_abstrncpy(NULL, &post_ws, &sbuff, 24), 0);
1135 char const in[] =
"i am a test string";
1137 TEST_CASE(
"Check for token at beginning of string");
1142 TEST_CASE(
"Check for token not at beginning of string");
1147 TEST_CASE(
"Check for token larger than the string");
1152 TEST_CASE(
"Check for token with zero length string");
1156 TEST_CASE(
"Check for token that is the string");
1166 char const in[] =
"i am a test string";
1168 TEST_CASE(
"Check for token at beginning of string");
1173 TEST_CASE(
"Check for token not at beginning of string");
1178 TEST_CASE(
"Check for token larger than the string");
1183 TEST_CASE(
"Check for token with zero length string");
1187 TEST_CASE(
"Check for token that is the string");
1197 char const in[] =
" i am a test string";
1198 char const in_ns[] =
"i am a test string";
1199 char const in_ws[] =
" ";
1201 TEST_CASE(
"Check for token at beginning of string");
1206 TEST_CASE(
"Check for token not at beginning of string");
1211 TEST_CASE(
"Check for token with zero length string");
1215 TEST_CASE(
"Check for token that is the string");
1219 TEST_CASE(
"Length constraint with token match");
1224 TEST_CASE(
"Length constraint without token match");
1233 char const in[] =
" i am a test string";
1234 char const in_ns[] =
"i am a test string";
1235 char const in_ws[] =
" ";
1237 TEST_CASE(
"Check for token at beginning of string");
1242 TEST_CASE(
"Check for token not at beginning of string");
1247 TEST_CASE(
"Check for token with zero length string");
1252 TEST_CASE(
"Check for token at the end of the string");
1257 TEST_CASE(
"Length constraint with token match");
1262 TEST_CASE(
"Length constraint with token match");
1271 char const in[] =
" abcdefgh ijklmnopp";
1273 TEST_CASE(
"Check for token at beginning of string");
1278 TEST_CASE(
"Check for token not at beginning of string");
1283 TEST_CASE(
"Check for token with zero length string");
1288 TEST_CASE(
"Check for token that is not in the string");
1298 TEST_CASE(
"Check for token that is not in the string with length constraint");
1307 char const in[] =
"🥺🥺🥺🥺🍪😀";
1310 TEST_CASE(
"Check for token at beginning of string");
1316 TEST_CASE(
"Check for token not at beginning of string");
1319 TEST_CHECK(p == (sbuff.start + (
sizeof(
"🥺🥺🥺🥺") - 1)));
1322 TEST_CASE(
"Check for token with zero length string");
1328 TEST_CASE(
"Check for token at the end of the string");
1331 TEST_CHECK(p == sbuff.start + (
sizeof(
"🥺🥺🥺🥺🍪") - 1));
1333 TEST_CASE(
"Check for token not in the string");
1338 TEST_CASE(
"Check for token at the end of the string within len constraints");
1341 TEST_CHECK(p == sbuff.start + (
sizeof(
"🥺🥺🥺🥺🍪") - 1));
1343 TEST_CASE(
"Check for token at the end of the string outside len constraints #1");
1348 TEST_CASE(
"Check for token at the end of the string outside len constraints #2");
1353 TEST_CASE(
"Check for token at the end of the string outside len constraints #3");
1358 TEST_CASE(
"Check for token at the end of the string outside len constraints #4");
1367 char const in[] =
"AAAAbC";
1370 TEST_CASE(
"Check for token at beginning of string");
1376 TEST_CASE(
"Check for token not at beginning of string");
1379 TEST_CHECK(p == (sbuff.start + (
sizeof(
"AAAA") - 1)));
1382 TEST_CASE(
"Check for token with zero length string");
1387 TEST_CASE(
"Check for token at the end of the string");
1390 TEST_CHECK(p == sbuff.start + (
sizeof(
"AAAAb") - 1));
1392 TEST_CASE(
"Check for token not in the string");
1397 TEST_CASE(
"Check for token not at beginning of string within length constraints");
1400 TEST_CHECK(p == (sbuff.start + (
sizeof(
"AAAA") - 1)));
1403 TEST_CASE(
"Check for token not at beginning of string outside length constraints");
1412 char const in[] =
"i am a test string";
1415 TEST_CASE(
"Check for token at beginning of string");
1421 TEST_CASE(
"Check for token not at beginning of string");
1427 TEST_CASE(
"Check for token at the end of string");
1433 TEST_CASE(
"Check for token larger than the string");
1439 TEST_CASE(
"Check for token shorter than string, not in the string");
1445 TEST_CASE(
"Check for token with zero length string");
1451 TEST_CASE(
"Check for token that is the string");
1458 TEST_CASE(
"Check for token not at beginning of string within length constraints");
1464 TEST_CASE(
"Check for token not at beginning of string outside length constraints");
1473 char const in[] =
"i am a test string";
1476 TEST_CASE(
"Check for token at beginning of string");
1482 TEST_CASE(
"Check for token not at beginning of string");
1488 TEST_CASE(
"Check for token at the end of string");
1494 TEST_CASE(
"Check for token larger than the string");
1500 TEST_CASE(
"Check for token shorter than string, not in the string");
1506 TEST_CASE(
"Check for token with zero length string");
1512 TEST_CASE(
"Check for token that is the string");
1519 TEST_CASE(
"Check for token not at beginning of string within length constraints");
1525 TEST_CASE(
"Check for token not at beginning of string outside length constraints");
1534 char const in[] =
"i ";
1536 TEST_CASE(
"Check for advancement on match");
1541 TEST_CASE(
"Check for non-advancement on non-match");
1545 TEST_CASE(
"Check for advancement at end");
1549 TEST_CASE(
"Check we can't advance off the end of the buffer");
1557 char const in[] =
"i ";
1559 TEST_CASE(
"Check for advancement on non-match");
1564 TEST_CASE(
"Check for non-advancement on match");
1568 TEST_CASE(
"Check for advancement at end");
1572 TEST_CASE(
"Check we can't advance off the end of the buffer");
#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.
#define static_assert
For systems with an old version libc, define static_assert.
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)
ssize_t fr_sbuff_out_bstrncpy_exact(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, bool const allowed[static UINT8_MAX+1])
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.
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
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.
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.
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.
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.
char * fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c)
Wind position to first instance of specified char.
size_t fr_sbuff_extend_file(fr_sbuff_t *sbuff, size_t extension)
Refresh the buffer with more data from the file.
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.
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.
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
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.
bool fr_sbuff_next_unless_char(fr_sbuff_t *sbuff, char c)
Return true and advance if the next char does not match.
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.
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.
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
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.
char chr
Character at the start of an escape sequence.
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
size_t len
Length of the list.
#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.
#define FR_SBUFF_TERM(_str)
Initialise a terminal structure with a single string.
Set of terminal elements.
File sbuff extension structure.
Talloc sbuff extension structure.
Set of parsing rules for *unescape_until functions.
static void test_adv_past_allowed(void)
static void test_no_advance(void)
static void test_file_extend_max(void)
static void test_talloc_extend_multi_level(void)
static void test_adv_past_strcase(void)
static void test_adv_to_strcase(void)
static void test_talloc_extend(void)
static void test_bstrncpy_until(void)
static void test_bstrncpy_allowed(void)
#define TEST_SBUFF_USED(_sbuff, _num)
static void test_adv_until(void)
static bool allow_lowercase_and_space_no_t[UINT8_MAX+1]
static void test_is_char(void)
static void test_bstrncpy(void)
static void test_bstrncpy_exact(void)
static void test_adv_to_utf8(void)
static void test_adv_to_chr(void)
static void test_eof_terminal(void)
static void test_next_unless_char(void)
static void test_adv_past_whitespace(void)
static void test_next_if_char(void)
static void test_unescape_until(void)
static void test_adv_past_str(void)
static bool allow_lowercase_and_space[UINT8_MAX+1]
static void test_unescape_multi_char_terminals(void)
#define TEST_SBUFF_LEN(_sbuff, _num)
static void test_file_extend(void)
static void test_terminal_merge(void)
static void test_parse_init(void)
static void test_adv_to_str(void)
static void test_talloc_extend_init_zero(void)
static void test_talloc_extend_with_marker(void)
static char buff[sizeof("18446744073709551615")+3]
static size_t char ** out