ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_esp_hosted_fmt.c
Go to the documentation of this file.
1
27
28#include <stdarg.h>
29#include <stddef.h>
30#include <stdint.h>
31
32#include "ra8_attributes.h"
34
64
91
117
144
154static const char s_ra8_esp_hosted_fmt_digits_lower[] = "0123456789abcdef";
155
164static const char s_ra8_esp_hosted_fmt_digits_upper[] = "0123456789ABCDEF";
165
188{
189 if ((cur->len + 1U) >= cur->cap) {
190 return;
191 }
192 cur->out[cur->len] = ch;
193 cur->len++;
194}
195
218static void internal_pad(ra8_esp_hosted_fmt_cursor_t* cur, char pad, uint16_t count)
219{
220 uint16_t remaining = count;
221 if (remaining > (uint16_t)k_ra8_esp_hosted_fmt_width_max) {
222 remaining = (uint16_t)k_ra8_esp_hosted_fmt_width_max;
223 }
224 for (uint16_t i = 0U; i < remaining; i++) {
225 internal_put(cur, pad);
226 }
227}
228
232uint8_t priv_ra8_esp_hosted_fmt_utoa(char* buf, uint64_t value, uint8_t base, bool upper)
233{
234 if (buf == nullptr) {
235 return 0U;
236 }
237 if ((base < (uint8_t)k_ra8_esp_hosted_fmt_radix_min) ||
238 (base > (uint8_t)k_ra8_esp_hosted_fmt_radix_hex)) {
239 return 0U;
240 }
241
242 const char* glyphs =
244 uint64_t remaining = value;
245 uint8_t count = 0U;
246
247 for (uint8_t i = 0U; i < (uint8_t)k_ra8_esp_hosted_fmt_digits_max; i++) {
248 buf[count] = glyphs[remaining % (uint64_t)base];
249 count++;
250 remaining /= (uint64_t)base;
251 if (remaining == 0U) {
252 break;
253 }
254 }
255 buf[count] = (char)k_ra8_esp_hosted_fmt_ch_nul;
256
257 for (uint8_t i = 0U; i < (uint8_t)(count / 2U); i++) {
258 const char swap = buf[i];
259 buf[i] = buf[(uint8_t)(count - 1U - i)];
260 buf[(uint8_t)(count - 1U - i)] = swap;
261 }
262 return count;
263}
264
287static uint8_t internal_parse_flags(const char* text, ra8_esp_hosted_fmt_spec_t* out)
288{
289 uint8_t used = 0U;
290 for (uint8_t i = 0U; i < (uint8_t)k_ra8_esp_hosted_fmt_spec_max; i++) {
291 if (text[used] == (char)k_ra8_esp_hosted_fmt_ch_zero) {
292 out->zero_pad = true;
293 } else if (text[used] == (char)k_ra8_esp_hosted_fmt_ch_minus) {
294 out->left_justify = true;
295 } else {
296 break;
297 }
298 used++;
299 }
300 return used;
301}
302
325static uint8_t internal_parse_width(const char* text, uint16_t* out_width)
326{
327 uint32_t width = 0U;
328 uint8_t used = 0U;
329 for (uint8_t i = 0U; i < (uint8_t)k_ra8_esp_hosted_fmt_spec_max; i++) {
330 if ((text[used] < (char)k_ra8_esp_hosted_fmt_ch_zero) ||
331 (text[used] > (char)k_ra8_esp_hosted_fmt_ch_nine)) {
332 break;
333 }
334 width = (width * (uint32_t)k_ra8_esp_hosted_fmt_radix_dec) +
335 (uint32_t)(text[used] - (char)k_ra8_esp_hosted_fmt_ch_zero);
336 if (width > (uint32_t)k_ra8_esp_hosted_fmt_width_max) {
337 width = (uint32_t)k_ra8_esp_hosted_fmt_width_max;
338 }
339 used++;
340 }
341 *out_width = (uint16_t)width;
342 return used;
343}
344
367static uint8_t internal_parse_len(const char* text, ra8_esp_hosted_fmt_len_t* out_len)
368{
370 if (text[0] == (char)k_ra8_esp_hosted_fmt_ch_zed) {
372 return 1U;
373 }
374 if (text[0] != (char)k_ra8_esp_hosted_fmt_ch_ell) {
375 return 0U;
376 }
377 if (text[1] == (char)k_ra8_esp_hosted_fmt_ch_ell) {
379 return 2U;
380 }
382 return 1U;
383}
384
408static bool internal_is_supported(char conv)
409{
410 return (conv == 'd') || (conv == 'i') || (conv == 'u') || (conv == 'x') || (conv == 'X') ||
411 (conv == 'c') || (conv == 's') || (conv == 'p') ||
412 (conv == (char)k_ra8_esp_hosted_fmt_ch_percent);
413}
414
419bool priv_ra8_esp_hosted_fmt_parse(const char* after_percent, ra8_esp_hosted_fmt_spec_t* out)
420{
421 if ((after_percent == nullptr) || (out == nullptr)) {
422 return false;
423 }
424
425 *out = (ra8_esp_hosted_fmt_spec_t){};
426
427 uint8_t used = internal_parse_flags(after_percent, out);
428 used = (uint8_t)(used + internal_parse_width(&after_percent[used], &out->width));
429 used = (uint8_t)(used + internal_parse_len(&after_percent[used], &out->len));
430
431 const char conv = after_percent[used];
432 if (!internal_is_supported(conv)) {
433 return false;
434 }
435
436 out->conv = conv;
437 out->consumed = (uint8_t)(used + 1U);
438 return true;
439}
440
475{
477 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses caller va_copy state.
478 return (uint64_t)va_arg(args->ap, unsigned long long);
479 }
481 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses caller va_copy state.
482 return (uint64_t)va_arg(args->ap, unsigned long);
483 }
485 return (uint64_t)va_arg(args->ap, size_t);
486 }
487 return (uint64_t)va_arg(args->ap, unsigned int);
488}
489
514{
516 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses caller va_copy state.
517 return (int64_t)va_arg(args->ap, long long);
518 }
520 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses caller va_copy state.
521 return (int64_t)va_arg(args->ap, long);
522 }
524 return (int64_t)va_arg(args->ap, ptrdiff_t);
525 }
526 return (int64_t)va_arg(args->ap, int);
527}
528
557 const ra8_esp_hosted_fmt_spec_t* spec,
558 const char* token,
559 uint16_t token_len)
560{
561 const uint16_t pad_count = (spec->width > token_len) ? (uint16_t)(spec->width - token_len) : 0U;
562 const char pad = (spec->zero_pad && !spec->left_justify) ? (char)k_ra8_esp_hosted_fmt_ch_zero
564
565 if (!spec->left_justify) {
566 internal_pad(cur, pad, pad_count);
567 }
568 for (uint16_t i = 0U; i < token_len; i++) {
569 internal_put(cur, token[i]);
570 }
571 if (spec->left_justify) {
572 internal_pad(cur, (char)k_ra8_esp_hosted_fmt_ch_space, pad_count);
573 }
574}
575
599static uint16_t internal_bounded_len(const char* text)
600{
601 uint16_t len = 0U;
602 for (uint16_t i = 0U; i < (uint16_t)k_ra8_esp_hosted_fmt_width_max; i++) {
603 if (text[len] == (char)k_ra8_esp_hosted_fmt_ch_nul) {
604 break;
605 }
606 len++;
607 }
608 return len;
609}
610
636 const ra8_esp_hosted_fmt_spec_t* spec,
638{
639 char digits[(uint32_t)k_ra8_esp_hosted_fmt_digits_max + 2U] = {};
640
641 if (spec->conv == (char)k_ra8_esp_hosted_fmt_ch_percent) {
643 return;
644 }
645 if (spec->conv == 'c') {
646 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses caller va_copy state.
647 digits[0] = (char)va_arg(args->ap, int);
648 internal_emit_token(cur, spec, digits, 1U);
649 return;
650 }
651 if (spec->conv == 's') {
652 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses caller va_copy state.
653 const char* text = va_arg(args->ap, const char*);
654 const char* safe = (text == nullptr) ? "(null)" : text;
655 internal_emit_token(cur, spec, safe, internal_bounded_len(safe));
656 return;
657 }
658 if ((spec->conv == 'd') || (spec->conv == 'i')) {
659 const int64_t value = internal_next_signed(args, spec->len);
660 const bool negative = (value < 0);
661 const uint64_t magnitude = negative ? (uint64_t)(-(value + 1)) + 1U : (uint64_t)value;
662 uint8_t used = 0U;
663 if (negative) {
664 digits[0] = (char)k_ra8_esp_hosted_fmt_ch_minus;
665 used = 1U;
666 }
667 const uint8_t n = priv_ra8_esp_hosted_fmt_utoa(&digits[used],
668 magnitude,
670 false);
671 internal_emit_token(cur, spec, digits, (uint16_t)(used + n));
672 return;
673 }
674
675 const bool is_pointer = (spec->conv == 'p');
676 const bool upper = (spec->conv == 'X');
677 const uint8_t base = ((spec->conv == 'u') ? (uint8_t)k_ra8_esp_hosted_fmt_radix_dec
679 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses caller va_copy state.
680 const uint64_t value = is_pointer ? (uint64_t)(uintptr_t)va_arg(args->ap, void*)
681 : internal_next_unsigned(args, spec->len);
682 const uint8_t n = priv_ra8_esp_hosted_fmt_utoa(digits, value, base, upper);
683 internal_emit_token(cur, spec, digits, (uint16_t)n);
684}
685
689uint32_t priv_ra8_esp_hosted_fmt_vformat(char* out, uint32_t cap, const char* fmt, va_list ap)
690{
691 if ((out == nullptr) || (cap == 0U) || (fmt == nullptr)) {
692 if ((out != nullptr) && (cap != 0U)) {
693 out[0] = (char)k_ra8_esp_hosted_fmt_ch_nul;
694 }
695 return 0U;
696 }
697
698 ra8_esp_hosted_fmt_cursor_t cur = {.out = out, .cap = cap, .len = 0U};
700 uint32_t pos = 0U;
701
702 va_copy(args.ap, ap);
703
704 for (uint32_t guard = 0U; guard < cap; guard++) {
705 const char ch = fmt[pos];
706 if (ch == (char)k_ra8_esp_hosted_fmt_ch_nul) {
707 break;
708 }
709 pos++;
710 if (ch != (char)k_ra8_esp_hosted_fmt_ch_percent) {
711 internal_put(&cur, ch);
712 continue;
713 }
714
716 if (!priv_ra8_esp_hosted_fmt_parse(&fmt[pos], &spec)) {
718 continue;
719 }
720 internal_emit_conv(&cur, &spec, &args);
721 pos += spec.consumed;
722 }
723
724 // NOLINTNEXTLINE(clang-analyzer-security.VAList) -- Clang 22 loses local va_copy state.
725 va_end(args.ap);
726 cur.out[cur.len] = (char)k_ra8_esp_hosted_fmt_ch_nul;
727 return cur.len;
728}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
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.
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.
ra8_esp_hosted_fmt_char_t
Characters the parser and emitter test for by name.
@ k_ra8_esp_hosted_fmt_ch_space
Pad character when not zero-padding.
@ k_ra8_esp_hosted_fmt_ch_nul
String terminator.
@ k_ra8_esp_hosted_fmt_ch_zed
Size-type length modifier.
@ k_ra8_esp_hosted_fmt_ch_ell
Long length modifier.
@ k_ra8_esp_hosted_fmt_ch_minus
Left-justify flag and sign.
@ k_ra8_esp_hosted_fmt_ch_percent
Starts a conversion.
@ k_ra8_esp_hosted_fmt_ch_zero
Zero-pad flag and first digit.
@ k_ra8_esp_hosted_fmt_ch_nine
Last decimal digit.
struct ra8_esp_hosted_fmt_args ra8_esp_hosted_fmt_args_t
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_pad(ra8_esp_hosted_fmt_cursor_t *cur, char pad, uint16_t count)
Append the pad character a bounded number of times.
static const char s_ra8_esp_hosted_fmt_digits_lower[]
Digit glyphs for lower-case hexadecimal and every smaller base.
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 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 uint16_t internal_bounded_len(const char *text)
Measure a NUL-terminated string, bounded by the width cap.
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 bool internal_is_supported(char conv)
Whether a character is a conversion this formatter emits.
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.
ra8_esp_hosted_fmt_radix_t
Radices the formatter renders.
@ k_ra8_esp_hosted_fmt_radix_dec
Decimal, for the integer conversions.
@ k_ra8_esp_hosted_fmt_radix_min
Smallest base the digit table serves.
@ k_ra8_esp_hosted_fmt_radix_hex
Hexadecimal, for x, X and p.
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,...
static uint8_t internal_parse_width(const char *text, uint16_t *out_width)
Read the optional decimal field width of a specification.
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,...
struct ra8_esp_hosted_fmt_cursor ra8_esp_hosted_fmt_cursor_t
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,...
static const char s_ra8_esp_hosted_fmt_digits_upper[]
Digit glyphs for upper-case hexadecimal.
Bounded, heap-free printf-subset formatter for the esp-hosted port.
struct ra8_esp_hosted_fmt_spec ra8_esp_hosted_fmt_spec_t
ra8_esp_hosted_fmt_len_t
Argument width selected by a conversion's length modifier.
@ k_ra8_esp_hosted_fmt_len_size
z; argument is size_t.
@ k_ra8_esp_hosted_fmt_len_long
l; argument is long.
@ k_ra8_esp_hosted_fmt_len_int
No modifier; argument is int.
@ k_ra8_esp_hosted_fmt_len_llong
ll; argument is long long.
@ k_ra8_esp_hosted_fmt_spec_max
Longest run of specification characters accepted after a per-cent sign, which bounds the parser's loo...
@ k_ra8_esp_hosted_fmt_width_max
Largest field width honoured; anything wider is clamped.
@ k_ra8_esp_hosted_fmt_digits_max
Widest digit run the supported bases produce, from UINT64_MAX in base ten.
Single-member holder that names this unit's argument list once.
va_list ap
The held variable-argument list.
Write cursor over the caller's output buffer.
uint32_t len
Characters written so far.
char * out
Destination buffer.
uint32_t cap
Capacity including the terminator.
bool left_justify
The - flag was present.
bool zero_pad
The 0 flag was present.
uint16_t width
Minimum field width; zero if absent.
char conv
Conversion character, zero on failure.
ra8_esp_hosted_fmt_len_t len
Argument width from the length modifier.
uint8_t consumed
Characters consumed after the per-cent.