From 45d43b89126057ea6ffbd0cfd5e22840977b7d03 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 4 Nov 2026 22:01:34 +0900 Subject: [PATCH] hexdump: add hexdump_iovw_compare() Add a helper dumping the differing elements of two iovec wrappers, element by element, reusing hexdump_memcmp(). Meant for assertion failure messages. --- src/shared/hexdump.c | 33 +++++++++++++++++++++++++++++++++ src/shared/hexdump.h | 3 +++ src/test/test-hexdump.c | 24 ++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/shared/hexdump.c b/src/shared/hexdump.c index 8664b86a748..66bbdf30b6f 100644 --- a/src/shared/hexdump.c +++ b/src/shared/hexdump.c @@ -7,6 +7,8 @@ #include "alloc-util.h" #include "hexdump.h" +#include "iovec-util.h" +#include "iovec-wrapper.h" #include "memstream-util.h" void hexdump(FILE *f, const void *p, size_t s) { @@ -148,3 +150,34 @@ char* hexdump_memcmp_byte(uint8_t byte, const void *data, size_t length) { return hexdump_memcmp(data, length, expected, length); } + +char* hexdump_iovw_compare(const struct iovec_wrapper *a, const struct iovec_wrapper *b) { + _cleanup_(memstream_done) MemStream m = {}; + size_t na = a ? a->count : 0, nb = b ? b->count : 0, mx = MAX(na, nb); + FILE *f; + + /* Dumps the elements of two iovec wrappers that differ, element by element, reusing + * hexdump_memcmp(). Used by ASSERT_IOVW_EQ(). */ + + f = memstream_init(&m); + if (!f) + return NULL; + + for (size_t i = 0; i < mx; i++) { + const struct iovec *ia = i < na ? a->iovec + i : NULL; + const struct iovec *ib = i < nb ? b->iovec + i : NULL; + + if (ia && ib && iovec_memcmp(ia, ib) == 0) + continue; + + _cleanup_free_ char *d = hexdump_memcmp(ia ? ia->iov_base : NULL, ia ? ia->iov_len : 0, + ib ? ib->iov_base : NULL, ib ? ib->iov_len : 0); + fprintf(f, "element %zu differs:\n%s", i, d ?: ""); + } + + char *ret; + if (memstream_finalize(&m, &ret, NULL) < 0) + return NULL; + + return ret; +} diff --git a/src/shared/hexdump.h b/src/shared/hexdump.h index 15b71612a25..22bf084b7ad 100644 --- a/src/shared/hexdump.h +++ b/src/shared/hexdump.h @@ -11,3 +11,6 @@ char* hexdump_memcmp(const void *m1, size_t l1, const void *m2, size_t l2); /* Like hexdump_memcmp(), but compares against a region of the same length filled with 'byte'. */ char* hexdump_memcmp_byte(uint8_t byte, const void *data, size_t length); + +/* Dumps the differing elements of two iovec wrappers, element by element. Used by ASSERT_IOVW_EQ(). */ +char* hexdump_iovw_compare(const struct iovec_wrapper *a, const struct iovec_wrapper *b); diff --git a/src/test/test-hexdump.c b/src/test/test-hexdump.c index 48d518f4929..77dec502d00 100644 --- a/src/test/test-hexdump.c +++ b/src/test/test-hexdump.c @@ -2,6 +2,8 @@ #include "alloc-util.h" #include "hexdump.h" +#include "iovec-util.h" +#include "iovec-wrapper.h" #include "tests.h" TEST(hexdump) { @@ -68,4 +70,26 @@ TEST(hexdump_memcmp_byte) { ASSERT_STREQ(s, ""); } +TEST(hexdump_iovw_compare) { + struct iovec iov_a[] = { IOVEC_MAKE_STRING("foo"), IOVEC_MAKE_STRING("bar") }; + struct iovec iov_b[] = { IOVEC_MAKE_STRING("foo"), IOVEC_MAKE_STRING("baz") }; + struct iovec_wrapper wa = { .iovec = iov_a, .count = ELEMENTSOF(iov_a) }; + struct iovec_wrapper wb = { .iovec = iov_b, .count = ELEMENTSOF(iov_b) }; + + /* Equal wrappers: empty dump. */ + _cleanup_free_ char *s = ASSERT_NOT_NULL(hexdump_iovw_compare(&wa, &wa)); + ASSERT_STREQ(s, ""); + + /* One element differs. */ + free(s); + s = ASSERT_NOT_NULL(hexdump_iovw_compare(&wa, &wb)); + log_info("differ:\n%s", s); + + /* Different element count. */ + struct iovec_wrapper wc = { .iovec = iov_a, .count = 1 }; + free(s); + s = ASSERT_NOT_NULL(hexdump_iovw_compare(&wa, &wc)); + log_info("count differ:\n%s", s); +} + DEFINE_TEST_MAIN(LOG_INFO);