From 309ff95f3420721d8ef41e31f4b567c7d62b80ac Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Wed, 28 Aug 2024 19:40:38 +0200 Subject: [PATCH 1/4] sys/string_utils: add string_writer helper --- sys/include/string_utils.h | 76 ++++++++++++++++++++++++++++++++++++++ sys/libc/string.c | 30 +++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/sys/include/string_utils.h b/sys/include/string_utils.h index 4a6c71ac9f..32e8ee2907 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 @@ -29,6 +30,7 @@ #include #include +#include "flash_utils.h" #include "modules.h" #ifndef STRING_UTILS_H @@ -38,6 +40,80 @@ 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 internal helper macro + */ +#if IS_ACTIVE(HAS_FLASH_UTILS_ARCH) +#define __swprintf flash_swprintf +#else +#define __swprintf swprintf +__attribute__ ((format (printf, 2, 3))) +#endif +/** + * @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, FLASH_ATTR const char *restrict format, ...); + +#if IS_ACTIVE(HAS_FLASH_UTILS_ARCH) +#define swprintf(sw, fmt, ...) flash_swprintf(sw, TO_FLASH(fmt), ## __VA_ARGS__) +#endif + /* 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 7a8b4ba047..1ab18f461c 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,32 @@ const void *memchk(const void *data, uint8_t c, size_t len) return NULL; } +int __swprintf(string_writer_t *sw, FLASH_ATTR const char *restrict format, ...) +{ + va_list args; + int res; + + va_start(args, format); +#ifdef __clang__ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wformat-nonliteral\"") \ + res = flash_vsnprintf(sw->position, sw->capacity, format, args); + _Pragma("clang diagnostic pop") +#else + res = flash_vsnprintf(sw->position, sw->capacity, format, args); +#endif + 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 2bf4c715d8a4fef9c54c80d5877b24d7787966e2 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 29 Aug 2024 11:34:30 +0200 Subject: [PATCH 2/4] 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 9fa8f10c20..1d0f27e331 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), }; From 46259fb27e08601c304aafd807fd2c127debb1ab Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 17 Dec 2024 13:59:37 +0100 Subject: [PATCH 3/4] cpu/atxmega: set HAS_FLASH_UTILS_ARCH --- cpu/atxmega/include/cpu_conf.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpu/atxmega/include/cpu_conf.h b/cpu/atxmega/include/cpu_conf.h index 4866afe39c..f0a590c34b 100644 --- a/cpu/atxmega/include/cpu_conf.h +++ b/cpu/atxmega/include/cpu_conf.h @@ -64,6 +64,10 @@ extern "C" { */ #define IRQ_API_INLINED (1) +#ifndef DOXYGEN +#define HAS_FLASH_UTILS_ARCH 1 +#endif + #ifdef __cplusplus } #endif From db6196e061d5540495680c65222f1ff3e3665eaf Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 20 Dec 2024 16:35:21 +0100 Subject: [PATCH 4/4] tests/pkg/relic: blacklist ATXmega boards --- tests/pkg/relic/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/pkg/relic/Makefile b/tests/pkg/relic/Makefile index cc8356765b..2ee9749397 100644 --- a/tests/pkg/relic/Makefile +++ b/tests/pkg/relic/Makefile @@ -9,6 +9,9 @@ BOARD_BLACKLIST := arduino-duemilanove \ atmega256rfr2-xpro \ atmega328p \ atmega328p-xplained-mini \ + atxmega-a1-xplained \ + atxmega-a1u-xpro \ + atxmega-a3bu-xplained \ derfmega128 \ derfmega256 \ f4vi1 \