1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #20843 from benpicco/string_writer

sys/string_utils: add string_writer helper
This commit is contained in:
benpicco 2024-12-20 16:36:09 +00:00 committed by GitHub
commit 243ca3114b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 149 additions and 0 deletions

View File

@ -64,6 +64,10 @@ extern "C" {
*/
#define IRQ_API_INLINED (1)
#ifndef DOXYGEN
#define HAS_FLASH_UTILS_ARCH 1
#endif
#ifdef __cplusplus
}
#endif

View File

@ -21,6 +21,7 @@
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*/
#include <assert.h>
#include <errno.h>
#include <stdint.h>
/* if explicit_bzero() is provided by standard C lib, it may be defined in
@ -29,6 +30,7 @@
#include <strings.h>
#include <sys/types.h>
#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

View File

@ -14,6 +14,8 @@
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*/
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#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;
}
/** @} */

View File

@ -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 \

View File

@ -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),
};