]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
iovec-wrapper: introduce several more helper functions 41689/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 17 Apr 2026 14:06:28 +0000 (23:06 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 18 Apr 2026 21:15:54 +0000 (06:15 +0900)
src/basic/iovec-wrapper.c
src/basic/iovec-wrapper.h
src/test/test-iovec-wrapper.c

index f1b64dc0d5ad4ddb4f4b63c01285d7d954029428..59b1addaaf266898247edd56ce53fb38a16204a7 100644 (file)
@@ -63,6 +63,36 @@ int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
         return 1;
 }
 
+int iovw_put_iov(struct iovec_wrapper *iovw, const struct iovec *iov) {
+        assert(iovw);
+
+        if (!iov)
+                return 0;
+
+        return iovw_put(iovw, iov->iov_base, iov->iov_len);
+}
+
+int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) {
+        assert(iovw);
+
+        if (iovw_isempty(source))
+                return 0;
+
+        /* We will reallocate iovw->iovec, hence the source cannot point to the same object. */
+        if (iovw == source)
+                return -EINVAL;
+
+        if (iovw->count > SIZE_MAX - source->count)
+                return -E2BIG;
+        if (iovw->count + source->count > IOV_MAX)
+                return -E2BIG;
+
+        if (!GREEDY_REALLOC_APPEND(iovw->iovec, iovw->count, source->iovec, source->count))
+                return -ENOMEM;
+
+        return 0;
+}
+
 int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
         /* Move data into iovw or free on error */
         int r;
@@ -74,6 +104,25 @@ int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
         return r;
 }
 
+int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov) {
+        int r;
+
+        assert(iovw);
+
+        if (!iov)
+                return 0;
+
+        r = iovw_put_iov(iovw, iov);
+        if (r <= 0)
+                iovec_done(iov);
+        else
+                /* On success, iov->iov_base is now owned by iovw. Let's emptify iov, but do not call
+                 * iovec_done(), of course. */
+                *iov = (struct iovec) {};
+
+        return r;
+}
+
 int iovw_extend(struct iovec_wrapper *iovw, const void *data, size_t len) {
         if (len == 0)
                 return 0;
index 88d30c40df17d0d1c686248c783af956dc91270f..26b7f5ad4be30ccd3d1faa317e713f29e37c926f 100644 (file)
@@ -17,7 +17,10 @@ static inline bool iovw_equal(const struct iovec_wrapper *a, const struct iovec_
 }
 
 int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
+int iovw_put_iov(struct iovec_wrapper *iovw, const struct iovec *iov);
+int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source);
 int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len);
+int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov);
 int iovw_extend(struct iovec_wrapper *iovw, const void *data, size_t len);
 int iovw_extend_iov(struct iovec_wrapper *iovw, const struct iovec *iov);
 int iovw_extend_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source);
index 51ff3689359a6a01f30ae8090a6f632fcc89e970..6764d2e78eb6b2046bf466b45614e2905985fbf8 100644 (file)
@@ -84,6 +84,67 @@ TEST(iovw_put) {
         ASSERT_EQ(memcmp(iovw.iovec[2].iov_base, "q", 1), 0);
 }
 
+TEST(iovw_put_iov) {
+        /* iovw_put_iov() does not copy the input, hence do not use iovw_done_free */
+        _cleanup_(iovw_done) struct iovec_wrapper iovw = {};
+
+        /* Appending an empty/NULL source is a no-op */
+        ASSERT_OK_ZERO(iovw_put_iov(&iovw, NULL));
+        ASSERT_OK_ZERO(iovw_put_iov(&iovw, &(struct iovec) {}));
+        ASSERT_EQ(iovw.count, 0U);
+
+        ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("aaa")));
+        ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("bbb")));
+        ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("ccc")));
+        ASSERT_EQ(iovw.count, 3U);
+        ASSERT_EQ(iovec_memcmp(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa")), 0);
+        ASSERT_EQ(iovec_memcmp(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb")), 0);
+        ASSERT_EQ(iovec_memcmp(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc")), 0);
+}
+
+TEST(iovw_put_iovw) {
+        _cleanup_(iovw_done) struct iovec_wrapper target = {}, source = {};
+
+        /* Appending an empty/NULL source is a no-op */
+        ASSERT_OK_ZERO(iovw_put_iovw(&target, NULL));
+        ASSERT_OK_ZERO(iovw_put_iovw(&target, &source));
+        ASSERT_EQ(target.count, 0U);
+
+        ASSERT_OK(iovw_put_iov(&source, &IOVEC_MAKE_STRING("aaa")));
+        ASSERT_OK(iovw_put_iov(&source, &IOVEC_MAKE_STRING("bbb")));
+        ASSERT_OK(iovw_put_iov(&source, &IOVEC_MAKE_STRING("ccc")));
+        ASSERT_EQ(source.count, 3U);
+
+        /* Pre-seed target with one entry to check that append adds on top rather than replacing */
+        ASSERT_OK(iovw_put_iov(&target, &IOVEC_MAKE_STRING("xxx")));
+        ASSERT_OK(iovw_put_iov(&target, &IOVEC_MAKE_STRING("yyy")));
+        ASSERT_OK(iovw_put_iov(&target, &IOVEC_MAKE_STRING("zzz")));
+        ASSERT_EQ(target.count, 3U);
+
+        ASSERT_OK(iovw_put_iovw(&target, &source));
+        ASSERT_EQ(target.count, 6U);
+        ASSERT_EQ(iovec_memcmp(&target.iovec[0], &IOVEC_MAKE_STRING("xxx")), 0);
+        ASSERT_EQ(iovec_memcmp(&target.iovec[1], &IOVEC_MAKE_STRING("yyy")), 0);
+        ASSERT_EQ(iovec_memcmp(&target.iovec[2], &IOVEC_MAKE_STRING("zzz")), 0);
+        ASSERT_EQ(iovec_memcmp(&target.iovec[3], &IOVEC_MAKE_STRING("aaa")), 0);
+        ASSERT_EQ(iovec_memcmp(&target.iovec[4], &IOVEC_MAKE_STRING("bbb")), 0);
+        ASSERT_EQ(iovec_memcmp(&target.iovec[5], &IOVEC_MAKE_STRING("ccc")), 0);
+
+        /* iovw_put_iovw() does not copy data, hence the pointers must be equal */
+        ASSERT_PTR_EQ(target.iovec[3].iov_base, source.iovec[0].iov_base);
+        ASSERT_PTR_EQ(target.iovec[4].iov_base, source.iovec[1].iov_base);
+        ASSERT_PTR_EQ(target.iovec[5].iov_base, source.iovec[2].iov_base);
+
+        /* Source is unchanged */
+        ASSERT_EQ(source.count, 3U);
+        ASSERT_EQ(iovec_memcmp(&source.iovec[0], &IOVEC_MAKE_STRING("aaa")), 0);
+        ASSERT_EQ(iovec_memcmp(&source.iovec[1], &IOVEC_MAKE_STRING("bbb")), 0);
+        ASSERT_EQ(iovec_memcmp(&source.iovec[2], &IOVEC_MAKE_STRING("ccc")), 0);
+
+        /* Cannot pass the same objects */
+        ASSERT_ERROR(iovw_put_iovw(&target, &target), EINVAL);
+}
+
 TEST(iovw_extend) {
         _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
 
@@ -181,6 +242,36 @@ TEST(iovw_consume) {
         ASSERT_EQ(iovw.count, 1U);
 }
 
+TEST(iovw_consume_iov) {
+        _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
+
+        ASSERT_OK_ZERO(iovw_consume_iov(&iovw, NULL));
+        ASSERT_EQ(iovw.count, 0U);
+
+        ASSERT_OK_ZERO(iovw_consume_iov(&iovw, &(struct iovec) {}));
+        ASSERT_EQ(iovw.count, 0U);
+
+        struct iovec iov = {
+                .iov_base = ASSERT_NOT_NULL(strdup("consumed")),
+                .iov_len = strlen("consumed"),
+        };
+        ASSERT_OK(iovw_consume_iov(&iovw, &iov));
+        ASSERT_EQ(iovw.count, 1U);
+        /* iovw_consume_iov takes the ownership of the buffer, and emptifies the iovec. */
+        ASSERT_NULL(iov.iov_base);
+        ASSERT_EQ(iov.iov_len, 0U);
+
+        iov = (struct iovec) {
+                .iov_base = ASSERT_NOT_NULL(strdup("")),
+                .iov_len = 0,
+        };
+        ASSERT_OK_ZERO(iovw_consume_iov(&iovw, &iov));
+        ASSERT_EQ(iovw.count, 1U);
+        /* zero length iovec is also freed */
+        ASSERT_NULL(iov.iov_base);
+        ASSERT_EQ(iov.iov_len, 0U);
+}
+
 TEST(iovw_isempty) {
         ASSERT_TRUE(iovw_isempty(NULL));