return CMP(a->count, b->count);
}
-int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
+int iovw_put_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len) {
assert(iovw);
+ assert(data || len == 0);
- if (len == 0)
+ if (len == 0 && !accept_zero)
return 0;
- assert(data);
-
if (iovw->count >= IOV_MAX)
return -E2BIG;
return 1;
}
-int iovw_put_iov(struct iovec_wrapper *iovw, const struct iovec *iov) {
+int iovw_put_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov) {
assert(iovw);
if (!iov)
return 0;
- return iovw_put(iovw, iov->iov_base, iov->iov_len);
+ return iovw_put_full(iovw, accept_zero, iov->iov_base, iov->iov_len);
}
-int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) {
+int iovw_put_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source) {
+ int r;
+
assert(iovw);
if (iovw_isempty(source))
if (iovw->count + source->count > IOV_MAX)
return -E2BIG;
- if (!GREEDY_REALLOC_APPEND(iovw->iovec, iovw->count, source->iovec, source->count))
- return -ENOMEM;
+ if (accept_zero) {
+ if (!GREEDY_REALLOC_APPEND(iovw->iovec, iovw->count, source->iovec, source->count))
+ return -ENOMEM;
+
+ return 0;
+ }
+
+ /* When accept_zero is false, we need to filter zero length iovec in source. */
+ size_t original_count = iovw->count;
+
+ FOREACH_ARRAY(iovec, source->iovec, source->count) {
+ r = iovw_put_iov_full(iovw, accept_zero, iovec);
+ if (r < 0)
+ goto rollback;
+ }
return 0;
+
+rollback:
+ iovw->count = original_count;
+ return r;
}
-int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
+int iovw_consume_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len) {
/* Move data into iovw or free on error */
int r;
- r = iovw_put(iovw, data, len);
+ r = iovw_put_full(iovw, accept_zero, data, len);
if (r <= 0)
free(data);
return r;
}
-int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov) {
+int iovw_consume_iov_full(struct iovec_wrapper *iovw, bool accept_zero, struct iovec *iov) {
int r;
assert(iovw);
if (!iov)
return 0;
- r = iovw_put_iov(iovw, iov);
+ r = iovw_put_iov_full(iovw, accept_zero, iov);
if (r <= 0)
iovec_done(iov);
else
return r;
}
-int iovw_extend(struct iovec_wrapper *iovw, const void *data, size_t len) {
+int iovw_extend_full(struct iovec_wrapper *iovw, bool accept_zero, const void *data, size_t len) {
+ assert(iovw);
+ assert(data || len == 0);
+
if (len == 0)
- return 0;
+ return iovw_put_full(iovw, accept_zero, /* data= */ NULL, /* len= */ 0);
void *c = memdup(data, len);
if (!c)
return -ENOMEM;
- return iovw_consume(iovw, c, len);
+ return iovw_consume_full(iovw, accept_zero, c, len);
}
-int iovw_extend_iov(struct iovec_wrapper *iovw, const struct iovec *iov) {
+int iovw_extend_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov) {
assert(iovw);
- if (!iovec_is_set(iov))
+ if (!iov)
return 0;
- return iovw_extend(iovw, iov->iov_base, iov->iov_len);
+ return iovw_extend_full(iovw, accept_zero, iov->iov_base, iov->iov_len);
}
-int iovw_extend_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) {
+int iovw_extend_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source) {
int r;
assert(iovw);
size_t original_count = iovw->count;
FOREACH_ARRAY(iovec, source->iovec, source->count) {
- r = iovw_extend_iov(iovw, iovec);
+ r = iovw_extend_iov_full(iovw, accept_zero, iovec);
if (r < 0)
goto rollback;
}
uint8_t *p = buf;
FOREACH_ARRAY(i, iovw->iovec, iovw->count)
- p = mempcpy(p, i->iov_base, i->iov_len);
+ p = mempcpy_safe(p, i->iov_base, i->iov_len);
*p = 0;
return iovw_compare(a, b) == 0;
}
-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);
+int iovw_put_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len);
+static inline int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
+ return iovw_put_full(iovw, false, data, len);
+}
+int iovw_put_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov);
+static inline int iovw_put_iov(struct iovec_wrapper *iovw, const struct iovec *iov) {
+ return iovw_put_iov_full(iovw, false, iov);
+}
+int iovw_put_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source);
+static inline int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) {
+ return iovw_put_iovw_full(iovw, false, source);
+}
+int iovw_consume_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len);
+static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
+ return iovw_consume_full(iovw, false, data, len);
+}
+int iovw_consume_iov_full(struct iovec_wrapper *iovw, bool accept_zero, struct iovec *iov);
+static inline int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov) {
+ return iovw_consume_iov_full(iovw, false, iov);
+}
+int iovw_extend_full(struct iovec_wrapper *iovw, bool accept_zero, const void *data, size_t len);
+static inline int iovw_extend(struct iovec_wrapper *iovw, const void *data, size_t len) {
+ return iovw_extend_full(iovw, false, data, len);
+}
+int iovw_extend_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov);
+static inline int iovw_extend_iov(struct iovec_wrapper *iovw, const struct iovec *iov) {
+ return iovw_extend_iov_full(iovw, false, iov);
+}
+int iovw_extend_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source);
+static inline int iovw_extend_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) {
+ return iovw_extend_iovw_full(iovw, false, source);
+}
static inline bool iovw_isempty(const struct iovec_wrapper *iovw) {
return !iovw || iovw->count == 0;
ASSERT_EQ(memcmp(iovw.iovec[1].iov_base, "barbar", 6), 0);
ASSERT_EQ(iovw.iovec[2].iov_len, 1U);
ASSERT_EQ(memcmp(iovw.iovec[2].iov_base, "q", 1), 0);
+
+ ASSERT_OK(iovw_put_full(&iovw, /* accept_zero= */ false, NULL, 0));
+ ASSERT_EQ(iovw.count, 3U);
+ ASSERT_OK(iovw_put_full(&iovw, /* accept_zero= */ true, NULL, 0));
+ ASSERT_EQ(iovw.count, 4U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[3], &(struct iovec) {}));
}
TEST(iovw_put_iov) {
ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa")));
ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb")));
ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc")));
+
+ ASSERT_OK(iovw_put_iov_full(&iovw, /* accept_zero= */ false, &(struct iovec) {}));
+ ASSERT_EQ(iovw.count, 3U);
+ ASSERT_OK(iovw_put_iov_full(&iovw, /* accept_zero= */ true, &(struct iovec) {}));
+ ASSERT_EQ(iovw.count, 4U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[3], &(struct iovec) {}));
}
TEST(iovw_put_iovw) {
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);
+ ASSERT_OK(iovw_put_iov_full(&source, /* accept_zero= */ true, &(struct iovec) {}));
+ ASSERT_EQ(source.count, 4U);
/* 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_PTR_EQ(target.iovec[5].iov_base, source.iovec[2].iov_base);
/* Source is unchanged */
- ASSERT_EQ(source.count, 3U);
+ ASSERT_EQ(source.count, 4U);
ASSERT_TRUE(iovec_equal(&source.iovec[0], &IOVEC_MAKE_STRING("aaa")));
ASSERT_TRUE(iovec_equal(&source.iovec[1], &IOVEC_MAKE_STRING("bbb")));
ASSERT_TRUE(iovec_equal(&source.iovec[2], &IOVEC_MAKE_STRING("ccc")));
+ ASSERT_TRUE(iovec_equal(&source.iovec[3], &(struct iovec) {}));
+
+ ASSERT_OK(iovw_put_iovw_full(&target, /* accept_zero= */ true, &source));
+ ASSERT_EQ(target.count, 10U);
+ ASSERT_TRUE(iovec_equal(&target.iovec[0], &IOVEC_MAKE_STRING("xxx")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[1], &IOVEC_MAKE_STRING("yyy")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[2], &IOVEC_MAKE_STRING("zzz")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[3], &IOVEC_MAKE_STRING("aaa")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[4], &IOVEC_MAKE_STRING("bbb")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[5], &IOVEC_MAKE_STRING("ccc")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[6], &IOVEC_MAKE_STRING("aaa")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[7], &IOVEC_MAKE_STRING("bbb")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[8], &IOVEC_MAKE_STRING("ccc")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[9], &(struct iovec) {}));
/* Cannot pass the same objects */
ASSERT_ERROR(iovw_put_iovw(&target, &target), EINVAL);
/* Mutating the caller's buffer does not affect what's stored */
memset(buf, 'X', sizeof buf);
ASSERT_EQ(memcmp(iovw.iovec[0].iov_base, "one", 3), 0);
+
+ ASSERT_OK(iovw_extend_full(&iovw, /* accept_zero= */ false, NULL, 0));
+ ASSERT_EQ(iovw.count, 2U);
+ ASSERT_OK(iovw_extend_full(&iovw, /* accept_zero= */ true, NULL, 0));
+ ASSERT_EQ(iovw.count, 3U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &(struct iovec) {}));
}
TEST(iovw_extend_iov) {
ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa")));
ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb")));
ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc")));
+
+ ASSERT_OK(iovw_extend_iov_full(&iovw, /* accept_zero= */ false, &(struct iovec) {}));
+ ASSERT_EQ(iovw.count, 3U);
+ ASSERT_OK(iovw_extend_iov_full(&iovw, /* accept_zero= */ true, &(struct iovec) {}));
+ ASSERT_EQ(iovw.count, 4U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[3], &(struct iovec) {}));
}
TEST(iovw_extend_iovw) {
ASSERT_OK(iovw_put(&source, (char*) "one", 3));
ASSERT_OK(iovw_put(&source, (char*) "twotwo", 6));
- ASSERT_EQ(source.count, 2U);
+ ASSERT_OK(iovw_put_full(&source, /* accept_zero= */ true, NULL, 0));
+ ASSERT_EQ(source.count, 3U);
/* Pre-seed target with one entry to check that append adds on top rather than replacing */
char *seed = strdup("zero");
ASSERT_EQ(target.iovec[2].iov_len, 6U);
ASSERT_EQ(memcmp(target.iovec[2].iov_base, "twotwo", 6), 0);
+ ASSERT_OK(iovw_extend_iovw_full(&target, /* accept_zero= */ true, &source));
+ ASSERT_EQ(target.count, 6U);
+ ASSERT_TRUE(iovec_equal(&target.iovec[0], &IOVEC_MAKE_STRING("zero")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[1], &IOVEC_MAKE_STRING("one")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[2], &IOVEC_MAKE_STRING("twotwo")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[3], &IOVEC_MAKE_STRING("one")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[4], &IOVEC_MAKE_STRING("twotwo")));
+ ASSERT_TRUE(iovec_equal(&target.iovec[5], &(struct iovec) {}));
+
/* Source is unchanged */
- ASSERT_EQ(source.count, 2U);
+ ASSERT_EQ(source.count, 3U);
/* Cannot pass the same objects */
ASSERT_ERROR(iovw_extend_iovw(&target, &target), EINVAL);
char *q = ASSERT_NOT_NULL(strdup(""));
ASSERT_OK_ZERO(iovw_consume(&iovw, q, 0));
ASSERT_EQ(iovw.count, 1U);
+
+ ASSERT_OK(iovw_consume_full(&iovw, /* accept_zero= */ false, NULL, 0));
+ ASSERT_EQ(iovw.count, 1U);
+ ASSERT_OK(iovw_consume_full(&iovw, /* accept_zero= */ true, NULL, 0));
+ ASSERT_EQ(iovw.count, 2U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &(struct iovec) {}));
+ q = ASSERT_NOT_NULL(strdup(""));
+ ASSERT_OK(iovw_consume_full(&iovw, /* accept_zero= */ true, q, 0));
+ ASSERT_EQ(iovw.count, 3U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &(struct iovec) {}));
}
TEST(iovw_consume_iov) {
/* zero length iovec is also freed */
ASSERT_NULL(iov.iov_base);
ASSERT_EQ(iov.iov_len, 0U);
+
+ ASSERT_OK(iovw_consume_iov_full(&iovw, /* accept_zero= */ false, &(struct iovec) {}));
+ ASSERT_EQ(iovw.count, 1U);
+ ASSERT_OK(iovw_consume_iov_full(&iovw, /* accept_zero= */ true, &(struct iovec) {}));
+ ASSERT_EQ(iovw.count, 2U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &(struct iovec) {}));
+ iov = (struct iovec) {
+ .iov_base = ASSERT_NOT_NULL(strdup("")),
+ .iov_len = 0,
+ };
+ ASSERT_OK(iovw_consume_iov_full(&iovw, /* accept_zero= */ true, &iov));
+ ASSERT_EQ(iovw.count, 3U);
+ ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &(struct iovec) {}));
}
TEST(iovw_isempty) {