fr_sbuff_advance(_in, _copied); \
} while(0)
+/** Constrain end pointer to prevent advancing more than the amount the called specified
+ *
+ * @param[in] _sbuff to constrain.
+ * @param[in] _max maximum amount to advance.
+ * @param[in] _used how much we've advanced so far.
+ * @return a temporary end pointer.
+ */
+#define CONSTRAINED_END(_sbuff, _max, _used) \
+ (((_max) - (_used)) > fr_sbuff_remaining(_sbuff) ? (_sbuff)->end : (_sbuff)->p + ((_max) - (_used)))
+
/** Copy as many bytes as possible from a sbuff to a sbuff
*
* Copy size is limited by available data in sbuff and space in output sbuff.
bool const allowed[static UINT8_MAX + 1])
{
fr_sbuff_t our_in = FR_SBUFF_NO_ADVANCE(in);
- size_t remaining;
- size_t chunk_len;
CHECK_SBUFF_INIT(in);
- do {
-
+ while (fr_sbuff_used_total(&our_in) < len) {
char *p;
+ char *end;
- remaining = (len - fr_sbuff_used_total(&our_in));
if (FR_SBUFF_CANT_EXTEND(&our_in)) break;
- chunk_len = fr_sbuff_remaining(&our_in);
- if (chunk_len > remaining) chunk_len = remaining;
+ p = our_in.p;
+ end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in));
- for (p = our_in.p; (p < (our_in.start + chunk_len)) && allowed[(uint8_t)*p]; p++);
- chunk_len = p - our_in.p;
+ while((p < end) && allowed[(uint8_t)*p]) p++;
- FILL_OR_GOTO_DONE(out, &our_in, chunk_len);
- } while (remaining && chunk_len);
+ FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p);
+
+ if (p != end) break; /* stopped early, break */
+ };
done:
return fr_sbuff_set(in, &our_in);
* @param[in] in Where to copy from. Will copy len bytes from current position in buffer.
* @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied.
* @param[in] until Characters which stop the copy operation.
+ * @param[in] escape If not '\0', ignore characters in the until set when
+ * prefixed with this escape character.
* @return
* - 0 no bytes copied.
* - >0 the number of bytes copied.
*/
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len,
- bool const until[static UINT8_MAX + 1])
+ bool const until[static UINT8_MAX + 1], char escape)
{
fr_sbuff_t our_in = FR_SBUFF_NO_ADVANCE(in);
- size_t remaining;
- size_t chunk_len;
+ bool do_escape = false; /* Track state across extensions */
CHECK_SBUFF_INIT(in);
- do {
-
+ while (fr_sbuff_used_total(&our_in) < len) {
char *p;
+ char *end;
- remaining = (len - fr_sbuff_used_total(&our_in));
if (FR_SBUFF_CANT_EXTEND(&our_in)) break;
- chunk_len = fr_sbuff_remaining(&our_in);
- if (chunk_len > remaining) chunk_len = remaining;
+ p = our_in.p;
+ end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in));
- for (p = our_in.p; (p < (our_in.start + chunk_len)) && !until[(uint8_t)*p]; p++);
- chunk_len = p - our_in.p;
+ if (escape == '\0') {
+ while((p < end) && !until[(uint8_t)*p]) p++;
+ } else {
+ while (p < end) {
+ if (do_escape) {
+ do_escape = false;
+ } else if (*p == escape) {
+ do_escape = true;
+ } else if (until[(uint8_t)*p]) {
+ break;
+ }
+ p++;
+ }
+ }
- FILL_OR_GOTO_DONE(out, &our_in, chunk_len);
- } while (remaining && chunk_len);
+ FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p);
+
+ if (p != end) break; /* stopped early, break */
+ }
done:
return fr_sbuff_set(in, &our_in);
return fr_sbuff_advance(sbuff, needle_len);
}
-/** Constrain end pointer to prevent advancing more than the amount the called specified
- *
- * @param[in] _sbuff to constrain.
- * @param[in] _max maximum amount to advance.
- * @param[in] _used how much we've advanced so far.
- * @return a temporary end pointer.
- */
-#define CONSTRAINED_END(_sbuff, _max, _used) \
- (((_max) - (_used)) > fr_sbuff_remaining(sbuff) ? sbuff->end : sbuff->p + ((_max) - (_used)))
-
/** Wind position to the first non-whitespace character
*
* @param[in] sbuff sbuff to search in.
* set.
*/
TEST_CASE("Copy 5 bytes to out");
- slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 5, (bool[UINT8_MAX + 1]){ });
+ slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 5,
+ (bool[UINT8_MAX + 1]){ }, '\0');
TEST_CHECK_SLEN(5, slen);
TEST_CHECK_STRCMP("i am ", out);
TEST_CHECK_STRCMP("a test string", sbuff.p);
TEST_CASE("Copy 13 bytes to out");
- slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 13, (bool[UINT8_MAX + 1]){ });
+ slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 13,
+ (bool[UINT8_MAX + 1]){ }, '\0');
TEST_CHECK_SLEN(13, slen);
TEST_CHECK_STRCMP("a test string", out);
TEST_CHECK_STRCMP("", sbuff.p);
TEST_CHECK(sbuff.p == sbuff.end);
TEST_CASE("Copy would overrun input");
- slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 1, (bool[UINT8_MAX + 1]){ });
+ slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 1,
+ (bool[UINT8_MAX + 1]){ }, '\0');
TEST_CHECK_SLEN(0, slen);
TEST_CHECK(sbuff.p == sbuff.end);
+ TEST_CASE("Check escapes");
+ fr_sbuff_set_to_start(&sbuff);
+ slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, SIZE_MAX,
+ (bool[UINT8_MAX + 1]){ ['g'] = true }, 'n');
+ TEST_CHECK_SLEN(18, slen);
+ TEST_CHECK_STRCMP("i am a test string", out);
+ TEST_CHECK_STRCMP("", sbuff.p);
+
TEST_CASE("Copy would overrun output (and SIZE_MAX special value)");
fr_sbuff_init(&sbuff, in_long, sizeof(in_long));
- slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ });
+ slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff,
+ SIZE_MAX, (bool[UINT8_MAX + 1]){ }, '\0');
TEST_CHECK_SLEN(18, slen);
TEST_CHECK_STRCMP("i am a longer test", out);
TEST_CASE("Zero length output buffer");
fr_sbuff_set_to_start(&sbuff);
out[0] = 'a';
- slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, (size_t)1), &sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ });
+ slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, (size_t)1), &sbuff,
+ SIZE_MAX, (bool[UINT8_MAX + 1]){ }, '\0');
TEST_CHECK_SLEN(0, slen);
TEST_CHECK(out[0] == '\0'); /* should be set to \0 */
TEST_CHECK(sbuff.p == sbuff.start);
TEST_CASE("Copy until first t");
fr_sbuff_set_to_start(&sbuff);
slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, SIZE_MAX,
- (bool[UINT8_MAX + 1]){ ['t'] = true });
+ (bool[UINT8_MAX + 1]){ ['t'] = true }, '\0');
TEST_CHECK_SLEN(14, slen);
TEST_CHECK_STRCMP("i am a longer ", out);
TEST_CASE("Copy until first t with length constraint (same len as token)");
fr_sbuff_set_to_start(&sbuff);
slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, 15), &sbuff, SIZE_MAX,
- (bool[UINT8_MAX + 1]){ ['t'] = true });
+ (bool[UINT8_MAX + 1]){ ['t'] = true }, '\0');
TEST_CHECK_SLEN(14, slen);
TEST_CHECK_STRCMP("i am a longer ", out);
TEST_CASE("Copy until first t with length constraint (one shorter than token)");
fr_sbuff_set_to_start(&sbuff);
slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, 14), &sbuff, SIZE_MAX,
- (bool[UINT8_MAX + 1]){ ['t'] = true });
+ (bool[UINT8_MAX + 1]){ ['t'] = true }, '\0');
TEST_CHECK_SLEN(13, slen);
TEST_CHECK_STRCMP("i am a longer", out);
TEST_CASE("Zero length token (should still be terminated)");
fr_sbuff_set_to_start(&sbuff);
slen = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_TMP(out, 14), &sbuff, SIZE_MAX,
- (bool[UINT8_MAX + 1]){ ['i'] = true });
+ (bool[UINT8_MAX + 1]){ ['i'] = true }, '\0');
TEST_CHECK_SLEN(0, slen);
TEST_CHECK_STRCMP("", out);
}
static void test_adv_until(void)
{
fr_sbuff_t sbuff;
- char const in[] = " abcdefgh ijklmnop";
+ char const in[] = " abcdefgh ijklmnopp";
TEST_CASE("Check for token at beginning of string");
fr_sbuff_init(&sbuff, in, sizeof(in));
- TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }));
- TEST_CHECK_STRCMP(" abcdefgh ijklmnop", sbuff.p);
+ TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ [' '] = true }, '\0'));
+ TEST_CHECK_STRCMP(" abcdefgh ijklmnopp", sbuff.p);
TEST_CASE("Check for token not at beginning of string");
fr_sbuff_init(&sbuff, in, sizeof(in));
- TEST_CHECK_LEN(1, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true }));
- TEST_CHECK_STRCMP("abcdefgh ijklmnop", sbuff.p);
+ TEST_CHECK_LEN(1, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true }, '\0'));
+ TEST_CHECK_STRCMP("abcdefgh ijklmnopp", sbuff.p);
TEST_CASE("Check for token with zero length string");
fr_sbuff_init(&sbuff, in, 0 + 1);
- TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true }));
+ TEST_CHECK_LEN(0, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['a'] = true }, '\0'));
TEST_CHECK(sbuff.p == sbuff.start);
TEST_CASE("Check for token that is not in the string");
fr_sbuff_init(&sbuff, in, sizeof(in));
- TEST_CHECK_LEN(18, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['|'] = true }));
+ TEST_CHECK_LEN(19, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['|'] = true }, '\0'));
TEST_CHECK(sbuff.p == sbuff.end);
+ TEST_CASE("Check escapes");
+ fr_sbuff_init(&sbuff, in, sizeof(in));
+ TEST_CHECK_LEN(18, fr_sbuff_adv_until(&sbuff, SIZE_MAX, (bool[UINT8_MAX + 1]){ ['p'] = true }, 'o'));
+ TEST_CHECK_STRCMP("p", sbuff.p);
+
TEST_CASE("Check for token that is not in the string with length constraint");
fr_sbuff_init(&sbuff, in, sizeof(in));
- TEST_CHECK_LEN(5, fr_sbuff_adv_until(&sbuff, 5, (bool[UINT8_MAX + 1]){ ['|'] = true }));
+ TEST_CHECK_LEN(5, fr_sbuff_adv_until(&sbuff, 5, (bool[UINT8_MAX + 1]){ ['|'] = true }, '\0'));
TEST_CHECK(sbuff.p == (sbuff.start + 5));
}