diff options
Diffstat (limited to 'src/klibc/rt_vsnprintf_std.c')
| -rw-r--r-- | src/klibc/rt_vsnprintf_std.c | 1868 |
1 files changed, 1026 insertions, 842 deletions
diff --git a/src/klibc/rt_vsnprintf_std.c b/src/klibc/rt_vsnprintf_std.c index 481c99013b..56e2f3eb3f 100644 --- a/src/klibc/rt_vsnprintf_std.c +++ b/src/klibc/rt_vsnprintf_std.c @@ -57,14 +57,14 @@ // 'ntoa' conversion buffer size, this must be big enough to hold one converted // numeric number including padded zeros (dynamically created on stack) #ifndef RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE -#define RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE 32 +#define RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE 32 #endif // size of the fixed (on-stack) buffer for printing individual decimal numbers. // this must be big enough to hold one converted floating-point value including // padded zeros. #ifndef RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE -#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE 32 +#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE 32 #endif // Support for the decimal notation floating point conversion specifiers (%f, %F) @@ -84,7 +84,7 @@ // Default precision for the floating point conversion specifiers (the C standard sets this at 6) #ifndef RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION -#define RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION 6 +#define RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION 6 #endif // According to the C languages standard, printf() and related functions must be able to print any @@ -124,88 +124,87 @@ #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_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,RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL) +#define PRINTF_FLOAT_NOTATION_THRESHOLD PRINTF_EXPAND_THEN_CONCATENATE(1e, RT_KLIBC_USING_VSNPRINTF_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_INT (1U << 8U) +#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_INT (1U << 8U) // Only used with RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS -#define FLAGS_LONG (1U << 9U) +#define FLAGS_LONG (1U << 9U) #define FLAGS_LONG_LONG (1U << 10U) #define FLAGS_PRECISION (1U << 11U) #define FLAGS_ADAPT_EXP (1U << 12U) #define FLAGS_POINTER (1U << 13U) // Note: Similar, but not identical, effect as FLAGS_HASH -#define FLAGS_SIGNED (1U << 14U) +#define FLAGS_SIGNED (1U << 14U) // Only used with RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS #ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS #define FLAGS_INT8 FLAGS_CHAR -#if (SHRT_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_SHORT -#elif (INT_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_INT -#elif (LONG_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_LONG -#elif (LLONG_MAX == 32767LL) -#define FLAGS_INT16 FLAGS_LONG_LONG +#if (SHRT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_SHORT +#elif (INT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_INT +#elif (LONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG +#elif (LLONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG_LONG #else #error "No basic integer type has a size of 16 bits exactly" #endif -#if (SHRT_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_SHORT -#elif (INT_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_INT -#elif (LONG_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_LONG -#elif (LLONG_MAX == 2147483647LL) -#define FLAGS_INT32 FLAGS_LONG_LONG +#if (SHRT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_SHORT +#elif (INT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_INT +#elif (LONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG +#elif (LLONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG_LONG #else #error "No basic integer type has a size of 32 bits exactly" #endif -#if (SHRT_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_SHORT -#elif (INT_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_INT -#elif (LONG_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_LONG -#elif (LLONG_MAX == 9223372036854775807LL) -#define FLAGS_INT64 FLAGS_LONG_LONG +#if (SHRT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_SHORT +#elif (INT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_INT +#elif (LONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG +#elif (LLONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG_LONG #else #error "No basic integer type has a size of 64 bits exactly" #endif #endif // RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS - typedef unsigned int printf_flags_t; -#define BASE_BINARY 2 -#define BASE_OCTAL 8 -#define BASE_DECIMAL 10 -#define BASE_HEX 16 +#define BASE_BINARY 2 +#define BASE_OCTAL 8 +#define BASE_DECIMAL 10 +#define BASE_HEX 16 typedef uint8_t numeric_base_t; #ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG typedef unsigned long long printf_unsigned_value_t; -typedef long long printf_signed_value_t; +typedef long long printf_signed_value_t; #else typedef unsigned long printf_unsigned_value_t; -typedef long printf_signed_value_t; +typedef long printf_signed_value_t; #endif // The printf()-family functions return an `int`; it is therefore @@ -215,9 +214,9 @@ typedef long printf_signed_value_t; // of these buffers. instead, we use: typedef unsigned int printf_size_t; #define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX - // If we were to nitpick, this would actually be INT_MAX + 1, - // since INT_MAX is the maximum return value, which excludes the - // trailing '\0'. +// If we were to nitpick, this would actually be INT_MAX + 1, +// since INT_MAX is the maximum return value, which excludes the +// trailing '\0'. #if defined(RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS) || defined(RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS) #include <float.h> @@ -230,28 +229,29 @@ typedef unsigned int printf_size_t; #define DOUBLE_SIZE_IN_BITS 32 typedef uint32_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0xFFU -#define DOUBLE_BASE_EXPONENT 127 +#define DOUBLE_EXPONENT_MASK 0xFFU +#define DOUBLE_BASE_EXPONENT 127 #define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 -#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-38 +#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-38 #elif DBL_MANT_DIG == 53 -#define DOUBLE_SIZE_IN_BITS 64 +#define DOUBLE_SIZE_IN_BITS 64 typedef uint64_t double_uint_t; -#define DOUBLE_EXPONENT_MASK 0x7FFU -#define DOUBLE_BASE_EXPONENT 1023 +#define DOUBLE_EXPONENT_MASK 0x7FFU +#define DOUBLE_BASE_EXPONENT 1023 #define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 -#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 ((double)1e-308L) +#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 ((double)1e-308L) #else #error "Unsupported double type configuration" #endif #define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1) -typedef union { - double_uint_t U; - double F; +typedef union +{ + double_uint_t U; + double F; } double_with_bit_access; // This is unnecessary in C99, since compound initializers can be used, @@ -261,33 +261,33 @@ typedef union { // 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; + double_with_bit_access dwba; + dwba.F = x; + return dwba; } static inline int get_sign_bit(double x) { - // The sign is stored in the highest bit - return (int) (get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1)); + // The sign is stored in the highest bit + return (int)(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; + // 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) ) +#define PRINTF_ABS(_x) ((_x) > 0 ? (_x) : -(_x)) #endif // (RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS || RT_KLIBC_USING_VSNPRINTF_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) )) +#define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t)((_x) > 0 ? (_x) : -((printf_signed_value_t)_x))) // wrapper (used as buffer) for output function type // @@ -297,298 +297,334 @@ static inline int get_exp2(double_with_bit_access x) // 3. function is non-null // // ... otherwise bad things will happen. -typedef struct { - void (*function)(char c, void* extra_arg); - void* extra_function_arg; - char* buffer; - printf_size_t pos; - printf_size_t max_chars; +typedef struct +{ + void (*function)(char c, void *extra_arg); + void *extra_function_arg; + char *buffer; + printf_size_t pos; + printf_size_t max_chars; } output_gadget_t; // Note: This function currently assumes it is not passed a '\0' c, // or alternatively, that '\0' can be passed to the function in the output // gadget. The former assumption holds within the printf library. It also // assumes that the output gadget has been properly initialized. -static inline void putchar_via_gadget(output_gadget_t* gadget, char c) +static inline void putchar_via_gadget(output_gadget_t *gadget, char c) { - printf_size_t write_pos = gadget->pos++; + printf_size_t write_pos = gadget->pos++; // We're _always_ increasing pos, so as to count how may characters // _would_ have been written if not for the max_chars limitation - if (write_pos >= gadget->max_chars) { - return; - } - if (gadget->function != NULL) { - // No check for c == '\0' . - gadget->function(c, gadget->extra_function_arg); - } - else { - // it must be the case that gadget->buffer != NULL , due to the constraint - // on output_gadget_t ; and note we're relying on write_pos being non-negative. - gadget->buffer[write_pos] = c; - } + if (write_pos >= gadget->max_chars) + { + return; + } + if (gadget->function != NULL) + { + // No check for c == '\0' . + gadget->function(c, gadget->extra_function_arg); + } + else + { + // it must be the case that gadget->buffer != NULL , due to the constraint + // on output_gadget_t ; and note we're relying on write_pos being non-negative. + gadget->buffer[write_pos] = c; + } } // Possibly-write the string-terminating '\0' character -static inline void append_termination_with_gadget(output_gadget_t* gadget) +static inline void append_termination_with_gadget(output_gadget_t *gadget) { - if (gadget->function != NULL || gadget->max_chars == 0) { - return; - } - if (gadget->buffer == NULL) { - return; - } - printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; - gadget->buffer[null_char_pos] = '\0'; + if (gadget->function != NULL || gadget->max_chars == 0) + { + return; + } + if (gadget->buffer == NULL) + { + return; + } + printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; + gadget->buffer[null_char_pos] = '\0'; } static inline output_gadget_t discarding_gadget(void) { - output_gadget_t gadget; - gadget.function = NULL; - gadget.extra_function_arg = NULL; - gadget.buffer = NULL; - gadget.pos = 0; - gadget.max_chars = 0; - return gadget; + output_gadget_t gadget; + gadget.function = NULL; + gadget.extra_function_arg = NULL; + gadget.buffer = NULL; + gadget.pos = 0; + gadget.max_chars = 0; + return gadget; } -static inline output_gadget_t buffer_gadget(char* buffer, size_t buffer_size) +static inline output_gadget_t buffer_gadget(char *buffer, size_t buffer_size) { - printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? - PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t) buffer_size; - output_gadget_t result = discarding_gadget(); - if (buffer != NULL) { - result.buffer = buffer; - result.max_chars = usable_buffer_size; - } - return result; + printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t)buffer_size; + output_gadget_t result = discarding_gadget(); + if (buffer != NULL) + { + result.buffer = buffer; + result.max_chars = usable_buffer_size; + } + return result; } // internal secure strlen // @return The length of the string (excluding the terminating 0) limited by 'maxsize' // @note strlen uses size_t, but wes only use this function with printf_size_t // variables - hence the signature. -static inline printf_size_t strnlen_s_(const char* str, printf_size_t maxsize) +static inline printf_size_t strnlen_s_(const char *str, printf_size_t maxsize) { - const char* s; - for (s = str; *s && maxsize--; ++s); - return (printf_size_t)(s - str); + const char *s; + for (s = str; *s && maxsize--; ++s); + return (printf_size_t)(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'); + return (ch >= '0') && (ch <= '9'); } - // internal ASCII string to printf_size_t conversion -static printf_size_t atou_(const char** str) +static printf_size_t atou_(const char **str) { - printf_size_t i = 0U; - while (is_digit_(**str)) { - i = i * 10U + (printf_size_t)(*((*str)++) - '0'); - } - return i; + printf_size_t i = 0U; + while (is_digit_(**str)) + { + i = i * 10U + (printf_size_t)(*((*str)++) - '0'); + } + return i; } - // output the specified string in reverse, taking care of any zero-padding -static void out_rev_(output_gadget_t* output, const char* buf, printf_size_t len, printf_size_t width, printf_flags_t flags) +static void out_rev_(output_gadget_t *output, const char *buf, printf_size_t len, printf_size_t width, printf_flags_t flags) { - const printf_size_t start_pos = output->pos; + const printf_size_t start_pos = output->pos; - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (printf_size_t i = len; i < width; i++) { - putchar_via_gadget(output, ' '); + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (printf_size_t i = len; i < width; i++) + { + putchar_via_gadget(output, ' '); + } } - } - // reverse string - while (len) { - putchar_via_gadget(output, buf[--len]); - } + // reverse string + while (len) + { + putchar_via_gadget(output, buf[--len]); + } - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (output->pos - start_pos < width) { - putchar_via_gadget(output, ' '); + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (output->pos - start_pos < width) + { + putchar_via_gadget(output, ' '); + } } - } } - // 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 void print_integer_finalization(output_gadget_t* output, char* buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +static void print_integer_finalization(output_gadget_t *output, char *buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) { - printf_size_t unpadded_len = len; + printf_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 < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } + // 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 < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } - while ((len < precision) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = '0'; - } + while ((len < precision) && (len < RT_KLIBC_USING_VSNPRINTF_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; + 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--; // This should suffice for BASE_OCTAL - } - if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) { - len--; // ... and an extra one for 0x or 0b - } - } - if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'x'; - } - else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'X'; - } - else if ((base == BASE_BINARY) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE) { - buf[len++] = '0'; + // 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--; // This should suffice for BASE_OCTAL + } + if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) + { + len--; // ... and an extra one for 0x or 0b + } + } + if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == BASE_BINARY) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE) + { + buf[len++] = '0'; + } } - } - if (len < RT_KLIBC_USING_VSNPRINTF_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++] = ' '; + if (len < RT_KLIBC_USING_VSNPRINTF_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++] = ' '; + } } - } - out_rev_(output, buf, len, width, flags); + out_rev_(output, buf, len, width, flags); } // An internal itoa-like function -static void print_integer(output_gadget_t* output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +static void print_integer(output_gadget_t *output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) { - char buf[RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE]; - printf_size_t len = 0U; + char buf[RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE]; + printf_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. + 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 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 < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)); } - } - 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 < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)); - } - print_integer_finalization(output, buf, len, negative, base, precision, width, flags); + print_integer_finalization(output, buf, len, negative, base, precision, width, flags); } #if defined(RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS) || defined(RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS) // Stores a fixed-precision representation of a double relative // to a fixed precision (which cannot be determined by examining this structure) -struct double_components { - int_fast64_t integral; - int_fast64_t fractional; +struct double_components +{ + int_fast64_t integral; + int_fast64_t fractional; // ... truncation of the actual fractional part of the double value, scaled // by the precision value - bool is_negative; + bool is_negative; }; -#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 -#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T +#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 + 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, printf_size_t precision) { - struct double_components number_; - number_.is_negative = get_sign_bit(number); - double abs_number = (number_.is_negative) ? -number : number; - number_.integral = (int_fast64_t)abs_number; - double remainder = (abs_number - (double) number_.integral) * powers_of_10[precision]; - number_.fractional = (int_fast64_t)remainder; + struct double_components number_; + number_.is_negative = get_sign_bit(number); + double abs_number = (number_.is_negative) ? -number : number; + number_.integral = (int_fast64_t)abs_number; + double remainder = (abs_number - (double)number_.integral) * powers_of_10[precision]; + number_.fractional = (int_fast64_t)remainder; - remainder -= (double) number_.fractional; + 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; + 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) && ((number_.fractional == 0U) || (number_.fractional & 1U))) + { + // if halfway, round up if odd OR if last digit is 0 + ++number_.fractional; } - } - else if ((remainder == 0.5) && ((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; + 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_; + return number_; } #ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS -struct scaling_factor { - double raw_factor; - bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it +struct scaling_factor +{ + double raw_factor; + bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it }; static double apply_scaling(double num, struct scaling_factor normalization) { - return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; + return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; } static double unapply_scaling(double normalized, struct scaling_factor normalization) @@ -598,7 +634,7 @@ static double unapply_scaling(double normalized, struct scaling_factor normaliza #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif - return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; + return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; #if defined(__GNUC__) && !defined(__clang__) && !defined(__ARMCC_VERSION) /* GCC */ #pragma GCC diagnostic pop #endif @@ -606,156 +642,182 @@ static double unapply_scaling(double normalized, struct scaling_factor normaliza static 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; + struct scaling_factor result; + if (sf.multiply) + { + result.multiply = true; + result.raw_factor = sf.raw_factor * extra_multiplicative_factor; } - else { - result.multiply = true; - result.raw_factor = extra_multiplicative_factor / sf.raw_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; + return result; } static struct double_components get_normalized_components(bool negative, printf_size_t precision, double non_normalized, struct scaling_factor normalization, int floored_exp10) { - struct double_components components; - components.is_negative = negative; - double scaled = apply_scaling(non_normalized, normalization); + struct double_components components; + components.is_negative = negative; + double scaled = apply_scaling(non_normalized, normalization); - bool close_to_representation_extremum = ( (-floored_exp10 + (int) precision) >= DBL_MAX_10_EXP - 1 ); - if (close_to_representation_extremum) { - // We can't have a normalization factor which also accounts for the precision, i.e. moves - // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. - // So, we'll give up early on getting extra precision... - return get_components(negative ? -scaled : scaled, precision); - } - components.integral = (int_fast64_t) scaled; - 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; + bool close_to_representation_extremum = ((-floored_exp10 + (int)precision) >= DBL_MAX_10_EXP - 1); + if (close_to_representation_extremum) + { + // We can't have a normalization factor which also accounts for the precision, i.e. moves + // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. + // So, we'll give up early on getting extra precision... + return get_components(negative ? -scaled : scaled, precision); + } + components.integral = (int_fast64_t)scaled; + 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; - components.fractional = (int_fast64_t) scaled_remainder; // when precision == 0, the assigned value should be 0 - scaled_remainder -= (double) components.fractional; //when precision == 0, this will not change scaled_remainder + components.fractional = (int_fast64_t)scaled_remainder; // when precision == 0, the assigned value should be 0 + scaled_remainder -= (double)components.fractional; //when precision == 0, this will not change scaled_remainder - 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). - // Note: for precision = 0, this will "translate" the rounding effect from - // the fractional part to the integral part where it should actually be - // felt (as prec_power_of_10 is 1) - if ((double) components.fractional >= prec_power_of_10) { - components.fractional = 0; - ++components.integral; - } - return components; + 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). + // Note: for precision = 0, this will "translate" the rounding effect from + // the fractional part to the integral part where it should actually be + // felt (as prec_power_of_10 is 1) + if ((double)components.fractional >= prec_power_of_10) + { + components.fractional = 0; + ++components.integral; + } + return components; } #endif // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS static void print_broken_up_decimal( - struct double_components number_, output_gadget_t* output, printf_size_t precision, - printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) + struct double_components number_, output_gadget_t *output, printf_size_t precision, + printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) { - if (precision != 0U) { - // do fractional part, as an unsigned number + if (precision != 0U) + { + // do fractional part, as an unsigned number - printf_size_t count = precision; + printf_size_t count = precision; - // %g/%G mandates we skip the trailing 0 digits... - if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) { - while(true) { - int_fast64_t digit = number_.fractional % 10U; - if (digit != 0) { - break; + // %g/%G mandates we skip the trailing 0 digits... + if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (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) } - --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 < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) { - --count; - buf[len++] = (char)('0' + number_.fractional % 10U); - if (!(number_.fractional /= 10U)) { - break; + if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH)) + { + while (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) + { + --count; + buf[len++] = (char)('0' + number_.fractional % 10U); + if (!(number_.fractional /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) + { + buf[len++] = '0'; + --count; + } + if (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) + { + buf[len++] = '.'; + } } - } - // add extra 0s - while ((len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) { - buf[len++] = '0'; - --count; - } - if (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) { - buf[len++] = '.'; - } } - } - else { - if ((flags & FLAGS_HASH) && (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE)) { - buf[len++] = '.'; + else + { + if ((flags & FLAGS_HASH) && (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE)) + { + buf[len++] = '.'; + } } - } - // Write the integer part of the number (it comes after the fractional - // since the character order is reversed) - while (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) { - buf[len++] = (char)('0' + (number_.integral % 10)); - if (!(number_.integral /= 10)) { - break; + // Write the integer part of the number (it comes after the fractional + // since the character order is reversed) + while (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_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 < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE)) { - buf[len++] = '0'; + // 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 < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE)) + { + buf[len++] = '0'; + } } - } - if (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_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++] = ' '; + if (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_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++] = ' '; + } } - } - out_rev_(output, buf, len, width, flags); + out_rev_(output, buf, len, width, flags); } - // internal ftoa for fixed decimal floating point -static void print_decimal_number(output_gadget_t* output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len) +// internal ftoa for fixed decimal floating point +static void print_decimal_number(output_gadget_t *output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) { - struct double_components value_ = get_components(number, precision); - print_broken_up_decimal(value_, output, precision, width, flags, buf, len); + struct double_components value_ = get_components(number, precision); + print_broken_up_decimal(value_, output, precision, width, flags, buf, len); } #ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS @@ -764,570 +826,692 @@ static void print_decimal_number(output_gadget_t* output, double number, printf_ // floor value is representable by an int. static int bastardized_floor(double x) { - if (x >= 0) { return (int) x; } - int n = (int) x; - return ( ((double) n) == x ) ? n : n-1; + if (x >= 0) + { + return (int)x; + } + int n = (int)x; + return (((double)n) == x) ? n : n - 1; } // Computes the base-10 logarithm of the input number - which must be an actual // positive number (not infinity or NaN, nor a sub-normal) static double log10_of_positive(double positive_number) { - // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). - // - // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of - // our input number, and need only solve log_10(M) for M between 1 and 2 (as - // the base-2 mantissa is always 1-point-something). In that limited range, a - // Taylor series expansion of log10(x) should serve us well enough; and we'll - // take the mid-point, 1.5, as the point of expansion. + // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). + // + // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of + // our input number, and need only solve log_10(M) for M between 1 and 2 (as + // the base-2 mantissa is always 1-point-something). In that limited range, a + // Taylor series expansion of log10(x) should serve us well enough; and we'll + // take the mid-point, 1.5, as the point of expansion. - double_with_bit_access dwba = get_bit_access(positive_number); - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - int exp2 = get_exp2(dwba); - // drop the exponent, so dwba.F comes into the range [1,2) - dwba.U = (dwba.U & (((double_uint_t) (1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | - ((double_uint_t) DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS); - double z = (dwba.F - 1.5); - return ( - // Taylor expansion around 1.5: - 0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) - + z * 0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) + double_with_bit_access dwba = get_bit_access(positive_number); + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + int exp2 = get_exp2(dwba); + // drop the exponent, so dwba.F comes into the range [1,2) + dwba.U = (dwba.U & (((double_uint_t)(1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | + ((double_uint_t)DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS); + double z = (dwba.F - 1.5); + return ( + // Taylor expansion around 1.5: + 0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) + + z * 0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) #if RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS > 2 - - z*z * 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) + - z * z * 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) #if RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS > 3 - + z*z*z * 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) + + z * z * z * 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) #endif #endif - // exact log_2 of the exponent x, with logarithm base change - + exp2 * 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) - ); + // exact log_2 of the exponent x, with logarithm base change + + exp2 * 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) + ); } - static double pow10_of_int(int floored_exp10) { - // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. - if (floored_exp10 == DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10) { - return DOUBLE_MAX_SUBNORMAL_POWER_OF_10; - } - // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow - double_with_bit_access dwba; - int exp2 = bastardized_floor(floored_exp10 * 3.321928094887362 + 0.5); - const double z = floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - dwba.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 - dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - return dwba.F; + // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. + if (floored_exp10 == DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10) + { + return DOUBLE_MAX_SUBNORMAL_POWER_OF_10; + } + // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow + double_with_bit_access dwba; + int exp2 = bastardized_floor(floored_exp10 * 3.321928094887362 + 0.5); + const double z = floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + dwba.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 + dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + return dwba.F; } -static void print_exponential_number(output_gadget_t* output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len) +static void print_exponential_number(output_gadget_t *output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) { - const bool negative = get_sign_bit(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 floored_exp10; - bool abs_exp10_covered_by_powers_table; - struct scaling_factor normalization; + const bool negative = get_sign_bit(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 floored_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. - floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table - } - else { - double exp10 = log10_of_positive(abs_number); - floored_exp10 = bastardized_floor(exp10); - double p10 = pow10_of_int(floored_exp10); - // correct for rounding errors - if (abs_number < p10) { - floored_exp10--; - p10 /= 10; + // 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. + floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table + } + else + { + double exp10 = log10_of_positive(abs_number); + floored_exp10 = bastardized_floor(exp10); + double p10 = pow10_of_int(floored_exp10); + // correct for rounding errors + if (abs_number < p10) + { + floored_exp10--; + p10 /= 10; + } + abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; + normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; } - abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; - normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; - } - // 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". + // 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 = (floored_exp10 >= -4 && floored_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 - floored_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 - } + 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 = (floored_exp10 >= -4 && floored_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 - floored_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 = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); - bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_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, floored_exp10); + normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); + bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_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, floored_exp10); - // 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) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) { - floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used - precision--; - // ... and it should already be the case that decimal_part_components.fractional == 0 + // 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) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) + { + floored_exp10++; // Not strictly necessary, since floored_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? } - // TODO: What about rollover strictly within the fractional part? - } - else { - if (decimal_part_components.integral >= 10) { - floored_exp10++; - decimal_part_components.integral = 1; - decimal_part_components.fractional = 0; + else + { + if (decimal_part_components.integral >= 10) + { + floored_exp10++; + decimal_part_components.integral = 1; + decimal_part_components.fractional = 0; + } } - } - // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double - // is "307" (for 2^1023), so we set aside 4-5 characters overall - printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U : 5U; + // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double + // is "307" (for 2^1023), so we set aside 4-5 characters overall + printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U + : 5U; - printf_size_t 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); + printf_size_t 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 printf_size_t printed_exponential_start_pos = output->pos; - print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len); + const printf_size_t printed_exponential_start_pos = output->pos; + print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len); - if (! fall_back_to_decimal_only_mode) { - putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); - print_integer(output, - ABS_FOR_PRINTING(floored_exp10), - floored_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 (output->pos - printed_exponential_start_pos < width) { - putchar_via_gadget(output, ' '); - } + if (!fall_back_to_decimal_only_mode) + { + putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); + print_integer(output, + ABS_FOR_PRINTING(floored_exp10), + floored_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 (output->pos - printed_exponential_start_pos < width) + { + putchar_via_gadget(output, ' '); + } + } } - } } -#endif // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS +#endif // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS -static void print_floating_point(output_gadget_t* output, double value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential) +static void print_floating_point(output_gadget_t *output, double value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential) { - char buf[RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE]; - printf_size_t len = 0U; + char buf[RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE]; + printf_size_t len = 0U; - // test for special values - if (value != value) { - out_rev_(output, "nan", 3, width, flags); - return; - } - if (value < -DBL_MAX) { - out_rev_(output, "fni-", 4, width, flags); - return; - } - if (value > DBL_MAX) { - out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - return; - } + // test for special values + if (value != value) + { + out_rev_(output, "nan", 3, width, flags); + return; + } + if (value < -DBL_MAX) + { + out_rev_(output, "fni-", 4, width, flags); + return; + } + if (value > DBL_MAX) + { + out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + return; + } - 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 (!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. #ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS - print_exponential_number(output, value, precision, width, flags, buf, len); + print_exponential_number(output, value, precision, width, flags, buf, len); #endif - return; - } + return; + } - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - precision = RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION; - } + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + precision = RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION; + } - // limit precision so that our integer holding the fractional part does not overflow - while ((len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { - buf[len++] = '0'; // This respects the precision in terms of result length only - precision--; - } + // limit precision so that our integer holding the fractional part does not overflow + while ((len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) + { + buf[len++] = '0'; // This respects the precision in terms of result length only + precision--; + } #ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS - if (prefer_exponential) - print_exponential_number(output, value, precision, width, flags, buf, len); - else + if (prefer_exponential) + { + print_exponential_number(output, value, precision, width, flags, buf, len); + } + else #endif - print_decimal_number(output, value, precision, width, flags, buf, len); + print_decimal_number(output, value, precision, width, flags, buf, len); } -#endif // (RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS || RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS) +#endif // (RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS || RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS) // Advances the format pointer past the flags, and returns the parsed flags // due to the characters passed -static printf_flags_t parse_flags(const char** format) +static printf_flags_t parse_flags(const char **format) { - printf_flags_t flags = 0U; - do { - switch (**format) { - case '0': flags |= FLAGS_ZEROPAD; (*format)++; break; - case '-': flags |= FLAGS_LEFT; (*format)++; break; - case '+': flags |= FLAGS_PLUS; (*format)++; break; - case ' ': flags |= FLAGS_SPACE; (*format)++; break; - case '#': flags |= FLAGS_HASH; (*format)++; break; - default : return flags; - } - } while (true); + printf_flags_t flags = 0U; + do + { + switch (**format) + { + case '0': + flags |= FLAGS_ZEROPAD; + (*format)++; + break; + case '-': + flags |= FLAGS_LEFT; + (*format)++; + break; + case '+': + flags |= FLAGS_PLUS; + (*format)++; + break; + case ' ': + flags |= FLAGS_SPACE; + (*format)++; + break; + case '#': + flags |= FLAGS_HASH; + (*format)++; + break; + default: + return flags; + } + } while (true); } -static inline void format_string_loop(output_gadget_t* output, const char* format, va_list args) +static inline void format_string_loop(output_gadget_t *output, const char *format, va_list args) { #ifdef RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER -#define ADVANCE_IN_FORMAT_STRING(cptr_) do { (cptr_)++; if (!*(cptr_)) return; } while(0) +#define ADVANCE_IN_FORMAT_STRING(cptr_) \ + do \ + { \ + (cptr_)++; \ + if (!*(cptr_)) \ + return; \ + } while (0) #else #define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++ #endif + while (*format) + { + if (*format != '%') + { + // A regular content character + putchar_via_gadget(output, *format); + format++; + continue; + } + // We're parsing a format specifier: %[flags][width][.precision][length] + ADVANCE_IN_FORMAT_STRING(format); - while (*format) - { - if (*format != '%') { - // A regular content character - putchar_via_gadget(output, *format); - format++; - continue; - } - // We're parsing a format specifier: %[flags][width][.precision][length] - ADVANCE_IN_FORMAT_STRING(format); - - printf_flags_t flags = parse_flags(&format); + printf_flags_t flags = parse_flags(&format); - // evaluate width field - printf_size_t width = 0U; - if (is_digit_(*format)) { - width = (printf_size_t) atou_(&format); - } - else if (*format == '*') { - const int w = va_arg(args, int); - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (printf_size_t)-w; - } - else { - width = (printf_size_t)w; - } - ADVANCE_IN_FORMAT_STRING(format); - } + // evaluate width field + printf_size_t width = 0U; + if (is_digit_(*format)) + { + width = (printf_size_t)atou_(&format); + } + else if (*format == '*') + { + const int w = va_arg(args, int); + if (w < 0) + { + flags |= FLAGS_LEFT; // reverse padding + width = (printf_size_t)-w; + } + else + { + width = (printf_size_t)w; + } + ADVANCE_IN_FORMAT_STRING(format); + } - // evaluate precision field - printf_size_t precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - ADVANCE_IN_FORMAT_STRING(format); - if (is_digit_(*format)) { - precision = (printf_size_t) atou_(&format); - } - else if (*format == '*') { - const int precision_ = va_arg(args, int); - precision = precision_ > 0 ? (printf_size_t) precision_ : 0U; - ADVANCE_IN_FORMAT_STRING(format); - } - } + // evaluate precision field + printf_size_t precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + ADVANCE_IN_FORMAT_STRING(format); + if (is_digit_(*format)) + { + precision = (printf_size_t)atou_(&format); + } + else if (*format == '*') + { + const int precision_ = va_arg(args, int); + precision = precision_ > 0 ? (printf_size_t)precision_ : 0U; + ADVANCE_IN_FORMAT_STRING(format); + } + } - // evaluate length field - switch (*format) { + // evaluate length field + switch (*format) + { #ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS - case 'I' : { - ADVANCE_IN_FORMAT_STRING(format); - // Greedily parse for size in bits: 8, 16, 32 or 64 - switch(*format) { - case '8': flags |= FLAGS_INT8; + case 'I': + { ADVANCE_IN_FORMAT_STRING(format); + // Greedily parse for size in bits: 8, 16, 32 or 64 + switch (*format) + { + case '8': + flags |= FLAGS_INT8; + ADVANCE_IN_FORMAT_STRING(format); + break; + case '1': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '6') + { + format++; + flags |= FLAGS_INT16; + } + break; + case '3': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '2') + { + ADVANCE_IN_FORMAT_STRING(format); + flags |= FLAGS_INT32; + } + break; + case '6': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '4') + { + ADVANCE_IN_FORMAT_STRING(format); + flags |= FLAGS_INT64; + } + break; + default: + break; + } break; - case '1': + } +#endif + case 'l': + flags |= FLAGS_LONG; ADVANCE_IN_FORMAT_STRING(format); - if (*format == '6') { format++; flags |= FLAGS_INT16; } + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + } break; - case '3': + case 'h': + flags |= FLAGS_SHORT; ADVANCE_IN_FORMAT_STRING(format); - if (*format == '2') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT32; } + if (*format == 'h') + { + flags |= FLAGS_CHAR; + ADVANCE_IN_FORMAT_STRING(format); + } break; - case '6': + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); ADVANCE_IN_FORMAT_STRING(format); - if (*format == '4') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT64; } break; - default: break; - } - break; - } -#endif - case 'l' : - flags |= FLAGS_LONG; - ADVANCE_IN_FORMAT_STRING(format); - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - ADVANCE_IN_FORMAT_STRING(format); - } - break; - case 'h' : - flags |= FLAGS_SHORT; - ADVANCE_IN_FORMAT_STRING(format); - if (*format == 'h') { - flags |= FLAGS_CHAR; - ADVANCE_IN_FORMAT_STRING(format); + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + ADVANCE_IN_FORMAT_STRING(format); + break; + default: + break; } - break; - case 't' : - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - case 'j' : - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - case 'z' : - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - ADVANCE_IN_FORMAT_STRING(format); - break; - default: - break; - } - // evaluate specifier - switch (*format) { - case 'd' : - case 'i' : - case 'u' : - case 'x' : - case 'X' : - case 'o' : - case 'b' : { - - if (*format == 'd' || *format == 'i') { - flags |= FLAGS_SIGNED; - } + // evaluate specifier + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + { + if (*format == 'd' || *format == 'i') + { + flags |= FLAGS_SIGNED; + } - 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; // decimal integers have no alternative presentation - } + 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; // decimal integers have no alternative presentation + } - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } + if (*format == 'X') + { + flags |= FLAGS_UPPERCASE; + } - format++; - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; - } + format++; + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) + { + flags &= ~FLAGS_ZEROPAD; + } - if (flags & FLAGS_SIGNED) { - // A signed specifier: d, i or possibly I + bit size if enabled + if (flags & FLAGS_SIGNED) + { + // A signed specifier: d, i or possibly I + bit size if enabled - if (flags & FLAGS_LONG_LONG) { + if (flags & FLAGS_LONG_LONG) + { #ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG - const long long value = va_arg(args, long long); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + const long long value = va_arg(args, long long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); #endif - } - else if (flags & FLAGS_LONG) { - const long value = va_arg(args, long); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); - } - else { - // We never try to interpret the argument as something potentially-smaller than int, - // due to integer promotion rules: Even if the user passed a short int, short unsigned - // etc. - these will come in after promotion, as int's (or unsigned for the case of - // short unsigned when it has the same size as int) - const int value = - (flags & FLAGS_CHAR) ? (signed char) va_arg(args, int) : - (flags & FLAGS_SHORT) ? (short int) va_arg(args, int) : - va_arg(args, int); - print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); - } - } - else { - // An unsigned specifier: u, x, X, o, b + } + else if (flags & FLAGS_LONG) + { + const long value = va_arg(args, long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + else + { + // We never try to interpret the argument as something potentially-smaller than int, + // due to integer promotion rules: Even if the user passed a short int, short unsigned + // etc. - these will come in after promotion, as int's (or unsigned for the case of + // short unsigned when it has the same size as int) + const int value = + (flags & FLAGS_CHAR) ? (signed char)va_arg(args, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(args, int) + : va_arg(args, int); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + } + else + { + // An unsigned specifier: u, x, X, o, b - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - if (flags & FLAGS_LONG_LONG) { + if (flags & FLAGS_LONG_LONG) + { #ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG - print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long long), false, base, precision, width, flags); + print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long long), false, base, precision, width, flags); #endif - } - else if (flags & FLAGS_LONG) { - print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long), false, base, precision, width, flags); - } - else { - const unsigned int value = - (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : - (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) : - va_arg(args, unsigned int); - print_integer(output, (printf_unsigned_value_t) value, false, base, precision, width, flags); - } + } + else if (flags & FLAGS_LONG) + { + print_integer(output, (printf_unsigned_value_t)va_arg(args, unsigned long), false, base, precision, width, flags); + } + else + { + const unsigned int value = + (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) + : va_arg(args, unsigned int); + print_integer(output, (printf_unsigned_value_t)value, false, base, precision, width, flags); + } + } + break; } - break; - } #ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS - case 'f' : - case 'F' : - if (*format == 'F') flags |= FLAGS_UPPERCASE; - print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL); - format++; - break; + case 'f': + case 'F': + if (*format == 'F') + { + flags |= FLAGS_UPPERCASE; + } + print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; #endif #ifdef RT_KLIBC_USING_VSNPRINTF_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; - print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); - format++; - break; -#endif // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS - case 'c' : { - printf_size_t l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - putchar_via_gadget(output, ' '); - } - } - // char output - putchar_via_gadget(output, (char) va_arg(args, int) ); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - putchar_via_gadget(output, ' '); - } + 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; + } + print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + format++; + break; +#endif // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + case 'c': + { + printf_size_t l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } + // char output + putchar_via_gadget(output, (char)va_arg(args, int)); + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } + format++; + break; } - format++; - break; - } - case 's' : { - const char* p = va_arg(args, char*); - if (p == NULL) { - out_rev_(output, ")llun(", 6, width, flags); - } - else { - printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - putchar_via_gadget(output, ' '); + case 's': + { + const char *p = va_arg(args, char *); + if (p == NULL) + { + out_rev_(output, ")llun(", 6, width, flags); } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) { - putchar_via_gadget(output, *(p++)); - --precision; - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - putchar_via_gadget(output, ' '); + else + { + printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); + // pre padding + if (flags & FLAGS_PRECISION) + { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) + { + putchar_via_gadget(output, *(p++)); + --precision; + } + // post padding + if (flags & FLAGS_LEFT) + { + while (l++ < width) + { + putchar_via_gadget(output, ' '); + } + } } - } + format++; + break; } - 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(args, void*); - (value == (uintptr_t) NULL) ? - out_rev_(output, ")lin(", 5, width, flags) : - print_integer(output, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); - 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(args, void *); + (value == (uintptr_t)NULL) ? out_rev_(output, ")lin(", 5, width, flags) : print_integer(output, (printf_unsigned_value_t)value, false, BASE_HEX, precision, width, flags); + format++; + break; + } - case '%' : - putchar_via_gadget(output, '%'); - format++; - break; + case '%': + putchar_via_gadget(output, '%'); + 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. + // 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. #ifdef RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER - case 'n' : { - if (flags & FLAGS_CHAR) *(va_arg(args, char*)) = (char) output->pos; - else if (flags & FLAGS_SHORT) *(va_arg(args, short*)) = (short) output->pos; - else if (flags & FLAGS_LONG) *(va_arg(args, long*)) = (long) output->pos; + case 'n': + { + if (flags & FLAGS_CHAR) + { + *(va_arg(args, char *)) = (char)output->pos; + } + else if (flags & FLAGS_SHORT) + { + *(va_arg(args, short *)) = (short)output->pos; + } + else if (flags & FLAGS_LONG) + { + *(va_arg(args, long *)) = (long)output->pos; + } #ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG - else if (flags & FLAGS_LONG_LONG) *(va_arg(args, long long*)) = (long long int) output->pos; + else if (flags & FLAGS_LONG_LONG) + { + *(va_arg(args, long long *)) = (long long int)output->pos; + } #endif // RT_KLIBC_USING_VSNPRINTF_LONGLONG - else *(va_arg(args, int*)) = (int) output->pos; - format++; - break; - } + else + { + *(va_arg(args, int *)) = (int)output->pos; + } + format++; + break; + } #endif // RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER - default : - putchar_via_gadget(output, *format); - format++; - break; + default: + putchar_via_gadget(output, *format); + format++; + break; + } } - } } // internal vsnprintf - used for implementing _all library functions -static int vsnprintf_impl(output_gadget_t* output, const char* format, va_list args) +static int vsnprintf_impl(output_gadget_t *output, const char *format, va_list args) { - // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is - // possible to call this function with a non-zero pos value for some "remedial printing". - format_string_loop(output, format, args); + // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is + // possible to call this function with a non-zero pos value for some "remedial printing". + format_string_loop(output, format, args); - // termination - append_termination_with_gadget(output); + // termination + append_termination_with_gadget(output); - // return written chars without terminating \0 - return (int)output->pos; + // return written chars without terminating \0 + return (int)output->pos; } /////////////////////////////////////////////////////////////////////////////// @@ -1347,6 +1531,6 @@ static int vsnprintf_impl(output_gadget_t* output, const char* format, va_list a */ int rt_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) { - output_gadget_t gadget = buffer_gadget(buf, size); - return vsnprintf_impl(&gadget, fmt, args); + output_gadget_t gadget = buffer_gadget(buf, size); + return vsnprintf_impl(&gadget, fmt, args); } |
