14#include "sw/device/lib/base/status.h"
28 kUnsignedHexLow =
'x',
29 kUnsignedHexHigh =
'X',
47OT_NONSTRING static const char kDigitsLow[16] =
"0123456789abcdef";
48OT_NONSTRING static const char kDigitsHigh[16] =
"0123456789ABCDEF";
50OT_NONSTRING static const char kErrorNul[17] =
"%<unexpected nul>";
51OT_NONSTRING static const char kUnknownSpec[15] =
"%<unknown spec>";
52OT_NONSTRING static const char kErrorTooWide[12] =
"%<bad width>";
54static size_t base_dev_null(
void *data,
const char *buf,
size_t len) {
65 if (out.sink == NULL) {
66 out.sink = &base_dev_null;
71size_t base_printf(
const char *format, ...) {
73 va_start(args, format);
74 size_t bytes_left = base_vprintf(format, args);
79size_t base_vprintf(
const char *format, va_list args) {
80 return base_vfprintf(base_stdout, format, args);
88static size_t snprintf_sink(
void *data,
const char *buf,
size_t len) {
90 if (captures->bytes_left == 0) {
94 if (len > captures->bytes_left) {
95 len = captures->bytes_left;
97 memcpy(captures->buf, buf, len);
99 captures->bytes_left -= len;
103size_t base_snprintf(
char *buf,
size_t len,
const char *format, ...) {
105 va_start(args, format);
113 .sink = &snprintf_sink,
115 size_t bytes_left = base_vfprintf(out, format, args);
122 va_start(args, format);
123 size_t bytes_left = base_vfprintf(out, format, args);
137static bool consume_until_percent(
buffer_sink_t out,
const char **format,
138 size_t *bytes_written) {
141 char c = (*format)[text_len];
142 if (c ==
'\0' || c == kPercent) {
144 *bytes_written += out.sink(out.data, *format, text_len);
172static bool consume_format_specifier(
buffer_sink_t out,
const char **format,
173 size_t *bytes_written,
174 format_specifier_t *spec) {
175 *spec = (format_specifier_t){0};
182 if ((*format)[0] ==
'!') {
183 spec->is_nonstd =
true;
195 char c = (*format)[spec_len];
197 *bytes_written += out.sink(out.data, kErrorNul,
sizeof(kErrorNul));
200 if (c <
'0' || c >
'9') {
203 if (spec->padding == 0) {
212 spec->width += (c -
'0');
216 if ((spec->width == 0 && spec->padding != 0) || spec->width > 32) {
217 *bytes_written += out.sink(out.data, kErrorTooWide,
sizeof(kErrorTooWide));
221 spec->type = (*format)[spec_len];
222 *format += spec_len + 1;
238static size_t write_digits(
buffer_sink_t out, uint32_t value, uint32_t width,
239 char padding, uint32_t base,
const char *glyphs) {
242 static const int kWordBits =
sizeof(uint32_t) * 8;
243 char buffer[kWordBits];
247 buffer[kWordBits - 1] = glyphs[0];
251 uint32_t digit = value % base;
253 buffer[kWordBits - 1 - len] = glyphs[digit];
256 width = width == 0 ? 1 : width;
257 width = width > kWordBits ? kWordBits : width;
258 while (len < width) {
259 buffer[kWordBits - len - 1] = padding;
262 return out.sink(out.data, buffer + (kWordBits - len), len);
265static size_t write_status(
buffer_sink_t out, status_t value,
bool as_json) {
267 char mod[] = {0, 0, 0};
270 bool err = status_extract(value, &start, &arg, mod);
273 const char *end = start;
278 len += out.sink(out.data,
"{\"", as_json ? 2 : 0);
279 len += out.sink(out.data, start, (
size_t)(end - start));
280 len += out.sink(out.data,
"\"", as_json ? 1 : 0);
282 len += out.sink(out.data,
":", 1);
285 len += out.sink(out.data,
"[\"", 2);
286 for (
size_t i = 0; i < 3; i++) {
287 if (mod[i] ==
'\\') {
288 len += out.sink(out.data,
"\\\\", 2);
290 len += out.sink(out.data, &mod[i], 1);
293 len += out.sink(out.data,
"\",", 2);
294 len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
295 len += out.sink(out.data,
"]", 1);
298 len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
300 len += out.sink(out.data,
"}", as_json ? 1 : 0);
318static size_t hex_dump(
buffer_sink_t out,
const char *bytes,
size_t len,
319 uint32_t width,
char padding,
bool big_endian,
320 const char *glyphs) {
321 size_t bytes_written = 0;
326 memset(buf, padding,
sizeof(buf));
328 size_t to_write = width >
ARRAYSIZE(buf) ? 32 : width;
329 bytes_written += out.sink(out.data, buf, to_write);
334 for (
size_t i = 0; i < len; ++i) {
335 size_t idx = big_endian ? len - i - 1 : i;
336 buf[buffered] = glyphs[(bytes[idx] >> 4) & 0xf];
337 buf[buffered + 1] = glyphs[bytes[idx] & 0xf];
341 bytes_written += out.sink(out.data, buf, buffered);
347 bytes_written += out.sink(out.data, buf, buffered);
349 return bytes_written;
363static void process_specifier(
buffer_sink_t out, format_specifier_t spec,
364 size_t *bytes_written, va_list *args) {
370 if (spec.is_nonstd) {
373 *bytes_written += out.sink(out.data,
"%", 1);
377 if (spec.is_nonstd) {
380 char value = (char)va_arg(*args, uint32_t);
381 *bytes_written += out.sink(out.data, &value, 1);
385 uint32_t value = va_arg(*args, uint32_t);
386 for (
size_t i = 0; i <
sizeof(uint32_t); ++i, value >>= 8) {
387 uint8_t ch = (uint8_t)value;
388 if (ch >= 32 && ch < 127) {
389 *bytes_written += out.sink(out.data, (
const char *)&ch, 1);
391 *bytes_written += out.sink(out.data,
"\\x", 2);
392 *bytes_written += out.sink(out.data, &kDigitsLow[ch >> 4], 1);
393 *bytes_written += out.sink(out.data, &kDigitsLow[ch & 15], 1);
400 if (spec.is_nonstd) {
402 len = va_arg(*args,
size_t);
405 char *value = va_arg(*args,
char *);
406 while (!spec.is_nonstd && value[len] !=
'\0') {
411 *bytes_written += out.sink(out.data, value, len);
416 if (spec.is_nonstd) {
419 uint32_t value = va_arg(*args, uint32_t);
420 if (((int32_t)value) < 0) {
421 *bytes_written += out.sink(out.data,
"-", 1);
425 write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
429 if (spec.is_nonstd) {
432 uint32_t value = va_arg(*args, uint32_t);
434 write_digits(out, value, spec.width, spec.padding, 8, kDigitsLow);
438 if (spec.is_nonstd) {
447 *bytes_written += out.sink(out.data,
"0x", 2);
448 uintptr_t value = va_arg(*args, uintptr_t);
450 write_digits(out, value,
sizeof(uintptr_t) * 2,
'0', 16, kDigitsLow);
453 case kUnsignedHexLow:
454 if (spec.is_nonstd) {
455 size_t len = va_arg(*args,
size_t);
456 char *value = va_arg(*args,
char *);
457 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
463 uint32_t value = va_arg(*args, uint32_t);
465 write_digits(out, value, spec.width, spec.padding, 16, kDigitsLow);
468 case kUnsignedHexHigh:
469 if (spec.is_nonstd) {
470 size_t len = va_arg(*args,
size_t);
471 char *value = va_arg(*args,
char *);
472 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
478 uint32_t value = va_arg(*args, uint32_t);
480 write_digits(out, value, spec.width, spec.padding, 16, kDigitsHigh);
484 if (!spec.is_nonstd) {
487 size_t len = va_arg(*args,
size_t);
488 char *value = va_arg(*args,
char *);
489 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
494 if (!spec.is_nonstd) {
497 size_t len = va_arg(*args,
size_t);
498 char *value = va_arg(*args,
char *);
499 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
504 if (spec.is_nonstd) {
507 uint32_t value = va_arg(*args, uint32_t);
509 write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
513 if (spec.is_nonstd) {
516 if (va_arg(*args,
int) != 0) {
517 *bytes_written += out.sink(out.data,
"true", 4);
519 *bytes_written += out.sink(out.data,
"false", 5);
523 uint32_t value = va_arg(*args, uint32_t);
525 write_digits(out, value, spec.width, spec.padding, 2, kDigitsLow);
528 case kStatusResult: {
529 status_t value = va_arg(*args, status_t);
530 *bytes_written += write_status(out, value, spec.is_nonstd);
535 *bytes_written += out.sink(out.data, kUnknownSpec,
sizeof(kUnknownSpec));
541 if (out.sink == NULL) {
542 out.sink = &base_dev_null;
550 va_copy(args_copy, args);
552 size_t bytes_written = 0;
553 while (format[0] !=
'\0') {
554 if (!consume_until_percent(out, &format, &bytes_written)) {
557 format_specifier_t spec;
558 if (!consume_format_specifier(out, &format, &bytes_written, &spec)) {
562 process_specifier(out, spec, &bytes_written, &args_copy);
566 return bytes_written;
572 "................................"
574 " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
576 "................................................................"
577 "................................................................"
584 .alphabet = &kBaseHexdumpDefaultFmtAlphabet,
587size_t base_hexdump(
const char *buf,
size_t len) {
588 return base_hexdump_with(kBaseHexdumpDefaultFmt, buf, len);
591size_t base_snhexdump(
char *out,
size_t out_len,
const char *buf,
size_t len) {
592 return base_snhexdump_with(out, out_len, kBaseHexdumpDefaultFmt, buf, len);
596 return base_fhexdump_with(out, kBaseHexdumpDefaultFmt, buf, len);
600 return base_fhexdump_with(base_stdout, fmt, buf, len);
604 const char *buf,
size_t len) {
607 .bytes_left = out_len,
611 .sink = &snprintf_sink,
613 return base_fhexdump_with(sink, fmt, buf, len);
617 const char *buf,
size_t len) {
618 if (out.sink == NULL) {
619 out.sink = &base_dev_null;
622 size_t bytes_written = 0;
625 for (
size_t line = 0; line < len; line += bytes_per_line) {
626 bytes_written += base_fprintf(out,
"%08x:", line);
629 size_t line_bytes_written = 0;
630 for (
size_t word = 0; word < bytes_per_line; word += fmt.
bytes_per_word) {
631 if (len < line + word) {
633 while (line_bytes_written < chars_per_line) {
634 size_t to_print = chars_per_line - line_bytes_written;
635 if (to_print >
sizeof(spaces)) {
636 to_print =
sizeof(spaces);
638 line_bytes_written += base_fprintf(out,
"%!s", to_print, spaces);
643 size_t bytes_left = len - line - word;
647 line_bytes_written += base_fprintf(out,
" ");
648 line_bytes_written +=
649 hex_dump(out, buf + line + word, bytes_left, bytes_left,
'0',
652 bytes_written += line_bytes_written;
654 bytes_written += base_fprintf(out,
" ");
656 char glyph_buffer[16];
657 for (
size_t byte = 0;
byte < bytes_per_line; ++byte) {
658 if (buffered ==
sizeof(glyph_buffer)) {
659 bytes_written += base_fprintf(out,
"%!s", buffered, glyph_buffer);
662 if (line +
byte >= len) {
665 glyph_buffer[buffered++] =
666 (*fmt.
alphabet)[(
size_t)(uint8_t)buf[line +
byte]];
669 bytes_written += base_fprintf(out,
"%!s", buffered, glyph_buffer);
671 bytes_written += base_fprintf(out,
"\n");
674 return bytes_written;