]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
iovec-wrapper: introduce iovw_concat()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 17 Apr 2026 13:54:54 +0000 (22:54 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 18 Apr 2026 20:51:05 +0000 (05:51 +0900)
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
src/basic/iovec-wrapper.h
src/test/test-iovec-wrapper.c

index a76087b859e83deb6865a8ece9efd58d3a298eca..a7bcc95df5ee85c4dd25318cac64ea76d023c0cc 100644 (file)
@@ -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 C string. Embedded NULs are not allowed.
-         * The caller is expected to filter them out when populating the data. */
+        /* Squish a series of iovecs into a single iovec. */
 
-        size = iovw_size(iovw);
-        if (size == SIZE_MAX)
-                return NULL;  /* Prevent theoretical overflow */
-        size ++;
+        size_t len = iovw_size(iovw);
+        if (len == SIZE_MAX)
+                return -E2BIG;  /* Prevent theoretical overflow */
 
-        p = ans = new(char, size);
-        if (!ans)
-                return NULL;
+        /* 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);
 
-        FOREACH_ARRAY(iovec, iovw->iovec, iovw->count) {
-                assert(!memchr(iovec->iov_base, 0, iovec->iov_len));
+        *p = 0;
 
-                p = mempcpy(p, iovec->iov_base, iovec->iov_len);
-        }
+        *ret = IOVEC_MAKE(TAKE_PTR(buf), len);
+        return 0;
+}
+
+char* iovw_to_cstring(const struct iovec_wrapper *iovw) {
+        assert(iovw);
 
-        *p = '\0';
+        /* 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. */
+
+        _cleanup_(iovec_done) struct iovec iov = {};
+        if (iovw_concat(iovw, &iov) < 0)
+                return NULL;
 
-        return ans;
+        assert(!memchr(iov.iov_base, 0, iov.iov_len));
+        return TAKE_PTR(iov.iov_base);
 }
index d2437b60f1925f8ed71860e0c221e3e37c31b952..cbf40b725af9b4ec2f708e5530466a9053e89c49 100644 (file)
@@ -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);
index 168217f16c2377096ed72078443385e210f7bd8d..8d05617154759cd08a0f418824e311f96f86562a 100644 (file)
@@ -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;