Skip to content

Commit 3cf8241

Browse files
committed
Add xhexdump()
1 parent 48f0519 commit 3cf8241

File tree

2 files changed

+110
-47
lines changed

2 files changed

+110
-47
lines changed

README.md

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ following routines:
1515
- `json_get_num()` - fetch numeric value from a JSON string
1616
- `json_get_bool()` - fetch boolean value from a JSON string
1717
- `json_get_str()` - fetch string value from a JSON string
18+
- `xhexdump()` - print hex dump of the given memory buffer
1819

1920
## Features
2021

@@ -76,16 +77,16 @@ file2.c, file3.c, ...:
7677

7778
### xprintf(), vxprintf()
7879
```c
79-
size_t vxprintf(void (*out)(char, void *), void *arg, const char *fmt, va_list *);
80-
size_t xprintf(void (*out)(char, void *), void *arg, const char *fmt, ...);
80+
size_t vxprintf(void (*fn)(char, void *), void *arg, const char *fmt, va_list *);
81+
size_t xprintf(void (*fn)(char, void *), void *arg, const char *fmt, ...);
8182
```
8283
8384
Print formatted string using an output function `fn()`. The output function
8485
outputs a single byte: `void fn(char ch, void *param) { ... }`. By using
8586
different output functions, it is possible to print data to anywhere.
8687
Parameters:
87-
- `out` - an output function
88-
- `arg` - an parameter for the `out()` output function
88+
- `fn` - an output function
89+
- `arg` - an parameter for the `fn()` output function
8990
- `fmt` - printf-like format string which supports the following specifiers:
9091
- `%hhd`, `%hd`, `%d`, `%ld`, `%lld` - for `char`, `short`, `int`, `long`, `int64_t`
9192
- `%hhu`, `%hu`, `%u`, `%lu`, `%llu` - same but for unsigned variants
@@ -107,11 +108,11 @@ positional arguments. That format function should return the number of bytes
107108
it has printed. Here its signature:
108109
109110
```c
110-
size_t (*ff)(void (*out)(char, void *), void *arg, va_list *ap);
111+
size_t (*ff)(void (*fn)(char, void *), void *arg, va_list *ap);
111112
```
112113
Parameters:
113-
- `out` - an output function
114-
- `arg` - an parameter for the `out()` output function
114+
- `fn` - an output function
115+
- `arg` - an parameter for the `fn()` output function
115116
- `ap` - a pointer for fetching positional arguments
116117

117118
This library ships with several pre-defined format functions described below.
@@ -273,11 +274,31 @@ char dst[100];
273274
json_get_str("[1,2,\"hi\"]", "$[2]", dst, sizeof(dst)); // dst contains "hi"
274275
```
275276

277+
### xhexdump()
278+
279+
```c
280+
void xhexdump(void (*fn)(char, void *), void *arg, const void *buf, size_t len);
281+
```
282+
283+
Print hex dump of the given memory buffer.
284+
285+
Parameters:
286+
- `fn` - an output function
287+
- `arg` - an parameter for the `fn()` output function
288+
- `buf` - a pointer to a buffer to print
289+
- `len` - a length of a buffer
290+
291+
Usage example:
292+
293+
```c
294+
xhexdump(xputchar, NULL, "hi", 2);
295+
```
296+
276297

277298
## Pre-defined `%M`, `%m` format functions
278299

279300
```c
280-
size_t fmt_*(void (*out)(char, void *), void *arg, va_list *ap);
301+
size_t fmt_*(void (*fn)(char, void *), void *arg, va_list *ap);
281302
```
282303

283304
Pre-defined helper functions for `%M` specifier:
@@ -315,10 +336,10 @@ data structure as JSON string, just create your custom formatting function:
315336
```c
316337
struct foo { int a; double b; const char *c; };
317338

318-
size_t fmt_foo(void (*out)(char, void *), void *arg, va_list *ap) {
339+
size_t fmt_foo(void (*fn)(char, void *), void *arg, va_list *ap) {
319340
struct foo *foo = va_arg(*ap, struct foo *);
320-
return xxprintf(out, arg, "{\"a\":%d, \"b\":%g, \"c\":%m}",
321-
foo->a, foo->b, ESC(c));
341+
return xxprintf(fn, arg, "{\"a\":%d, \"b\":%g, \"c\":%m}",
342+
foo->a, foo->b, ESC(c));
322343
}
323344
```
324345

str.h

Lines changed: 78 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ size_t xvsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
3838
size_t xsnprintf(char *, size_t, const char *fmt, ...);
3939

4040
// Pre-defined %M/%m formatting functions
41-
size_t fmt_ip4(void (*out)(char, void *), void *arg, va_list *ap);
42-
size_t fmt_ip6(void (*out)(char, void *), void *arg, va_list *ap);
43-
size_t fmt_mac(void (*out)(char, void *), void *arg, va_list *ap);
44-
size_t fmt_b64(void (*out)(char, void *), void *arg, va_list *ap);
45-
size_t fmt_esc(void (*out)(char, void *), void *arg, va_list *ap);
41+
size_t fmt_ip4(void (*fn)(char, void *), void *arg, va_list *ap);
42+
size_t fmt_ip6(void (*fn)(char, void *), void *arg, va_list *ap);
43+
size_t fmt_mac(void (*fn)(char, void *), void *arg, va_list *ap);
44+
size_t fmt_b64(void (*fn)(char, void *), void *arg, va_list *ap);
45+
size_t fmt_esc(void (*fn)(char, void *), void *arg, va_list *ap);
46+
47+
// Utility functions
48+
void xhexdump(void (*fn)(char, void *), void *arg, const void *buf, size_t len);
4649

4750
// JSON parsing API
4851
int json_get(const char *buf, int len, const char *path, int *size);
@@ -83,26 +86,28 @@ size_t xsnprintf(char *buf, size_t len, const char *fmt, ...) {
8386
return n;
8487
}
8588

86-
size_t fmt_ip4(void (*out)(char, void *), void *arg, va_list *ap) {
89+
size_t fmt_ip4(void (*fn)(char, void *), void *arg, va_list *ap) {
8790
uint8_t *p = va_arg(*ap, uint8_t *);
88-
return xprintf(out, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
91+
return xprintf(fn, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
8992
}
9093

9194
#define U16(p) ((((uint16_t) (p)[0]) << 8) | (p)[1])
92-
size_t fmt_ip6(void (*out)(char, void *), void *arg, va_list *ap) {
95+
size_t fmt_ip6(void (*fn)(char, void *), void *arg, va_list *ap) {
9396
uint8_t *p = va_arg(*ap, uint8_t *);
94-
return xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", U16(&p[0]), U16(&p[2]),
97+
return xprintf(fn, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", U16(&p[0]), U16(&p[2]),
9598
U16(&p[4]), U16(&p[6]), U16(&p[8]), U16(&p[10]), U16(&p[12]),
9699
U16(&p[14]));
97100
}
98101

99-
size_t fmt_mac(void (*out)(char, void *), void *arg, va_list *ap) {
102+
size_t fmt_mac(void (*fn)(char, void *), void *arg, va_list *ap) {
100103
uint8_t *p = va_arg(*ap, uint8_t *);
101-
return xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2],
104+
return xprintf(fn, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2],
102105
p[3], p[4], p[5]);
103106
}
104107

105-
static int xisdigit(int c) { return c >= '0' && c <= '9'; }
108+
static int xisdigit(int c) {
109+
return c >= '0' && c <= '9';
110+
}
106111

107112
static size_t xstrlen(const char *s) {
108113
size_t n = 0;
@@ -282,24 +287,24 @@ static char xesc(int c, int esc) {
282287
return 0;
283288
}
284289

285-
size_t fmt_esc(void (*out)(char, void *), void *param, va_list *ap) {
290+
size_t fmt_esc(void (*fn)(char, void *), void *param, va_list *ap) {
286291
unsigned len = va_arg(*ap, unsigned);
287292
const char *s = va_arg(*ap, const char *);
288293
size_t i, n = 0;
289294
if (len == 0) len = s == NULL ? 0 : (unsigned) xstrlen(s);
290295
for (i = 0; i < len && s[i] != '\0'; i++) {
291296
char c = xesc(s[i], 1);
292297
if (c) {
293-
out('\\', param), out(c, param), n += 2;
298+
fn('\\', param), fn(c, param), n += 2;
294299
} else {
295-
out(s[i], param);
300+
fn(s[i], param);
296301
n++;
297302
}
298303
}
299304
return n;
300305
}
301306

302-
size_t fmt_b64(void (*out)(char, void *), void *param, va_list *ap) {
307+
size_t fmt_b64(void (*fn)(char, void *), void *param, va_list *ap) {
303308
unsigned len = va_arg(*ap, unsigned);
304309
uint8_t *buf = va_arg(*ap, uint8_t *);
305310
size_t i, n = 0;
@@ -311,21 +316,21 @@ size_t fmt_b64(void (*out)(char, void *), void *param, va_list *ap) {
311316
char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='};
312317
if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)];
313318
if (i + 2 < len) tmp[3] = t[c3 & 63];
314-
n += scpy(out, param, tmp, sizeof(tmp));
319+
n += scpy(fn, param, tmp, sizeof(tmp));
315320
}
316321
return n;
317322
}
318323

319-
size_t xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) {
324+
size_t xprintf(void (*fn)(char, void *), void *ptr, const char *fmt, ...) {
320325
size_t len = 0;
321326
va_list ap;
322327
va_start(ap, fmt);
323-
len = xvprintf(out, ptr, fmt, &ap);
328+
len = xvprintf(fn, ptr, fmt, &ap);
324329
va_end(ap);
325330
return len;
326331
}
327332

328-
size_t xvprintf(xout_t out, void *param, const char *fmt, va_list *ap) {
333+
size_t xvprintf(xout_t fn, void *param, const char *fmt, va_list *ap) {
329334
size_t i = 0, n = 0;
330335
while (fmt[i] != '\0') {
331336
if (fmt[i] == '%') {
@@ -375,41 +380,41 @@ size_t xvprintf(xout_t out, void *param, const char *fmt, va_list *ap) {
375380
}
376381
for (j = 0; j < xl && w > 0; j++) w--;
377382
for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++)
378-
n += scpy(out, param, &pad, 1);
379-
n += scpy(out, param, (char *) "0x", xl);
383+
n += scpy(fn, param, &pad, 1);
384+
n += scpy(fn, param, (char *) "0x", xl);
380385
for (j = 0; pad == '0' && k < w && j + k < w; j++)
381-
n += scpy(out, param, &pad, 1);
382-
n += scpy(out, param, tmp, k);
386+
n += scpy(fn, param, &pad, 1);
387+
n += scpy(fn, param, tmp, k);
383388
for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++)
384-
n += scpy(out, param, &pad, 1);
389+
n += scpy(fn, param, &pad, 1);
385390
} else if (c == 'm' || c == 'M') {
386391
xfmt_t f = va_arg(*ap, xfmt_t);
387-
if (c == 'm') out('"', param);
388-
n += f(out, param, ap);
389-
if (c == 'm') n += 2, out('"', param);
392+
if (c == 'm') fn('"', param);
393+
n += f(fn, param, ap);
394+
if (c == 'm') n += 2, fn('"', param);
390395
} else if (c == 'c') {
391396
int ch = va_arg(*ap, int);
392-
out((char) ch, param);
397+
fn((char) ch, param);
393398
n++;
394399
} else if (c == 's') {
395400
char *p = va_arg(*ap, char *);
396401
if (pr == ~0U) pr = p == NULL ? 0 : strlen(p);
397402
for (j = 0; !minus && pr < w && j + pr < w; j++)
398-
n += scpy(out, param, &pad, 1);
399-
n += scpy(out, param, p, pr);
403+
n += scpy(fn, param, &pad, 1);
404+
n += scpy(fn, param, p, pr);
400405
for (j = 0; minus && pr < w && j + pr < w; j++)
401-
n += scpy(out, param, &pad, 1);
406+
n += scpy(fn, param, &pad, 1);
402407
} else if (c == '%') {
403-
out('%', param);
408+
fn('%', param);
404409
n++;
405410
} else {
406-
out('%', param);
407-
out(c, param);
411+
fn('%', param);
412+
fn(c, param);
408413
n += 2;
409414
}
410415
i++;
411416
} else {
412-
out(fmt[i], param), n++, i++;
417+
fn(fmt[i], param), n++, i++;
413418
}
414419
}
415420
return n;
@@ -644,6 +649,43 @@ long json_get_long(const char *buf, int len, const char *path, long dflt) {
644649
return dflt;
645650
}
646651

652+
static char xnibble(char c) {
653+
return c < 10 ? c + '0' : c + 'W';
654+
}
655+
656+
void xhexdump(void (*fn)(char, void *), void *a, const void *buf, size_t len) {
657+
const uint8_t *p = (const uint8_t *) buf;
658+
char ascii[16];
659+
size_t i, j, n = 0;
660+
for (i = 0; i < len; i++) {
661+
if ((i & 15) == 0) {
662+
// Print buffered ascii chars
663+
if (i > 0) {
664+
fn(' ', a), fn(' ', a);
665+
for (j = 0; j < sizeof(ascii); j++) fn(ascii[j], a);
666+
fn('\n', a), n = 0;
667+
}
668+
// Print hex address, then \t
669+
fn(xnibble((i >> 12) & 15), a), fn(xnibble((i >> 8) & 15), a);
670+
fn(xnibble((i >> 4) & 15), a), fn('0', a);
671+
fn(' ', a), fn(' ', a), fn(' ', a);
672+
}
673+
fn(xnibble(p[i] >> 4), a), fn(xnibble(p[i] & 15), a);
674+
fn(' ', a); // Space after hex number
675+
if (p[i] >= ' ' && p[i] <= '~') {
676+
ascii[n++] = (char) p[i]; // Printable
677+
} else {
678+
ascii[n++] = '.'; // Non-printable
679+
}
680+
}
681+
if (n > 0) {
682+
while (n < 16) fn(' ', a), fn(' ', a), fn(' ', a), ascii[n++] = ' ';
683+
fn(' ', a), fn(' ', a);
684+
for (j = 0; j < sizeof(ascii); j++) fn(ascii[j], a);
685+
}
686+
fn('\n', a);
687+
}
688+
647689
#endif // STR_API_ONLY
648690

649691
#ifdef __cplusplus

0 commit comments

Comments
 (0)