]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
sbuff: Add additional sbuff functions
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 17 Jul 2020 19:25:02 +0000 (15:25 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 17 Jul 2020 19:25:02 +0000 (15:25 -0400)
src/lib/util/sbuff.c
src/lib/util/sbuff.h

index 781ff5dcbdfe447bc4ae6495b474682ff051c215..d25ab75683da90cac5a57f0a45c4ec50190b9a66 100644 (file)
  */
 RCSID("$Id$")
 
-#include <freeradius-devel/util/sbuff.h>
 #include <freeradius-devel/util/print.h>
+#include <freeradius-devel/util/sbuff.h>
+#include <freeradius-devel/util/strerror.h>
 #include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/util/thread_local.h>
 
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 
+_Thread_local char *sbuff_scratch;
+
 static_assert(sizeof(long long) >= sizeof(int64_t), "long long must be as wide or wider than an int64_t");
 static_assert(sizeof(unsigned long long) >= sizeof(uint64_t), "long long must be as wide or wider than an uint64_t");
 
 fr_table_num_ordered_t const sbuff_parse_error_table[] = {
        { "ok",                 FR_SBUFF_PARSE_OK                               },
        { "token not found",    FR_SBUFF_PARSE_ERROR_NOT_FOUND                  },
-       { "integer overflow",   FR_SBUFF_PARSE_ERROR_INTEGER_OVERFLOW           },
-       { "integer underflow",  FR_SBUFF_PARSE_ERROR_INTEGER_UNDERFLOW          },
+       { "integer overflow",   FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW               },
+       { "integer underflow",  FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW              },
 };
 size_t sbuff_parse_error_table_len = NUM_ELEMENTS(sbuff_parse_error_table);
 
@@ -418,7 +422,7 @@ size_t fr_sbuff_bstrncpy_out_until(char *out, size_t outlen, fr_sbuff_t *in, siz
  *     - >0 the number of bytes copied.
  */
 #define PARSE_INT_DEF(_name, _type, _min, _max, _max_char) \
-size_t fr_sbuff_parse_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
+size_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
 { \
        char            buff[_max_char + 1]; \
        char            *end; \
@@ -426,21 +430,25 @@ size_t fr_sbuff_parse_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_
        long long       num; \
        fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in); \
        len = fr_sbuff_bstrncpy_out(buff, sizeof(buff), &our_in, _max_char); \
-       if ((len == 0) && err) { \
-               *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
+       if (len == 0) { \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
                return 0; \
        } \
        num = strtoll(buff, &end, 10); \
        if (end == buff) { \
-               if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
                return 0; \
        } \
-       if ((num > (_max)) || ((errno == EINVAL) && (num == LLONG_MAX)) || (no_trailing && isdigit(*(in->p + (end - buff)))))  { \
-               if (err) *err = FR_SBUFF_PARSE_ERROR_INTEGER_OVERFLOW; \
+       if ((num > (_max)) || ((errno == EINVAL) && (num == LLONG_MAX))) { \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \
+               *out = (_type)(_max); \
+               return 0; \
+       } else if (no_trailing && (*end != '\0')) { \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
                *out = (_type)(_max); \
                return 0; \
        } else if (num < (_min) || ((errno == EINVAL) && (num == LLONG_MIN))) { \
-               if (err) *err = FR_SBUFF_PARSE_ERROR_INTEGER_UNDERFLOW; \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \
                *out = (_type)(_min); \
                return 0; \
        } else { \
@@ -466,7 +474,7 @@ PARSE_INT_DEF(int64, int64_t, INT64_MIN, INT64_MAX, 20)
  *                     used in <stdint.h>.
  */
 #define PARSE_UINT_DEF(_name, _type, _max, _max_char) \
-size_t fr_sbuff_parse_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
+size_t fr_sbuff_in_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
 { \
        char                    buff[_max_char + 1]; \
        char                    *end; \
@@ -474,17 +482,21 @@ size_t fr_sbuff_parse_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_
        unsigned long long      num; \
        fr_sbuff_t              our_in = FR_SBUFF_NO_ADVANCE(in); \
        len = fr_sbuff_bstrncpy_out(buff, sizeof(buff), &our_in, _max_char); \
-       if ((len == 0) && err) { \
-               *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
+       if (len == 0) { \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
                return 0; \
        } \
        num = strtoull(buff, &end, 10); \
        if (end == buff) { \
-               if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
                return 0; \
        } \
-       if ((num > (_max)) || ((errno == EINVAL) && (num == ULLONG_MAX)) || (no_trailing && isdigit(*(in->p + (end - buff))))) { \
-               if (err) *err = FR_SBUFF_PARSE_ERROR_INTEGER_OVERFLOW; \
+       if ((num > (_max)) || ((errno == EINVAL) && (num == ULLONG_MAX))) { \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \
+               *out = (_type)(_max); \
+               return 0; \
+       } else if (no_trailing && (*end != '\0')) { \
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
                *out = (_type)(_max); \
                return 0; \
        } else { \
@@ -499,3 +511,269 @@ PARSE_UINT_DEF(uint8, uint8_t, UINT8_MAX, 1)
 PARSE_UINT_DEF(uint16, uint16_t, UINT16_MAX, 5)
 PARSE_UINT_DEF(uint32, uint32_t, UINT32_MAX, 10)
 PARSE_UINT_DEF(uint64, uint64_t, UINT64_MAX, 20)
+
+static bool float_chars[UINT8_MAX + 1] = {
+       ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
+       ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
+       ['-'] = true, ['+'] = true, ['e'] = true, ['E'] = true, ['.'] = true,
+};
+
+/** Attempt to parse a float
+ *
+ * @param[out] err             If non null, will be filled with any parse errors.
+ * @param[out] out             Where to write the resulting float.
+ * @param[in] in               Sbuff to parse float from.
+ * @param[in] no_trailing      Emit a parse error if there are trailing characters
+ *                             after the float parsed.
+ * @return
+ *     - >0 the number of bytes copied into the sbuff.
+ *     - 0 a parse error occurred.
+ */
+size_t fr_sbuff_out_float32(fr_sbuff_parse_error_t *err, float *out, fr_sbuff_t *in, bool no_trailing)
+{
+       char            buffer[100];    /* Should be sufficient */
+       char            *end;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       size_t          len;
+       float           res;
+
+       len = fr_sbuff_bstrncpy_out_allowed(buffer, sizeof(buffer), &our_in, SIZE_MAX, float_chars);
+       if (len == sizeof(buffer)) {
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING;
+               return 0;
+       } else if (len == 0) {
+               if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND;
+               return 0;
+       }
+
+       res = strtof(buffer, &end);
+       if (errno == ERANGE) {
+               if (err) *err = res == 0 ? FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW : FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW;
+               return 0;
+       }
+       if (no_trailing && (*end != '\0')) {
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING;
+               *out = res;
+               return 0;
+       }
+
+       return fr_sbuff_advance(in, end - buffer);
+}
+
+/** Attempt to parse a double
+ *
+ * @param[out] err             If non null, will be filled with any parse errors.
+ * @param[out] out             Where to write the resulting float.
+ * @param[in] in               Sbuff to parse float from.
+ * @param[in] no_trailing      Emit a parse error if there are trailing characters
+ *                             after the float parsed.
+ * @return
+ *     - >0 the number of bytes copied into the sbuff.
+ *     - 0 a parse error occurred.
+ */
+size_t fr_sbuff_out_float64(fr_sbuff_parse_error_t *err, double *out, fr_sbuff_t *in, bool no_trailing)
+{
+       char            buffer[100];    /* Should be sufficient */
+       char            *end;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       size_t          len;
+       float           res;
+
+       len = fr_sbuff_bstrncpy_out_allowed(buffer, sizeof(buffer), &our_in, SIZE_MAX, float_chars);
+       if (len == sizeof(buffer)) {
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING;
+               return 0;
+       } else if (len == 0) {
+               if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND;
+               return 0;
+       }
+
+       res = strtof(buffer, &end);
+       if (errno == ERANGE) {
+               if (err) *err = res == 0 ? FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW : FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW;
+               return 0;
+       }
+       if (no_trailing && (*end != '\0')) {
+               if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING;
+               *out = res;
+               return 0;
+       }
+
+       return fr_sbuff_advance(in, end - buffer);
+}
+
+/** Copy bytes into the sbuff up to the first \0
+ *
+ * @param[in] sbuff    to copy into.
+ * @param[in] str      to copy into buffer.
+ * @return
+ *     - >= 0 the number of bytes copied into the sbuff.
+ *     - <0 the number of bytes required to complete the copy operation.
+ */
+ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
+{
+       size_t len = strlen(str);
+
+       FR_SBUFF_CHECK_REMAINING_RETURN(sbuff, len);
+
+       strlcpy(sbuff->p, str, len + 1);
+
+       return fr_sbuff_advance(sbuff, len);
+}
+
+/** Copy bytes into the sbuff up to the first \0
+ *
+ * @param[in] sbuff    to copy into.
+ * @param[in] str      to copy into buffer.
+ * @param[in] len      number of bytes to copy.
+ * @return
+ *     - >= 0 the number of bytes copied into the sbuff.
+ *     - <0 the number of bytes required to complete the copy operation.
+ */
+ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
+{
+       FR_SBUFF_CHECK_REMAINING_RETURN(sbuff, len);
+
+       memcpy(sbuff->p, str, len);
+       sbuff->p[len] = '\0';
+
+       return fr_sbuff_advance(sbuff, len);
+}
+
+/** Copy bytes into the sbuff up to the first \0
+ *
+ * @param[in] sbuff    to copy into.
+ * @param[in] str      talloced buffer to copy into sbuff.
+ * @return
+ *     - >= 0 the number of bytes copied into the sbuff.
+ *     - <0 the number of bytes required to complete the copy operation.
+ */
+ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str)
+{
+       size_t len = talloc_array_length(str) - 1;
+
+       FR_SBUFF_CHECK_REMAINING_RETURN(sbuff, len);
+
+       memcpy(sbuff->p, str, len);
+       sbuff->p[len] = '\0';
+
+       return fr_sbuff_advance(sbuff, len);
+}
+
+/** Free the scratch buffer used for printf
+ *
+ */
+static void _sbuff_scratch_free(void *arg)
+{
+       talloc_free(arg);
+}
+
+static inline CC_HINT(always_inline) int sbuff_scratch_init(TALLOC_CTX **out)
+{
+       TALLOC_CTX      *scratch;
+
+       scratch = sbuff_scratch;
+       if (!scratch) {
+               scratch = talloc_pool(NULL, 4096);
+               if (unlikely(!scratch)) {
+                       fr_strerror_printf("Out of Memory");
+                       return -1;
+               }
+               fr_thread_local_set_destructor(sbuff_scratch, _sbuff_scratch_free, scratch);
+       }
+
+       *out = scratch;
+
+       return 0;
+}
+
+/** Print using a fmt string to an sbuff
+ *
+ * @param[in] sbuff    to print into.
+ * @param[in] fmt      string.
+ * @param[in] ap       arguments for format string.
+ * @return
+ *     - >= 0 the number of bytes printed into the sbuff.
+ *     - <0 the number of bytes required to complete the print operation.
+ */
+ssize_t fr_sbuff_in_vsprintf(fr_sbuff_t *sbuff, char const *fmt, va_list ap)
+{
+       TALLOC_CTX      *scratch;
+       va_list         ap_p;
+       char            *tmp;
+       ssize_t         slen;
+
+       if (sbuff_scratch_init(&scratch) < 0) return 0;
+
+       va_copy(ap_p, ap);
+       tmp = fr_vasprintf(scratch, fmt, ap_p);
+       va_end(ap_p);
+       if (!tmp) return 0;
+
+       slen = fr_sbuff_in_bstrcpy_buffer(sbuff, tmp);
+       talloc_free(tmp);       /* Free the temporary buffer */
+
+       return slen;
+}
+
+/** Print using a fmt string to an sbuff
+ *
+ * @param[in] sbuff    to print into.
+ * @param[in] fmt      string.
+ * @param[in] ...      arguments for format string.
+ * @return
+ *     - >= 0 the number of bytes printed into the sbuff.
+ *     - <0 the number of bytes required to complete the print operation.
+ */
+ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt, ...)
+{
+       va_list         ap;
+       ssize_t         slen;
+
+       va_start(ap, fmt);
+       slen = fr_sbuff_in_vsprintf(sbuff, fmt, ap);
+       va_end(ap);
+
+       return slen;
+}
+
+/** Print an escaped string to an sbuff
+ *
+ * @param[in] sbuff    to print into.
+ * @param[in] in       to escape.
+ * @param[in] inlen    of string to escape.
+ * @param[in] quote    Which quoting character to escape.  Also controls
+ *                     which characters are escaped.
+ * @return
+ *     - >= 0 the number of bytes printed into the sbuff.
+ *     - <0 the number of bytes required to complete the print operation.
+ */
+ssize_t fr_sbuff_in_snprint(fr_sbuff_t *sbuff, char const *in, size_t inlen, char quote)
+{
+       size_t          len;
+
+       len = fr_snprint_len(in, inlen, quote);
+       FR_SBUFF_CHECK_REMAINING_RETURN(sbuff, len);
+
+       len = fr_snprint(fr_sbuff_current(sbuff), fr_sbuff_remaining(sbuff) + 1, in, inlen, quote);
+       fr_sbuff_advance(sbuff, len);
+
+       return len;
+}
+
+/** Print an escaped string to an sbuff taking a talloced buffer as input
+ *
+ * @param[in] sbuff    to print into.
+ * @param[in] in       to escape.
+ * @param[in] quote    Which quoting character to escape.  Also controls
+ *                     which characters are escaped.
+ * @return
+ *     - >= 0 the number of bytes printed into the sbuff.
+ *     - <0 the number of bytes required to complete the print operation.
+ */
+ssize_t fr_sbuff_in_snprint_buffer(fr_sbuff_t *sbuff, char const *in, char quote)
+{
+       if (unlikely(!in)) return 0;
+
+       return fr_sbuff_in_snprint(sbuff, in, talloc_array_length(in) - 1, quote);
+}
index ee0625e945c05b6d39da697236da16e37032e00f..fe37927e52c3222b8a67574b28bdc2e34d736c99 100644 (file)
@@ -77,8 +77,9 @@ typedef enum {
        FR_SBUFF_PARSE_OK                       = 0,            //!< No error.
        FR_SBUFF_PARSE_ERROR_NOT_FOUND          = -1,           //!< String does not contain a token
                                                                ///< matching the output type.
-       FR_SBUFF_PARSE_ERROR_INTEGER_OVERFLOW   = -2,           //!< Integer type would overflow.
-       FR_SBUFF_PARSE_ERROR_INTEGER_UNDERFLOW  = -3            //!< Integer type would underflow.
+       FR_SBUFF_PARSE_ERROR_TRAILING           = -2,           //!< Trailing characters found.
+       FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW       = -3,           //!< Integer type would overflow.
+       FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW      = -4            //!< Integer type would underflow.
 } fr_sbuff_parse_error_t;
 
 extern fr_table_num_ordered_t const sbuff_parse_error_table[];
@@ -204,6 +205,17 @@ static inline size_t fr_sbuff_marker_used(fr_sbuff_t const *sbuff, fr_sbuff_mark
  *
  */
 #define FR_SBUFF_ERROR_RETURN(_sbuff) return -(fr_sbuff_used(_sbuff))
+
+/** Check if _len bytes are available in the sbuff, and if not return the number of bytes we'd need
+ *
+ */
+#define FR_SBUFF_CHECK_REMAINING_RETURN(_sbuff, _len) \
+do { \
+       if ((_len) > fr_sbuff_remaining(_sbuff)) { \
+               return -((_len) - fr_sbuff_remaining(_sbuff));  \
+       }\
+} while (0)
+
 /** @} */
 
 /** @name Sbuff position manipulation
@@ -406,6 +418,21 @@ static inline bool fr_sbuff_next_if_str(fr_sbuff_t *sbuff, char const *needle, s
        return true;
 }
 
+bool   fr_sbuff_adv_if_str(fr_sbuff_t *sbuff, char const *needle, size_t len);
+
+#define fr_sbuff_adv_if_str_literal(_sbuff, _needle) fr_sbuff_adv_if_str(_sbuff, _needle, sizeof(_needle) - 1)
+
+bool   fr_sbuff_adv_if_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len);
+
+#define fr_sbuff_adv_if_strcase_literal(_sbuff, _needle) fr_sbuff_adv_if_strcase(_sbuff, _needle, sizeof(_needle) - 1)
+
+size_t fr_sbuff_adv_if_whitespace(fr_sbuff_t *sbuff);
+
+size_t fr_sbuff_adv_strstr(fr_sbuff_t *sbuff, char const *needle, ssize_t len);
+
+#define fr_sbuff_adv_strstr_literal(_sbuff, _needle) fr_sbuff_adv_strstr(_sbuff, _needle, sizeof(_needle) - 1)
+/** @} */
+
 /** Return true and advance past the end of the needle if needle occurs next in the sbuff
  *
  * This function is similar to fr_sbuff_next_if_str but is case insensitive.
@@ -441,6 +468,12 @@ static inline bool fr_sbuff_is_allowed(fr_sbuff_t *sbuff, bool const allowed_cha
        return allowed_chars[(uint8_t)*sbuff->p];
 }
 
+static inline bool fr_sbuff_is_char(fr_sbuff_t *sbuff, char c)
+{
+       if (sbuff->p >= sbuff->end) return false;
+       return *sbuff->p == c;
+}
+
 static inline bool fr_sbuff_is_digit(fr_sbuff_t *sbuff)
 {
        if (sbuff->p >= sbuff->end) return false;
@@ -550,21 +583,25 @@ size_t    fr_sbuff_bstrncpy_out_until(char *out, size_t outlen, fr_sbuff_t *sbuff,
  * so that if the output variable type changes, the parse rules are automatically changed.
  * @{
  */
-size_t fr_sbuff_parse_int8(fr_sbuff_parse_error_t *err, int8_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_int8(fr_sbuff_parse_error_t *err, int8_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+
+size_t fr_sbuff_out_int16(fr_sbuff_parse_error_t *err, int16_t *out, fr_sbuff_t *sbuff, bool no_trailing);
 
-size_t fr_sbuff_parse_int16(fr_sbuff_parse_error_t *err, int16_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_int32(fr_sbuff_parse_error_t *err, int32_t *out, fr_sbuff_t *sbuff, bool no_trailing);
 
-size_t fr_sbuff_parse_int32(fr_sbuff_parse_error_t *err, int32_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_int64(fr_sbuff_parse_error_t *err, int64_t *out, fr_sbuff_t *sbuff, bool no_trailing);
 
-size_t fr_sbuff_parse_int64(fr_sbuff_parse_error_t *err, int64_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_uint8(fr_sbuff_parse_error_t *err, uint8_t *out, fr_sbuff_t *sbuff, bool no_trailing);
 
-size_t fr_sbuff_parse_uint8(fr_sbuff_parse_error_t *err, uint8_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_uint16(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_t *sbuff, bool no_trailing);
 
-size_t fr_sbuff_parse_uint16(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_uint32(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *sbuff, bool no_trailing);
 
-size_t fr_sbuff_parse_uint32(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_uint64(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuff_t *sbuff, bool no_trailing);
 
-size_t fr_sbuff_parse_uint64(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuff_t *sbuff, bool no_trailing);
+size_t fr_sbuff_out_float32(fr_sbuff_parse_error_t *err, float *out, fr_sbuff_t *sbuff, bool no_trailing);
+
+size_t fr_sbuff_out_float64(fr_sbuff_parse_error_t *err, double *out, fr_sbuff_t *sbuff, bool no_trailing);
 
 /** Parse a value based on the output type
  *
@@ -576,14 +613,16 @@ size_t fr_sbuff_parse_uint64(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuf
  */
 #define fr_sbuff_parse(_err, _out, _in) \
        _Generic((_out), \
-                int8_t *       : fr_sbuff_parse_int8(_err, (int8_t *)_out, _in, true), \
-                int16_t *      : fr_sbuff_parse_int16(_err, (int16_t *)_out, _in, true), \
-                int32_t *      : fr_sbuff_parse_int32(_err, (int32_t *)_out, _in, true), \
-                int64_t *      : fr_sbuff_parse_int64(_err, (int64_t *)_out, _in, true), \
-                uint8_t *      : fr_sbuff_parse_uint8(_err, (uint8_t *)_out, _in, true), \
-                uint16_t *     : fr_sbuff_parse_uint16(_err, (uint16_t *)_out, _in, true), \
-                uint32_t *     : fr_sbuff_parse_uint32(_err, (uint32_t *)_out, _in, true), \
-                uint64_t *     : fr_sbuff_parse_uint64(_err, (uint64_t *)_out, _in, true) \
+                int8_t *       : fr_sbuff_out_int8(_err, (int8_t *)_out, _in, true), \
+                int16_t *      : fr_sbuff_out_int16(_err, (int16_t *)_out, _in, true), \
+                int32_t *      : fr_sbuff_out_int32(_err, (int32_t *)_out, _in, true), \
+                int64_t *      : fr_sbuff_out_int64(_err, (int64_t *)_out, _in, true), \
+                uint8_t *      : fr_sbuff_out_uint8(_err, (uint8_t *)_out, _in, true), \
+                uint16_t *     : fr_sbuff_out_uint16(_err, (uint16_t *)_out, _in, true), \
+                uint32_t *     : fr_sbuff_out_uint32(_err, (uint32_t *)_out, _in, true), \
+                uint64_t *     : fr_sbuff_out_uint64(_err, (uint64_t *)_out, _in, true), \
+                float *        : fr_sbuff_out_float32(_err, (float *)_out, _in, true), \
+                double *       : fr_sbuff_out_float64(_err, (double *)_out, _in, true), \
        )
 /** @} */
 
@@ -659,6 +698,40 @@ static inline void _fr_sbuff_print_init(fr_sbuff_t *out, char *start, char *end,
        out->is_extendable = extendable;
 }
 
+size_t         fr_sbuff_in_int8(fr_sbuff_parse_error_t *err, int8_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_int16(fr_sbuff_parse_error_t *err, int16_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_int32(fr_sbuff_parse_error_t *err, int32_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_int64(fr_sbuff_parse_error_t *err, int64_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_uint8(fr_sbuff_parse_error_t *err, uint8_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_uint16(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_uint32(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_uint64(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_float32(fr_sbuff_parse_error_t *err, float *out, fr_sbuff_t *in, bool no_trailing);
+
+size_t         fr_sbuff_in_float64(fr_sbuff_parse_error_t *err, double *out, fr_sbuff_t *in, bool no_trailing);
+
+ssize_t                fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str);
+
+ssize_t                fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len);
+
+ssize_t                fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str);
+
+ssize_t                fr_sbuff_in_vsprintf(fr_sbuff_t *sbuff, char const *fmt, va_list ap);
+
+ssize_t                fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt, ...);
+
+ssize_t                fr_sbuff_in_snprint(fr_sbuff_t *sbuff, char const *in, size_t inlen, char quote);
+
+ssize_t                fr_sbuff_in_snprint_buffer(fr_sbuff_t *sbuff, char const *in, char quote);
+
 #ifdef __cplusplus
 }
 #endif