mirror of
https://gitee.com/Lyon1998/pikapython.git
synced 2025-01-15 17:02:53 +08:00
1271 lines
47 KiB
C
1271 lines
47 KiB
C
/*
|
|
* Copyright (c) 2021, Meco Jianting Man <jiantingman@foxmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2021-11-27 Meco Man porting for rt_vsnprintf as the fully functional
|
|
* version
|
|
*/
|
|
|
|
/**
|
|
* @author (c) Eyal Rozenberg <eyalroz1@gmx.com>
|
|
* 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 <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include "PikaObj.h"
|
|
#include "pika_adapter_rtt.h"
|
|
|
|
#if !PIKASCRIPT_VERSION_REQUIRE_MINIMUN(1, 12, 0)
|
|
#error "pika_vsnprintf.c requires at least PikaScript 1.12.0"
|
|
#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 notatin floating point conversion specifiers (%e,
|
|
// %g, %E, %G)
|
|
#ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS
|
|
#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 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 <float.h>
|
|
#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 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 <m.jasperse@gmail.com>
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* This function will fill a formatted string to buffer.
|
|
*
|
|
* @param buf is the buffer to save formatted string.
|
|
*
|
|
* @param size is the size of buffer.
|
|
*
|
|
* @param fmt is the format parameters.
|
|
*
|
|
* @param args is a list of variable parameters.
|
|
*
|
|
* @return The number of characters actually written to buffer.
|
|
*/
|
|
|
|
int pika_platform_vsnprintf(char* buff,
|
|
size_t size,
|
|
const char* fmt,
|
|
va_list args) {
|
|
return __vsnprintf(out_buffer, buff, size, fmt, args);
|
|
}
|