|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Bounded printf-subset formatter used by the esp-hosted port logger. More...
#include <stdarg.h>#include <stddef.h>#include <stdint.h>#include "ra8_attributes.h"#include "ra8_esp_hosted_fmt_internal.h"Go to the source code of this file.
Data Structures | |
| struct | ra8_esp_hosted_fmt_args |
| Single-member holder that names this unit's argument list once. More... | |
| struct | ra8_esp_hosted_fmt_cursor |
| Write cursor over the caller's output buffer. More... | |
Typedefs | |
| typedef struct ra8_esp_hosted_fmt_args | ra8_esp_hosted_fmt_args_t |
| typedef struct ra8_esp_hosted_fmt_cursor | ra8_esp_hosted_fmt_cursor_t |
Enumerations | |
| enum | ra8_esp_hosted_fmt_char_t : char { k_ra8_esp_hosted_fmt_ch_nul = '\0' , k_ra8_esp_hosted_fmt_ch_percent = '' , k_ra8_esp_hosted_fmt_ch_zero = '0' , k_ra8_esp_hosted_fmt_ch_nine = '9' , k_ra8_esp_hosted_fmt_ch_minus = '-' , k_ra8_esp_hosted_fmt_ch_space = ' ' , k_ra8_esp_hosted_fmt_ch_ell = 'l' , k_ra8_esp_hosted_fmt_ch_zed = 'z' } |
| Characters the parser and emitter test for by name. More... | |
| enum | ra8_esp_hosted_fmt_radix_t : uint8_t { k_ra8_esp_hosted_fmt_radix_min = 2U , k_ra8_esp_hosted_fmt_radix_dec = 10U , k_ra8_esp_hosted_fmt_radix_hex = 16U } |
| Radices the formatter renders. More... | |
Functions | |
| static void | internal_put (ra8_esp_hosted_fmt_cursor_t *cur, char ch) |
| Append one character if the buffer still has room for it. | |
| static void | internal_pad (ra8_esp_hosted_fmt_cursor_t *cur, char pad, uint16_t count) |
| Append the pad character a bounded number of times. | |
| uint8_t | priv_ra8_esp_hosted_fmt_utoa (char *buf, uint64_t value, uint8_t base, bool upper) |
| Implementation of priv_ra8_esp_hosted_fmt_utoa() – divides down and reverses in place, so no scratch beyond the caller's buffer. | |
| static uint8_t | internal_parse_flags (const char *text, ra8_esp_hosted_fmt_spec_t *out) |
| Recognise and record the flag characters of a specification. | |
| static uint8_t | internal_parse_width (const char *text, uint16_t *out_width) |
| Read the optional decimal field width of a specification. | |
| static uint8_t | internal_parse_len (const char *text, ra8_esp_hosted_fmt_len_t *out_len) |
| Read the optional length modifier of a specification. | |
| static bool | internal_is_supported (char conv) |
| Whether a character is a conversion this formatter emits. | |
| bool | priv_ra8_esp_hosted_fmt_parse (const char *after_percent, ra8_esp_hosted_fmt_spec_t *out) |
| Implementation of priv_ra8_esp_hosted_fmt_parse() – flags, width, length modifier and conversion, each bounded by the specification length cap. | |
| static uint64_t | internal_next_unsigned (ra8_esp_hosted_fmt_args_t *args, ra8_esp_hosted_fmt_len_t len) |
| Pull the next unsigned argument at the width the modifier names. | |
| static int64_t | internal_next_signed (ra8_esp_hosted_fmt_args_t *args, ra8_esp_hosted_fmt_len_t len) |
| Pull the next signed argument at the width the modifier names. | |
| static void | internal_emit_token (ra8_esp_hosted_fmt_cursor_t *cur, const ra8_esp_hosted_fmt_spec_t *spec, const char *token, uint16_t token_len) |
| Emit an already-rendered token honouring width and justification. | |
| static uint16_t | internal_bounded_len (const char *text) |
| Measure a NUL-terminated string, bounded by the width cap. | |
| static void | internal_emit_conv (ra8_esp_hosted_fmt_cursor_t *cur, const ra8_esp_hosted_fmt_spec_t *spec, ra8_esp_hosted_fmt_args_t *args) |
| Expand one parsed conversion into the cursor. | |
| uint32_t | priv_ra8_esp_hosted_fmt_vformat (char *out, uint32_t cap, const char *fmt, va_list ap) |
| Implementation of priv_ra8_esp_hosted_fmt_vformat() – single pass, every loop bounded, no allocation. | |
Variables | |
| static const char | s_ra8_esp_hosted_fmt_digits_lower [] = "0123456789abcdef" |
| Digit glyphs for lower-case hexadecimal and every smaller base. | |
| static const char | s_ra8_esp_hosted_fmt_digits_upper [] = "0123456789ABCDEF" |
| Digit glyphs for upper-case hexadecimal. | |
Bounded printf-subset formatter used by the esp-hosted port logger.
Implementation of the contract in ra8_esp_hosted_fmt_internal.h. The design follows from two constraints that pull in the same direction: the image has no heap, and every loop must have a statically provable bound. So the formatter never allocates, writes only into the caller's buffer, and bounds each of its three loops – specification parsing, digit generation and padding – by a compile-time constant from ra8_esp_hosted_fmt_bound_t.
Emission goes through a small cursor structure rather than a running pointer so the "one byte reserved for the terminator" rule is expressed once, in internal_put, instead of at every call site. The formatter's validated entry point constructs the cursor, so the emitter does not carry unreachable duplicate pointer guards.
Definition in file ra8_esp_hosted_fmt.c.
| typedef struct ra8_esp_hosted_fmt_args ra8_esp_hosted_fmt_args_t |
| typedef struct ra8_esp_hosted_fmt_cursor ra8_esp_hosted_fmt_cursor_t |
| enum ra8_esp_hosted_fmt_char_t : char |
Characters the parser and emitter test for by name.
Named so the parsing logic reads as grammar rather than as arithmetic on character codes, and so the project's no-magic-number rule is satisfied without scattering casts through the control flow.
Definition at line 54 of file ra8_esp_hosted_fmt.c.
| enum ra8_esp_hosted_fmt_radix_t : uint8_t |
Radices the formatter renders.
Only the two the vendored core's format strings ask for. Any other base is rejected by priv_ra8_esp_hosted_fmt_utoa rather than silently rendered, because a base the caller did not intend would produce a plausible but wrong number.
Definition at line 86 of file ra8_esp_hosted_fmt.c.
|
static |
Measure a NUL-terminated string, bounded by the width cap.
A bounded measurement rather than strlen so a co-processor-supplied string that lost its terminator cannot run off the end of memory. A string longer than the cap is reported as exactly the cap and is therefore truncated in the output, which is visible rather than fatal.
| [in] | text | String to measure. Must be non-null. |
| 0 | The string was empty. |
Definition at line 599 of file ra8_esp_hosted_fmt.c.
References k_ra8_esp_hosted_fmt_ch_nul, and k_ra8_esp_hosted_fmt_width_max.
Referenced by internal_emit_conv().
|
static |
Expand one parsed conversion into the cursor.
Dispatches on the conversion character, pulls exactly one argument for the conversions that take one, and hands the rendered token to internal_emit_token so padding is applied uniformly.
| [in,out] | cur | Cursor to append through. |
| [in] | spec | Parsed conversion specification. |
| [in,out] | args | Copied argument list to advance. |
Definition at line 635 of file ra8_esp_hosted_fmt.c.
References ra8_esp_hosted_fmt_args::ap, ra8_esp_hosted_fmt_spec::conv, internal_bounded_len(), internal_emit_token(), internal_next_signed(), internal_next_unsigned(), internal_put(), k_ra8_esp_hosted_fmt_ch_minus, k_ra8_esp_hosted_fmt_ch_percent, k_ra8_esp_hosted_fmt_digits_max, k_ra8_esp_hosted_fmt_radix_dec, k_ra8_esp_hosted_fmt_radix_hex, ra8_esp_hosted_fmt_spec::len, and priv_ra8_esp_hosted_fmt_utoa().
Referenced by priv_ra8_esp_hosted_fmt_vformat().
|
static |
Emit an already-rendered token honouring width and justification.
The single place the padding rules live, so every conversion pads the same way. Zero padding only applies when the token is right-justified; a left-justified field pads with spaces regardless, matching C's own rule.
| [in,out] | cur | Cursor to append through. |
| [in] | spec | Specification supplying width and flags. |
| [in] | token | NUL-terminated text to emit. |
| [in] | token_len | Length of token in characters. |
Definition at line 556 of file ra8_esp_hosted_fmt.c.
References internal_pad(), internal_put(), k_ra8_esp_hosted_fmt_ch_space, k_ra8_esp_hosted_fmt_ch_zero, ra8_esp_hosted_fmt_spec::left_justify, ra8_esp_hosted_fmt_spec::width, and ra8_esp_hosted_fmt_spec::zero_pad.
Referenced by internal_emit_conv().
|
static |
Whether a character is a conversion this formatter emits.
The set is closed deliberately: a conversion outside it is copied through verbatim by the driver instead of consuming an argument, which keeps every later argument aligned with its conversion.
| [in] | conv | Candidate conversion character. |
| true | The driver will expand it. |
| false | The driver will copy the specification through verbatim. |
Definition at line 408 of file ra8_esp_hosted_fmt.c.
References k_ra8_esp_hosted_fmt_ch_percent.
Referenced by priv_ra8_esp_hosted_fmt_parse().
|
static |
Pull the next signed argument at the width the modifier names.
Mirrors internal_next_unsigned for the d and i conversions, including its reason for dispatching with guarded early returns instead of a switch.
| [in,out] | args | Copied argument list to advance. |
| [in] | len | Argument width to read. |
| 0 | The argument itself was zero. |
Definition at line 513 of file ra8_esp_hosted_fmt.c.
References ra8_esp_hosted_fmt_args::ap, k_ra8_esp_hosted_fmt_len_llong, k_ra8_esp_hosted_fmt_len_long, and k_ra8_esp_hosted_fmt_len_size.
Referenced by internal_emit_conv().
|
static |
Pull the next unsigned argument at the width the modifier names.
Reading at the wrong width would misalign every later argument, so each modifier reads at exactly the type it names – z at size_t, not at whichever fixed-width type happens to share its size on one target. C leaves va_arg undefined when the requested type is merely the same size as the argument's rather than compatible with it, so the four reads cannot be merged even where two of them are the same number of bytes.
The dispatch is a sequence of guarded early returns rather than a switch: it matches how internal_emit_conv below selects a conversion, and it keeps clang-tidy's bugprone-branch-clone off a construct it cannot read correctly – its statement profiler ignores the type operand of va_arg, so four reads of four different types profile as one.
| [in,out] | args | Copied argument list to advance. |
| [in] | len | Argument width to read. |
| 0 | The argument itself was zero. |
Definition at line 473 of file ra8_esp_hosted_fmt.c.
References ra8_esp_hosted_fmt_args::ap, k_ra8_esp_hosted_fmt_len_llong, k_ra8_esp_hosted_fmt_len_long, and k_ra8_esp_hosted_fmt_len_size.
Referenced by internal_emit_conv().
|
static |
Append the pad character a bounded number of times.
Used for both leading and trailing padding; which one is decided by the caller, which is what keeps the left-justify decision in one place.
| [in,out] | cur | Cursor to append through. Must be non-null. |
| [in] | pad | Character to repeat. |
| [in] | count | Repeat count, already clamped by the caller. |
Definition at line 218 of file ra8_esp_hosted_fmt.c.
References internal_put(), and k_ra8_esp_hosted_fmt_width_max.
Referenced by internal_emit_token().
|
static |
Recognise and record the flag characters of a specification.
Consumes any run of 0 and - characters, bounded so a pathological format string cannot spin here.
| [in] | text | Text after the per-cent sign. Must be non-null. |
| [in,out] | out | Specification whose flag fields are set. |
| 0 | No flag characters were present. |
Definition at line 287 of file ra8_esp_hosted_fmt.c.
References k_ra8_esp_hosted_fmt_ch_minus, k_ra8_esp_hosted_fmt_ch_zero, k_ra8_esp_hosted_fmt_spec_max, ra8_esp_hosted_fmt_spec::left_justify, and ra8_esp_hosted_fmt_spec::zero_pad.
Referenced by priv_ra8_esp_hosted_fmt_parse().
|
static |
Read the optional length modifier of a specification.
Recognises l, ll and z. Anything else leaves the default int width in place.
| [in] | text | Text positioned at the modifier, if any. |
| [out] | out_len | Parsed argument width. |
| 0 | No modifier was present. |
Definition at line 367 of file ra8_esp_hosted_fmt.c.
References k_ra8_esp_hosted_fmt_ch_ell, k_ra8_esp_hosted_fmt_ch_zed, k_ra8_esp_hosted_fmt_len_int, k_ra8_esp_hosted_fmt_len_llong, k_ra8_esp_hosted_fmt_len_long, and k_ra8_esp_hosted_fmt_len_size.
Referenced by priv_ra8_esp_hosted_fmt_parse().
|
static |
Read the optional decimal field width of a specification.
Accumulates digits, clamping at k_ra8_esp_hosted_fmt_width_max so an absurd width cannot make one conversion fill the whole line.
| [in] | text | Text positioned at the first width digit, if any. |
| [out] | out_width | Parsed width; zero when no digits were present. |
| 0 | The next character was not a digit. |
Definition at line 325 of file ra8_esp_hosted_fmt.c.
References k_ra8_esp_hosted_fmt_ch_nine, k_ra8_esp_hosted_fmt_ch_zero, k_ra8_esp_hosted_fmt_radix_dec, k_ra8_esp_hosted_fmt_spec_max, and k_ra8_esp_hosted_fmt_width_max.
Referenced by priv_ra8_esp_hosted_fmt_parse().
|
static |
Append one character if the buffer still has room for it.
Reserves the last byte for the terminator, so a full buffer silently drops further characters rather than overwriting the NUL.
| [in,out] | cur | Cursor to append through. Must be non-null. |
| [in] | ch | Character to append. |
Definition at line 187 of file ra8_esp_hosted_fmt.c.
References ra8_esp_hosted_fmt_cursor::cap, ra8_esp_hosted_fmt_cursor::len, and ra8_esp_hosted_fmt_cursor::out.
Referenced by internal_emit_conv(), internal_emit_token(), internal_pad(), and priv_ra8_esp_hosted_fmt_vformat().
|
nodiscard |
Implementation of priv_ra8_esp_hosted_fmt_parse() – flags, width, length modifier and conversion, each bounded by the specification length cap.
Parse one conversion specification.
Definition at line 419 of file ra8_esp_hosted_fmt.c.
References ra8_esp_hosted_fmt_spec::consumed, ra8_esp_hosted_fmt_spec::conv, internal_is_supported(), internal_parse_flags(), internal_parse_len(), internal_parse_width(), ra8_esp_hosted_fmt_spec::len, and ra8_esp_hosted_fmt_spec::width.
Referenced by priv_ra8_esp_hosted_fmt_vformat().
|
nodiscard |
Implementation of priv_ra8_esp_hosted_fmt_utoa() – divides down and reverses in place, so no scratch beyond the caller's buffer.
Render an unsigned value into a digit buffer, least digit last.
Definition at line 232 of file ra8_esp_hosted_fmt.c.
References k_ra8_esp_hosted_fmt_ch_nul, k_ra8_esp_hosted_fmt_digits_max, k_ra8_esp_hosted_fmt_radix_hex, k_ra8_esp_hosted_fmt_radix_min, s_ra8_esp_hosted_fmt_digits_lower, and s_ra8_esp_hosted_fmt_digits_upper.
Referenced by internal_emit_conv(), and ra8_esp_hosted_log_hexdump().
| uint32_t priv_ra8_esp_hosted_fmt_vformat | ( | char * | out, |
| uint32_t | cap, | ||
| const char * | fmt, | ||
| va_list | ap ) |
Implementation of priv_ra8_esp_hosted_fmt_vformat() – single pass, every loop bounded, no allocation.
Format into a bounded buffer from a variable-argument list.
Definition at line 689 of file ra8_esp_hosted_fmt.c.
References ra8_esp_hosted_fmt_args::ap, ra8_esp_hosted_fmt_spec::consumed, internal_emit_conv(), internal_put(), k_ra8_esp_hosted_fmt_ch_nul, k_ra8_esp_hosted_fmt_ch_percent, ra8_esp_hosted_fmt_cursor::len, ra8_esp_hosted_fmt_cursor::out, and priv_ra8_esp_hosted_fmt_parse().
Referenced by priv_ra8_esp_hosted_log_vwrite().
|
static |
Digit glyphs for lower-case hexadecimal and every smaller base.
Indexed by the remainder, so the table doubles as the decimal digit set.
Definition at line 154 of file ra8_esp_hosted_fmt.c.
Referenced by priv_ra8_esp_hosted_fmt_utoa().
|
static |
Digit glyphs for upper-case hexadecimal.
Selected by the X conversion only.
Definition at line 164 of file ra8_esp_hosted_fmt.c.
Referenced by priv_ra8_esp_hosted_fmt_utoa().