Software APIs
print.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
7 #include <stdarg.h>
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 
14 #include "sw/device/lib/base/status.h"
17 
18 #include "spi_device_regs.h" // Generated.
19 
20 // This is declared as an enum to force the values to be
21 // compile-time constants, but the type is otherwise not
22 // used for anything.
23 enum {
24  // Standard format specifiers.
25  kPercent = '%',
26  kCharacter = 'c',
27  kString = 's',
28  kSignedDec1 = 'd',
29  kSignedDec2 = 'i',
30  kUnsignedOct = 'o',
31  kUnsignedHexLow = 'x',
32  kUnsignedHexHigh = 'X',
33  kUnsignedDec = 'u',
34  kPointer = 'p',
35 
36  // Verilog-style format specifiers.
37  kSvBinary = 'b',
38  kSvHexLow = 'h',
39  kSvHexHigh = 'H',
40 
41  // Other non-standard specifiers.
42  kHexLeLow = 'y',
43  kHexLeHigh = 'Y',
44  kStatusResult = 'r',
45  kFourCC = 'C',
46 };
47 
48 // NOTE: all of the lengths of the strings below are given so that the NUL
49 // terminator is left off; that way, `sizeof(kConst)` does not include it.
50 static const char kDigitsLow[16] = "0123456789abcdef";
51 static const char kDigitsHigh[16] = "0123456789ABCDEF";
52 
53 static const char kErrorNul[17] = "%<unexpected nul>";
54 static const char kUnknownSpec[15] = "%<unknown spec>";
55 static const char kErrorTooWide[12] = "%<bad width>";
56 
57 static size_t base_dev_null(void *data, const char *buf, size_t len) {
58  return len;
59 }
60 static buffer_sink_t base_stdout = {
61  .data = NULL,
62  // Note: Using `&base_dev_null` causes this variable to be placed in the
63  // .data section and triggers the assertion in rom.ld.
64  .sink = NULL,
65 };
66 
68  if (out.sink == NULL) {
69  out.sink = &base_dev_null;
70  }
71  base_stdout = out;
72 }
73 
74 static const size_t kSpiDeviceReadBufferSizeBytes =
75  SPI_DEVICE_PARAM_SRAM_READ_BUFFER_DEPTH * sizeof(uint32_t);
76 static const size_t kSpiDeviceFrameHeaderSizeBytes = 12;
77 static const size_t kSpiDeviceBufferPreservedSizeBytes =
78  kSpiDeviceFrameHeaderSizeBytes;
79 static const size_t kSpiDeviceMaxFramePayloadSizeBytes =
80  kSpiDeviceReadBufferSizeBytes - kSpiDeviceFrameHeaderSizeBytes -
81  kSpiDeviceBufferPreservedSizeBytes - 4;
82 static const uint32_t kSpiDeviceFrameMagicNumber = 0xa5a5beef;
83 static uint32_t spi_device_frame_num = 0;
84 
85 static status_t spi_device_send_data(dif_spi_device_handle_t *spi_device,
86  const uint8_t *buf, size_t len,
87  size_t address) {
88  if (len == 0) {
89  return OK_STATUS();
90  }
91 
92  size_t space_to_end_of_buffer = kSpiDeviceReadBufferSizeBytes - address;
93  size_t first_part_size =
94  space_to_end_of_buffer < len ? space_to_end_of_buffer : len;
95 
96  TRY(dif_spi_device_write_flash_buffer(spi_device,
98  address, first_part_size, buf));
99 
100  // Handle wrap-around.
101  if (first_part_size < len) {
102  size_t second_part_size = len - first_part_size;
104  spi_device, kDifSpiDeviceFlashBufferTypeEFlash, 0, second_part_size,
105  (uint8_t *)(buf + first_part_size)));
106  }
107 
108  return OK_STATUS();
109 }
110 
111 /**
112  * Sends data out of the SPI device.
113  *
114  * Data is packaged into a frame that is described below.
115  * The host side reads the header first, then decides how many words
116  * to read from the data section.
117  *
118  * -----------------------------------------------
119  * | Magic Number | 4-bytes | |
120  * -----------------------------------| |
121  * | Frame Number | 4-bytes | Header |
122  * -----------------------------------| |
123  * | Data Length (bytes) | 4-bytes | |
124  * -----------------------------------|----------|
125  * | Data (word aligned) | |
126  * -----------------------------------| Data |
127  * | 0xFF Pad Bytes | <4-bytes | |
128  * -----------------------------------|----------|
129  */
130 static size_t spi_device_send_frame(void *data, const char *buf, size_t len) {
131  dif_spi_device_handle_t *spi_device = (dif_spi_device_handle_t *)data;
132  const size_t data_packet_size_bytes = ((len + 3u) & ~3u);
133  const size_t frame_size_bytes =
134  kSpiDeviceFrameHeaderSizeBytes + data_packet_size_bytes;
135  uint8_t frame_header_bytes[kSpiDeviceFrameHeaderSizeBytes];
136 
137  static uint32_t next_write_address = 0;
138 
139  if (frame_size_bytes >= kSpiDeviceReadBufferSizeBytes) {
140  return 0;
141  }
142 
143  // Add the magic bytes.
144  for (size_t i = 0; i < 4; ++i) {
145  frame_header_bytes[i] = (kSpiDeviceFrameMagicNumber >> (i * 8)) & 0xff;
146  }
147  // Add the frame number.
148  for (size_t i = 0; i < 4; ++i) {
149  frame_header_bytes[i + 4] = (spi_device_frame_num >> (i * 8)) & 0xff;
150  }
151  // Add the data length.
152  for (size_t i = 0; i < 4; ++i) {
153  frame_header_bytes[i + 8] = (len >> (i * 8)) & 0xff;
154  }
155 
156  uint32_t available_buffer_size = 0;
157  do {
158  uint32_t last_read_address = 0;
159  if (dif_spi_device_get_last_read_address(spi_device, &last_read_address) !=
160  kDifOk) {
161  return 0;
162  }
163 
164  // Adjust the last read address. The host continuously reads from the read
165  // buffer, unaware of whether a new frame has arrived. This could result in
166  // the reported last_read_address being the header size ahead of the actual
167  // address of the last valid frame if all the frames in the read buffer has
168  // been consumed by the host. While it's harmless to adjust the last read
169  // address even if the reported value is correct, doing so might temporarily
170  // underestimate the available buffer size by the size of a header.
171  uint32_t adjusted_last_read_address =
172  (kSpiDeviceReadBufferSizeBytes + last_read_address -
173  kSpiDeviceFrameHeaderSizeBytes) %
174  kSpiDeviceReadBufferSizeBytes;
175  uint32_t next_read_address = ((adjusted_last_read_address + 1) & ~3u) %
176  kSpiDeviceReadBufferSizeBytes;
177 
178  if (next_read_address > next_write_address) {
179  available_buffer_size = next_read_address - next_write_address - 1;
180  } else {
181  available_buffer_size =
182  next_read_address +
183  (kSpiDeviceReadBufferSizeBytes - next_write_address) - 1;
184  }
185  } while ((frame_size_bytes + kSpiDeviceBufferPreservedSizeBytes) >
186  available_buffer_size);
187 
188  // Send aligned data.
189  size_t data_write_address =
190  (next_write_address + kSpiDeviceFrameHeaderSizeBytes) %
191  kSpiDeviceReadBufferSizeBytes;
192  size_t aligned_data_len = len & (~3u);
193  if (!status_ok(spi_device_send_data(spi_device, (uint8_t *)buf,
194  aligned_data_len, data_write_address))) {
195  return 0;
196  }
197 
198  // Send unaligned data.
199  if (len != aligned_data_len) {
200  uint8_t pad_bytes[4] = {0xff, 0xff, 0xff, 0xff};
201  size_t pad_write_address =
202  (data_write_address + aligned_data_len) % kSpiDeviceReadBufferSizeBytes;
203 
204  for (size_t i = 0; i + aligned_data_len < len; i++) {
205  pad_bytes[i] = buf[aligned_data_len + i];
206  }
207  if (!status_ok(spi_device_send_data(spi_device, pad_bytes, 4,
208  pad_write_address))) {
209  return 0;
210  }
211  }
212 
213  // Send frame header.
214  if (!status_ok(spi_device_send_data(spi_device, frame_header_bytes,
215  kSpiDeviceFrameHeaderSizeBytes,
216  next_write_address))) {
217  return 0;
218  }
219 
220  next_write_address =
221  (next_write_address + frame_size_bytes) % kSpiDeviceReadBufferSizeBytes;
222  spi_device_frame_num++;
223 
224  return len;
225 }
226 
227 static size_t base_dev_spi_device(void *data, const char *buf, size_t len) {
228  size_t write_data_len = 0;
229 
230  while (write_data_len < len) {
231  size_t payload_len = len - write_data_len;
232  if (payload_len > kSpiDeviceMaxFramePayloadSizeBytes) {
233  payload_len = kSpiDeviceMaxFramePayloadSizeBytes;
234  }
235 
236  if (spi_device_send_frame(data, buf + write_data_len, payload_len) ==
237  payload_len) {
238  write_data_len += payload_len;
239  }
240  }
241 
242  return write_data_len;
243 }
244 
245 sink_func_ptr get_spi_device_sink(void) { return &base_dev_spi_device; }
246 
247 static size_t base_dev_uart(void *data, const char *buf, size_t len) {
248  const dif_uart_t *uart = (const dif_uart_t *)data;
249  for (size_t i = 0; i < len; ++i) {
250  if (dif_uart_byte_send_polled(uart, (uint8_t)buf[i]) != kDifOk) {
251  return i;
252  }
253  }
254  return len;
255 }
256 
257 sink_func_ptr get_uart_sink(void) { return &base_dev_uart; }
258 
260  // Reset the frame counter.
261  spi_device_frame_num = 0;
262  base_set_stdout((buffer_sink_t){.data = (void *)spi_device,
263  .sink = &base_dev_spi_device});
264 }
265 
266 void base_uart_stdout(const dif_uart_t *uart) {
268  (buffer_sink_t){.data = (void *)uart, .sink = &base_dev_uart});
269 }
270 
271 size_t base_printf(const char *format, ...) {
272  va_list args;
273  va_start(args, format);
274  size_t bytes_left = base_vprintf(format, args);
275  va_end(args);
276  return bytes_left;
277 }
278 
279 size_t base_vprintf(const char *format, va_list args) {
280  return base_vfprintf(base_stdout, format, args);
281 }
282 
283 typedef struct snprintf_captures_t {
284  char *buf;
285  size_t bytes_left;
287 
288 static size_t snprintf_sink(void *data, const char *buf, size_t len) {
289  snprintf_captures_t *captures = (snprintf_captures_t *)data;
290  if (captures->bytes_left == 0) {
291  return 0;
292  }
293 
294  if (len > captures->bytes_left) {
295  len = captures->bytes_left;
296  }
297  memcpy(captures->buf, buf, len);
298  captures->buf += len;
299  captures->bytes_left -= len;
300  return len;
301 }
302 
303 size_t base_snprintf(char *buf, size_t len, const char *format, ...) {
304  va_list args;
305  va_start(args, format);
306 
307  snprintf_captures_t data = {
308  .buf = buf,
309  .bytes_left = len,
310  };
311  buffer_sink_t out = {
312  .data = &data,
313  .sink = &snprintf_sink,
314  };
315  size_t bytes_left = base_vfprintf(out, format, args);
316  va_end(args);
317  return bytes_left;
318 }
319 
320 size_t base_fprintf(buffer_sink_t out, const char *format, ...) {
321  va_list args;
322  va_start(args, format);
323  size_t bytes_left = base_vfprintf(out, format, args);
324  va_end(args);
325  return bytes_left;
326 }
327 
328 /**
329  * Consumes characters from `format` until a '%' or NUL is reached. All
330  * characters seen before that are then sinked into `out`.
331  *
332  * @param out the sink to write bytes to.
333  * @param format a pointer to the format string to consume a prefix of.
334  * @param[out] bytes_written out param for the number of bytes writen to `out`.
335  * @return true if an unprocessed '%' was found.
336  */
337 static bool consume_until_percent(buffer_sink_t out, const char **format,
338  size_t *bytes_written) {
339  size_t text_len = 0;
340  while (true) {
341  char c = (*format)[text_len];
342  if (c == '\0' || c == kPercent) {
343  if (text_len > 0) {
344  *bytes_written += out.sink(out.data, *format, text_len);
345  }
346  *format += text_len;
347  return c != '\0';
348  }
349  ++text_len;
350  }
351 }
352 
353 /**
354  * Represents a parsed format specifier.
355  */
356 typedef struct format_specifier {
357  char type;
358  size_t width;
359  char padding;
360  bool is_nonstd;
362 
363 /**
364  * Consumes characters from `format` until a complete format specifier is
365  * parsed. See the documentation in `print.h` for full syntax.
366  *
367  * @param out the sink to write bytes to.
368  * @param format a pointer to the format string to consume a prefix of.
369  * @param[out] spec out param for the specifier.
370  * @return whether the parse succeeded.
371  */
372 static bool consume_format_specifier(buffer_sink_t out, const char **format,
373  size_t *bytes_written,
374  format_specifier_t *spec) {
375  *spec = (format_specifier_t){0};
376 
377  // Consume the percent sign.
378  ++(*format);
379 
380  // Parse a ! to detect an OpenTitan-specific extension (that isn't one
381  // of the Verilog ones...).
382  if ((*format)[0] == '!') {
383  spec->is_nonstd = true;
384  ++(*format);
385  }
386 
387  // Attempt to parse out an unsigned, decimal number, a "width",
388  // after the percent sign; the format specifier is the character
389  // immediately after this width.
390  //
391  // `spec->padding` is used to indicate whether we've seen a width;
392  // if nonzero, we have an actual width.
393  size_t spec_len = 0;
394  while (true) {
395  char c = (*format)[spec_len];
396  if (c == '\0') {
397  *bytes_written += out.sink(out.data, kErrorNul, sizeof(kErrorNul));
398  return false;
399  }
400  if (c < '0' || c > '9') {
401  break;
402  }
403  if (spec->padding == 0) {
404  if (c == '0') {
405  spec->padding = '0';
406  ++spec_len;
407  continue;
408  }
409  spec->padding = ' ';
410  }
411  spec->width *= 10;
412  spec->width += (c - '0');
413  ++spec_len;
414  }
415 
416  if ((spec->width == 0 && spec->padding != 0) || spec->width > 32) {
417  *bytes_written += out.sink(out.data, kErrorTooWide, sizeof(kErrorTooWide));
418  return false;
419  }
420 
421  spec->type = (*format)[spec_len];
422  *format += spec_len + 1;
423  return true;
424 }
425 
426 /**
427  * Write the digits of `value` onto `out`.
428  *
429  * @param out the sink to write bytes to.
430  * @param value the value to "stringify".
431  * @param width the minimum width to print; going below will result in writing
432  * out zeroes.
433  * @param base the base to express `value` in.
434  * @param glyphs an array of characters to use as the digits of a number, which
435  * should be at least ast long as `base`.
436  * @return the number of bytes written.
437  */
438 static size_t write_digits(buffer_sink_t out, uint32_t value, uint32_t width,
439  char padding, uint32_t base, const char *glyphs) {
440  // All allocations are done relative to a buffer that could hold the longest
441  // textual representation of a number: ~0x0 in base 2, i.e., 32 ones.
442  static const int kWordBits = sizeof(uint32_t) * 8;
443  char buffer[kWordBits];
444 
445  size_t len = 0;
446  if (value == 0) {
447  buffer[kWordBits - 1] = glyphs[0];
448  ++len;
449  }
450  while (value > 0) {
451  uint32_t digit = value % base;
452  value /= base;
453  buffer[kWordBits - 1 - len] = glyphs[digit];
454  ++len;
455  }
456  width = width == 0 ? 1 : width;
457  width = width > kWordBits ? kWordBits : width;
458  while (len < width) {
459  buffer[kWordBits - len - 1] = padding;
460  ++len;
461  }
462  return out.sink(out.data, buffer + (kWordBits - len), len);
463 }
464 
465 static size_t write_status(buffer_sink_t out, status_t value, bool as_json) {
466  // The module id is defined to be 3 chars long.
467  char mod[] = {'"', 0, 0, 0, '"', ','};
468  int32_t arg;
469  const char *start;
470  bool err = status_extract(value, &start, &arg, &mod[1]);
471 
472  // strlen of the status code.
473  const char *end = start;
474  while (*end)
475  end++;
476  size_t len = 0;
477 
478  len += out.sink(out.data, "{\"", as_json ? 2 : 0);
479  len += out.sink(out.data, start, (size_t)(end - start));
480  len += out.sink(out.data, "\"", as_json ? 1 : 0);
481 
482  len += out.sink(out.data, ":", 1);
483  if (err) {
484  // All error codes include the module identifier.
485  len += out.sink(out.data, "[", 1);
486  len += out.sink(out.data, mod, sizeof(mod));
487  len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
488  len += out.sink(out.data, "]", 1);
489  } else {
490  // Ok codes include only the arg
491  len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
492  }
493  len += out.sink(out.data, "}", as_json ? 1 : 0);
494  return len;
495 }
496 
497 /**
498  * Hexdumps `bytes` onto `out`.
499  *
500  * @param out the sink to write bytes to.
501  * @param bytes the bytes to dump.
502  * @param len the number of bytes to dump.
503  * @param width the minimum width to print; going below will result in writing
504  * out zeroes.
505  * @param padding the character to use for padding.
506  * @param big_endian whether to print in big-endian order (i.e. as %x would).
507  * @param glyphs an array of characters to use as the digits of a number, which
508  * should be at least ast long as `base`.
509  * @return the number of bytes written.
510  */
511 static size_t hex_dump(buffer_sink_t out, const char *bytes, size_t len,
512  uint32_t width, char padding, bool big_endian,
513  const char *glyphs) {
514  size_t bytes_written = 0;
515  char buf[32];
516  size_t buffered = 0;
517  if (len < width) {
518  width -= len;
519  memset(buf, padding, sizeof(buf));
520  while (width > 0) {
521  size_t to_write = width > ARRAYSIZE(buf) ? 32 : width;
522  bytes_written += out.sink(out.data, buf, to_write);
523  width -= to_write;
524  }
525  }
526 
527  for (size_t i = 0; i < len; ++i) {
528  size_t idx = big_endian ? len - i - 1 : i;
529  buf[buffered] = glyphs[(bytes[idx] >> 4) & 0xf];
530  buf[buffered + 1] = glyphs[bytes[idx] & 0xf];
531  buffered += 2;
532 
533  if (buffered == ARRAYSIZE(buf)) {
534  bytes_written += out.sink(out.data, buf, buffered);
535  buffered = 0;
536  }
537  }
538 
539  if (buffered != 0) {
540  bytes_written += out.sink(out.data, buf, buffered);
541  }
542  return bytes_written;
543 }
544 
545 /**
546  * Prints out the next entry in `args` according to `spec`.
547  *
548  * This function assumes that `spec` accurately describes the next entry in
549  * `args`.
550  *
551  * @param out the sink to write bytes to.
552  * @param spec the specifier to use for stringifying.
553  * @param[out] bytes_written out param for the number of bytes writen to `out`.
554  * @param args the list to pull an entry from.
555  */
556 static void process_specifier(buffer_sink_t out, format_specifier_t spec,
557  size_t *bytes_written, va_list *args) {
558  // Switch on the specifier. At this point, we assert that there is
559  // an initialized value of correct type in the VA list; if it is
560  // missing, the caller has caused UB.
561  switch (spec.type) {
562  case kPercent: {
563  if (spec.is_nonstd) {
564  goto bad_spec;
565  }
566  *bytes_written += out.sink(out.data, "%", 1);
567  break;
568  }
569  case kCharacter: {
570  if (spec.is_nonstd) {
571  goto bad_spec;
572  }
573  char value = (char)va_arg(*args, uint32_t);
574  *bytes_written += out.sink(out.data, &value, 1);
575  break;
576  }
577  case kFourCC: {
578  uint32_t value = va_arg(*args, uint32_t);
579  for (size_t i = 0; i < sizeof(uint32_t); ++i, value >>= 8) {
580  uint8_t ch = (uint8_t)value;
581  if (ch >= 32 && ch < 127) {
582  *bytes_written += out.sink(out.data, (const char *)&ch, 1);
583  } else {
584  *bytes_written += out.sink(out.data, "\\x", 2);
585  *bytes_written += out.sink(out.data, &kDigitsLow[ch >> 4], 1);
586  *bytes_written += out.sink(out.data, &kDigitsLow[ch & 15], 1);
587  }
588  }
589  break;
590  }
591  case kString: {
592  size_t len = 0;
593  if (spec.is_nonstd) {
594  // This implements %!s.
595  len = va_arg(*args, size_t);
596  }
597 
598  char *value = va_arg(*args, char *);
599  while (!spec.is_nonstd && value[len] != '\0') {
600  // This implements %s.
601  ++len;
602  }
603 
604  *bytes_written += out.sink(out.data, value, len);
605  break;
606  }
607  case kSignedDec1:
608  case kSignedDec2: {
609  if (spec.is_nonstd) {
610  goto bad_spec;
611  }
612  uint32_t value = va_arg(*args, uint32_t);
613  if (((int32_t)value) < 0) {
614  *bytes_written += out.sink(out.data, "-", 1);
615  value = -value;
616  }
617  *bytes_written +=
618  write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
619  break;
620  }
621  case kUnsignedOct: {
622  if (spec.is_nonstd) {
623  goto bad_spec;
624  }
625  uint32_t value = va_arg(*args, uint32_t);
626  *bytes_written +=
627  write_digits(out, value, spec.width, spec.padding, 8, kDigitsLow);
628  break;
629  }
630  case kPointer: {
631  if (spec.is_nonstd) {
632  goto bad_spec;
633  }
634  // Pointers are formatted as 0x<hex digits>, where the width is always
635  // set to the number necessary to represent a pointer on the current
636  // platform, that is, the size of uintptr_t in nybbles. For example, on
637  // different architecutres the null pointer prints as
638  // - rv32imc: 0x00000000 (four bytes, eight nybbles).
639  // - amd64: 0x0000000000000000 (eight bytes, sixteen nybbles).
640  *bytes_written += out.sink(out.data, "0x", 2);
641  uintptr_t value = va_arg(*args, uintptr_t);
642  *bytes_written +=
643  write_digits(out, value, sizeof(uintptr_t) * 2, '0', 16, kDigitsLow);
644  break;
645  }
646  case kUnsignedHexLow:
647  if (spec.is_nonstd) {
648  size_t len = va_arg(*args, size_t);
649  char *value = va_arg(*args, char *);
650  *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
651  /*big_endian=*/true, kDigitsLow);
652  break;
653  }
655  case kSvHexLow: {
656  uint32_t value = va_arg(*args, uint32_t);
657  *bytes_written +=
658  write_digits(out, value, spec.width, spec.padding, 16, kDigitsLow);
659  break;
660  }
661  case kUnsignedHexHigh:
662  if (spec.is_nonstd) {
663  size_t len = va_arg(*args, size_t);
664  char *value = va_arg(*args, char *);
665  *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
666  /*big_endian=*/true, kDigitsHigh);
667  break;
668  }
670  case kSvHexHigh: {
671  uint32_t value = va_arg(*args, uint32_t);
672  *bytes_written +=
673  write_digits(out, value, spec.width, spec.padding, 16, kDigitsHigh);
674  break;
675  }
676  case kHexLeLow: {
677  if (!spec.is_nonstd) {
678  goto bad_spec;
679  }
680  size_t len = va_arg(*args, size_t);
681  char *value = va_arg(*args, char *);
682  *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
683  /*big_endian=*/false, kDigitsLow);
684  break;
685  }
686  case kHexLeHigh: {
687  if (!spec.is_nonstd) {
688  goto bad_spec;
689  }
690  size_t len = va_arg(*args, size_t);
691  char *value = va_arg(*args, char *);
692  *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
693  /*big_endian=*/false, kDigitsHigh);
694  break;
695  }
696  case kUnsignedDec: {
697  if (spec.is_nonstd) {
698  goto bad_spec;
699  }
700  uint32_t value = va_arg(*args, uint32_t);
701  *bytes_written +=
702  write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
703  break;
704  }
705  case kSvBinary: {
706  if (spec.is_nonstd) {
707  // Bools passed into a ... function will be automatically promoted
708  // to int; va_arg(..., bool) is actually UB!
709  if (va_arg(*args, int) != 0) {
710  *bytes_written += out.sink(out.data, "true", 4);
711  } else {
712  *bytes_written += out.sink(out.data, "false", 5);
713  }
714  break;
715  }
716  uint32_t value = va_arg(*args, uint32_t);
717  *bytes_written +=
718  write_digits(out, value, spec.width, spec.padding, 2, kDigitsLow);
719  break;
720  }
721  case kStatusResult: {
722  status_t value = va_arg(*args, status_t);
723  *bytes_written += write_status(out, value, spec.is_nonstd);
724  break;
725  }
726  bad_spec: // Used with `goto` to bail out early.
727  default: {
728  *bytes_written += out.sink(out.data, kUnknownSpec, sizeof(kUnknownSpec));
729  }
730  }
731 }
732 
733 size_t base_vfprintf(buffer_sink_t out, const char *format, va_list args) {
734  if (out.sink == NULL) {
735  out.sink = &base_dev_null;
736  }
737 
738  // NOTE: This copy is necessary on amd64 and other platforms, where
739  // `va_list` is a fixed array type (and, as such, decays to a pointer in
740  // an argument list). On PSABI RV32IMC, however, `va_list` is a `void*`, so
741  // this is a copy of the pointer, not the array.
742  va_list args_copy;
743  va_copy(args_copy, args);
744 
745  size_t bytes_written = 0;
746  while (format[0] != '\0') {
747  if (!consume_until_percent(out, &format, &bytes_written)) {
748  break;
749  }
750  format_specifier_t spec;
751  if (!consume_format_specifier(out, &format, &bytes_written, &spec)) {
752  break;
753  }
754 
755  process_specifier(out, spec, &bytes_written, &args_copy);
756  }
757 
758  va_end(args_copy);
759  return bytes_written;
760 }
761 
763  // clang-format off
764  // First 32 characters are not printable.
765  "................................"
766  // Printable ASCII.
767  " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
768  // The rest of the range is also not printable (129 characters).
769  "................................................................"
770  "................................................................"
771  ".";
772 // clang-format on
773 
774 static const base_hexdump_fmt_t kBaseHexdumpDefaultFmt = {
775  .bytes_per_word = 2,
776  .words_per_line = 8,
777  .alphabet = &kBaseHexdumpDefaultFmtAlphabet,
778 };
779 
780 size_t base_hexdump(const char *buf, size_t len) {
781  return base_hexdump_with(kBaseHexdumpDefaultFmt, buf, len);
782 }
783 
784 size_t base_snhexdump(char *out, size_t out_len, const char *buf, size_t len) {
785  return base_snhexdump_with(out, out_len, kBaseHexdumpDefaultFmt, buf, len);
786 }
787 
788 size_t base_fhexdump(buffer_sink_t out, const char *buf, size_t len) {
789  return base_fhexdump_with(out, kBaseHexdumpDefaultFmt, buf, len);
790 }
791 
792 size_t base_hexdump_with(base_hexdump_fmt_t fmt, const char *buf, size_t len) {
793  return base_fhexdump_with(base_stdout, fmt, buf, len);
794 }
795 
796 size_t base_snhexdump_with(char *out, size_t out_len, base_hexdump_fmt_t fmt,
797  const char *buf, size_t len) {
798  snprintf_captures_t data = {
799  .buf = out,
800  .bytes_left = out_len,
801  };
802  buffer_sink_t sink = {
803  .data = &data,
804  .sink = &snprintf_sink,
805  };
806  return base_fhexdump_with(sink, fmt, buf, len);
807 }
808 
810  const char *buf, size_t len) {
811  if (out.sink == NULL) {
812  out.sink = &base_dev_null;
813  }
814 
815  size_t bytes_written = 0;
816  size_t bytes_per_line = fmt.bytes_per_word * fmt.words_per_line;
817 
818  for (size_t line = 0; line < len; line += bytes_per_line) {
819  bytes_written += base_fprintf(out, "%08x:", line);
820 
821  size_t chars_per_line = bytes_per_line * 2 + fmt.words_per_line;
822  size_t line_bytes_written = 0;
823  for (size_t word = 0; word < bytes_per_line; word += fmt.bytes_per_word) {
824  if (len < line + word) {
825  char spaces[16] = " ";
826  while (line_bytes_written < chars_per_line) {
827  size_t to_print = chars_per_line - line_bytes_written;
828  if (to_print > sizeof(spaces)) {
829  to_print = sizeof(spaces);
830  }
831  line_bytes_written += base_fprintf(out, "%!s", to_print, spaces);
832  }
833  break;
834  }
835 
836  size_t bytes_left = len - line - word;
837  if (bytes_left > fmt.bytes_per_word) {
838  bytes_left = fmt.bytes_per_word;
839  }
840  line_bytes_written += base_fprintf(out, " ");
841  line_bytes_written +=
842  hex_dump(out, buf + line + word, bytes_left, bytes_left, '0',
843  /*big_endian=*/false, kDigitsLow);
844  }
845  bytes_written += line_bytes_written;
846 
847  bytes_written += base_fprintf(out, " ");
848  size_t buffered = 0;
849  char glyph_buffer[16];
850  for (size_t byte = 0; byte < bytes_per_line; ++byte) {
851  if (buffered == sizeof(glyph_buffer)) {
852  bytes_written += base_fprintf(out, "%!s", buffered, glyph_buffer);
853  buffered = 0;
854  }
855  if (line + byte >= len) {
856  break;
857  }
858  glyph_buffer[buffered++] =
859  (*fmt.alphabet)[(size_t)(uint8_t)buf[line + byte]];
860  }
861  if (buffered > 0) {
862  bytes_written += base_fprintf(out, "%!s", buffered, glyph_buffer);
863  }
864  bytes_written += base_fprintf(out, "\n");
865  }
866 
867  return bytes_written;
868 }