From 97b586695cd80821455ae06ee178c6c8cf759ce6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 May 2025 07:37:01 -0600 Subject: abuf: Add a helper for initing and allocating a buffer This construct appears in various places. Reduce code size by adding a function for it. It inits the abuf, then allocates it to the requested size. Signed-off-by: Simon Glass --- lib/abuf.c | 9 +++++++++ lib/of_live.c | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/abuf.c b/lib/abuf.c index 61adf7fc6b1..3cbe320fb08 100644 --- a/lib/abuf.c +++ b/lib/abuf.c @@ -119,6 +119,15 @@ void abuf_init_set(struct abuf *abuf, void *data, size_t size) abuf_set(abuf, data, size); } +bool abuf_init_size(struct abuf *buf, size_t size) +{ + abuf_init(buf); + if (!abuf_realloc(buf, size)) + return false; + + return true; +} + 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/lib/of_live.c b/lib/of_live.c index c1620616513..24200b948a6 100644 --- a/lib/of_live.c +++ b/lib/of_live.c @@ -448,8 +448,7 @@ int of_live_flatten(const struct device_node *root, struct abuf *buf) { int ret; - abuf_init(buf); - if (!abuf_realloc(buf, BUF_STEP)) + if (!abuf_init_size(buf, BUF_STEP)) return log_msg_ret("ini", -ENOMEM); ret = fdt_create(abuf_data(buf), abuf_size(buf)); -- cgit v1.3.1 From d58cebbbc7617fbc45e604c883e8501a58d25e62 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:03 -0600 Subject: abuf: Add a function to copy a buffer It is useful to be able to copy an abuf, to allow changes while preserving the original. Add a function for this. Signed-off-by: Simon Glass --- include/abuf.h | 11 +++++++++++ lib/abuf.c | 14 ++++++++++++++ test/lib/abuf.c | 23 +++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'lib') diff --git a/include/abuf.h b/include/abuf.h index 749bb188b0c..bbb3c51f334 100644 --- a/include/abuf.h +++ b/include/abuf.h @@ -111,6 +111,17 @@ bool abuf_realloc(struct abuf *abuf, size_t new_size); */ bool abuf_realloc_inc(struct abuf *abuf, size_t inc); +/** + * abuf_copy() - Make a copy of an abuf + * + * Creates an allocated copy of @old in @new + * + * @old: abuf to copy + * @new: new abuf to hold the copy (inited by this function) + * Return: true if OK, false if out of memory + */ +bool abuf_copy(const struct abuf *old, struct abuf *new); + /** * abuf_uninit_move() - Return the allocated contents and uninit the abuf * diff --git a/lib/abuf.c b/lib/abuf.c index 3cbe320fb08..28c748acb9f 100644 --- a/lib/abuf.c +++ b/lib/abuf.c @@ -128,6 +128,20 @@ bool abuf_init_size(struct abuf *buf, size_t size) return true; } +bool abuf_copy(const struct abuf *old, struct abuf *copy) +{ + char *data; + + data = malloc(old->size); + if (!data) + return false; + memcpy(data, old->data, old->size); + abuf_init_set(copy, data, old->size); + copy->alloced = true; + + return true; +} + 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 cdc86aad988..96c77ed2379 100644 --- a/test/lib/abuf.c +++ b/test/lib/abuf.c @@ -420,6 +420,29 @@ static int lib_test_abuf_init(struct unit_test_state *uts) } LIB_TEST(lib_test_abuf_init, 0); +/* Test abuf_copy() */ +static int lib_test_abuf_copy(struct unit_test_state *uts) +{ + struct abuf buf, copy; + ulong start; + + start = ut_check_free(); + + abuf_init_set(&buf, test_data, TEST_DATA_LEN); + ut_assert(abuf_copy(&buf, ©)); + ut_asserteq(buf.size, copy.size); + ut_assert(buf.data != copy.data); + ut_assert(copy.alloced); + abuf_uninit(©); + abuf_uninit(&buf); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_abuf_copy, 0); + /* Test abuf_init_size() */ static int lib_test_abuf_init_size(struct unit_test_state *uts) { -- cgit v1.3.1 From 4f4b9477f4476cd86ffd4219111065d610c5237a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:04 -0600 Subject: 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 --- include/abuf.h | 21 ++++++++++++++++++++ lib/abuf.c | 35 +++++++++++++++++++++++++++++++++ test/lib/abuf.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) (limited to 'lib') 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); -- cgit v1.3.1