From f71dcac4dc6184e269d1a5766075409b35ff4072 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 17 Apr 2026 23:06:28 +0900 Subject: [PATCH] iovec-wrapper: introduce several more helper functions --- src/basic/iovec-wrapper.c | 49 +++++++++++++++++++ src/basic/iovec-wrapper.h | 3 ++ src/test/test-iovec-wrapper.c | 91 +++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) diff --git a/src/basic/iovec-wrapper.c b/src/basic/iovec-wrapper.c index f1b64dc0d5a..59b1addaaf2 100644 --- a/src/basic/iovec-wrapper.c +++ b/src/basic/iovec-wrapper.c @@ -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; diff --git a/src/basic/iovec-wrapper.h b/src/basic/iovec-wrapper.h index 88d30c40df1..26b7f5ad4be 100644 --- a/src/basic/iovec-wrapper.h +++ b/src/basic/iovec-wrapper.h @@ -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); diff --git a/src/test/test-iovec-wrapper.c b/src/test/test-iovec-wrapper.c index 51ff3689359..6764d2e78eb 100644 --- a/src/test/test-iovec-wrapper.c +++ b/src/test/test-iovec-wrapper.c @@ -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)); -- 2.47.3