diff --git a/src/boot/console.cpp b/src/boot/console.cpp index b0f0c77..7958eb7 100644 --- a/src/boot/console.cpp +++ b/src/boot/console.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "console.h" #include "error.h" @@ -58,130 +59,13 @@ console::announce() m_out->output_string(L" booting...\r\n"); } -size_t -console::print_hex(uint32_t n) const -{ - wchar_t buffer[9]; - wchar_t *p = buffer; - for (int i = 7; i >= 0; --i) { - uint8_t nibble = (n >> (i*4)) & 0xf; - *p++ = digits[nibble]; - } - *p = 0; - m_out->output_string(buffer); - return 8; -} - -size_t -console::print_long_hex(uint64_t n) const -{ - wchar_t buffer[17]; - wchar_t *p = buffer; - for (int i = 15; i >= 0; --i) { - uint8_t nibble = (n >> (i*4)) & 0xf; - *p++ = digits[nibble]; - } - *p = 0; - m_out->output_string(buffer); - return 16; -} - -size_t -console::print_dec(uint32_t n) const -{ - wchar_t buffer[11]; - wchar_t *p = buffer + 10; - *p-- = 0; - do { - *p-- = digits[n % 10]; - n /= 10; - } while (n != 0); - - m_out->output_string(++p); - return 10 - (p - buffer); -} - -size_t -console::print_long_dec(uint64_t n) const -{ - wchar_t buffer[21]; - wchar_t *p = buffer + 20; - *p-- = 0; - do { - *p-- = digits[n % 10]; - n /= 10; - } while (n != 0); - - m_out->output_string(++p); - return 20 - (p - buffer); -} - size_t console::vprintf(const wchar_t *fmt, va_list args) const { wchar_t buffer[256]; - const wchar_t *r = fmt; - wchar_t *w = buffer; - size_t count = 0; + util::counted output {buffer, sizeof(buffer)/sizeof(wchar_t)}; + size_t count = util::vformat(output, fmt, args); - while (r && *r) { - if (*r != L'%') { - count++; - *w++ = *r++; - continue; - } - - *w = 0; - m_out->output_string(buffer); - w = buffer; - - r++; // chomp the % - - switch (*r++) { - case L'%': - m_out->output_string(const_cast(L"%")); - count++; - break; - - case L'x': - count += print_hex(va_arg(args, uint32_t)); - break; - - case L'd': - case L'u': - count += print_dec(va_arg(args, uint32_t)); - break; - - case L's': - { - wchar_t *s = va_arg(args, wchar_t*); - count += wstrlen(s); - m_out->output_string(s); - } - break; - - case L'l': - switch (*r++) { - case L'x': - count += print_long_hex(va_arg(args, uint64_t)); - break; - - case L'd': - case L'u': - count += print_long_dec(va_arg(args, uint64_t)); - break; - - default: - break; - } - break; - - default: - break; - } - } - - *w = 0; m_out->output_string(buffer); return count; } diff --git a/src/boot/console.h b/src/boot/console.h index 7411dce..c2ced45 100644 --- a/src/boot/console.h +++ b/src/boot/console.h @@ -21,10 +21,6 @@ public: void announce(); - size_t print_hex(uint32_t n) const; - size_t print_dec(uint32_t n) const; - size_t print_long_hex(uint64_t n) const; - size_t print_long_dec(uint64_t n) const; size_t printf(const wchar_t *fmt, ...) const; static console & get() { return *s_console; } diff --git a/src/kernel/panic.serial/display.cpp b/src/kernel/panic.serial/display.cpp index a53cc08..7568530 100644 --- a/src/kernel/panic.serial/display.cpp +++ b/src/kernel/panic.serial/display.cpp @@ -1,4 +1,4 @@ -#include "printf/printf.h" +#include #include "cpu.h" #include "display.h" @@ -29,7 +29,7 @@ print_header( out.write(":"); char linestr[6]; - snprintf(linestr, sizeof(linestr), "%ld", line); + util::format({linestr, sizeof(linestr)}, "%ld", line); out.write(linestr); } @@ -38,7 +38,7 @@ print_cpu(serial_port &out, cpu_data &cpu) { out.write("\n \e[0;31m==[ CPU: "); char cpuid[7]; - snprintf(cpuid, sizeof(cpuid), "%4x", cpu.id); + util::format({cpuid, sizeof(cpuid)}, "%4x", cpu.id); out.write(cpuid); out.write(" ]====================================================================\n"); } @@ -54,7 +54,7 @@ print_callstack(serial_port &out, symbol_table &syms, frame const *fp) if (!name) name = ""; - snprintf(message, sizeof(message), + util::format({message, sizeof(message)}, " \e[0;33mframe %2d: <0x%016lx> \e[1;33m%s\n", count++, fp->return_addr, name); @@ -68,7 +68,7 @@ print_reg(serial_port &out, const char *name, uint64_t val, const char *color) { char message[512]; - snprintf(message, sizeof(message), " \e[0;%sm%-3s %016lx\e[0m", color, name, val); + util::format({message, sizeof(message)}, " \e[0;%sm%3s %016lx\e[0m", color, name, val); out.write(message); } @@ -124,11 +124,11 @@ print_rip(serial_port &out, uintptr_t addr) out.write("\n"); for (unsigned i = 0; i < per_line*3; i += per_line) { - snprintf(bit, sizeof(bit), "\e[0;33m%20lx: \e[0m", addr + i); + util::format({bit, sizeof(bit)}, "\e[0;33m%20lx: \e[0m", addr + i); out.write(bit); for (unsigned j = 0; j < per_line; ++j) { - snprintf(bit, sizeof(bit), "%02x ", data[i+j]); + util::format({bit, sizeof(bit)}, "%02x ", data[i+j]); out.write(bit); } out.write("\n"); @@ -155,6 +155,3 @@ print_user_state(serial_port &out, const cpu_state ®s) } } // namespace panicking - -// For printf.c -extern "C" void putchar_(char c) {} diff --git a/src/kernel/panic.serial/serial.module b/src/kernel/panic.serial/serial.module index c405598..357a196 100644 --- a/src/kernel/panic.serial/serial.module +++ b/src/kernel/panic.serial/serial.module @@ -13,5 +13,4 @@ panic = module("panic.serial", "main.cpp", "serial.cpp", "symbol_table.cpp", - "../printf/printf.c", ]) diff --git a/src/kernel/printf/printf.c b/src/kernel/printf/printf.c deleted file mode 100644 index 1295694..0000000 --- a/src/kernel/printf/printf.c +++ /dev/null @@ -1,1184 +0,0 @@ -/** - * @author (c) Eyal Rozenberg - * 2021, Haifa, Palestine/Israel - * @author (c) Marco Paland (info@paland.com) - * 2014-2019, PALANDesign Hannover, Germany - * - * @note Others have made smaller contributions to this file: see the - * contributors page at https://github.com/eyalroz/printf/graphs/contributors - * or ask one of the authors. - * - * @brief Small stand-alone implementation of the printf family of functions - * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with - * a very limited resources. - * - * @note the implementations are thread-safe; re-entrant; use no functions from - * the standard library; and do not dynamically allocate any memory. - * - * @license The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include -#include - -// Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the -// printf_config.h header file -#ifndef PRINTF_INCLUDE_CONFIG_H -#define PRINTF_INCLUDE_CONFIG_H 0 -#endif - -#if PRINTF_INCLUDE_CONFIG_H -#include "printf_config.h" -#endif - -#include - -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES -# define printf_ printf -# define sprintf_ sprintf -# define vsprintf_ vsprintf -# define snprintf_ snprintf -# define vsnprintf_ vsnprintf -# define vprintf_ vprintf -#endif - - -// 'ntoa' conversion buffer size, this must be big enough to hold one converted -// numeric number including padded zeros (dynamically created on stack) -#ifndef PRINTF_INTEGER_BUFFER_SIZE -#define PRINTF_INTEGER_BUFFER_SIZE 32 -#endif - -// 'ftoa' conversion buffer size, this must be big enough to hold one converted -// float number including padded zeros (dynamically created on stack) -#ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32 -#endif - -// Support for the decimal notation floating point conversion specifiers (%f, %F) -#ifndef PRINTF_SUPPORT_DECIMAL_SPECIFIERS -#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 -#endif - -// Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G) -#ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS -#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 -#endif - -// Support for the length write-back specifier (%n) -#ifndef PRINTF_SUPPORT_WRITEBACK_SPECIFIER -#define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1 -#endif - -// Default precision for the floating point conversion specifiers (the C standard sets this at 6) -#ifndef PRINTF_DEFAULT_FLOAT_PRECISION -#define PRINTF_DEFAULT_FLOAT_PRECISION 6 -#endif - -// According to the C languages standard, printf() and related functions must be able to print any -// integral number in floating-point notation, regardless of length, when using the %f specifier - -// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, -// all values beyond this threshold are switched to exponential notation. -#ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL -#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 -#endif - -// Support for the long long integral types (with the ll, z and t length modifiers for specifiers -// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported. -#ifndef PRINTF_SUPPORT_LONG_LONG -#define PRINTF_SUPPORT_LONG_LONG 1 -#endif - -#if PRINTF_SUPPORT_LONG_LONG -typedef unsigned long long printf_unsigned_value_t; -typedef long long printf_signed_value_t; -#else -typedef unsigned long printf_unsigned_value_t; -typedef long printf_signed_value_t; -#endif - -#define PRINTF_PREFER_DECIMAL false -#define PRINTF_PREFER_EXPONENTIAL true - -/////////////////////////////////////////////////////////////////////////////// - -// The following will convert the number-of-digits into an exponential-notation literal -#define PRINTF_CONCATENATE(s1, s2) s1##s2 -#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) -#define PRINTF_FLOAT_NOTATION_THRESHOLD PRINTF_EXPAND_THEN_CONCATENATE(1e,PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL) - -// internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_LONG (1U << 8U) -#define FLAGS_LONG_LONG (1U << 9U) -#define FLAGS_PRECISION (1U << 10U) -#define FLAGS_ADAPT_EXP (1U << 11U) -#define FLAGS_POINTER (1U << 12U) -// Note: Similar, but not identical, effect as FLAGS_HASH - -#define BASE_BINARY 2 -#define BASE_OCTAL 8 -#define BASE_DECIMAL 10 -#define BASE_HEX 16 - -typedef uint8_t numeric_base_t; - -#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) -#include -#if FLT_RADIX != 2 -#error "Non-binary-radix floating-point types are unsupported." -#endif - -#if DBL_MANT_DIG == 24 - -#define DOUBLE_SIZE_IN_BITS 32 -typedef uint32_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0xFFU -#define DOUBLE_BASE_EXPONENT 127 - -#elif DBL_MANT_DIG == 53 - -#define DOUBLE_SIZE_IN_BITS 64 -typedef uint64_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0x7FFU -#define DOUBLE_BASE_EXPONENT 1023 - -#else -#error "Unsupported double type configuration" -#endif -#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1) - -typedef union { - double_uint_t U; - double F; -} double_with_bit_access; - -// This is unnecessary in C99, since compound initializers can be used, -// but: 1. Some compilers are finicky about this; 2. Some people may want to convert this to C89; -// 3. If you try to use it as C++, only C++20 supports compound literals -static inline double_with_bit_access get_bit_access(double x) -{ - double_with_bit_access dwba; - dwba.F = x; - return dwba; -} - -static inline int get_sign(double x) -{ - // The sign is stored in the highest bit - return get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1); -} - -static inline int get_exp2(double_with_bit_access x) -{ - // The exponent in an IEEE-754 floating-point number occupies a contiguous - // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An - // unsigned offset from some negative value (with the extremal offset values reserved for - // special use). - return (int)((x.U >> DOUBLE_STORED_MANTISSA_BITS ) & DOUBLE_EXPONENT_MASK) - DOUBLE_BASE_EXPONENT; -} -#define PRINTF_ABS(_x) ( (_x) > 0 ? (_x) : -(_x) ) - -#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) - -// Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid -// and well-defined, but if you're not careful you can easily trigger undefined -// behavior with -LONG_MIN or -LLONG_MIN -#define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t) ( (_x) > 0 ? (_x) : -((printf_signed_value_t)_x) )) - -// output function type -typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); - - -// wrapper (used as buffer) for output function type -typedef struct { - void (*fct)(char character, void* arg); - void* arg; -} out_function_wrapper_type; - - -// internal buffer output -static inline void out_buffer(char character, void* buffer, size_t idx, size_t maxlen) -{ - if (idx < maxlen) { - ((char*)buffer)[idx] = character; - } -} - - -// internal null output -static inline void out_discard(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)character; (void)buffer; (void)idx; (void)maxlen; -} - - -// internal putchar_ wrapper -static inline void out_putchar(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)buffer; (void)idx; (void)maxlen; - if (character) { - putchar_(character); - } -} - - -// internal output function wrapper -static inline void out_wrapped_function(char character, void* wrapped_function, size_t idx, size_t maxlen) -{ - (void)idx; (void)maxlen; - if (character) { - // buffer is the output fct pointer - ((out_function_wrapper_type*)wrapped_function)->fct(character, ((out_function_wrapper_type*)wrapped_function)->arg); - } -} - - -// internal secure strlen -// @return The length of the string (excluding the terminating 0) limited by 'maxsize' -static inline unsigned int strnlen_s_(const char* str, size_t maxsize) -{ - const char* s; - for (s = str; *s && maxsize--; ++s); - return (unsigned int)(s - str); -} - - -// internal test if char is a digit (0-9) -// @return true if char is a digit -static inline bool is_digit_(char ch) -{ - return (ch >= '0') && (ch <= '9'); -} - - -// internal ASCII string to unsigned int conversion -static unsigned int atoi_(const char** str) -{ - unsigned int i = 0U; - while (is_digit_(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; -} - - -// output the specified string in reverse, taking care of any zero-padding -static size_t out_rev_(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) -{ - const size_t start_idx = idx; - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - while (len) { - out(buf[--len], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} - - -// Invoked by print_integer after the actual number has been printed, performing necessary -// work on the number's prefix (as the number is initially printed in reverse order) -static size_t print_integer_finalization(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, numeric_base_t base, unsigned int precision, unsigned int width, unsigned int flags) -{ - size_t unpadded_len = len; - - // pad with leading zeros - { - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = '0'; - } - - if (base == BASE_OCTAL && (len > unpadded_len)) { - // Since we've written some zeros, we've satisfied the alternative format leading space requirement - flags &= ~FLAGS_HASH; - } - } - - // handle hash - if (flags & (FLAGS_HASH | FLAGS_POINTER)) { - if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { - // Let's take back some padding digits to fit in what will eventually - // be the format-specific prefix - if (unpadded_len < len) { - len--; - } - if (len && (base == BASE_HEX)) { - if (unpadded_len < len) { - len--; - } - } - } - if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'x'; - } - else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'X'; - } - else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < PRINTF_INTEGER_BUFFER_SIZE) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_INTEGER_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return out_rev_(out, buffer, idx, maxlen, buf, len, width, flags); -} - -// An internal itoa-like function -static size_t print_integer(out_fct_type out, char* buffer, size_t idx, size_t maxlen, printf_unsigned_value_t value, bool negative, numeric_base_t base, unsigned int precision, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_INTEGER_BUFFER_SIZE]; - size_t len = 0U; - - if (!value) { - if ( !(flags & FLAGS_PRECISION) ) { - buf[len++] = '0'; - flags &= ~FLAGS_HASH; - // We drop this flag this since either the alternative and regular modes of the specifier - // don't differ on 0 values, or (in the case of octal) we've already provided the special - // handling for this mode. - } - else if (base == BASE_HEX) { - flags &= ~FLAGS_HASH; - // We drop this flag this since either the alternative and regular modes of the specifier - // don't differ on 0 values - } - } - else { - do { - const char digit = (char)(value % base); - buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); - value /= base; - } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); - } - - return print_integer_finalization(out, buffer, idx, maxlen, buf, len, negative, base, precision, width, flags); -} - -#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) - -struct double_components { - int_fast64_t integral; - int_fast64_t fractional; - bool is_negative; -}; - -#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 -#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T -static const double powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = { - 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, - 1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17 -}; - -#define PRINTF_MAX_SUPPORTED_PRECISION NUM_DECIMAL_DIGITS_IN_INT64_T - 1 - - -// Break up a double number - which is known to be a finite non-negative number - -// into its base-10 parts: integral - before the decimal point, and fractional - after it. -// Taken the precision into account, but does not change it even internally. -static struct double_components get_components(double number, unsigned int precision) -{ - struct double_components number_; - number_.is_negative = get_sign(number); - double abs_number = (number_.is_negative) ? -number : number; - number_.integral = (int_fast64_t)abs_number; - double remainder = (abs_number - number_.integral) * powers_of_10[precision]; - number_.fractional = (int_fast64_t)remainder; - - remainder -= (double) number_.fractional; - - if (remainder > 0.5) { - ++number_.fractional; - // handle rollover, e.g. case 0.99 with precision 1 is 1.0 - if ((double) number_.fractional >= powers_of_10[precision]) { - number_.fractional = 0; - ++number_.integral; - } - } - else if (remainder == 0.5) { - if ((number_.fractional == 0U) || (number_.fractional & 1U)) { - // if halfway, round up if odd OR if last digit is 0 - ++number_.fractional; - } - } - - if (precision == 0U) { - remainder = abs_number - (double) number_.integral; - if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++number_.integral; - } - } - return number_; -} - -struct scaling_factor { - double raw_factor; - bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it -}; - -double apply_scaling(double num, struct scaling_factor normalization) -{ - return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; -} - -double unapply_scaling(double normalized, struct scaling_factor normalization) -{ - return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; -} - -struct scaling_factor update_normalization(struct scaling_factor sf, double extra_multiplicative_factor) -{ - struct scaling_factor result; - if (sf.multiply) { - result.multiply = true; - result.raw_factor = sf.raw_factor * extra_multiplicative_factor; - } - else { - int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); - int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); - - // Divide the larger-exponent raw raw_factor by the smaller - if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) { - result.multiply = false; - result.raw_factor = sf.raw_factor / extra_multiplicative_factor; - } - else { - result.multiply = true; - result.raw_factor = extra_multiplicative_factor / sf.raw_factor; - } - } - return result; -} - -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS -static struct double_components get_normalized_components(bool negative, unsigned int precision, double non_normalized, struct scaling_factor normalization) -{ - struct double_components components; - components.is_negative = negative; - components.integral = (int_fast64_t) apply_scaling(non_normalized, normalization); - double remainder = non_normalized - unapply_scaling((double) components.integral, normalization); - double prec_power_of_10 = powers_of_10[precision]; - struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); - double scaled_remainder = apply_scaling(remainder, account_for_precision); - double rounding_threshold = 0.5; - - if (precision == 0U) { - components.fractional = 0; - components.integral += (scaled_remainder >= rounding_threshold); - if (scaled_remainder == rounding_threshold) { - // banker's rounding: Round towards the even number (making the mean error 0) - components.integral &= ~((int_fast64_t) 0x1); - } - } - else { - components.fractional = (int_fast64_t) scaled_remainder; - scaled_remainder -= components.fractional; - - components.fractional += (scaled_remainder >= rounding_threshold); - if (scaled_remainder == rounding_threshold) { - // banker's rounding: Round towards the even number (making the mean error 0) - components.fractional &= ~((int_fast64_t) 0x1); - } - // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), - // and must then be corrected into (1, 0). - if ((double) components.fractional >= prec_power_of_10) { - components.fractional = 0; - ++components.integral; - } - } - return components; -} -#endif - -static size_t print_broken_up_decimal( - struct double_components number_, out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned int precision, - unsigned int width, unsigned int flags, char *buf, size_t len) -{ - if (precision != 0U) { - // do fractional part, as an unsigned number - - unsigned int count = precision; - - if (flags & FLAGS_ADAPT_EXP && !(flags & FLAGS_HASH)) { - // %g/%G mandates we skip the trailing 0 digits... - if (number_.fractional > 0) { - while(true) { - int_fast64_t digit = number_.fractional % 10U; - if (digit != 0) { - break; - } - --count; - number_.fractional /= 10U; - } - - } - // ... and even the decimal point if there are no - // non-zero fractional part digits (see below) - } - - if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH) ) { - while (len < PRINTF_FTOA_BUFFER_SIZE) { - --count; - buf[len++] = (char)('0' + number_.fractional % 10U); - if (!(number_.fractional /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { - buf[len++] = '0'; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = '.'; - } - } - } - else { - if (flags & FLAGS_HASH) { - if (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = '.'; - } - } - } - - // Write the integer part of the number (it comes after the fractional - // since the character order is reversed) - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)('0' + (number_.integral % 10)); - if (!(number_.integral /= 10)) { - break; - } - } - - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { - if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (number_.is_negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return out_rev_(out, buffer, idx, maxlen, buf, len, width, flags); -} - - // internal ftoa for fixed decimal floating point -static size_t print_decimal_number(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double number, unsigned int precision, unsigned int width, unsigned int flags, char* buf, size_t len) -{ - struct double_components value_ = get_components(number, precision); - return print_broken_up_decimal(value_, out, buffer, idx, maxlen, precision, width, flags, buf, len); -} - -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS -// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse -static size_t print_exponential_number(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double number, unsigned int precision, unsigned int width, unsigned int flags, char* buf, size_t len) -{ - const bool negative = get_sign(number); - // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it - double abs_number = negative ? -number : number; - - int exp10; - bool abs_exp10_covered_by_powers_table; - struct scaling_factor normalization; - - - // Determine the decimal exponent - if (abs_number == 0.0) { - // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. - exp10 = 0; // ... and no need to set a normalization factor or check the powers table - } - else { - double_with_bit_access conv = get_bit_access(abs_number); - { - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - int exp2 = get_exp2(conv); - // drop the exponent, so conv.F comes into the range [1,2) - conv.U = (conv.U & (( (double_uint_t)(1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | ((double_uint_t) DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS); - // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - exp10 = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); - // now we want to compute 10^exp10 but we want to be sure it won't overflow - exp2 = (int)(exp10 * 3.321928094887362 + 0.5); - const double z = exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - conv.U = ((double_uint_t)(exp2) + DOUBLE_BASE_EXPONENT) << DOUBLE_STORED_MANTISSA_BITS; - // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - // correct for rounding errors - if (abs_number < conv.F) { - exp10--; - conv.F /= 10; - } - } - abs_exp10_covered_by_powers_table = PRINTF_ABS(exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; - normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(exp10)] : conv.F; - } - - // We now begin accounting for the widths of the two parts of our printed field: - // the decimal part after decimal exponent extraction, and the base-10 exponent part. - // For both of these, the value of 0 has a special meaning, but not the same one: - // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width - // means "use as many characters as necessary". - - bool fall_back_to_decimal_only_mode = false; - if (flags & FLAGS_ADAPT_EXP) { - int required_significant_digits = (precision == 0) ? 1 : (int) precision; - // Should we want to fall-back to "%f" mode, and only print the decimal part? - fall_back_to_decimal_only_mode = (exp10 >= -4 && exp10 < required_significant_digits); - // Now, let's adjust the precision - // This also decided how we adjust the precision value - as in "%g" mode, - // "precision" is the number of _significant digits_, and this is when we "translate" - // the precision value to an actual number of decimal digits. - int precision_ = (fall_back_to_decimal_only_mode) ? - (int) precision - 1 - exp10 : - (int) precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point - precision = (precision_ > 0 ? (unsigned) precision_ : 0U); - flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above - } - - normalization.multiply = (exp10 < 0 && abs_exp10_covered_by_powers_table); - bool should_skip_normalization = (fall_back_to_decimal_only_mode || exp10 == 0); - struct double_components decimal_part_components = - should_skip_normalization ? - get_components(negative ? -abs_number : abs_number, precision) : - get_normalized_components(negative, precision, abs_number, normalization); - - // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects - // the exponent and may require additional tweaking of the parts - if (fall_back_to_decimal_only_mode) { - if ( (flags & FLAGS_ADAPT_EXP) && exp10 >= -1 && decimal_part_components.integral == powers_of_10[exp10 + 1]) { - exp10++; // Not strictly necessary, since exp10 is no longer really used - precision--; - // ... and it should already be the case that decimal_part_components.fractional == 0 - } - // TODO: What about rollover strictly within the fractional part? - } - else { - if (decimal_part_components.integral >= 10) { - exp10++; - decimal_part_components.integral = 1; - decimal_part_components.fractional = 0; - } - } - - // the exp10 format is "E%+03d" and largest possible exp10 value for a 64-bit double - // is "307" (for 2^1023), so we set aside 4-5 characters overall - unsigned int exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(exp10) < 100) ? 4U : 5U; - - unsigned int decimal_part_width = - ((flags & FLAGS_LEFT) && exp10_part_width) ? - // We're padding on the right, so the width constraint is the exponent part's - // problem, not the decimal part's, so we'll use as many characters as we need: - 0U : - // We're padding on the left; so the width constraint is the decimal part's - // problem. Well, can both the decimal part and the exponent part fit within our overall width? - ((width > exp10_part_width) ? - // Yes, so we limit our decimal part's width. - // (Note this is trivially valid even if we've fallen back to "%f" mode) - width - exp10_part_width : - // No; we just give up on any restriction on the decimal part and use as many - // characters as we need - 0U); - - const size_t start_idx = idx; - idx = print_broken_up_decimal(decimal_part_components, out, buffer, idx, maxlen, precision, decimal_part_width, flags, buf, len); - - if (! fall_back_to_decimal_only_mode) { - out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); - idx = print_integer(out, buffer, idx, maxlen, - ABS_FOR_PRINTING(exp10), - exp10 < 0, 10, 0, exp10_part_width - 1, - FLAGS_ZEROPAD | FLAGS_PLUS); - if (flags & FLAGS_LEFT) { - // We need to right-pad with spaces to meet the width requirement - while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); - } - } - return idx; -} -#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - - -static size_t print_floating_point(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int precision, unsigned int width, unsigned int flags, bool prefer_exponential) -{ - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - - // test for special values - if (value != value) - return out_rev_(out, buffer, idx, maxlen, "nan", 3, width, flags); - if (value < -DBL_MAX) - return out_rev_(out, buffer, idx, maxlen, "fni-", 4, width, flags); - if (value > DBL_MAX) - return out_rev_(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - - if (!prefer_exponential && ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) { - // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean - // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated - // implementation. -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - return print_exponential_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len); -#else - return 0U; -#endif - } - - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - precision = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - // limit precision so that our integer holding the fractional part does not overflow - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { - buf[len++] = '0'; // This respects the precision in terms of result length only - precision--; - } - - return -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - prefer_exponential ? - print_exponential_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len) : -#endif - print_decimal_number(out, buffer, idx, maxlen, value, precision, width, flags, buf, len); -} - -#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) - -// internal vsnprintf -static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) -{ - unsigned int flags, width, precision, n; - size_t idx = 0U; - - if (!buffer) { - // use null output function - out = out_discard; - } - - while (*format) - { - // format specifier? %[flags][width][.precision][length] - if (*format != '%') { - // no - out(*format, buffer, idx++, maxlen); - format++; - continue; - } - else { - // yes, evaluate it - format++; - } - - // evaluate flags - flags = 0U; - do { - switch (*format) { - case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; - case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; - case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; - case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; - case '#': flags |= FLAGS_HASH; format++; n = 1U; break; - default : n = 0U; break; - } - } while (n); - - // evaluate width field - width = 0U; - if (is_digit_(*format)) { - width = atoi_(&format); - } - else if (*format == '*') { - const int w = va_arg(va, int); - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (unsigned int)-w; - } - else { - width = (unsigned int)w; - } - format++; - } - - // evaluate precision field - precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - format++; - if (is_digit_(*format)) { - precision = atoi_(&format); - } - else if (*format == '*') { - const int precision_ = (int)va_arg(va, int); - precision = precision_ > 0 ? (unsigned int)precision_ : 0U; - format++; - } - } - - // evaluate length field - switch (*format) { - case 'l' : - flags |= FLAGS_LONG; - format++; - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - format++; - } - break; - case 'h' : - flags |= FLAGS_SHORT; - format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; - case 't' : - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'j' : - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z' : - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default: - break; - } - - // evaluate specifier - switch (*format) { - case 'd' : - case 'i' : - case 'u' : - case 'x' : - case 'X' : - case 'o' : - case 'b' : { - // set the base - numeric_base_t base; - if (*format == 'x' || *format == 'X') { - base = BASE_HEX; - } - else if (*format == 'o') { - base = BASE_OCTAL; - } - else if (*format == 'b') { - base = BASE_BINARY; - } - else { - base = BASE_DECIMAL; - flags &= ~FLAGS_HASH; // no hash for dec format - } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - } - - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; - } - - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { -#if PRINTF_SUPPORT_LONG_LONG - const long long value = va_arg(va, long long); - idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); -#endif - } - else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); - } - else { - const int value = (flags & FLAGS_CHAR) ? (signed char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); - idx = print_integer(out, buffer, idx, maxlen, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); - } - } - else { - // unsigned - if (flags & FLAGS_LONG_LONG) { -#if PRINTF_SUPPORT_LONG_LONG - idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) va_arg(va, unsigned long long), false, base, precision, width, flags); -#endif - } - else if (flags & FLAGS_LONG) { - idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) va_arg(va, unsigned long), false, base, precision, width, flags); - } - else { - const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); - idx = print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) value, false, base, precision, width, flags); - } - } - format++; - break; - } -#if PRINTF_SUPPORT_DECIMAL_SPECIFIERS - case 'f' : - case 'F' : - if (*format == 'F') flags |= FLAGS_UPPERCASE; - idx = print_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_DECIMAL); - format++; - break; -#endif -#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - case 'e': - case 'E': - case 'g': - case 'G': - if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; - if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; - idx = print_floating_point(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); - format++; - break; -#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS - case 'c' : { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's' : { - const char* p = va_arg(va, char*); - if (p == NULL) { - idx = out_rev_(out, buffer, idx, maxlen, ")llun(", 6, width, flags); - } - else { - unsigned int l = strnlen_s_(p, precision ? precision : (size_t)-1); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - } - format++; - break; - } - - case 'p' : { - width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix - flags |= FLAGS_ZEROPAD | FLAGS_POINTER; - uintptr_t value = (uintptr_t)va_arg(va, void*); - idx = (value == (uintptr_t) NULL) ? - out_rev_(out, buffer, idx, maxlen, ")lin(", 5, width, flags) : - print_integer(out, buffer, idx, maxlen, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); - format++; - break; - } - - case '%' : - out('%', buffer, idx++, maxlen); - format++; - break; - - // Many people prefer to disable support for %n, as it lets the caller - // engineer a write to an arbitrary location, of a value the caller - // effectively controls - which could be a security concern in some cases. -#if PRINTF_SUPPORT_WRITEBACK_SPECIFIER - case 'n' : { - if (flags & FLAGS_CHAR) *(va_arg(va, char*)) = (char) idx; - else if (flags & FLAGS_SHORT) *(va_arg(va, short*)) = (short) idx; - else if (flags & FLAGS_LONG) *(va_arg(va, long*)) = (long) idx; -#if PRINTF_SUPPORT_LONG_LONG - else if (flags & FLAGS_LONG_LONG) *(va_arg(va, long long*)) = (long long int) idx; -#endif // PRINTF_SUPPORT_LONG_LONG - else *(va_arg(va, int*)) = (int) idx; - format++; - break; - } -#endif // PRINTF_SUPPORT_WRITEBACK_SPECIFIER - - default : - out(*format, buffer, idx++, maxlen); - format++; - break; - } - } - - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - return (int)idx; -} - - -/////////////////////////////////////////////////////////////////////////////// - -int printf_(const char* format, ...) -{ - va_list va; - va_start(va, format); - char buffer[1]; - const int ret = _vsnprintf(out_putchar, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - - -int sprintf_(char* buffer, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _vsnprintf(out_buffer, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - - -int snprintf_(char* buffer, size_t count, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _vsnprintf(out_buffer, buffer, count, format, va); - va_end(va); - return ret; -} - - -int vprintf_(const char* format, va_list va) -{ - char buffer[1]; - return _vsnprintf(out_putchar, buffer, (size_t)-1, format, va); -} - -int vsprintf_(char* buffer, const char* format, va_list va) -{ - return _vsnprintf(out_buffer, buffer, (size_t)-1, format, va); -} - -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) -{ - return _vsnprintf(out_buffer, buffer, count, format, va); -} - - -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = vfctprintf(out, arg, format, va); - va_end(va); - return ret; -} - -int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) -{ - const out_function_wrapper_type out_fct_wrap = { out, arg }; - return _vsnprintf(out_wrapped_function, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); -} - diff --git a/src/kernel/printf/printf.h b/src/kernel/printf/printf.h deleted file mode 100644 index 84c83c9..0000000 --- a/src/kernel/printf/printf.h +++ /dev/null @@ -1,151 +0,0 @@ -/** - * @author (c) Eyal Rozenberg - * 2021, Haifa, Palestine/Israel - * @author (c) Marco Paland (info@paland.com) - * 2014-2019, PALANDesign Hannover, Germany - * - * @note Others have made smaller contributions to this file: see the - * contributors page at https://github.com/eyalroz/printf/graphs/contributors - * or ask one of the authors. - * - * @brief Small stand-alone implementation of the printf family of functions - * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with - * a very limited resources. - * - * @note the implementations are thread-safe; re-entrant; use no functions from - * the standard library; and do not dynamically allocate any memory. - * - * @license The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef PRINTF_H_ -#define PRINTF_H_ - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __GNUC__ -# define ATTR_PRINTF(one_based_format_index, first_arg) \ -__attribute__((format(__printf__, (one_based_format_index), (first_arg)))) -# define ATTR_VPRINTF(one_based_format_index) ATTR_PRINTF(one_based_format_index, 0) -#else -# define ATTR_PRINTF(one_based_format_index, first_arg) -# define ATTR_VPRINTF(one_based_format_index) -#endif - -#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES -#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 0 -#endif - -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES -# define printf_ printf -# define sprintf_ sprintf -# define vsprintf_ vsprintf -# define snprintf_ snprintf -# define vsnprintf_ vsnprintf -# define vprintf_ vprintf -#endif - -/** - * Output a character to a custom device like UART, used by the printf() function - * This function is declared here only. You have to write your custom implementation somewhere - * @param character Character to output - */ -void putchar_(char character); - - -/** - * Tiny printf implementation - * You have to implement putchar_ if you use printf() - * To avoid conflicts with the regular printf() API it is overridden by macro defines - * and internal underscore-appended functions like printf_() are used - * @param format A string that specifies the format of the output - * @return The number of characters that are written into the array, not counting the terminating null character - */ -int printf_(const char* format, ...) ATTR_PRINTF(1, 2); - - -/** - * Tiny sprintf/vsprintf implementation - * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! - * @param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that are WRITTEN into the buffer, not counting the terminating null character - */ -int sprintf_(char* buffer, const char* format, ...) ATTR_PRINTF(2, 3); -int vsprintf_(char* buffer, const char* format, va_list va) ATTR_VPRINTF(2); - - -/** - * Tiny snprintf/vsnprintf implementation - * @param buffer A pointer to the buffer where to store the formatted string - * @param count The maximum number of characters to store in the buffer, including a terminating null character - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that COULD have been written into the buffer, not counting the terminating - * null character. A value equal or larger than count indicates truncation. Only when the returned value - * is non-negative and less than count, the string has been completely written. - */ -int snprintf_(char* buffer, size_t count, const char* format, ...) ATTR_PRINTF(3, 4); -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) ATTR_VPRINTF(3); - - -/** - * Tiny vprintf implementation - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that are WRITTEN into the buffer, not counting the terminating null character - */ -int vprintf_(const char* format, va_list va) ATTR_VPRINTF(1); - - -/** - * printf/vprintf with output function - * You may use this as dynamic alternative to printf() with its fixed _putchar() output - * @param out An output function which takes one character and an argument pointer - * @param arg An argument pointer for user data passed to output function - * @param format A string that specifies the format of the output - * @param va A value identifying a variable arguments list - * @return The number of characters that are sent to the output function, not counting the terminating null character - */ -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) ATTR_PRINTF(3, 4); -int vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va) ATTR_VPRINTF(3); - -#ifdef __cplusplus -} -#endif - -#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES -# undef printf_ -# undef sprintf_ -# undef vsprintf_ -# undef snprintf_ -# undef vsnprintf_ -# undef vprintf_ -#endif - -#endif // PRINTF_H_ diff --git a/src/kernel/printf/printf_config.h b/src/kernel/printf/printf_config.h deleted file mode 100644 index 972fff7..0000000 --- a/src/kernel/printf/printf_config.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -// \file printf_config.h -// Configuration for eyalroz/printf - -#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 0 -#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 0 -#define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 0 -#define PRINTF_SUPPORT_LONG_LONG 1 -#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 1 diff --git a/src/libraries/util/format.cpp b/src/libraries/util/format.cpp index 3161a32..0a2f73c 100644 --- a/src/libraries/util/format.cpp +++ b/src/libraries/util/format.cpp @@ -5,22 +5,60 @@ namespace util { namespace { -const char digits[] = "0123456789abcdef"; +template struct char_traits; -template void -append_int(char *&out, size_t &count, size_t max, I value, unsigned min_width, char pad) +template <> +struct char_traits { - static constexpr size_t bufsize = sizeof(I)*3; + static constexpr const char digits[] = "0123456789abcdef"; + + static constexpr char d0 = '0'; + static constexpr char d9 = '9'; + + static constexpr char per = '%'; + static constexpr char space = ' '; + + static constexpr char d = 'd'; + static constexpr char l = 'l'; + static constexpr char s = 's'; + static constexpr char u = 'u'; + static constexpr char x = 'x'; +}; + +template <> +struct char_traits +{ + static constexpr const wchar_t digits[] = L"0123456789abcdef"; + + static constexpr wchar_t d0 = L'0'; + static constexpr wchar_t d9 = L'9'; + + static constexpr wchar_t per = L'%'; + static constexpr wchar_t space = L' '; + + static constexpr wchar_t d = L'd'; + static constexpr wchar_t l = L'l'; + static constexpr wchar_t s = L's'; + static constexpr wchar_t u = L'u'; + static constexpr wchar_t x = L'x'; +}; + + +template void +append_int(char_t *&out, size_t &count, size_t max, I value, unsigned min_width, char_t pad) +{ + using chars = char_traits; + static constexpr size_t bufsize = sizeof(I)*4; unsigned num_digits = 0; - char buffer[bufsize]; - char *p = buffer + (bufsize - 1); + char_t buffer[bufsize]; + char_t *p = buffer + (bufsize - 1); do { if (value) { - *p-- = digits[value % N]; + *p-- = chars::digits[value % N]; value /= N; } else { - *p-- = num_digits ? pad : '0'; + *p-- = num_digits ? pad : chars::d0; } num_digits++; } while (value || num_digits < min_width); @@ -33,81 +71,91 @@ append_int(char *&out, size_t &count, size_t max, I value, unsigned min_width, c } } -void -append_string(char *&out, size_t &count, size_t max, char const *value) +template void +append_string(char_t *&out, size_t &count, size_t max, unsigned width, char_t const *value) { + using chars = char_traits; + while (value && *value && count < max) { - count++; + ++count; + if (width) --width; *out++ = *value++; } + + while (width-- && count < max) { + ++count; + *out++ = chars::space; + } } } // namespace -size_t -vformat(stringbuf output, char const *format, va_list va) +template size_t +vformat(counted output, char_t const *format, va_list va) { - char * out = output.pointer; + using chars = char_traits; + + char_t * out = output.pointer; const size_t max = output.count - 1; size_t count = 0; while (format && *format && count < max) { - if (*format != '%') { + if (*format != chars::per) { count++; *out++ = *format++; continue; } format++; // chomp the % character - char spec = *format++; + char_t spec = *format++; bool long_type = false; - if (spec == '%') { + if (spec == chars::per) { count++; - *out++ = '%'; + *out++ = chars::per; continue; } unsigned width = 0; - char pad = ' '; + char_t pad = chars::space; while (spec) { bool done = false; - if (spec >= '0' && spec <= '9') { - if (spec == '0' && width == 0) - pad = '0'; + if (spec >= chars::d0 && spec <= chars::d9) { + if (spec == chars::d0 && width == 0) + pad = chars::d0; else - width = width * 10 + (spec - '0'); + width = width * 10 + (spec - chars::d0); spec = *format++; continue; } switch (spec) { - case 'l': + case chars::l: long_type = true; break; - case 'x': + case chars::x: if (long_type) - append_int(out, count, max, va_arg(va, uint64_t), width, pad); + append_int(out, count, max, va_arg(va, uint64_t), width, pad); else - append_int(out, count, max, va_arg(va, uint32_t), width, pad); + append_int(out, count, max, va_arg(va, uint32_t), width, pad); done = true; break; - case 'd': - case 'u': + case chars::d: + case chars::u: if (long_type) - append_int(out, count, max, va_arg(va, uint64_t), width, pad); + append_int(out, count, max, va_arg(va, uint64_t), width, pad); else - append_int(out, count, max, va_arg(va, uint32_t), width, pad); + append_int(out, count, max, va_arg(va, uint32_t), width, pad); done = true; break; - case 's': - append_string(out, count, max, va_arg(va, const char *)); + case chars::s: + append_string(out, count, max, width, va_arg(va, const char_t *)); done = true; break; } @@ -117,17 +165,24 @@ vformat(stringbuf output, char const *format, va_list va) } } + *out = 0; return count; } -size_t -format(stringbuf output, const char *format, ...) +template size_t +format(counted output, const char_t *format, ...) { va_list va; va_start(va, format); - size_t result = vformat(output, format, va); + size_t result = vformat(output, format, va); va_end(va); return result; } +template size_t format(counted output, const char *format, ...); +template size_t vformat(counted output, const char *format, va_list va); + +template size_t format(counted output, const wchar_t *format, ...); +template size_t vformat(counted output, const wchar_t *format, va_list va); + } //namespace util diff --git a/src/libraries/util/util/format.h b/src/libraries/util/util/format.h index 44ddde2..6051ee1 100644 --- a/src/libraries/util/util/format.h +++ b/src/libraries/util/util/format.h @@ -8,9 +8,10 @@ namespace util { -using stringbuf = counted; +template +size_t format(counted output, const char_t *format, ...); -size_t format(stringbuf output, const char *format, ...); -size_t vformat(stringbuf output, const char *format, va_list va); +template +size_t vformat(counted output, const char_t *format, va_list va); }