From: Simon Glass Date: Fri, 2 May 2025 14:46:04 +0000 (-0600) Subject: abuf: Add a way to printf() into a buffer X-Git-Tag: v2025.10-rc1~118^2~56^2~43 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4f4b9477f4476cd86ffd4219111065d610c5237a;p=thirdparty%2Fu-boot.git abuf: Add a way to printf() into a buffer It is useful to format a string into a buffer, with the sizing handled automatically. Add a function for this. Signed-off-by: Simon Glass --- diff --git a/include/abuf.h b/include/abuf.h index bbb3c51f334..7872e9c9b27 100644 --- a/include/abuf.h +++ b/include/abuf.h @@ -122,6 +122,27 @@ bool abuf_realloc_inc(struct abuf *abuf, size_t inc); */ bool abuf_copy(const struct abuf *old, struct abuf *new); +/** + * abuf_printf() - Format a string and place it in an abuf + * + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @...: Arguments for the format string + * Return: the number of characters writtenwhich would be + * generated for the given input, excluding the trailing null, + * as per ISO C99. + * + * The abuf is expanded as necessary to fit the formated string + * + * See the vsprintf() documentation for format string extensions over C99. + * + * Returns: number of characters written (excluding trailing nul) on success, + * -E2BIG if the size exceeds 4K, -ENOMEM if out of memory, -EFAULT if there is + * an internal bug in the vsnprintf() implementation + */ +int abuf_printf(struct abuf *buf, const char *fmt, ...) + __attribute__ ((format (__printf__, 2, 3))); + /** * abuf_uninit_move() - Return the allocated contents and uninit the abuf * diff --git a/lib/abuf.c b/lib/abuf.c index 28c748acb9f..3a2fd1782e9 100644 --- a/lib/abuf.c +++ b/lib/abuf.c @@ -10,8 +10,11 @@ #include #include #include +#include #endif +#include +#include #include void abuf_set(struct abuf *abuf, void *data, size_t size) @@ -142,6 +145,38 @@ bool abuf_copy(const struct abuf *old, struct abuf *copy) return true; } +int abuf_printf(struct abuf *buf, const char *fmt, ...) +{ + int maxlen = buf->size; + va_list args; + int len; + + va_start(args, fmt); + len = vsnprintf(buf->data, buf->size, fmt, args); + va_end(args); + + /* add the terminator */ + len++; + + if (len > 4096) + return -E2BIG; + if (len > maxlen) { + /* make more space and try again */ + maxlen = len; + if (!abuf_realloc(buf, maxlen)) + return -ENOMEM; + va_start(args, fmt); + len = vsnprintf(buf->data, maxlen, fmt, args); + va_end(args); + + /* check there isn't anything strange going on */ + if (len > maxlen) + return -EFAULT; + } + + return len; +} + void abuf_init_const(struct abuf *abuf, const void *data, size_t size) { /* for now there is no flag indicating that the abuf data is constant */ diff --git a/test/lib/abuf.c b/test/lib/abuf.c index 96c77ed2379..97b128c01c0 100644 --- a/test/lib/abuf.c +++ b/test/lib/abuf.c @@ -463,3 +463,64 @@ static int lib_test_abuf_init_size(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_abuf_init_size, 0); + +/* Test abuf_printf() */ +static int lib_test_abuf_printf(struct unit_test_state *uts) +{ + struct abuf buf, fmt; + ulong start; + char *ptr; + + start = ut_check_free(); + + /* start with a fresh buffer */ + abuf_init(&buf); + + /* check handling of out-of-memory condition */ + malloc_enable_testing(0); + ut_asserteq(-ENOMEM, abuf_printf(&buf, "%s", "")); + malloc_enable_testing(1); + + ut_asserteq(0, abuf_printf(&buf, "%s", "")); + ut_asserteq(1, buf.size); + ut_asserteq(true, buf.alloced); + ut_asserteq_str("", buf.data); + + /* check expanding it, initially failing */ + ut_asserteq(-ENOMEM, abuf_printf(&buf, "%s", "testing")); + malloc_disable_testing(); + + ut_asserteq(7, abuf_printf(&buf, "%s", "testing")); + ut_asserteq(8, buf.size); + ut_asserteq_str("testing", buf.data); + + ut_asserteq(11, abuf_printf(&buf, "testing %d", 123)); + ut_asserteq(12, buf.size); + ut_asserteq_str("testing 123", buf.data); + + /* make it smaller; buffer should not shrink */ + ut_asserteq(9, abuf_printf(&buf, "test %d", 456)); + ut_asserteq(12, buf.size); + ut_asserteq_str("test 456", buf.data); + + /* test the maximum size */ + abuf_init(&fmt); + ut_assert(abuf_realloc(&fmt, 4100)); + memset(fmt.data, 'x', 4100); + ptr = fmt.data; + ptr[4096] = '\0'; + + /* we are allowed up to 4K including the terminator */ + ut_asserteq(-E2BIG, abuf_printf(&buf, "%s", ptr)); + ptr[4095] = '\0'; + ut_asserteq(4095, abuf_printf(&buf, "%s", ptr)); + + abuf_uninit(&fmt); + abuf_uninit(&buf); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_abuf_printf, 0);