From abbed13f27d5cb1f310e71d2a44be519efe616aa Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 17 Dec 2022 12:38:22 +0100 Subject: [PATCH] Add a 'pure' variant of cstyle_format which is infallible If we cannot fail to output, it doesn't make sense to propagate errors. So if you're SURE there are no errors, use pure_cstyle_format(). If, however, output can fail, use cstyle_format(). This has a drawback of adding quite a bit of code duplication to Format.cpp. Some of it is dealt using templates, but some code still remains duplicate. --- kernel/src/Log.cpp | 20 +-- kernel/src/arch/Serial.cpp | 10 +- kernel/src/video/TextConsole.cpp | 10 +- luna/include/luna/Format.h | 2 + luna/src/Format.cpp | 249 +++++++++++++++++++++++++++---- 5 files changed, 226 insertions(+), 65 deletions(-) diff --git a/kernel/src/Log.cpp b/kernel/src/Log.cpp index b9399326..005dcef3 100644 --- a/kernel/src/Log.cpp +++ b/kernel/src/Log.cpp @@ -26,14 +26,8 @@ static void log_serial(LogLevel level, const char* format, va_list origin) Serial::printf("%4zu.%.3zu ", Timer::ticks(), Timer::ticks_ms() - (Timer::ticks() * 1000)); - cstyle_format( - format, - [](char c, void*) -> Result { - Serial::putchar((u8)c); - return {}; - }, - nullptr, ap) - .expect_value("Sanity check failed in log_serial: Should never fail"); + pure_cstyle_format( + format, [](char c, void*) { Serial::putchar((u8)c); }, nullptr, ap); Serial::putchar('\n'); @@ -56,14 +50,8 @@ static void log_text_console(LogLevel level, const char* format, va_list origin) else TextConsole::set_foreground(WHITE); - cstyle_format( - format, - [](char c, void*) -> Result { - TextConsole::putchar(c); - return {}; - }, - nullptr, ap) - .expect_value("Sanity check failed in log_text_console: Should never fail"); + pure_cstyle_format( + format, [](char c, void*) { TextConsole::putchar(c); }, nullptr, ap); TextConsole::putchar('\n'); diff --git a/kernel/src/arch/Serial.cpp b/kernel/src/arch/Serial.cpp index ce12ddae..8bedd13c 100644 --- a/kernel/src/arch/Serial.cpp +++ b/kernel/src/arch/Serial.cpp @@ -25,14 +25,8 @@ namespace Serial { va_list ap; va_start(ap, format); - auto rc = cstyle_format( - format, - [](char c, void*) -> Result { - putchar((u8)c); - return {}; - }, - nullptr, ap) - .expect_value("Sanity check failed in Serial::printf: Should never fail"); + auto rc = pure_cstyle_format( + format, [](char c, void*) { putchar((u8)c); }, nullptr, ap); va_end(ap); return rc; } diff --git a/kernel/src/video/TextConsole.cpp b/kernel/src/video/TextConsole.cpp index 8ef6ad59..62928c70 100644 --- a/kernel/src/video/TextConsole.cpp +++ b/kernel/src/video/TextConsole.cpp @@ -161,14 +161,8 @@ namespace TextConsole { va_list ap; va_start(ap, format); - auto rc = cstyle_format( - format, - [](char c, void*) -> Result { - putchar(c); - return {}; - }, - nullptr, ap) - .expect_value("Sanity check failed in TextConsole::printf: Should never fail"); + auto rc = pure_cstyle_format( + format, [](char c, void*) { putchar(c); }, nullptr, ap); va_end(ap); return rc; } diff --git a/luna/include/luna/Format.h b/luna/include/luna/Format.h index fc3a4610..c44218d7 100644 --- a/luna/include/luna/Format.h +++ b/luna/include/luna/Format.h @@ -4,7 +4,9 @@ #include typedef Result (*callback_t)(char, void*); +typedef void (*pure_callback_t)(char, void*); Result cstyle_format(const char* format, callback_t callback, void* arg, va_list ap); +usize pure_cstyle_format(const char* format, pure_callback_t callback, void* arg, va_list ap); usize vstring_format(char* buf, usize max, const char* format, va_list ap); usize string_format(char* buf, usize max, const char* format, ...); \ No newline at end of file diff --git a/luna/src/Format.cpp b/luna/src/Format.cpp index 8a6a9c51..ff936a24 100644 --- a/luna/src/Format.cpp +++ b/luna/src/Format.cpp @@ -26,6 +26,13 @@ struct format_state void* arg; }; +struct pure_format_state +{ + usize count; + pure_callback_t callback; + void* arg; +}; + struct conv_state { flags_t flags; @@ -39,6 +46,12 @@ static Result format_putchar(char c, format_state& state) return state.callback(c, state.arg); } +static void pure_format_putchar(char c, pure_format_state& state) +{ + state.count++; + return state.callback(c, state.arg); +} + static Result format_puts(const char* s, format_state& state) { while (*s) @@ -50,6 +63,15 @@ static Result format_puts(const char* s, format_state& state) return {}; } +static void pure_format_puts(const char* s, pure_format_state& state) +{ + while (*s) + { + pure_format_putchar(*s, state); + s++; + } +} + static Result start_pad(const conv_state& vstate, format_state& state, usize start) { if (!(vstate.flags & FLAG_LEFT_ALIGN)) @@ -60,6 +82,14 @@ static Result start_pad(const conv_state& vstate, format_state& state, usi return {}; } +static void pure_start_pad(const conv_state& vstate, pure_format_state& state, usize start) +{ + if (!(vstate.flags & FLAG_LEFT_ALIGN)) + { + while (start++ < vstate.width) pure_format_putchar(' ', state); + } +} + static Result end_pad(const conv_state& vstate, format_state& state, usize start) { if (vstate.flags & FLAG_LEFT_ALIGN) @@ -70,6 +100,14 @@ static Result end_pad(const conv_state& vstate, format_state& state, usize return {}; } +static void pure_end_pad(const conv_state& vstate, pure_format_state& state, usize start) +{ + if (vstate.flags & FLAG_LEFT_ALIGN) + { + while (start++ < vstate.width) pure_format_putchar(' ', state); + } +} + static flags_t parse_flags(const char** format) { flags_t result = 0; @@ -203,7 +241,34 @@ static usize to_string(usize value, usize base, char* buf, usize max, bool upper return i; } -static Result output_integer(char specifier, conv_state& vstate, format_state& state, usize value, bool negative) +static Result output_integer_data(conv_state& vstate, format_state& state, char* buf, usize len) +{ + if (!(vstate.flags & FLAG_ZERO_PAD)) TRY(start_pad(vstate, state, len)); + + usize i = len; + + while (i--) TRY(format_putchar(buf[i], state)); + + TRY(end_pad(vstate, state, len)); + + return {}; +} + +static void output_pure_integer_data(conv_state& vstate, pure_format_state& state, char* buf, usize len) +{ + if (!(vstate.flags & FLAG_ZERO_PAD)) pure_start_pad(vstate, state, len); + + usize i = len; + + while (i--) pure_format_putchar(buf[i], state); + + pure_end_pad(vstate, state, len); +} + +template +static ReturnType output_integer_generic(char specifier, conv_state& vstate, FormatStateType& state, usize value, + bool negative, + ReturnType (*output_data)(conv_state&, FormatStateType&, char*, usize)) { usize base = 10; bool uppercase = false; @@ -260,18 +325,27 @@ static Result output_integer(char specifier, conv_state& vstate, format_st buf[buflen++] = ' '; } - if (!(vstate.flags & FLAG_ZERO_PAD)) TRY(start_pad(vstate, state, buflen)); - - usize i = buflen; - - while (i--) TRY(format_putchar(buf[i], state)); - - TRY(end_pad(vstate, state, buflen)); - - return {}; + return output_data(vstate, state, buf, buflen); } -static Result va_output_integer(char specifier, conv_state& vstate, format_state& state, va_list ap) +static Result output_integer(char specifier, conv_state& vstate, format_state& state, usize value, bool negative) +{ + return output_integer_generic, format_state>(specifier, vstate, state, value, negative, + output_integer_data); +} + +static void pure_output_integer(char specifier, conv_state& vstate, pure_format_state& state, usize value, + bool negative) +{ + return output_integer_generic(specifier, vstate, state, value, negative, + output_pure_integer_data); +} + +template +static ReturnType va_generic_output_integer(char specifier, conv_state& vstate, FormatStateType& state, + ReturnType (*integer_output)(char, conv_state&, FormatStateType&, usize, + bool), + va_list ap) { bool is_signed = false; bool negative = false; @@ -290,12 +364,12 @@ static Result va_output_integer(char specifier, conv_state& vstate, format v = -v; negative = true; } - return output_integer(specifier, vstate, state, (unsigned char)v, negative); + return integer_output(specifier, vstate, state, (unsigned char)v, negative); } else { const unsigned char v = (unsigned char)va_arg(ap, unsigned int); - return output_integer(specifier, vstate, state, v, false); + return integer_output(specifier, vstate, state, v, false); } } else if (vstate.flags & FLAG_SHORT) @@ -308,12 +382,12 @@ static Result va_output_integer(char specifier, conv_state& vstate, format v = -v; negative = true; } - return output_integer(specifier, vstate, state, (unsigned short)v, negative); + return integer_output(specifier, vstate, state, (unsigned short)v, negative); } else { const unsigned short v = (unsigned short)va_arg(ap, unsigned int); - return output_integer(specifier, vstate, state, v, false); + return integer_output(specifier, vstate, state, v, false); } } else if (vstate.flags & FLAG_LONG_LONG) @@ -326,12 +400,12 @@ static Result va_output_integer(char specifier, conv_state& vstate, format v = -v; negative = true; } - return output_integer(specifier, vstate, state, (unsigned long long)v, negative); + return integer_output(specifier, vstate, state, (unsigned long long)v, negative); } else { const unsigned long long v = va_arg(ap, unsigned long long); - return output_integer(specifier, vstate, state, v, false); + return integer_output(specifier, vstate, state, v, false); } } else if (vstate.flags & FLAG_LONG) @@ -344,12 +418,12 @@ static Result va_output_integer(char specifier, conv_state& vstate, format v = -v; negative = true; } - return output_integer(specifier, vstate, state, (unsigned long)v, negative); + return integer_output(specifier, vstate, state, (unsigned long)v, negative); } else { const unsigned long v = va_arg(ap, unsigned long); - return output_integer(specifier, vstate, state, v, false); + return integer_output(specifier, vstate, state, v, false); } } else @@ -362,16 +436,26 @@ static Result va_output_integer(char specifier, conv_state& vstate, format v = -v; negative = true; } - return output_integer(specifier, vstate, state, (unsigned int)v, negative); + return integer_output(specifier, vstate, state, (unsigned int)v, negative); } else { const unsigned int v = va_arg(ap, unsigned int); - return output_integer(specifier, vstate, state, v, false); + return integer_output(specifier, vstate, state, v, false); } } } +static Result va_output_integer(char specifier, conv_state& vstate, format_state& state, va_list ap) +{ + return va_generic_output_integer, format_state>(specifier, vstate, state, output_integer, ap); +} + +static void va_pure_output_integer(char specifier, conv_state& vstate, pure_format_state& state, va_list ap) +{ + return va_generic_output_integer(specifier, vstate, state, pure_output_integer, ap); +} + Result cstyle_format(const char* format, callback_t callback, void* arg, va_list ap) { format_state state; @@ -472,6 +556,106 @@ Result cstyle_format(const char* format, callback_t callback, void* arg, return state.count; } +usize pure_cstyle_format(const char* format, pure_callback_t callback, void* arg, va_list ap) +{ + pure_format_state state; + state.callback = callback; + state.arg = arg; + state.count = 0; + + while (*format) + { + if (*format != '%') + { + pure_format_putchar(*format, state); + format++; + continue; + } + + format++; + + if (*format == '%') + { + pure_format_putchar('%', state); + continue; + } + + // %[flags][width][.precision][length]conversion + + flags_t flags = parse_flags(&format); + const usize width = parse_width(&format, flags, ap); + usize precision = parse_precision(&format, flags, ap); + parse_length(&format, flags); + + conv_state vstate = {flags, width, precision}; + + const char specifier = *format; + format++; + + if (is_integer_format_specifier(specifier)) + { + va_pure_output_integer(specifier, vstate, state, ap); + continue; + } + else if (specifier == 'p') + { + const void* ptr = va_arg(ap, void*); + if (ptr == nullptr) + { + pure_start_pad(vstate, state, 5); + pure_format_puts("(nil)", state); + pure_end_pad(vstate, state, 5); + continue; + } + vstate.width = (sizeof(void*) * 2) + 2; + vstate.flags |= (FLAG_ZERO_PAD | FLAG_ALTERNATE); + pure_output_integer('p', vstate, state, (usize)ptr, false); + continue; + } + else if (specifier == 'c') + { + const char c = (char)va_arg(ap, int); + + pure_start_pad(vstate, state, 1); + pure_format_putchar(c, state); + pure_end_pad(vstate, state, 1); + + continue; + } + else if (specifier == 's') + { + const char* str = va_arg(ap, const char*); + if (str == nullptr) + { + pure_start_pad(vstate, state, 6); + pure_format_puts("(null)", state); + pure_end_pad(vstate, state, 6); + continue; + } + else + { + usize len = strlen(str); + + bool use_precision = (flags & FLAG_USE_PRECISION); + if (use_precision && len > precision) len = precision; + + pure_start_pad(vstate, state, len); + while (*str && (!use_precision || precision)) + { + pure_format_putchar(*str, state); + precision--; + str++; + } + pure_end_pad(vstate, state, len); + continue; + } + } + else { continue; } + } + + return state.count; +} + struct StringFormatInfo { char* buffer; @@ -482,18 +666,17 @@ usize vstring_format(char* buf, usize max, const char* format, va_list ap) { StringFormatInfo info = {.buffer = buf, .remaining = max - 1}; - usize result = cstyle_format( - format, - [](char c, void* arg) -> Result { - StringFormatInfo* info_arg = (StringFormatInfo*)arg; - if (!info_arg->remaining) return {}; - *(info_arg->buffer) = c; - info_arg->buffer++; - info_arg->remaining--; - return {}; - }, - &info, ap) - .expect_value("Sanity check failed in vstring_format: Should never fail"); + usize result = pure_cstyle_format( + format, + [](char c, void* arg) { + StringFormatInfo* info_arg = (StringFormatInfo*)arg; + if (!info_arg->remaining) return; + *(info_arg->buffer) = c; + info_arg->buffer++; + info_arg->remaining--; + return; + }, + &info, ap); *(info.buffer) = 0;