*
* @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;
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
* @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);
* 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
* @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
* @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);
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;
}
/*
* 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
* @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
{
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;
{
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;
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); \
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 = {
{ "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 }
};