Skip to content

Commit

Permalink
Merge pull request #20843 from benpicco/string_writer
Browse files Browse the repository at this point in the history
sys/string_utils: add string_writer helper
  • Loading branch information
benpicco authored Dec 16, 2024
2 parents 679ac92 + 2041c82 commit bf5289a
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
76 changes: 76 additions & 0 deletions sys/include/string_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* @author Marian Buschsieweke <[email protected]>
*/

#include <assert.h>
#include <errno.h>
#include <stdint.h>
/* if explicit_bzero() is provided by standard C lib, it may be defined in
Expand All @@ -29,6 +30,7 @@
#include <strings.h>
#include <sys/types.h>

#include "flash_utils.h"
#include "modules.h"

#ifndef STRING_UTILS_H
Expand All @@ -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
#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
Expand Down
30 changes: 30 additions & 0 deletions sys/libc/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* @author Benjamin Valentin <[email protected]>
*/

#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include "string_utils.h"

Expand Down Expand Up @@ -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;
}

/** @} */
36 changes: 36 additions & 0 deletions tests/unittests/tests-libc/tests-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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),
};
Expand Down

0 comments on commit bf5289a

Please sign in to comment.