ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_esp_hosted_fmt_internal.h File Reference

Bounded, heap-free printf-subset formatter for the esp-hosted port. More...

#include <stdarg.h>
#include <stdint.h>
#include "ra8_attributes.h"
Include dependency graph for ra8_esp_hosted_fmt_internal.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ra8_esp_hosted_fmt_spec
 One parsed conversion specification. More...

Typedefs

typedef struct ra8_esp_hosted_fmt_spec ra8_esp_hosted_fmt_spec_t

Enumerations

enum  ra8_esp_hosted_fmt_bound_t : uint16_t {
  k_ra8_esp_hosted_fmt_digits_max = 20U ,
  k_ra8_esp_hosted_fmt_width_max = 64U ,
  k_ra8_esp_hosted_fmt_spec_max = 12U
}
 Fixed bounds the formatter works within. More...
enum  ra8_esp_hosted_fmt_len_t : uint8_t {
  k_ra8_esp_hosted_fmt_len_int = 0U ,
  k_ra8_esp_hosted_fmt_len_long = 1U ,
  k_ra8_esp_hosted_fmt_len_llong = 2U ,
  k_ra8_esp_hosted_fmt_len_size = 3U
}
 Argument width selected by a conversion's length modifier. More...

Functions

bool priv_ra8_esp_hosted_fmt_parse (const char *after_percent, ra8_esp_hosted_fmt_spec_t *out)
 Parse one conversion specification.
uint8_t priv_ra8_esp_hosted_fmt_utoa (char *buf, uint64_t value, uint8_t base, bool upper)
 Render an unsigned value into a digit buffer, least digit last.
uint32_t priv_ra8_esp_hosted_fmt_vformat (char *out, uint32_t cap, const char *fmt, va_list ap)
 Format into a bounded buffer from a variable-argument list.

Detailed Description

Bounded, heap-free printf-subset formatter for the esp-hosted port.

Tag
[Ring 4 / PORT] {World: NS}

The vendored esp-hosted core logs with printf-style calls (ESP_LOGI(TAG, "len u if d", len, if_type)), but this project's logger takes a plain message string and has no formatting at all. Some formatter therefore has to exist between them.

It is written here rather than delegated to vsnprintf because the C library's printf family is not admissible in this image: NASA Power of 10 Rule 3 forbids allocation after initialisation, this board has no heap at all (_sbrk is a strong symbol that reports a fatal error), and newlib's formatting paths are not contractually allocation-free. A bounded formatter that only ever writes into a caller-supplied buffer removes the question.

Supported conversions
The set is derived from what the vendored tree actually uses, measured across every format string in libs/third_party/esp-hosted/: d i u x X c s p %%, with length modifiers l, ll and z, the flags 0 (zero pad) and - (left justify), and a decimal field width. Floating point is deliberately absent: the only f in the tree is in the raw-throughput test path, which this build disables, and pulling in soft-float formatting to serve a disabled path would be a poor trade.

An unsupported conversion is copied through verbatim, so an unhandled specifier shows up in the log as itself rather than silently consuming an argument and desynchronising every later one.

Since
0.1.0

Definition in file ra8_esp_hosted_fmt_internal.h.

Typedef Documentation

◆ ra8_esp_hosted_fmt_spec_t

Enumeration Type Documentation

◆ ra8_esp_hosted_fmt_bound_t

enum ra8_esp_hosted_fmt_bound_t : uint16_t

Fixed bounds the formatter works within.

Every loop in the formatter is bounded by one of these, which is what keeps it inside NASA Power of 10 Rule 2 while parsing a format string it did not write. The digit bound covers the widest value the supported bases can produce; the width bound is what a field width is clamped to, so one conversion can never fill an arbitrary buffer.

Invariant
k_ra8_esp_hosted_fmt_digits_max is at least the number of decimal digits in UINT64_MAX (twenty).
k_ra8_esp_hosted_fmt_width_max is at most the log line budget, so a clamped width still fits.
Example:
char digits[k_ra8_esp_hosted_fmt_digits_max + 1U] = {};
@ k_ra8_esp_hosted_fmt_digits_max
Widest digit run the supported bases produce, from UINT64_MAX in base ten.
See also
priv_ra8_esp_hosted_fmt_utoa
Since
0.1.0
Enumerator
k_ra8_esp_hosted_fmt_digits_max 

Widest digit run the supported bases produce, from UINT64_MAX in base ten.

k_ra8_esp_hosted_fmt_width_max 

Largest field width honoured; anything wider is clamped.

k_ra8_esp_hosted_fmt_spec_max 

Longest run of specification characters accepted after a per-cent sign, which bounds the parser's loops.

Definition at line 72 of file ra8_esp_hosted_fmt_internal.h.

◆ ra8_esp_hosted_fmt_len_t

enum ra8_esp_hosted_fmt_len_t : uint8_t

Argument width selected by a conversion's length modifier.

Decides how much the formatter pulls off the variable-argument list. Getting this wrong does not merely mis-print one value, it misaligns every argument after it, so the modifier is parsed explicitly rather than assumed.

Invariant
k_ra8_esp_hosted_fmt_len_int is the value used when no modifier is present, matching default argument promotion.
Example:
(void)priv_ra8_esp_hosted_fmt_parse("%llu", &spec);
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,...
struct ra8_esp_hosted_fmt_spec ra8_esp_hosted_fmt_spec_t
See also
ra8_esp_hosted_fmt_spec_t
Since
0.1.0
Enumerator
k_ra8_esp_hosted_fmt_len_int 

No modifier; argument is int.

k_ra8_esp_hosted_fmt_len_long 

l; argument is long.

k_ra8_esp_hosted_fmt_len_llong 

ll; argument is long long.

k_ra8_esp_hosted_fmt_len_size 

z; argument is size_t.

Definition at line 105 of file ra8_esp_hosted_fmt_internal.h.

Function Documentation

◆ priv_ra8_esp_hosted_fmt_parse()

bool priv_ra8_esp_hosted_fmt_parse ( const char * after_percent,
ra8_esp_hosted_fmt_spec_t * out )
nodiscard

Parse one conversion specification.

Reads flags, then an optional decimal width, then an optional length modifier, then the conversion character, from the text immediately following a per-cent sign. Stops at the first character that cannot belong to a specification.

A width longer than the formatter's own line budget is clamped rather than honoured, so a hostile or mistyped format string cannot make a single conversion consume the whole buffer.

Parameters
[in]after_percentText following the per-cent sign, NUL terminated. Must be non-null.
[out]outParsed specification. Must be non-null. Fully written on success; on failure conv and consumed are zero.
Returns
Whether a complete specification was recognised.
Return values
trueout holds a specification and consumed is non-zero.
falseA pointer was null, the text ended mid-specification, or the conversion character is not one this formatter emits.
Precondition
after_percent points at a NUL-terminated string.
out points at writable storage for one specification.
Postcondition
On success out->consumed is the exact number of characters to skip past, including the conversion character.
On failure out->conv is zero, which the driver reads as "copy the specification through verbatim".
Note
Pure; no module state, safe from any context.
MC/DC:
Promoted from static so the flag, width, length-modifier and conversion decisions can be driven directly. The production caller is priv_ra8_esp_hosted_fmt_vformat; nothing outside tests/ may call it.
Example:
(void)priv_ra8_esp_hosted_fmt_parse("-8s: ", &spec);
See also
priv_ra8_esp_hosted_fmt_vformat
Since
0.1.0

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().

◆ priv_ra8_esp_hosted_fmt_utoa()

uint8_t priv_ra8_esp_hosted_fmt_utoa ( char * buf,
uint64_t value,
uint8_t base,
bool upper )
nodiscard

Render an unsigned value into a digit buffer, least digit last.

Writes the digits of value in base into buf, NUL terminated, and reports the digit count. Zero renders as a single 0 rather than an empty string. The buffer must be large enough for the widest result the base allows, which is 64 binary-ish digits plus a terminator; the caller sizes it from k_ra8_esp_hosted_fmt_digits_max.

Parameters
[out]bufDestination for the digits. Must be non-null and at least k_ra8_esp_hosted_fmt_digits_max + 1 bytes.
[in]valueValue to render.
[in]baseRadix; only 10 and 16 are produced by this formatter.
[in]upperWhether hexadecimal digits use upper case.
Returns
Number of digits written, excluding the terminator.
Return values
0buf was null or base was outside 2..16.
Precondition
buf has room for the widest result in base.
base is between 2 and 16 inclusive.
Postcondition
buf is NUL terminated whenever a non-zero count is returned.
The returned count never exceeds k_ra8_esp_hosted_fmt_digits_max.
Note
Pure; no module state, safe from any context.
MC/DC:
Promoted from static so the base and null guards can be exercised without going through a format string. Tests only.
Example:
char digits[k_ra8_esp_hosted_fmt_digits_max + 1U] = {};
const uint8_t n = priv_ra8_esp_hosted_fmt_utoa(digits, 255U, 16U, false);
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,...
See also
priv_ra8_esp_hosted_fmt_vformat
Since
0.1.0

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().

◆ priv_ra8_esp_hosted_fmt_vformat()

uint32_t priv_ra8_esp_hosted_fmt_vformat ( char * out,
uint32_t cap,
const char * fmt,
va_list ap )

Format into a bounded buffer from a variable-argument list.

Walks fmt, copying ordinary characters and expanding the supported conversions listed in the file-level documentation. Writing stops when the buffer is one byte from full; the result is always NUL terminated when cap is non-zero, and the return value is the number of characters actually written, not the number that would have been written. Truncation is therefore visible to the caller without a second pass and without ever reporting a length past the end of the buffer.

Parameters
[out]outDestination buffer. Must be non-null when cap is non-zero.
[in]capCapacity of out in bytes, including the terminator.
[in]fmtFormat string. Must be non-null.
[in]apArgument list positioned at the first conversion argument. The caller owns starting and ending it.
Returns
Characters written, excluding the terminator.
Return values
0cap was zero, or a pointer was null, or fmt was empty.
Precondition
out has room for cap bytes.
ap supplies one argument per conversion in fmt.
Postcondition
out is NUL terminated whenever cap is non-zero.
The return value is strictly less than cap.
Note
Pure with respect to module state; the only writes are into the caller's buffer, so it is reentrant and safe from interrupt context.
Warning
An unsupported conversion is copied through verbatim and its argument is NOT consumed, so later arguments stay aligned with their conversions.
MC/DC:
Promoted from static so the truncation, conversion-dispatch and padding decisions can be driven directly. Production callers reach it through ra8_esp_hosted_log_write; tests may call it directly.
Example:
char line[64] = {};
va_list ap;
va_start(ap, fmt);
(void)priv_ra8_esp_hosted_fmt_vformat(line, sizeof(line), fmt, ap);
va_end(ap);
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,...
See also
priv_ra8_esp_hosted_fmt_parse
Since
0.1.0

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().