From d49c509d9b2246025e546e70b4ab911a89106f49 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 17 Apr 2026 22:54:54 +0900 Subject: [PATCH] iovec-wrapper: introduce iovw_concat() This is similar to iovw_to_cstring(), but allows embedded NUL, as this just concat multiple iovec, the result may not be a string. Now, iovw_to_cstring() internally uses iovw_concat(). --- src/basic/iovec-wrapper.c | 48 +++++++++++++++++++++-------------- src/basic/iovec-wrapper.h | 1 + src/test/test-iovec-wrapper.c | 18 +++++++++++++ 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/basic/iovec-wrapper.c b/src/basic/iovec-wrapper.c index a76087b859..a7bcc95df5 100644 --- a/src/basic/iovec-wrapper.c +++ b/src/basic/iovec-wrapper.c @@ -175,31 +175,41 @@ rollback: return r; } -char* iovw_to_cstring(const struct iovec_wrapper *iovw) { - size_t size; - char *p, *ans; +int iovw_concat(const struct iovec_wrapper *iovw, struct iovec *ret) { + assert(iovw); + assert(ret); + /* Squish a series of iovecs into a single iovec. */ + + size_t len = iovw_size(iovw); + if (len == SIZE_MAX) + return -E2BIG; /* Prevent theoretical overflow */ + + /* Always allocate one more byte to make the result usable as a NUL-terminated string. */ + _cleanup_free_ uint8_t *buf = malloc(len + 1); + if (!buf) + return -ENOMEM; + + uint8_t *p = buf; + FOREACH_ARRAY(i, iovw->iovec, iovw->count) + p = mempcpy(p, i->iov_base, i->iov_len); + + *p = 0; + + *ret = IOVEC_MAKE(TAKE_PTR(buf), len); + return 0; +} + +char* iovw_to_cstring(const struct iovec_wrapper *iovw) { assert(iovw); /* Squish a series of iovecs into a C string. Embedded NULs are not allowed. * The caller is expected to filter them out when populating the data. */ - size = iovw_size(iovw); - if (size == SIZE_MAX) - return NULL; /* Prevent theoretical overflow */ - size ++; - - p = ans = new(char, size); - if (!ans) + _cleanup_(iovec_done) struct iovec iov = {}; + if (iovw_concat(iovw, &iov) < 0) return NULL; - FOREACH_ARRAY(iovec, iovw->iovec, iovw->count) { - assert(!memchr(iovec->iov_base, 0, iovec->iov_len)); - - p = mempcpy(p, iovec->iov_base, iovec->iov_len); - } - - *p = '\0'; - - return ans; + assert(!memchr(iov.iov_base, 0, iov.iov_len)); + return TAKE_PTR(iov.iov_base); } diff --git a/src/basic/iovec-wrapper.h b/src/basic/iovec-wrapper.h index d2437b60f1..cbf40b725a 100644 --- a/src/basic/iovec-wrapper.h +++ b/src/basic/iovec-wrapper.h @@ -38,4 +38,5 @@ int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, ch void iovw_rebase(struct iovec_wrapper *iovw, void *old, void *new); size_t iovw_size(const struct iovec_wrapper *iovw); int iovw_append_iovw(struct iovec_wrapper *target, const struct iovec_wrapper *source); +int iovw_concat(const struct iovec_wrapper *iovw, struct iovec *ret); char* iovw_to_cstring(const struct iovec_wrapper *iovw); diff --git a/src/test/test-iovec-wrapper.c b/src/test/test-iovec-wrapper.c index 168217f16c..8d05617154 100644 --- a/src/test/test-iovec-wrapper.c +++ b/src/test/test-iovec-wrapper.c @@ -279,6 +279,24 @@ TEST(iovw_append_iovw) { ASSERT_EQ(source.count, 2U); } +TEST(iovw_concat) { + _cleanup_(iovw_done) struct iovec_wrapper iovw = {}; + + /* Empty wrapper -> empty string with 0 length */ + _cleanup_(iovec_done) struct iovec iov = {}; + ASSERT_OK(iovw_concat(&iovw, &iov)); + ASSERT_FALSE(iovec_is_set(&iov)); + ASSERT_STREQ(iov.iov_base, ""); + iovec_done(&iov); + + ASSERT_OK(iovw_put(&iovw, (char*) "foo", 3)); + ASSERT_OK(iovw_put(&iovw, (char*) "\0", 1)); + ASSERT_OK(iovw_put(&iovw, (char*) "bar", 4)); + + ASSERT_OK(iovw_concat(&iovw, &iov)); + ASSERT_EQ(iovec_memcmp(&iov, &IOVEC_MAKE("foo\0bar\0", 8)), 0); +} + TEST(iovw_to_cstring) { _cleanup_(iovw_done) struct iovec_wrapper iovw = {}; _cleanup_free_ char *s;