From: Arran Cudbard-Bell Date: Thu, 23 Jul 2020 22:52:38 +0000 (-0400) Subject: sbuff: Fix advancement functions to return position, and add tests X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dbc991ecd9f8f2437d30b0799bdd4a536ee48a5b;p=thirdparty%2Ffreeradius-server.git sbuff: Fix advancement functions to return position, and add tests --- diff --git a/src/lib/util/sbuff.c b/src/lib/util/sbuff.c index a9f6f9121f4..59abf1dfcbb 100644 --- a/src/lib/util/sbuff.c +++ b/src/lib/util/sbuff.c @@ -863,10 +863,10 @@ bool fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len * * @param[in] sbuff sbuff to search in. * @return - * - 0, first character is not a whitespace character. - * - >0 how many whitespace characters we skipped. + * - true and advance past the need if whitespace occurs next. + * - false and don't advance if the whitespace does not occur next. */ -size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff) +bool fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff) { size_t total = 0; @@ -879,12 +879,12 @@ size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff) p = sbuff->p; while ((p < sbuff->end) && isspace(*p)) p++; - if (p == sbuff->p) return 0; + if (p == sbuff->p) break; total += fr_sbuff_advance(sbuff, p - sbuff->p); } while (p == sbuff->end); /* Hit the end of the chunk, try again */ - return total; + return (total > 0); } /** Wind position to first instance of specified multibyte utf8 char @@ -895,13 +895,13 @@ size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff) * @param[in,out] sbuff to search in. * @param[in] chr to search for. * @return - * - 0, no instances found. - * - >0 the offset at which the first occurrence of the multi-byte chr was found. + * - NULL, no instances found. + * - The position of the first character. */ -size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char *chr) +char *fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char const *chr) { - size_t total = 0; size_t clen = strlen(chr); + fr_sbuff_t our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff); CHECK_SBUFF_INIT(sbuff); @@ -912,18 +912,18 @@ size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char *chr) * Ensure we have enough chars to match * the needle. */ - if (FR_SBUFF_CANT_EXTEND_LOWAT(sbuff, clen)) break; + if (FR_SBUFF_CANT_EXTEND_LOWAT(&our_sbuff, clen)) break; - found = fr_utf8_strchr(NULL, fr_sbuff_current(sbuff), fr_sbuff_remaining(sbuff), chr); + found = fr_utf8_strchr(NULL, fr_sbuff_current(&our_sbuff), fr_sbuff_remaining(&our_sbuff), chr); if (found) { - total += fr_sbuff_set(sbuff, found); - break; + (void)fr_sbuff_set(sbuff, found); + return sbuff->p; } - total += fr_sbuff_advance(sbuff, 1); /* Can't advance by clen, because we're searching for a sequence */ + (void)fr_sbuff_advance(&our_sbuff, 1); /* Can't advance by clen, because we're searching for a sequence */ } - return total; + return NULL; } /** Wind position to first instance of specified char @@ -931,29 +931,30 @@ size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char *chr) * @param[in,out] sbuff to search in. * @param[in] c to search for. * @return - * - 0, no instances found. - * - >0 the offset at which the first occurrence of the char was found. + * - NULL, no instances found. + * - The position of the first character. */ -size_t fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, char c) +char *fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, char c) { - size_t total = 0; - char const *found; + fr_sbuff_t our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff); CHECK_SBUFF_INIT(sbuff); for (;;) { - if (FR_SBUFF_CANT_EXTEND(sbuff)) break; + char const *found; - found = memchr(fr_sbuff_current(sbuff), c, fr_sbuff_remaining(sbuff)); + if (FR_SBUFF_CANT_EXTEND(&our_sbuff)) break; + + found = memchr(fr_sbuff_current(&our_sbuff), c, fr_sbuff_remaining(&our_sbuff)); if (found) { - total += fr_sbuff_set(sbuff, found); - break; + (void)fr_sbuff_set(sbuff, found); + return sbuff->p; } - total += fr_sbuff_set(sbuff, sbuff->end); + (void)fr_sbuff_set(&our_sbuff, our_sbuff.end); } - return total; + return NULL; } /** Wind position to the first instance of the specified needle @@ -962,12 +963,12 @@ size_t fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, char c) * @param[in] needle to search for. * @param[in] len Length of the needle. -1 to use strlen. * @return - * - 0, no instances found. - * - >0 the offset at which the first occurrence of the needle was found. + * - NULL, no instances found. + * - The position of the first character. */ -size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len) +char *fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len) { - size_t total = 0; + fr_sbuff_t our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff); char const *found; CHECK_SBUFF_INIT(sbuff); @@ -975,13 +976,11 @@ size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len) if (len == SIZE_MAX) len = strlen(needle); if (!len) return 0; - for (;;) { - if (FR_SBUFF_CANT_EXTEND_LOWAT(sbuff, len)) break; - - found = memmem(fr_sbuff_current(sbuff), fr_sbuff_remaining(sbuff), needle, len); + while (!FR_SBUFF_CANT_EXTEND_LOWAT(&our_sbuff, len)) { + found = memmem(fr_sbuff_current(&our_sbuff), fr_sbuff_remaining(&our_sbuff), needle, len); if (found) { - total += fr_sbuff_set(sbuff, found); - break; + (void)fr_sbuff_set(sbuff, found); + return sbuff->p; } /* @@ -989,10 +988,10 @@ size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len) * the end of the buffer so * don't advance too far. */ - total += fr_sbuff_advance(sbuff, (fr_sbuff_remaining(sbuff) - len) - 1); + (void)fr_sbuff_advance(&our_sbuff, (fr_sbuff_remaining(&our_sbuff) - len) - 1); } - return total; + return NULL; } /** Wind position to the first instance of the specified needle @@ -1001,37 +1000,34 @@ size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len) * @param[in] needle to search for. * @param[in] len Length of the needle. -1 to use strlen. * @return - * - 0, no instances found. - * - >0 the offset at which the first occurrence of the needle was found. + * - NULL, no instances found. + * - The position of the first character. */ -size_t fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len) +char *fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len) { - size_t total = 0; + fr_sbuff_t our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff); CHECK_SBUFF_INIT(sbuff); if (len == SIZE_MAX) len = strlen(needle); if (!len) return 0; - for (;;) { + while (!FR_SBUFF_CANT_EXTEND_LOWAT(&our_sbuff, len)) { char *p, *end; char const *n_p; - if (FR_SBUFF_CANT_EXTEND_LOWAT(sbuff, len)) break; - - p = sbuff->p; - end = p + len; - - for (p = sbuff->p, n_p = needle; (p < end) && (tolower(*p) != tolower(*n_p)); p++, n_p++); + for (p = our_sbuff.p, n_p = needle, end = our_sbuff.p + len; + (p < end) && (tolower(*p) == tolower(*n_p)); + p++, n_p++); if (p == end) { - total += fr_sbuff_set(sbuff, end); - break; + (void)fr_sbuff_set(sbuff, our_sbuff.p); + return sbuff->p; } - total += fr_sbuff_advance(sbuff, 1); + (void)fr_sbuff_advance(&our_sbuff, 1); } - return total; + return NULL; } /** Return true if the current char matches, and if it does, advance @@ -1046,7 +1042,7 @@ bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c) { CHECK_SBUFF_INIT(sbuff); - if (!FR_SBUFF_CANT_EXTEND(sbuff)) return false; + if (FR_SBUFF_CANT_EXTEND(sbuff)) return false; if (*sbuff->p != c) return false; @@ -1067,7 +1063,7 @@ bool fr_sbuff_next_unless_char(fr_sbuff_t *sbuff, char c) { CHECK_SBUFF_INIT(sbuff); - if (!FR_SBUFF_CANT_EXTEND(sbuff)) return false; + if (FR_SBUFF_CANT_EXTEND(sbuff)) return false; if (*sbuff->p == c) return false; diff --git a/src/lib/util/sbuff.h b/src/lib/util/sbuff.h index 4c6ce516d8e..92075604921 100644 --- a/src/lib/util/sbuff.h +++ b/src/lib/util/sbuff.h @@ -715,17 +715,17 @@ bool fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len #define fr_sbuff_adv_past_strcase_literal(_sbuff, _needle) fr_sbuff_adv_past_strcase(_sbuff, _needle, sizeof(_needle) - 1) -size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff); +bool fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff); -size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *in, char *chr); +char *fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *in, char const *chr); -size_t fr_sbuff_adv_to_chr(fr_sbuff_t *in, char c); +char *fr_sbuff_adv_to_chr(fr_sbuff_t *in, char c); -size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len); +char *fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len); #define fr_sbuff_adv_to_str_literal(_sbuff, _needle) fr_sbuff_adv_to_str(_sbuff, _needle, sizeof(_needle) - 1) -size_t fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len); +char *fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len); #define fr_sbuff_adv_to_strcase_literal(_sbuff, _needle) fr_sbuff_adv_to_strcase(_sbuff, _needle, sizeof(_needle) - 1) diff --git a/src/lib/util/sbuff_tests.c b/src/lib/util/sbuff_tests.c index 75bc9e4f899..fa8302468d0 100644 --- a/src/lib/util/sbuff_tests.c +++ b/src/lib/util/sbuff_tests.c @@ -22,6 +22,13 @@ do { \ TEST_MSG("Got length : %zu", _len); \ } while(0) +#define TEST_CHECK_LEN(_exp, _got) \ +do { \ + TEST_CHECK(_exp == _got); \ + TEST_MSG("Expected length : %zu", (ssize_t)_exp); \ + TEST_MSG("Got length : %zu", (ssize_t)_got); \ +} while(0) + #define TEST_CHECK_SLEN(_exp, _got) \ do { \ TEST_CHECK(_exp == _got); \ @@ -619,21 +626,237 @@ static void test_adv_past_whitespace(void) TEST_CASE("Check for token at beginning of string"); fr_sbuff_init(&sbuff, in, sizeof(in)); - TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 5); + TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == true); TEST_CHECK_STRCMP("i am a test string", sbuff.p); TEST_CASE("Check for token not at beginning of string"); fr_sbuff_init(&sbuff, in_ns, sizeof(in_ns)); - TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 0); + TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == false); TEST_CHECK_STRCMP("i am a test string", sbuff.p); TEST_CASE("Check for token with zero length string"); fr_sbuff_init(&sbuff, in, 0 + 1); - TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 0); + TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == false); TEST_CASE("Check for token that is the string"); fr_sbuff_init(&sbuff, in_ws, sizeof(in_ws)); - TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 5); + TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == true); +} + +static void test_adv_to_utf8(void) +{ + fr_sbuff_t sbuff; + char const in[] = "🥺🥺🥺🥺🍪😀"; + char *p; + + TEST_CASE("Check for token at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🥺"); + TEST_CHECK(p == sbuff.p); + TEST_CHECK_STRCMP("🥺🥺🥺🥺🍪😀", sbuff.p); + + TEST_CASE("Check for token not at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🍪"); + TEST_CHECK(p == (sbuff.start + (sizeof("🥺🥺🥺🥺") - 1))); + TEST_CHECK_STRCMP("🍪😀", p); + + TEST_CASE("Check for token with zero length string"); + fr_sbuff_init(&sbuff, in, 0 + 1); + p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🍪"); + TEST_CHECK(p == NULL); + TEST_CHECK(sbuff.start == sbuff.p); + + TEST_CASE("Check for token at the end of the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr_utf8(&sbuff, "😀"); + TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1)); + + TEST_CASE("Check for token not in the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🍆 "); + TEST_CHECK(p == NULL); +} + +static void test_adv_to_chr(void) +{ + fr_sbuff_t sbuff; + char const in[] = "AAAAbC"; + char *p; + + TEST_CASE("Check for token at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr(&sbuff, 'A'); + TEST_CHECK(p == sbuff.p); + TEST_CHECK_STRCMP("AAAAbC", sbuff.p); + + TEST_CASE("Check for token not at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr(&sbuff, 'b'); + TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1))); + TEST_CHECK_STRCMP("bC", p); + + TEST_CASE("Check for token with zero length string"); + fr_sbuff_init(&sbuff, in, 0 + 1); + p = fr_sbuff_adv_to_chr(&sbuff, 'b'); + TEST_CHECK(p == NULL); + TEST_CHECK(sbuff.start == sbuff.p); + + TEST_CASE("Check for token at the end of the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr(&sbuff, 'C'); + TEST_CHECK(p == sbuff.start + (sizeof("AAAAb") - 1)); + + TEST_CASE("Check for token not in the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_chr(&sbuff, 'D'); + TEST_CHECK(p == NULL); +} + +static void test_adv_to_str(void) +{ + fr_sbuff_t sbuff; + char const in[] = "i am a test string"; + char *p; + + TEST_CASE("Check for token at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_str(&sbuff, "i am a test", SIZE_MAX); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("i am a test string", sbuff.p); + + TEST_CASE("Check for token not at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_str(&sbuff, "test", SIZE_MAX); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("test string", sbuff.p); + + TEST_CASE("Check for token at the end of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_str(&sbuff, "ing", SIZE_MAX); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("ing", sbuff.p); + + TEST_CASE("Check for token larger than the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_str(&sbuff, "i am a test string ", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(p == NULL); + + TEST_CASE("Check for token shorter than string, not in the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_strcase(&sbuff, "ng ", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(p == NULL); + + TEST_CASE("Check for token with zero length string"); + fr_sbuff_init(&sbuff, in, 0 + 1); + p = fr_sbuff_adv_to_str(&sbuff, "i am a", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(p == NULL); + + TEST_CASE("Check for token that is the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_str(&sbuff, "i am a test string", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("i am a test string", p); +} + +static void test_adv_to_strcase(void) +{ + fr_sbuff_t sbuff; + char const in[] = "i am a test string"; + char *p; + + TEST_CASE("Check for token at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_strcase(&sbuff, "i AM a TEST", SIZE_MAX); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("i am a test string", sbuff.p); + + TEST_CASE("Check for token not at beginning of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_strcase(&sbuff, "tEst", SIZE_MAX); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("test string", sbuff.p); + + TEST_CASE("Check for token at the end of string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_strcase(&sbuff, "Ing", SIZE_MAX); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("ing", sbuff.p); + + TEST_CASE("Check for token larger than the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_strcase(&sbuff, "i aM a tEst stRIng ", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(p == NULL); + + TEST_CASE("Check for token shorter than string, not in the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_strcase(&sbuff, "nG ", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(p == NULL); + + TEST_CASE("Check for token with zero length string"); + fr_sbuff_init(&sbuff, in, 0 + 1); + p = fr_sbuff_adv_to_strcase(&sbuff, "i AM a", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(p == NULL); + + TEST_CASE("Check for token that is the string"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + p = fr_sbuff_adv_to_strcase(&sbuff, "i AM a teST stRIng", SIZE_MAX); + TEST_CHECK(sbuff.p == sbuff.start); + TEST_CHECK(sbuff.p == p); + TEST_CHECK_STRCMP("i am a test string", p); +} + +static void test_next_if_char(void) +{ + fr_sbuff_t sbuff; + char const in[] = "i "; + + TEST_CASE("Check for advancement on match"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == true); + TEST_CHECK_STRCMP(" ", sbuff.p); + + TEST_CASE("Check for non-advancement on non-match"); + TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == false); + TEST_CHECK_STRCMP(" ", sbuff.p); + + TEST_CASE("Check for advancement at end"); + TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == true); + TEST_CHECK_STRCMP("", sbuff.p); + + TEST_CASE("Check we can't advance off the end of the buffer"); + TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == false); + TEST_CHECK_STRCMP("", sbuff.p); +} + +static void test_next_unless_char(void) +{ + fr_sbuff_t sbuff; + char const in[] = "i "; + + TEST_CASE("Check for advancement on non-match"); + fr_sbuff_init(&sbuff, in, sizeof(in)); + TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == true); + TEST_CHECK_STRCMP(" ", sbuff.p); + + TEST_CASE("Check for non-advancement on match"); + TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == false); + TEST_CHECK_STRCMP(" ", sbuff.p); + + TEST_CASE("Check for advancement at end"); + TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == true); + TEST_CHECK_STRCMP("", sbuff.p); + + TEST_CASE("Check we can't advance off the end of the buffer"); + TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == false); + TEST_CHECK_STRCMP("", sbuff.p); } TEST_LIST = { @@ -663,5 +886,19 @@ TEST_LIST = { { "fr_sbuff_adv_past_strcase", test_adv_past_strcase }, { "fr_sbuff_adv_past_whitespace", test_adv_past_whitespace }, + /* + * Token searching + */ + { "fr_sbuff_adv_to_utf8", test_adv_to_utf8 }, + { "fr_sbuff_adv_to_chr", test_adv_to_chr }, + { "fr_sbuff_adv_to_str", test_adv_to_str }, + { "fr_sbuff_adv_to_strcase", test_adv_to_strcase }, + + /* + * Advancement + */ + { "fr_sbuff_next_if_char", test_next_if_char }, + { "fr_sbuff_next_unless_char", test_next_unless_char }, + { NULL } };