From 0fef55143c986d6bbd81c71f97f3c73b204e81e2 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Wed, 28 Aug 2024 19:40:38 +0200 Subject: [PATCH 1/2] sys/string_utils: add string_writer helper --- sys/include/string_utils.h | 62 ++++++++++++++++++++++++++++++++++++++ sys/libc/string.c | 28 +++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/sys/include/string_utils.h b/sys/include/string_utils.h index 4a6c71ac9f43..635d46b1030e 100644 --- a/sys/include/string_utils.h +++ b/sys/include/string_utils.h @@ -21,6 +21,7 @@ * @author Marian Buschsieweke */ +#include #include #include /* if explicit_bzero() is provided by standard C lib, it may be defined in @@ -38,6 +39,67 @@ extern "C" { #endif +/** + * @brief String Writer structure. + * Helper for writing multiple formatted strings to a buffer + */ +typedef struct { + const char *start; /**< start of the target buffer */ + char *position; /**< current write pointer */ + size_t capacity; /**< remaining capacity of the buffer */ +} string_writer_t; + +/** + * @brief Initialize a string writer structure + * + * @param[out] sw String Writer object to initialize + * @param[in] buffer destination buffer + * @param[in] len size of the destination buffer + */ +static inline void string_writer_init(string_writer_t *sw, void *buffer, size_t len) +{ + assert(buffer && len); + + sw->start = buffer; + sw->position = buffer; + sw->capacity = len; + sw->position[0] = 0; +} + +/** + * @brief Get the size of the string contained by the string writer + * @param[in] sw String Writer to query + * @return size of the string + */ +static inline size_t string_writer_len(const string_writer_t *sw) +{ + return sw->position - sw->start; +} + +/** + * @brief Get the string contained by the string writer + * @param[in] sw String Writer to query + * @return the string assembled by string writer + */ +static inline const char *string_writer_str(const string_writer_t *sw) +{ + return sw->start; +} + +/** + * @brief Write a formatted string to a buffer + * The string will be truncated if there is not enough space left in + * the destination buffer. + * A terminating `\0` character is always included. + * + * @param[in] sw String Writer to write to + * @param[in] format format string to write + * + * @return number of bytes written on success + * -E2BIG if the string was truncated + */ +int swprintf(string_writer_t *sw, const char *restrict format, ...); + /* explicit_bzero is provided if: * - glibc is used as C lib (only with board natvie) * - newlib is used and __BSD_VISIBILE is set diff --git a/sys/libc/string.c b/sys/libc/string.c index 7a8b4ba0470a..2e8b5bb0dd36 100644 --- a/sys/libc/string.c +++ b/sys/libc/string.c @@ -14,6 +14,8 @@ * @author Benjamin Valentin */ +#include +#include #include #include "string_utils.h" @@ -50,4 +52,30 @@ const void *memchk(const void *data, uint8_t c, size_t len) return NULL; } +int swprintf(string_writer_t *sw, const char *restrict format, ...) +{ + va_list args; + int res; + + /* make sure flash_vsnprintf() is not used */ + #if HAS_FLASH_UTILS_ARCH + #undef vsnprintf + #endif + + va_start(args, format); + res = vsnprintf(sw->position, sw->capacity, format, args); + va_end(args); + + if (res <= (int)sw->capacity) { + sw->capacity -= res; + sw->position += res; + } else { + sw->position += sw->capacity; + sw->capacity = 0; + res = -E2BIG; + } + + return res; +} + /** @} */ From 0fe3ac4e256d692b5dc838a8ae2a0d54a0e1a2cf Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 29 Aug 2024 11:34:30 +0200 Subject: [PATCH 2/2] unittests: add test for string_writer --- tests/unittests/tests-libc/tests-libc.c | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/unittests/tests-libc/tests-libc.c b/tests/unittests/tests-libc/tests-libc.c index 9fa8f10c2008..1d0f27e331cf 100644 --- a/tests/unittests/tests-libc/tests-libc.c +++ b/tests/unittests/tests-libc/tests-libc.c @@ -30,6 +30,41 @@ static void test_libc_strscpy(void) TEST_ASSERT_EQUAL_INT(strscpy(buffer, "empty", 0), -E2BIG); } +static void test_libc_swprintf(void) +{ + string_writer_t sw; + char buffer[32]; + int res; + + string_writer_init(&sw, buffer, sizeof(buffer)); + + /* check that string can be written completely */ + res = swprintf(&sw, "Hello World!"); + TEST_ASSERT_EQUAL_INT(res, 12); + TEST_ASSERT_EQUAL_INT(string_writer_len(&sw), 12); + TEST_ASSERT_EQUAL_INT(strcmp("Hello World!", string_writer_str(&sw)), 0); + + /* check that we can add to the string */ + /* Writing 10 characters, returns 10 bytes written */ + res = swprintf(&sw, "0123456789"); + TEST_ASSERT_EQUAL_INT(res, 10); + TEST_ASSERT_EQUAL_INT(strcmp("Hello World!0123456789", string_writer_str(&sw)), 0); + + /* The string does not fit completely into the buffer, so it gets truncated */ + res = swprintf(&sw, "01234567891"); + TEST_ASSERT_EQUAL_INT(res, -E2BIG); + TEST_ASSERT_EQUAL_INT(strcmp("Hello World!0123456789012345678", string_writer_str(&sw)), 0); + + /* You can't write to a full buffer */ + res = swprintf(&sw, "###"); + TEST_ASSERT_EQUAL_INT(res, -E2BIG); + + /* check if string was truncated as expected */ + TEST_ASSERT_EQUAL_INT(string_writer_len(&sw), 32); + TEST_ASSERT_EQUAL_INT(strcmp("Hello World!0123456789012345678", + string_writer_str(&sw)), 0); +} + static void test_libc_memchk(void) { char buffer[32]; @@ -101,6 +136,7 @@ Test *tests_libc_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { new_TestFixture(test_libc_strscpy), + new_TestFixture(test_libc_swprintf), new_TestFixture(test_libc_memchk), new_TestFixture(test_libc_endian), };