mirror of
https://github.com/systemd/systemd.git
synced 2026-11-06 05:17:24 +00:00
hexdump: add hexdump_memcmp()
Add a helper producing a hexdump(3)-like side-by-side dump of two memory regions, marking the bytes that differ. Meant for assertion failure messages.
This commit is contained in:
1 parent
9b4509d2a6
commit
b26ac01e42
3 files changed
+104
No files matched your search
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "hexdump.h"
|
||||
#include "memstream-util.h"
|
||||
|
||||
void hexdump(FILE *f, const void *p, size_t s) {
|
||||
const uint8_t *b = p;
|
||||
@@ -54,3 +55,78 @@ void hexdump(FILE *f, const void *p, size_t s) {
|
||||
s -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_hex_line(FILE *f, char tag, const uint8_t *p, size_t size, size_t off) {
|
||||
assert(f);
|
||||
|
||||
fprintf(f, "%c %04zx ", tag, off);
|
||||
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
if (off + i < size)
|
||||
fprintf(f, "%02x ", p[off + i]);
|
||||
else
|
||||
fputs(" ", f);
|
||||
|
||||
if (i == 7)
|
||||
fputc(' ', f);
|
||||
}
|
||||
|
||||
fputs(" |", f);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
if (off + i < size) {
|
||||
uint8_t c = p[off + i];
|
||||
fputc(isprint(c) ? (char) c : '.', f);
|
||||
} else
|
||||
fputc(' ', f);
|
||||
}
|
||||
fputs("|\n", f);
|
||||
}
|
||||
|
||||
char* hexdump_memcmp(const void *m1, size_t l1, const void *m2, size_t l2) {
|
||||
_cleanup_(memstream_done) MemStream m = {};
|
||||
const uint8_t *a = m1, *b = m2;
|
||||
size_t mx = MAX(l1, l2);
|
||||
FILE *f;
|
||||
|
||||
/* Returns a hexdump(3)-like side-by-side representation of two memory regions. For each 16 byte
|
||||
* chunk that differs a marker line pointing at the diverging bytes ('<' is the first region, '>' the
|
||||
* second) is appended. */
|
||||
|
||||
f = memstream_init(&m);
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
for (size_t off = 0; off < mx; off += 16) {
|
||||
dump_hex_line(f, '<', a, l1, off);
|
||||
dump_hex_line(f, '>', b, l2, off);
|
||||
|
||||
/* Figure out whether this chunk differs at all, and only then emit the marker line. */
|
||||
bool any = false;
|
||||
for (size_t i = 0; i < 16 && off + i < mx; i++) {
|
||||
bool pa = off + i < l1, pb = off + i < l2;
|
||||
if (pa != pb || (pa && pb && a[off + i] != b[off + i])) {
|
||||
any = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!any)
|
||||
continue;
|
||||
|
||||
fputs(" ", f); /* align with the 8-char "%c %04zx " prefix: tag + space + 4 hex + 2 spaces */
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
bool pa = off + i < l1, pb = off + i < l2;
|
||||
bool diff = pa != pb || (pa && pb && a[off + i] != b[off + i]);
|
||||
fputs(diff ? "^^ " : " ", f);
|
||||
|
||||
if (i == 7)
|
||||
fputc(' ', f);
|
||||
}
|
||||
fputc('\n', f);
|
||||
}
|
||||
|
||||
char *ret;
|
||||
if (memstream_finalize(&m, &ret, NULL) < 0)
|
||||
return NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -4,3 +4,7 @@
|
||||
#include "forward.h"
|
||||
|
||||
void hexdump(FILE *f, const void *p, size_t s) _nonnull_if_nonzero_(2, 3);
|
||||
|
||||
/* Returns a hexdump(3)-like side-by-side dump of two memory regions, marking the differing bytes. Returns
|
||||
* NULL on OOM. Meant for assertion failure messages, see ASSERT_MEMEQ() and friends. */
|
||||
char* hexdump_memcmp(const void *m1, size_t l1, const void *m2, size_t l2);
|
||||
@@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "hexdump.h"
|
||||
#include "tests.h"
|
||||
|
||||
@@ -26,4 +27,27 @@ TEST(hexdump) {
|
||||
hexdump(stdout, data, sizeof(data));
|
||||
}
|
||||
|
||||
TEST(hexdump_memcmp) {
|
||||
static const uint8_t a[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11 };
|
||||
static const uint8_t b[] = { 0x00, 0x01, 0xff, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10 };
|
||||
|
||||
/* Equal regions: no marker lines, just the two sides. */
|
||||
_cleanup_free_ char *s = ASSERT_NOT_NULL(hexdump_memcmp(a, sizeof(a), a, sizeof(a)));
|
||||
log_info("equal:\n%s", s);
|
||||
|
||||
/* Differing byte and differing length. */
|
||||
free(s);
|
||||
s = ASSERT_NOT_NULL(hexdump_memcmp(a, sizeof(a), b, sizeof(b)));
|
||||
log_info("differ:\n%s", s);
|
||||
|
||||
/* Empty regions are fine. */
|
||||
free(s);
|
||||
s = ASSERT_NOT_NULL(hexdump_memcmp(NULL, 0, NULL, 0));
|
||||
ASSERT_STREQ(s, "");
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
Reference in new issue
Block a user