1597 lines
56 KiB
C++
1597 lines
56 KiB
C++
/* nanoprintf v0.5.5: a tiny embeddable printf replacement written in C.
|
|
https://github.com/charlesnicholson/nanoprintf
|
|
charles.nicholson+nanoprintf@gmail.com
|
|
dual-licensed under 0bsd and unlicense, take your pick. see eof for details. */
|
|
|
|
#ifndef NPF_H_INCLUDED
|
|
#define NPF_H_INCLUDED
|
|
|
|
#ifdef NANOPRINTF_CONFIG_FILE
|
|
#include NANOPRINTF_CONFIG_FILE
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
|
|
typedef void (*npf_putc)(int c, void *ctx);
|
|
|
|
// Define this to fully sandbox nanoprintf inside of a translation unit.
|
|
#ifdef NANOPRINTF_VISIBILITY_STATIC
|
|
#define NPF_VISIBILITY static
|
|
#else
|
|
#define NPF_VISIBILITY extern
|
|
#endif
|
|
|
|
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
|
|
#define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \
|
|
__attribute__((format(printf, FORMAT_INDEX, VARGS_INDEX)))
|
|
#else
|
|
#define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX)
|
|
#endif
|
|
|
|
#if defined(NANOPRINTF_USE_FLOAT_SINGLE_PRECISION) && NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1
|
|
#define NPF_PRINTF_SP_ATTR NPF_PRINTF_ATTR(3, 0)
|
|
#define NPF_MAP_ARGS(...) NPF__MAP(NPF__WRAP, __VA_ARGS__)
|
|
typedef struct { float val; } npf_float_t;
|
|
#define npf_snprintf_ npf_snprintf_sp_
|
|
#define npf_pprintf_ npf_pprintf_sp_
|
|
#define npf_vsnprintf npf_vsnprintf_sp
|
|
#define npf_vpprintf npf_vpprintf_sp
|
|
#else
|
|
#define NPF_PRINTF_SP_ATTR NPF_PRINTF_ATTR(3, 4)
|
|
#define NPF_MAP_ARGS(...) __VA_ARGS__
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
#define NPF_RESTRICT
|
|
extern "C" {
|
|
#else
|
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
|
#define NPF_RESTRICT restrict
|
|
#else
|
|
#define NPF_RESTRICT
|
|
#endif
|
|
#endif
|
|
|
|
NPF_VISIBILITY int npf_snprintf_(char * NPF_RESTRICT buffer,
|
|
size_t bufsz,
|
|
const char * NPF_RESTRICT format, ...)
|
|
NPF_PRINTF_SP_ATTR;
|
|
|
|
NPF_VISIBILITY int npf_pprintf_(npf_putc pc,
|
|
void * NPF_RESTRICT pc_ctx,
|
|
char const * NPF_RESTRICT format, ...)
|
|
NPF_PRINTF_SP_ATTR;
|
|
|
|
// Public API
|
|
|
|
// The npf_ functions all return the number of bytes required to express the
|
|
// fully-formatted string, not including the null terminator character.
|
|
// The npf_ functions do not return negative values, since the lack of 'l' length
|
|
// modifier support makes encoding errors impossible.
|
|
|
|
#define npf_snprintf(buf, sz, ...) npf_snprintf_((buf), (sz), NPF_MAP_ARGS(__VA_ARGS__))
|
|
#define npf_pprintf(pc, ctx, ...) npf_pprintf_((pc), (ctx), NPF_MAP_ARGS(__VA_ARGS__))
|
|
|
|
NPF_VISIBILITY int npf_vsnprintf(char * NPF_RESTRICT buffer,
|
|
size_t bufsz,
|
|
char const * NPF_RESTRICT format,
|
|
va_list vlist) NPF_PRINTF_ATTR(3, 0);
|
|
|
|
NPF_VISIBILITY int npf_vpprintf(npf_putc pc,
|
|
void * NPF_RESTRICT pc_ctx,
|
|
char const * NPF_RESTRICT format,
|
|
va_list vlist) NPF_PRINTF_ATTR(3, 0);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // NPF_H_INCLUDED
|
|
|
|
/* The implementation of nanoprintf begins here, to be compiled only if
|
|
NANOPRINTF_IMPLEMENTATION is defined. In a multi-file library what follows would
|
|
be nanoprintf.c. */
|
|
|
|
#ifdef NANOPRINTF_IMPLEMENTATION
|
|
|
|
#ifndef NPF_IMPLEMENTATION_INCLUDED
|
|
#define NPF_IMPLEMENTATION_INCLUDED
|
|
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
|
|
// Pick reasonable defaults if nothing's been configured.
|
|
#if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \
|
|
!defined(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS) && \
|
|
!defined(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS) && \
|
|
!defined(NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS) && \
|
|
!defined(NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS) && \
|
|
!defined(NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS) && \
|
|
!defined(NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS) && \
|
|
!defined(NANOPRINTF_USE_ALT_FORM_FLAG) && \
|
|
!defined(NANOPRINTF_USE_FLOAT_SINGLE_PRECISION)
|
|
#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1
|
|
#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1
|
|
#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1
|
|
#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS 1
|
|
#define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_ALT_FORM_FLAG 1
|
|
#define NANOPRINTF_USE_FLOAT_SINGLE_PRECISION 0
|
|
#endif
|
|
|
|
// Single-precision mode defaults to off unless explicitly enabled.
|
|
#ifndef NANOPRINTF_USE_FLOAT_SINGLE_PRECISION
|
|
#define NANOPRINTF_USE_FLOAT_SINGLE_PRECISION 0
|
|
#endif
|
|
|
|
// Optional flag, defaults to 0 if not explicitly configured.
|
|
#ifndef NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER
|
|
#define NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER 0
|
|
#endif
|
|
|
|
// If anything's been configured, everything must be configured.
|
|
#ifndef NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS
|
|
#error NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS must be #defined to 0 or 1
|
|
#endif
|
|
#ifndef NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS
|
|
#error NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS must be #defined to 0 or 1
|
|
#endif
|
|
#ifndef NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS
|
|
#error NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS must be #defined to 0 or 1
|
|
#endif
|
|
#ifndef NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS
|
|
#error NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS must be #defined to 0 or 1
|
|
#endif
|
|
#ifndef NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS
|
|
#error NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS must be #defined to 0 or 1
|
|
#endif
|
|
#ifndef NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS
|
|
#error NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS must be #defined to 0 or 1
|
|
#endif
|
|
#ifndef NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS
|
|
#error NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS must be #defined to 0 or 1
|
|
#endif
|
|
|
|
// Ensure flags are compatible.
|
|
#if (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1) && \
|
|
(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 0)
|
|
#error Precision format specifiers must be enabled if float support is enabled.
|
|
#endif
|
|
|
|
#if (NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1) && \
|
|
(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 0)
|
|
#error Float format specifiers must be enabled if float hex support is enabled.
|
|
#endif
|
|
|
|
#if (NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1) && \
|
|
(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 0)
|
|
#error Single precision requires float format specifiers to be enabled.
|
|
#endif
|
|
|
|
#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1
|
|
#if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL
|
|
#error Single-precision float mode requires /Zc:preprocessor on MSVC.
|
|
#endif
|
|
#ifdef __cplusplus
|
|
#if __cplusplus < 201103L && !defined(_MSC_VER)
|
|
#error Single-precision float wrapping requires C++11 or later.
|
|
#endif
|
|
#else
|
|
#if !(defined(__GNUC__) || defined(__clang__))
|
|
#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 201112L)
|
|
#error Single-precision float wrapping requires C11 or later (or GCC/Clang).
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
// The conversion buffer must fit at least UINT64_MAX in octal format with the leading '0'.
|
|
// When floats are enabled, a larger buffer is needed for values like FLT_MAX / DBL_MAX.
|
|
#ifndef NANOPRINTF_CONVERSION_BUFFER_SIZE
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
#define NANOPRINTF_CONVERSION_BUFFER_SIZE 64
|
|
#else
|
|
#define NANOPRINTF_CONVERSION_BUFFER_SIZE 23
|
|
#endif
|
|
#endif
|
|
#if NANOPRINTF_CONVERSION_BUFFER_SIZE < 23
|
|
#error The size of the conversion buffer must be at least 23 bytes.
|
|
#endif
|
|
|
|
// intmax_t / uintmax_t require stdint from c99 / c++11
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
#ifndef _MSC_VER
|
|
#ifdef __cplusplus
|
|
#if __cplusplus < 201103L
|
|
#error large format specifier support requires C++11 or later.
|
|
#endif
|
|
#else
|
|
#if __STDC_VERSION__ < 199409L
|
|
#error nanoprintf requires C99 or later.
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
// Figure out if we can disable warnings with pragmas.
|
|
#ifdef __clang__
|
|
#define NPF_CLANG 1
|
|
#define NPF_GCC_PAST_4_6 0
|
|
#else
|
|
#define NPF_CLANG 0
|
|
#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6)))
|
|
#define NPF_GCC_PAST_4_6 1
|
|
#else
|
|
#define NPF_GCC_PAST_4_6 0
|
|
#endif
|
|
#endif
|
|
|
|
#if NPF_CLANG || NPF_GCC_PAST_4_6
|
|
#define NPF_HAVE_GCC_WARNING_PRAGMAS 1
|
|
#else
|
|
#define NPF_HAVE_GCC_WARNING_PRAGMAS 0
|
|
#endif
|
|
|
|
#if NPF_HAVE_GCC_WARNING_PRAGMAS
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpragmas"
|
|
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
|
#pragma GCC diagnostic ignored "-Wgnu-statement-expression-from-macro-expansion"
|
|
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
|
#pragma GCC diagnostic ignored "-Wpadded"
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
|
|
|
#ifdef __cplusplus
|
|
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
|
#endif
|
|
|
|
#if NPF_CLANG
|
|
#pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
|
|
#pragma GCC diagnostic ignored "-Wcovered-switch-default"
|
|
#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
|
|
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
|
|
#ifndef __APPLE__
|
|
#pragma GCC diagnostic ignored "-Wunsafe-buffer-usage"
|
|
#endif
|
|
#elif NPF_GCC_PAST_4_6
|
|
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4619) // there is no warning number 'number'
|
|
// C4619 has to be disabled first!
|
|
#pragma warning(disable:4127) // conditional expression is constant
|
|
#pragma warning(disable:4505) // unreferenced local function has been removed
|
|
#pragma warning(disable:4514) // unreferenced inline function has been removed
|
|
#pragma warning(disable:4701) // potentially uninitialized local variable used
|
|
#pragma warning(disable:4706) // assignment within conditional expression
|
|
#pragma warning(disable:4710) // function not inlined
|
|
#pragma warning(disable:4711) // function selected for inline expansion
|
|
#pragma warning(disable:4820) // padding added after struct member
|
|
#pragma warning(disable:5039) // potentially throwing function passed to extern C function
|
|
#pragma warning(disable:5045) // compiler will insert Spectre mitigation for memory load
|
|
#pragma warning(disable:5262) // implicit switch fall-through
|
|
#pragma warning(disable:26812) // enum type is unscoped
|
|
#endif
|
|
|
|
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
|
|
#define NPF_NOINLINE __attribute__((noinline))
|
|
#define NPF_FORCE_INLINE inline __attribute__((always_inline))
|
|
|
|
#define NPF_MIN(X, Y) ({ \
|
|
__typeof__(X) const x = (X); __typeof__(Y) const y = (Y); x <= y ? x : y; })
|
|
#define NPF_MAX(X, Y) ({ \
|
|
__typeof__(X) const x = (X); __typeof__(Y) const y = (Y); x >= y ? x : y; })
|
|
#else
|
|
#if defined(_MSC_VER)
|
|
#define NPF_NOINLINE __declspec(noinline)
|
|
#define NPF_FORCE_INLINE inline __forceinline
|
|
#else
|
|
#define NPF_NOINLINE
|
|
#define NPF_FORCE_INLINE
|
|
#endif
|
|
|
|
#define NPF_MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
|
|
#define NPF_MAX(X, Y) ((X) >= (Y) ? (X) : (Y))
|
|
#endif
|
|
|
|
#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) || \
|
|
(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1)
|
|
enum {
|
|
NPF_FMT_SPEC_OPT_NONE,
|
|
NPF_FMT_SPEC_OPT_LITERAL,
|
|
NPF_FMT_SPEC_OPT_STAR,
|
|
};
|
|
#endif
|
|
|
|
enum {
|
|
NPF_FMT_SPEC_LEN_MOD_NONE,
|
|
#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1
|
|
NPF_FMT_SPEC_LEN_MOD_SHORT, // 'h'
|
|
NPF_FMT_SPEC_LEN_MOD_CHAR, // 'hh'
|
|
#endif
|
|
NPF_FMT_SPEC_LEN_MOD_LONG, // 'l'
|
|
NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE, // 'L'
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG, // 'll'
|
|
NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX, // 'j'
|
|
NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET, // 'z'
|
|
NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT, // 't'
|
|
#endif
|
|
};
|
|
|
|
enum {
|
|
NPF_FMT_SPEC_CONV_NONE,
|
|
NPF_FMT_SPEC_CONV_PERCENT, // '%'
|
|
NPF_FMT_SPEC_CONV_CHAR, // 'c'
|
|
NPF_FMT_SPEC_CONV_STRING, // 's'
|
|
NPF_FMT_SPEC_CONV_SIGNED_INT, // 'i', 'd'
|
|
#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1
|
|
NPF_FMT_SPEC_CONV_BINARY, // 'b'
|
|
#endif
|
|
NPF_FMT_SPEC_CONV_OCTAL, // 'o'
|
|
NPF_FMT_SPEC_CONV_HEX_INT, // 'x', 'X'
|
|
NPF_FMT_SPEC_CONV_UNSIGNED_INT, // 'u'
|
|
NPF_FMT_SPEC_CONV_POINTER, // 'p'
|
|
#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1
|
|
NPF_FMT_SPEC_CONV_WRITEBACK, // 'n'
|
|
#endif
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F'
|
|
NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E'
|
|
NPF_FMT_SPEC_CONV_FLOAT_SHORTEST, // 'g', 'G'
|
|
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
|
|
NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A'
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
typedef struct npf_format_spec {
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
int field_width;
|
|
#endif
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
int prec;
|
|
uint8_t prec_opt;
|
|
#endif
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
uint8_t field_width_opt;
|
|
char left_justified; // '-'
|
|
char leading_zero_pad; // '0'
|
|
#endif
|
|
char prepend; // ' ' or '+'
|
|
#if NANOPRINTF_USE_ALT_FORM_FLAG == 1
|
|
char alt_form; // '#'
|
|
#endif
|
|
char case_adjust; // 'a' - 'A' , or 0 (must be non-negative to work)
|
|
uint8_t length_modifier;
|
|
uint8_t conv_spec;
|
|
} npf_format_spec_t;
|
|
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
typedef intmax_t npf_int_t;
|
|
typedef uintmax_t npf_uint_t;
|
|
#elif ULONG_MAX > UINTPTR_MAX
|
|
typedef long npf_int_t;
|
|
typedef unsigned long npf_uint_t;
|
|
#else
|
|
typedef intptr_t npf_int_t;
|
|
typedef uintptr_t npf_uint_t;
|
|
#endif
|
|
|
|
typedef struct npf_bufputc_ctx {
|
|
char *dst;
|
|
size_t len;
|
|
size_t cur;
|
|
} npf_bufputc_ctx_t;
|
|
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
typedef char npf_size_is_ptrdiff[(sizeof(size_t) == sizeof(ptrdiff_t)) ? 1 : -1];
|
|
typedef ptrdiff_t npf_ssize_t;
|
|
typedef size_t npf_uptrdiff_t;
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#include <intrin.h>
|
|
#endif
|
|
|
|
static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) {
|
|
char const *cur = format;
|
|
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
out_spec->left_justified = 0;
|
|
out_spec->leading_zero_pad = 0;
|
|
#endif
|
|
out_spec->case_adjust = 'a' - 'A'; // lowercase
|
|
out_spec->prepend = 0;
|
|
#if NANOPRINTF_USE_ALT_FORM_FLAG == 1
|
|
out_spec->alt_form = 0;
|
|
#endif
|
|
|
|
while (*++cur) { // cur points at the leading '%' character
|
|
switch (*cur) { // Optional flags
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
case '-': out_spec->left_justified = '-'; continue;
|
|
case '0': out_spec->leading_zero_pad = 1; continue;
|
|
#endif
|
|
case '+': out_spec->prepend = '+'; continue;
|
|
case ' ': if (out_spec->prepend == 0) { out_spec->prepend = ' '; } continue;
|
|
#if NANOPRINTF_USE_ALT_FORM_FLAG == 1
|
|
case '#': out_spec->alt_form = '#'; continue;
|
|
#endif
|
|
default: break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
out_spec->field_width = 0;
|
|
out_spec->field_width_opt = NPF_FMT_SPEC_OPT_NONE;
|
|
if (*cur == '*') {
|
|
out_spec->field_width_opt = NPF_FMT_SPEC_OPT_STAR;
|
|
++cur;
|
|
} else {
|
|
while ((*cur >= '0') && (*cur <= '9')) {
|
|
out_spec->field_width_opt = NPF_FMT_SPEC_OPT_LITERAL;
|
|
out_spec->field_width = (out_spec->field_width * 10) + (*cur++ - '0');
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
out_spec->prec = 0;
|
|
out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE;
|
|
if (*cur == '.') {
|
|
++cur;
|
|
if (*cur == '*') {
|
|
out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR;
|
|
++cur;
|
|
} else {
|
|
if (*cur == '-') {
|
|
++cur;
|
|
} else {
|
|
out_spec->prec_opt = NPF_FMT_SPEC_OPT_LITERAL;
|
|
}
|
|
while ((*cur >= '0') && (*cur <= '9')) {
|
|
out_spec->prec = (out_spec->prec * 10) + (*cur++ - '0');
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
uint_fast8_t tmp_conv = NPF_FMT_SPEC_CONV_NONE;
|
|
out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE;
|
|
switch (*cur++) { // Length modifier
|
|
#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1
|
|
case 'h':
|
|
out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_SHORT;
|
|
if (*cur == 'h') {
|
|
out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_CHAR;
|
|
++cur;
|
|
}
|
|
break;
|
|
#endif
|
|
case 'l':
|
|
out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG;
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
if (*cur == 'l') {
|
|
out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG;
|
|
++cur;
|
|
}
|
|
#endif
|
|
break;
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
case 'L': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE; break;
|
|
#endif
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
case 'j': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX; break;
|
|
case 'z': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET; break;
|
|
case 't': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT; break;
|
|
#endif
|
|
default: --cur; break;
|
|
}
|
|
|
|
switch (*cur++) { // Conversion specifier
|
|
case '%': out_spec->conv_spec = NPF_FMT_SPEC_CONV_PERCENT;
|
|
break;
|
|
|
|
case 'c': out_spec->conv_spec = NPF_FMT_SPEC_CONV_CHAR;
|
|
break;
|
|
|
|
case 's': out_spec->conv_spec = NPF_FMT_SPEC_CONV_STRING;
|
|
break;
|
|
|
|
case 'i':
|
|
case 'd': tmp_conv = NPF_FMT_SPEC_CONV_SIGNED_INT; goto finish;
|
|
case 'o': tmp_conv = NPF_FMT_SPEC_CONV_OCTAL; goto finish;
|
|
case 'u': tmp_conv = NPF_FMT_SPEC_CONV_UNSIGNED_INT; goto finish;
|
|
case 'X': out_spec->case_adjust = 0;
|
|
case 'x': tmp_conv = NPF_FMT_SPEC_CONV_HEX_INT; goto finish;
|
|
finish:
|
|
out_spec->conv_spec = (uint8_t)tmp_conv;
|
|
break;
|
|
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
case 'F': out_spec->case_adjust = 0;
|
|
case 'f':
|
|
out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_DEC;
|
|
break;
|
|
|
|
case 'E': out_spec->case_adjust = 0;
|
|
case 'e':
|
|
out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SCI;
|
|
break;
|
|
|
|
case 'G': out_spec->case_adjust = 0;
|
|
case 'g':
|
|
out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SHORTEST;
|
|
break;
|
|
|
|
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
|
|
case 'A': out_spec->case_adjust = 0;
|
|
case 'a':
|
|
out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_HEX;
|
|
break;
|
|
#endif
|
|
#endif
|
|
|
|
#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1
|
|
case 'n':
|
|
// todo: reject string if flags or width or precision exist
|
|
out_spec->conv_spec = NPF_FMT_SPEC_CONV_WRITEBACK;
|
|
break;
|
|
#endif
|
|
|
|
case 'p':
|
|
out_spec->conv_spec = NPF_FMT_SPEC_CONV_POINTER;
|
|
break;
|
|
|
|
#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1
|
|
case 'B':
|
|
out_spec->case_adjust = 0;
|
|
case 'b':
|
|
out_spec->conv_spec = NPF_FMT_SPEC_CONV_BINARY;
|
|
break;
|
|
#endif
|
|
|
|
default: return 0;
|
|
}
|
|
|
|
return (int)(cur - format);
|
|
}
|
|
|
|
static NPF_NOINLINE int npf_utoa_rev(
|
|
npf_uint_t val, char *buf, uint_fast8_t base, char case_adj) {
|
|
uint_fast8_t n = 0;
|
|
do {
|
|
int_fast8_t const d = (int_fast8_t)(val % base);
|
|
*buf++ = (char)(((d < 10) ? '0' : ('A' - 10 + case_adj)) + d);
|
|
++n;
|
|
val /= base;
|
|
} while (val);
|
|
return (int)n;
|
|
}
|
|
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
|
|
#include <float.h>
|
|
|
|
#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1
|
|
typedef float npf_real_t;
|
|
#define NPF_REAL_MANT_DIG FLT_MANT_DIG
|
|
#define NPF_REAL_MAX_EXP FLT_MAX_EXP
|
|
#else
|
|
typedef double npf_real_t;
|
|
#define NPF_REAL_MANT_DIG DBL_MANT_DIG
|
|
#define NPF_REAL_MAX_EXP DBL_MAX_EXP
|
|
#endif
|
|
|
|
#if (NPF_REAL_MANT_DIG <= 11) && (NPF_REAL_MAX_EXP <= 16)
|
|
typedef uint_fast16_t npf_real_bin_t;
|
|
typedef int_fast8_t npf_ftoa_exp_t;
|
|
#elif (NPF_REAL_MANT_DIG <= 24) && (NPF_REAL_MAX_EXP <= 128)
|
|
typedef uint_fast32_t npf_real_bin_t;
|
|
typedef int_fast16_t npf_ftoa_exp_t;
|
|
#elif (NPF_REAL_MANT_DIG <= 53) && (NPF_REAL_MAX_EXP <= 1024)
|
|
typedef uint_fast64_t npf_real_bin_t;
|
|
typedef int_fast16_t npf_ftoa_exp_t;
|
|
#else
|
|
#error Unsupported width of the real type.
|
|
#endif
|
|
|
|
// The floating point conversion code works with an unsigned integer type of any size.
|
|
#ifndef NANOPRINTF_CONVERSION_FLOAT_TYPE
|
|
#define NANOPRINTF_CONVERSION_FLOAT_TYPE unsigned int
|
|
#endif
|
|
typedef NANOPRINTF_CONVERSION_FLOAT_TYPE npf_ftoa_man_t;
|
|
|
|
#if (NANOPRINTF_CONVERSION_BUFFER_SIZE <= UINT_FAST8_MAX) && (UINT_FAST8_MAX <= INT_MAX)
|
|
typedef uint_fast8_t npf_ftoa_dec_t;
|
|
#else
|
|
typedef int npf_ftoa_dec_t;
|
|
#endif
|
|
|
|
enum {
|
|
NPF_REAL_EXP_MASK = NPF_REAL_MAX_EXP * 2 - 1,
|
|
NPF_REAL_EXP_BIAS = NPF_REAL_MAX_EXP - 1,
|
|
NPF_REAL_MAN_BITS = NPF_REAL_MANT_DIG - 1,
|
|
NPF_REAL_BIN_BITS = sizeof(npf_real_bin_t) * CHAR_BIT,
|
|
NPF_REAL_SIGN_POS = sizeof(npf_real_t) * CHAR_BIT - 1,
|
|
NPF_FTOA_MAN_BITS = sizeof(npf_ftoa_man_t) * CHAR_BIT,
|
|
NPF_FTOA_SHIFT_BITS =
|
|
((NPF_FTOA_MAN_BITS < NPF_REAL_MANT_DIG) ? NPF_FTOA_MAN_BITS : NPF_REAL_MANT_DIG) - 1
|
|
};
|
|
|
|
/* Generally, floating-point conversion implementations use
|
|
grisu2 (https://bit.ly/2JgMggX) and ryu (https://bit.ly/2RLXSg0) algorithms,
|
|
which are mathematically exact and fast, but require large lookup tables.
|
|
|
|
This implementation was inspired by Wojciech Muła's (zdjęcia@garnek.pl)
|
|
algorithm (http://0x80.pl/notesen/2015-12-29-float-to-string.html) and
|
|
extended further by adding dynamic scaling and configurable integer width by
|
|
Oskars Rubenis (https://github.com/Okarss). */
|
|
|
|
static NPF_FORCE_INLINE npf_real_bin_t npf_real_to_int_rep(npf_real_t f) {
|
|
// Union-cast is UB pre-C11 and in all C++; the compiler optimizes the code below.
|
|
npf_real_bin_t bin = 0;
|
|
char const *src = (char const *)&f;
|
|
char *dst = (char *)&bin;
|
|
for (uint_fast8_t i = 0; i < sizeof(f); ++i) { dst[i] = src[i]; }
|
|
return bin;
|
|
}
|
|
|
|
static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, npf_real_t f) {
|
|
char const *ret = NULL;
|
|
npf_real_bin_t bin = npf_real_to_int_rep(f);
|
|
|
|
// Unsigned -> signed int casting is IB and can raise a signal but generally doesn't.
|
|
npf_ftoa_exp_t exp =
|
|
(npf_ftoa_exp_t)((npf_ftoa_exp_t)(bin >> NPF_REAL_MAN_BITS) & NPF_REAL_EXP_MASK);
|
|
|
|
bin &= ((npf_real_bin_t)0x1 << NPF_REAL_MAN_BITS) - 1;
|
|
if (exp == (npf_ftoa_exp_t)NPF_REAL_EXP_MASK) { // special value
|
|
ret = (bin) ? "NAN" : "FNI";
|
|
goto exit;
|
|
}
|
|
if (spec->prec > (NANOPRINTF_CONVERSION_BUFFER_SIZE - 2)) { goto exit; }
|
|
if (exp) { // normal number
|
|
bin |= (npf_real_bin_t)0x1 << NPF_REAL_MAN_BITS;
|
|
} else { // subnormal number
|
|
++exp;
|
|
}
|
|
exp = (npf_ftoa_exp_t)(exp - NPF_REAL_EXP_BIAS);
|
|
|
|
uint_fast8_t carry; carry = 0;
|
|
npf_ftoa_dec_t end, dec; dec = (npf_ftoa_dec_t)spec->prec;
|
|
if (dec
|
|
#if NANOPRINTF_USE_ALT_FORM_FLAG == 1
|
|
|| spec->alt_form
|
|
#endif
|
|
) {
|
|
buf[dec++] = '.';
|
|
}
|
|
|
|
{ // Integer part
|
|
npf_ftoa_man_t man_i;
|
|
|
|
if (exp >= 0) {
|
|
int_fast8_t shift_i =
|
|
(int_fast8_t)((exp > NPF_FTOA_SHIFT_BITS) ? (int)NPF_FTOA_SHIFT_BITS : exp);
|
|
npf_ftoa_exp_t exp_i = (npf_ftoa_exp_t)(exp - shift_i);
|
|
shift_i = (int_fast8_t)(NPF_REAL_MAN_BITS - shift_i);
|
|
man_i = (npf_ftoa_man_t)(bin >> shift_i);
|
|
|
|
if (exp_i) {
|
|
if (shift_i) {
|
|
carry = (bin >> (shift_i - 1)) & 0x1;
|
|
}
|
|
exp = NPF_REAL_MAN_BITS; // invalidate the fraction part
|
|
}
|
|
|
|
// Scale the exponent from base-2 to base-10.
|
|
for (; exp_i; --exp_i) {
|
|
if (!(man_i & ((npf_ftoa_man_t)0x1 << (NPF_FTOA_MAN_BITS - 1)))) {
|
|
man_i = (npf_ftoa_man_t)(man_i << 1);
|
|
man_i = (npf_ftoa_man_t)(man_i | carry); carry = 0;
|
|
} else {
|
|
if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; }
|
|
buf[dec++] = '0';
|
|
carry = (((uint_fast8_t)(man_i % 5) + carry) > 2);
|
|
man_i /= 5;
|
|
}
|
|
}
|
|
} else {
|
|
man_i = 0;
|
|
}
|
|
end = dec;
|
|
|
|
do { // Print the integer
|
|
if (end >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; }
|
|
buf[end++] = (char)('0' + (char)(man_i % 10));
|
|
man_i /= 10;
|
|
} while (man_i);
|
|
}
|
|
|
|
{ // Fraction part
|
|
npf_ftoa_man_t man_f;
|
|
npf_ftoa_dec_t dec_f = (npf_ftoa_dec_t)spec->prec;
|
|
|
|
if (exp < NPF_REAL_MAN_BITS) {
|
|
int_fast8_t shift_f = (int_fast8_t)((exp < 0) ? -1 : exp);
|
|
npf_ftoa_exp_t exp_f = (npf_ftoa_exp_t)(exp - shift_f);
|
|
npf_real_bin_t bin_f =
|
|
bin << ((NPF_REAL_BIN_BITS - NPF_REAL_MAN_BITS) + shift_f);
|
|
|
|
// This if-else statement can be completely optimized at compile time.
|
|
if (NPF_REAL_BIN_BITS > NPF_FTOA_MAN_BITS) {
|
|
man_f = (npf_ftoa_man_t)(bin_f >> ((unsigned)(NPF_REAL_BIN_BITS -
|
|
NPF_FTOA_MAN_BITS) %
|
|
NPF_REAL_BIN_BITS));
|
|
carry = (uint_fast8_t)((bin_f >> ((unsigned)(NPF_REAL_BIN_BITS -
|
|
NPF_FTOA_MAN_BITS - 1) %
|
|
NPF_REAL_BIN_BITS)) & 0x1);
|
|
} else {
|
|
man_f = (npf_ftoa_man_t)((npf_ftoa_man_t)bin_f
|
|
<< ((unsigned)(NPF_FTOA_MAN_BITS -
|
|
NPF_REAL_BIN_BITS) % NPF_FTOA_MAN_BITS));
|
|
carry = 0;
|
|
}
|
|
|
|
// Scale the exponent from base-2 to base-10 and prepare the first digit.
|
|
for (uint_fast8_t digit = 0; dec_f && (exp_f < 4); ++exp_f) {
|
|
if ((man_f > ((npf_ftoa_man_t)-4 / 5)) || digit) {
|
|
carry = (uint_fast8_t)(man_f & 0x1);
|
|
man_f = (npf_ftoa_man_t)(man_f >> 1);
|
|
} else {
|
|
man_f = (npf_ftoa_man_t)(man_f * 5);
|
|
if (carry) { man_f = (npf_ftoa_man_t)(man_f + 3); carry = 0; }
|
|
if (exp_f < 0) {
|
|
buf[--dec_f] = '0';
|
|
} else {
|
|
++digit;
|
|
}
|
|
}
|
|
}
|
|
man_f = (npf_ftoa_man_t)(man_f + carry);
|
|
carry = (exp_f >= 0);
|
|
dec = 0;
|
|
} else {
|
|
man_f = 0;
|
|
}
|
|
|
|
if (dec_f) {
|
|
// Print the fraction
|
|
for (;;) {
|
|
buf[--dec_f] = (char)('0' + (char)(man_f >> (NPF_FTOA_MAN_BITS - 4)));
|
|
man_f = (npf_ftoa_man_t)(man_f & ~((npf_ftoa_man_t)0xF << (NPF_FTOA_MAN_BITS - 4)));
|
|
if (!dec_f) { break; }
|
|
man_f = (npf_ftoa_man_t)(man_f * 10);
|
|
}
|
|
man_f = (npf_ftoa_man_t)(man_f << 4);
|
|
}
|
|
if (exp < NPF_REAL_MAN_BITS) {
|
|
carry &= (uint_fast8_t)(man_f >> (NPF_FTOA_MAN_BITS - 1));
|
|
}
|
|
}
|
|
|
|
// Round the number
|
|
for (; carry; ++dec) {
|
|
if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; }
|
|
if (dec >= end) { buf[end++] = '0'; }
|
|
if (buf[dec] == '.') { continue; }
|
|
carry = (buf[dec] == '9');
|
|
buf[dec] = (char)(carry ? '0' : (buf[dec] + 1));
|
|
}
|
|
|
|
return (int)end;
|
|
exit:
|
|
if (!ret) { ret = "RRE"; }
|
|
uint_fast8_t i;
|
|
for (i = 0; ret[i]; ++i) { buf[i] = (char)(ret[i] + spec->case_adjust); }
|
|
return -(int)i;
|
|
}
|
|
|
|
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
|
|
|
|
// Hex float always operates on IEEE 754 binary64 (double).
|
|
// When not in single-precision mode, npf_real_* already handles double.
|
|
#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1
|
|
typedef uint_fast64_t npf_double_bin_t;
|
|
enum {
|
|
NPF_DOUBLE_EXP_MASK = 2047,
|
|
NPF_DOUBLE_EXP_BIAS = 1023,
|
|
NPF_DOUBLE_MAN_BITS = 52,
|
|
};
|
|
static NPF_FORCE_INLINE npf_double_bin_t npf_double_to_int_rep(double f) {
|
|
npf_double_bin_t bin = 0;
|
|
char const *src = (char const *)&f;
|
|
char *dst = (char *)&bin;
|
|
for (uint_fast8_t i = 0; i < sizeof(f); ++i) { dst[i] = src[i]; }
|
|
return bin;
|
|
}
|
|
#else
|
|
typedef npf_real_bin_t npf_double_bin_t;
|
|
#define NPF_DOUBLE_EXP_MASK NPF_REAL_EXP_MASK
|
|
#define NPF_DOUBLE_EXP_BIAS NPF_REAL_EXP_BIAS
|
|
#define NPF_DOUBLE_MAN_BITS NPF_REAL_MAN_BITS
|
|
#define npf_double_to_int_rep(f) npf_real_to_int_rep(f)
|
|
#endif
|
|
|
|
static NPF_NOINLINE int npf_atoa_rev(
|
|
char *buf, npf_format_spec_t const *spec, double f) {
|
|
npf_double_bin_t bin = npf_double_to_int_rep(f);
|
|
npf_ftoa_exp_t exp =
|
|
(npf_ftoa_exp_t)((npf_ftoa_exp_t)(bin >> NPF_DOUBLE_MAN_BITS) & NPF_DOUBLE_EXP_MASK);
|
|
bin &= ((npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS) - 1;
|
|
|
|
if (exp == (npf_ftoa_exp_t)NPF_DOUBLE_EXP_MASK) { return 0; } // caller uses ftoa_rev
|
|
|
|
if (exp) {
|
|
bin |= (npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS;
|
|
exp = (npf_ftoa_exp_t)(exp - NPF_DOUBLE_EXP_BIAS);
|
|
} else if (bin) {
|
|
exp = (npf_ftoa_exp_t)(1 - NPF_DOUBLE_EXP_BIAS);
|
|
}
|
|
|
|
{ int const n_frac_dig = (NPF_DOUBLE_MAN_BITS + 3) / 4;
|
|
int const prec = NPF_MIN(spec->prec, n_frac_dig);
|
|
int end, i;
|
|
|
|
// Discard low nibbles and round (only constant shifts of 3 and 4)
|
|
{ npf_double_bin_t carry = 0;
|
|
for (i = n_frac_dig - prec; i > 0; --i) {
|
|
carry = (bin >> 3) & 1;
|
|
bin >>= 4;
|
|
}
|
|
bin += carry;
|
|
}
|
|
|
|
{ npf_ftoa_exp_t const ae = (exp < 0) ? (npf_ftoa_exp_t)-exp : exp;
|
|
end = npf_utoa_rev((npf_uint_t)ae, buf, 10, 0);
|
|
buf[end++] = (exp < 0) ? '-' : '+';
|
|
buf[end++] = (char)('P' + spec->case_adjust);
|
|
}
|
|
|
|
for (i = 0; i < prec; ++i) {
|
|
int_fast8_t const d = (int_fast8_t)(bin & 0xF);
|
|
buf[end++] = (char)(((d < 10) ? '0' : ('A' - 10 + spec->case_adjust)) + d);
|
|
bin >>= 4;
|
|
}
|
|
|
|
if (prec > 0
|
|
#if NANOPRINTF_USE_ALT_FORM_FLAG == 1
|
|
|| spec->alt_form
|
|
#endif
|
|
) { buf[end++] = '.'; }
|
|
|
|
buf[end++] = (char)('0' + (int_fast8_t)(bin & 0xF));
|
|
return end;
|
|
}
|
|
}
|
|
|
|
#endif // NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER
|
|
|
|
#endif // NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS
|
|
|
|
#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1
|
|
static int npf_bin_len(npf_uint_t u) {
|
|
// Return the length of the binary string format of 'u', preferring intrinsics.
|
|
if (!u) { return 1; }
|
|
|
|
#ifdef _MSC_VER // Win64, use _BSR64 for everything. If x86, use _BSR when non-large.
|
|
#ifdef _M_X64
|
|
#define NPF_HAVE_BUILTIN_CLZ
|
|
#define NPF_CLZ _BitScanReverse64
|
|
#elif NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0
|
|
#define NPF_HAVE_BUILTIN_CLZ
|
|
#define NPF_CLZ _BitScanReverse
|
|
#endif
|
|
#ifdef NPF_HAVE_BUILTIN_CLZ
|
|
unsigned long idx;
|
|
NPF_CLZ(&idx, u);
|
|
return (int)(idx + 1);
|
|
#endif
|
|
#elif NPF_CLANG || NPF_GCC_PAST_4_6
|
|
#define NPF_HAVE_BUILTIN_CLZ
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
#define NPF_CLZ(X) ((sizeof(long long) * CHAR_BIT) - (size_t)__builtin_clzll(X))
|
|
#else
|
|
#define NPF_CLZ(X) ((sizeof(long) * CHAR_BIT) - (size_t)__builtin_clzl(X))
|
|
#endif
|
|
return (int)NPF_CLZ(u);
|
|
#endif
|
|
|
|
#ifndef NPF_HAVE_BUILTIN_CLZ
|
|
int n;
|
|
for (n = 0; u; ++n, u >>= 1); // slow but small software fallback
|
|
return n;
|
|
#else
|
|
#undef NPF_HAVE_BUILTIN_CLZ
|
|
#undef NPF_CLZ
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static void npf_bufputc(int c, void *ctx) {
|
|
npf_bufputc_ctx_t *bpc = (npf_bufputc_ctx_t *)ctx;
|
|
if (bpc->cur < bpc->len) { bpc->dst[bpc->cur++] = (char)c; }
|
|
}
|
|
|
|
static void npf_bufputc_nop(int c, void *ctx) { (void)c; (void)ctx; }
|
|
|
|
typedef struct npf_cnt_putc_ctx {
|
|
npf_putc pc;
|
|
void *ctx;
|
|
int n;
|
|
} npf_cnt_putc_ctx_t;
|
|
|
|
static void npf_putc_cnt(int c, void *ctx) {
|
|
npf_cnt_putc_ctx_t *pc_cnt = (npf_cnt_putc_ctx_t *)ctx;
|
|
++pc_cnt->n;
|
|
pc_cnt->pc(c, pc_cnt->ctx); // sibling-call optimization
|
|
}
|
|
|
|
#define NPF_PUTC(VAL) do { npf_putc_cnt((int)(VAL), &pc_cnt); } while (0)
|
|
|
|
#define NPF_EXTRACT(MOD, CAST_TO, EXTRACT_AS) \
|
|
case NPF_FMT_SPEC_LEN_MOD_##MOD: val = (CAST_TO)va_arg(args, EXTRACT_AS); break
|
|
|
|
#define NPF_WRITEBACK(MOD, TYPE) \
|
|
case NPF_FMT_SPEC_LEN_MOD_##MOD: *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; break
|
|
|
|
int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) {
|
|
npf_format_spec_t fs;
|
|
char const *cur = format;
|
|
npf_cnt_putc_ctx_t pc_cnt;
|
|
pc_cnt.pc = pc;
|
|
pc_cnt.ctx = pc_ctx;
|
|
pc_cnt.n = 0;
|
|
|
|
while (*cur) {
|
|
int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs);
|
|
if (!fs_len) { NPF_PUTC(*cur++); continue; }
|
|
cur += fs_len;
|
|
|
|
// Extract star-args immediately
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
if (fs.field_width_opt == NPF_FMT_SPEC_OPT_STAR) {
|
|
fs.field_width = va_arg(args, int);
|
|
if (fs.field_width < 0) {
|
|
fs.field_width = -fs.field_width;
|
|
fs.left_justified = 1;
|
|
}
|
|
}
|
|
#endif
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
if (fs.prec_opt == NPF_FMT_SPEC_OPT_STAR) {
|
|
fs.prec = va_arg(args, int);
|
|
if (fs.prec < 0) { fs.prec = 0; fs.prec_opt = NPF_FMT_SPEC_OPT_NONE; }
|
|
}
|
|
#endif
|
|
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
// Set default precision (we can do that only now that we have extracted the
|
|
// argument-provided precision (".*"), and know whether to ignore that or not
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
if (fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) {
|
|
switch (fs.conv_spec) {
|
|
case NPF_FMT_SPEC_CONV_FLOAT_DEC:
|
|
case NPF_FMT_SPEC_CONV_FLOAT_SCI:
|
|
case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST:
|
|
fs.prec = 6;
|
|
break;
|
|
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
|
|
case NPF_FMT_SPEC_CONV_FLOAT_HEX:
|
|
fs.prec = (NPF_DOUBLE_MAN_BITS + 3) / 4;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) &&
|
|
(fs.conv_spec == NPF_FMT_SPEC_CONV_POINTER)) {
|
|
fs.prec = (sizeof(void *) * CHAR_BIT + 3) / 4;
|
|
}
|
|
#endif
|
|
|
|
#if (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) && \
|
|
(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1)
|
|
// For d i o u x X, the '0' flag must be ignored if a precision is provided
|
|
if (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) {
|
|
switch (fs.conv_spec) {
|
|
case NPF_FMT_SPEC_CONV_SIGNED_INT:
|
|
case NPF_FMT_SPEC_CONV_OCTAL:
|
|
case NPF_FMT_SPEC_CONV_HEX_INT:
|
|
case NPF_FMT_SPEC_CONV_UNSIGNED_INT:
|
|
fs.leading_zero_pad = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
union { char cbuf_mem[NANOPRINTF_CONVERSION_BUFFER_SIZE]; npf_uint_t binval; } u;
|
|
char *cbuf = u.cbuf_mem, sign_c = 0;
|
|
int cbuf_len = 0;
|
|
char need_0x = 0;
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
int field_pad = 0;
|
|
char pad_c = 0;
|
|
#endif
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
int prec_pad = 0;
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
uint_fast8_t zero = 0;
|
|
#endif
|
|
#endif
|
|
|
|
// Extract and convert the argument to string, point cbuf at the text.
|
|
switch (fs.conv_spec) {
|
|
case NPF_FMT_SPEC_CONV_PERCENT:
|
|
*cbuf = '%';
|
|
cbuf_len = 1;
|
|
break;
|
|
|
|
case NPF_FMT_SPEC_CONV_CHAR:
|
|
*cbuf = (char)va_arg(args, int);
|
|
cbuf_len = 1;
|
|
break;
|
|
|
|
case NPF_FMT_SPEC_CONV_STRING: {
|
|
cbuf = va_arg(args, char *);
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
for (char const *s = cbuf;
|
|
((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) || (cbuf_len < fs.prec)) && cbuf && *s;
|
|
++s, ++cbuf_len);
|
|
#else
|
|
for (char const *s = cbuf; cbuf && *s; ++s, ++cbuf_len); // strlen
|
|
#endif
|
|
} break;
|
|
|
|
case NPF_FMT_SPEC_CONV_SIGNED_INT: {
|
|
npf_int_t val = 0;
|
|
switch (fs.length_modifier) {
|
|
NPF_EXTRACT(NONE, int, int);
|
|
#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1
|
|
NPF_EXTRACT(SHORT, short, int);
|
|
NPF_EXTRACT(CHAR, signed char, int);
|
|
#endif
|
|
NPF_EXTRACT(LONG, long, long);
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
NPF_EXTRACT(LARGE_LONG_LONG, long long, long long);
|
|
NPF_EXTRACT(LARGE_INTMAX, intmax_t, intmax_t);
|
|
NPF_EXTRACT(LARGE_SIZET, npf_ssize_t, npf_ssize_t);
|
|
NPF_EXTRACT(LARGE_PTRDIFFT, ptrdiff_t, ptrdiff_t);
|
|
#endif
|
|
default: break;
|
|
}
|
|
|
|
sign_c = (val < 0) ? '-' : fs.prepend;
|
|
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
zero = !val;
|
|
#endif
|
|
// special case, if prec and value are 0, skip
|
|
if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) {
|
|
cbuf_len = 0;
|
|
} else
|
|
#endif
|
|
{
|
|
npf_uint_t uval = (npf_uint_t)val;
|
|
if (val < 0) { uval = 0 - uval; }
|
|
cbuf_len = npf_utoa_rev(uval, cbuf, 10, fs.case_adjust);
|
|
}
|
|
} break;
|
|
|
|
#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1
|
|
case NPF_FMT_SPEC_CONV_BINARY:
|
|
#endif
|
|
case NPF_FMT_SPEC_CONV_OCTAL:
|
|
case NPF_FMT_SPEC_CONV_HEX_INT:
|
|
case NPF_FMT_SPEC_CONV_UNSIGNED_INT:
|
|
case NPF_FMT_SPEC_CONV_POINTER: {
|
|
npf_uint_t val = 0;
|
|
|
|
if (fs.conv_spec == NPF_FMT_SPEC_CONV_POINTER) {
|
|
val = (npf_uint_t)(uintptr_t)va_arg(args, void *);
|
|
} else {
|
|
switch (fs.length_modifier) {
|
|
NPF_EXTRACT(NONE, unsigned, unsigned);
|
|
#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1
|
|
NPF_EXTRACT(SHORT, unsigned short, unsigned);
|
|
NPF_EXTRACT(CHAR, unsigned char, unsigned);
|
|
#endif
|
|
NPF_EXTRACT(LONG, unsigned long, unsigned long);
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
NPF_EXTRACT(LARGE_LONG_LONG, unsigned long long, unsigned long long);
|
|
NPF_EXTRACT(LARGE_INTMAX, uintmax_t, uintmax_t);
|
|
NPF_EXTRACT(LARGE_SIZET, size_t, size_t);
|
|
NPF_EXTRACT(LARGE_PTRDIFFT, npf_uptrdiff_t, npf_uptrdiff_t);
|
|
#endif
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
zero = !val;
|
|
#endif
|
|
if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) {
|
|
// Zero value and explicitly-requested zero precision means "print nothing".
|
|
#if NANOPRINTF_USE_ALT_FORM_FLAG == 1
|
|
if ((fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) && fs.alt_form) {
|
|
fs.prec = 1; // octal special case, print a single '0'
|
|
}
|
|
#endif
|
|
} else
|
|
#endif
|
|
#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1
|
|
if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) {
|
|
cbuf_len = npf_bin_len(val); u.binval = val;
|
|
} else
|
|
#endif
|
|
{
|
|
uint_fast8_t const base = (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) ?
|
|
8u : ((fs.conv_spec == NPF_FMT_SPEC_CONV_UNSIGNED_INT) ? 10u : 16u);
|
|
cbuf_len = npf_utoa_rev(val, cbuf, base, fs.case_adjust);
|
|
}
|
|
|
|
#if NANOPRINTF_USE_ALT_FORM_FLAG == 1
|
|
if (val && fs.alt_form && (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL)) {
|
|
cbuf[cbuf_len++] = '0'; // OK to add leading octal '0' immediately.
|
|
}
|
|
|
|
if (val && fs.alt_form) { // 0x or 0b but can't write it yet.
|
|
if ((fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) ||
|
|
(fs.conv_spec == NPF_FMT_SPEC_CONV_POINTER)) { need_0x = 'X'; }
|
|
#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1
|
|
else if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { need_0x = 'B'; }
|
|
#endif
|
|
if (need_0x) { need_0x = (char)(need_0x + fs.case_adjust); }
|
|
}
|
|
#endif
|
|
} break;
|
|
|
|
#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1
|
|
case NPF_FMT_SPEC_CONV_WRITEBACK:
|
|
switch (fs.length_modifier) {
|
|
NPF_WRITEBACK(NONE, int);
|
|
#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1
|
|
NPF_WRITEBACK(SHORT, short);
|
|
NPF_WRITEBACK(CHAR, signed char);
|
|
#endif
|
|
NPF_WRITEBACK(LONG, long);
|
|
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
|
|
NPF_WRITEBACK(LARGE_LONG_LONG, long long);
|
|
NPF_WRITEBACK(LARGE_INTMAX, intmax_t);
|
|
NPF_WRITEBACK(LARGE_SIZET, npf_ssize_t);
|
|
NPF_WRITEBACK(LARGE_PTRDIFFT, ptrdiff_t);
|
|
#endif
|
|
default: break;
|
|
} break;
|
|
#endif
|
|
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
case NPF_FMT_SPEC_CONV_FLOAT_DEC:
|
|
case NPF_FMT_SPEC_CONV_FLOAT_SCI:
|
|
case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST:
|
|
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
|
|
case NPF_FMT_SPEC_CONV_FLOAT_HEX:
|
|
#endif
|
|
{
|
|
npf_real_t val;
|
|
#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1
|
|
val = va_arg(args, npf_float_t).val;
|
|
#else
|
|
if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) {
|
|
val = (npf_real_t)va_arg(args, long double);
|
|
} else {
|
|
val = va_arg(args, double);
|
|
}
|
|
#endif
|
|
|
|
{ npf_real_bin_t const b = npf_real_to_int_rep(val);
|
|
sign_c = (b >> NPF_REAL_SIGN_POS) ? '-' : fs.prepend;
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
zero = !(b & ~((npf_real_bin_t)1 << NPF_REAL_SIGN_POS));
|
|
#endif
|
|
}
|
|
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
|
|
if ((fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_HEX) &&
|
|
((cbuf_len = npf_atoa_rev(cbuf, &fs, (double)val)) > 0)) {
|
|
need_0x = (char)('X' + fs.case_adjust);
|
|
} else
|
|
#endif
|
|
{ cbuf_len = npf_ftoa_rev(cbuf, &fs, val); }
|
|
if (cbuf_len < 0) { // negative means text (not number), so ignore the '0' flag
|
|
cbuf_len = -cbuf_len;
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
fs.leading_zero_pad = 0;
|
|
#endif
|
|
}
|
|
} break;
|
|
#endif
|
|
default: break;
|
|
}
|
|
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
// Compute the field width pad character
|
|
if (fs.field_width_opt != NPF_FMT_SPEC_OPT_NONE) {
|
|
// The '0' flag is only legal with numeric types, and '-' overrides '0'.
|
|
if (fs.leading_zero_pad && !fs.left_justified) {
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
if ((fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec && zero) {
|
|
pad_c = ' ';
|
|
} else
|
|
#endif
|
|
{ pad_c = '0'; }
|
|
} else { pad_c = ' '; }
|
|
}
|
|
#endif
|
|
|
|
// Compute the number of bytes to truncate or '0'-pad.
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
if (fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) {
|
|
#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1
|
|
// float precision is after the decimal point
|
|
if ((fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_DEC) &&
|
|
(fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_SCI) &&
|
|
(fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_SHORTEST)
|
|
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
|
|
&& (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_HEX)
|
|
#endif
|
|
)
|
|
#endif
|
|
{ prec_pad = NPF_MAX(0, fs.prec - cbuf_len); }
|
|
}
|
|
#endif
|
|
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
// Given the full converted length, how many pad bytes?
|
|
field_pad = fs.field_width - cbuf_len - !!sign_c;
|
|
if (need_0x) { field_pad -= 2; }
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
field_pad -= prec_pad;
|
|
#endif
|
|
field_pad = NPF_MAX(0, field_pad);
|
|
|
|
// Apply right-justified field width if requested
|
|
if (!fs.left_justified && pad_c) { // If leading zeros pad, sign goes first.
|
|
if (pad_c == '0') {
|
|
if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; }
|
|
// Pad byte is '0', write '0x' before '0' pad chars.
|
|
if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); }
|
|
}
|
|
while (field_pad-- > 0) { NPF_PUTC(pad_c); }
|
|
// Pad byte is ' ', write '0x' after ' ' pad chars but before number.
|
|
if ((pad_c != '0') && need_0x) {
|
|
if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; }
|
|
NPF_PUTC('0'); NPF_PUTC(need_0x);
|
|
}
|
|
} else
|
|
#endif
|
|
{ // no pad, '0x' requested.
|
|
if (need_0x) {
|
|
if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; }
|
|
NPF_PUTC('0'); NPF_PUTC(need_0x);
|
|
}
|
|
}
|
|
|
|
// Write the converted payload
|
|
if (fs.conv_spec == NPF_FMT_SPEC_CONV_STRING) {
|
|
for (int i = 0; cbuf && (i < cbuf_len); ++i) { NPF_PUTC(cbuf[i]); }
|
|
} else {
|
|
if (sign_c) { NPF_PUTC(sign_c); }
|
|
#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1
|
|
while (prec_pad-- > 0) { NPF_PUTC('0'); } // int precision leads.
|
|
#endif
|
|
#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1
|
|
if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) {
|
|
while (cbuf_len) { NPF_PUTC('0' + ((u.binval >> --cbuf_len) & 1)); }
|
|
} else
|
|
#endif
|
|
{ while (cbuf_len-- > 0) { NPF_PUTC(cbuf[cbuf_len]); } } // payload is reversed
|
|
}
|
|
|
|
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
|
|
if (fs.left_justified && pad_c) { // Apply left-justified field width
|
|
while (field_pad-- > 0) { NPF_PUTC(pad_c); }
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return pc_cnt.n;
|
|
}
|
|
|
|
#undef NPF_PUTC
|
|
#undef NPF_EXTRACT
|
|
#undef NPF_WRITEBACK
|
|
|
|
int npf_vsnprintf(char * NPF_RESTRICT buffer,
|
|
size_t bufsz,
|
|
char const * NPF_RESTRICT format,
|
|
va_list vlist) {
|
|
npf_bufputc_ctx_t bufputc_ctx;
|
|
bufputc_ctx.dst = buffer;
|
|
bufputc_ctx.len = bufsz;
|
|
bufputc_ctx.cur = 0;
|
|
|
|
npf_putc const pc = buffer ? npf_bufputc : npf_bufputc_nop;
|
|
int const n = npf_vpprintf(pc, &bufputc_ctx, format, vlist);
|
|
|
|
if (buffer && bufsz) {
|
|
#ifdef NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW
|
|
buffer[(n < 0 || (unsigned)n >= bufsz) ? 0 : n] = '\0';
|
|
#else
|
|
buffer[n < 0 ? 0 : NPF_MIN((unsigned)n, bufsz - 1)] = '\0';
|
|
#endif
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
int npf_pprintf_(npf_putc pc,
|
|
void * NPF_RESTRICT pc_ctx,
|
|
char const * NPF_RESTRICT format,
|
|
...) {
|
|
va_list val;
|
|
va_start(val, format);
|
|
int const rv = npf_vpprintf(pc, pc_ctx, format, val);
|
|
va_end(val);
|
|
return rv;
|
|
}
|
|
|
|
int npf_snprintf_(char * NPF_RESTRICT buffer,
|
|
size_t bufsz,
|
|
const char * NPF_RESTRICT format,
|
|
...) {
|
|
va_list val;
|
|
va_start(val, format);
|
|
int const rv = npf_vsnprintf(buffer, bufsz, format, val);
|
|
va_end(val);
|
|
return rv;
|
|
}
|
|
|
|
#if NPF_HAVE_GCC_WARNING_PRAGMAS
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#endif // NPF_IMPLEMENTATION_INCLUDED
|
|
#endif // NANOPRINTF_IMPLEMENTATION
|
|
|
|
// Single-precision argument wrapping and MAP macro expansion machinery.
|
|
// The npf_snprintf / npf_pprintf / NPF_MAP_ARGS macros defined above reference
|
|
// these, but that's fine: macro bodies are only expanded at the point of
|
|
// invocation, not at the point of definition.
|
|
|
|
#ifndef NPF_MAP_INCLUDED
|
|
#define NPF_MAP_INCLUDED
|
|
|
|
#if defined(NANOPRINTF_USE_FLOAT_SINGLE_PRECISION) && NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1
|
|
|
|
// NPF__WRAP: wrap float/double args into npf_float_t, pass other types through.
|
|
// C++ uses function overloading; C uses _Generic with function-pointer selection.
|
|
// _Generic picks a function, then (x) calls it. Non-selected branches are bare
|
|
// function names (always valid), sidestepping _Generic's type-check-all-branches
|
|
// behavior that would reject (float)(string_ptr) in non-selected branches.
|
|
#if defined(__cplusplus)
|
|
extern "C++" {
|
|
static inline npf_float_t npf__wrap_impl(float f) { npf_float_t r; r.val = f; return r; }
|
|
static inline npf_float_t npf__wrap_impl(double d) { npf_float_t r; r.val = (float)d; return r; }
|
|
template<typename T> static inline T npf__wrap_impl(T v) { return v; }
|
|
}
|
|
#define NPF__WRAP(x) npf__wrap_impl(x)
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#define NPF__IS_REAL(x) (__builtin_types_compatible_p(__typeof__(x), float) || \
|
|
__builtin_types_compatible_p(__typeof__(x), double))
|
|
#define NPF__WRAP(x) __builtin_choose_expr(NPF__IS_REAL(x), \
|
|
({ npf_float_t _npf_r; \
|
|
_npf_r.val = (float)__builtin_choose_expr(NPF__IS_REAL(x), (x), 0); \
|
|
_npf_r; }), \
|
|
(x))
|
|
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
|
|
static inline npf_float_t npf__wf(float f) { npf_float_t r; r.val = f; return r; }
|
|
static inline npf_float_t npf__wd(double d) { npf_float_t r; r.val = (float)d; return r; }
|
|
static inline npf_float_t npf__wld(long double d) { npf_float_t r; r.val = (float)d; return r; }
|
|
static inline int npf__id_i(int v) { return v; }
|
|
static inline unsigned npf__id_u(unsigned v) { return v; }
|
|
static inline long npf__id_l(long v) { return v; }
|
|
static inline unsigned long npf__id_ul(unsigned long v) { return v; }
|
|
static inline long long npf__id_ll(long long v) { return v; }
|
|
static inline unsigned long long npf__id_ull(unsigned long long v) { return v; }
|
|
static inline char npf__id_c(char v) { return v; }
|
|
static inline signed char npf__id_sc(signed char v) { return v; }
|
|
static inline unsigned char npf__id_uc(unsigned char v) { return v; }
|
|
static inline short npf__id_s(short v) { return v; }
|
|
static inline unsigned short npf__id_us(unsigned short v) { return v; }
|
|
static inline char *npf__id_cp(char *v) { return v; }
|
|
static inline char const *npf__id_ccp(char const *v) { return v; }
|
|
static inline void *npf__id_vp(void *v) { return v; }
|
|
static inline void const *npf__id_cvp(void const *v) { return v; }
|
|
static inline int *npf__id_ip(int *v) { return v; }
|
|
static inline short *npf__id_sp(short *v) { return v; }
|
|
static inline long *npf__id_lp(long *v) { return v; }
|
|
static inline long long *npf__id_llp(long long *v) { return v; }
|
|
static inline signed char *npf__id_scp(signed char *v) { return v; }
|
|
#define NPF__WRAP(x) _Generic((x), \
|
|
float: npf__wf, \
|
|
double: npf__wd, \
|
|
long double: npf__wld, \
|
|
int: npf__id_i, \
|
|
unsigned: npf__id_u, \
|
|
long: npf__id_l, \
|
|
unsigned long: npf__id_ul, \
|
|
long long: npf__id_ll, \
|
|
unsigned long long: npf__id_ull, \
|
|
char: npf__id_c, \
|
|
signed char: npf__id_sc, \
|
|
unsigned char: npf__id_uc, \
|
|
short: npf__id_s, \
|
|
unsigned short: npf__id_us, \
|
|
char *: npf__id_cp, \
|
|
char const *: npf__id_ccp, \
|
|
void *: npf__id_vp, \
|
|
void const *: npf__id_cvp, \
|
|
int *: npf__id_ip, \
|
|
short *: npf__id_sp, \
|
|
long *: npf__id_lp, \
|
|
long long *: npf__id_llp, \
|
|
signed char *: npf__id_scp)(x)
|
|
#else
|
|
#error Single-precision float wrapping requires C11, GCC/Clang, or C++.
|
|
#endif
|
|
|
|
// Argument counting (up to 64 variadic args)
|
|
#define NPF__NARG(...) NPF__NARG_(__VA_ARGS__, NPF__RSEQ())
|
|
#define NPF__NARG_(...) NPF__ARG_N(__VA_ARGS__)
|
|
#define NPF__ARG_N( \
|
|
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
|
|
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
|
|
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
|
|
_61,_62,_63,_64,N,...) N
|
|
#define NPF__RSEQ() \
|
|
64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38, \
|
|
37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11, \
|
|
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
|
|
|
// Token pasting
|
|
#define NPF__CAT(a,b) NPF__CAT_(a,b)
|
|
#define NPF__CAT_(a,b) a##b
|
|
|
|
// MAP: apply f to each argument
|
|
#define NPF__MAP(f,...) NPF__CAT(NPF__MAP_,NPF__NARG(__VA_ARGS__))(f,__VA_ARGS__)
|
|
#define NPF__MAP_1(f,a) f(a)
|
|
#define NPF__MAP_2(f,a,...) f(a), NPF__MAP_1(f,__VA_ARGS__)
|
|
#define NPF__MAP_3(f,a,...) f(a), NPF__MAP_2(f,__VA_ARGS__)
|
|
#define NPF__MAP_4(f,a,...) f(a), NPF__MAP_3(f,__VA_ARGS__)
|
|
#define NPF__MAP_5(f,a,...) f(a), NPF__MAP_4(f,__VA_ARGS__)
|
|
#define NPF__MAP_6(f,a,...) f(a), NPF__MAP_5(f,__VA_ARGS__)
|
|
#define NPF__MAP_7(f,a,...) f(a), NPF__MAP_6(f,__VA_ARGS__)
|
|
#define NPF__MAP_8(f,a,...) f(a), NPF__MAP_7(f,__VA_ARGS__)
|
|
#define NPF__MAP_9(f,a,...) f(a), NPF__MAP_8(f,__VA_ARGS__)
|
|
#define NPF__MAP_10(f,a,...) f(a), NPF__MAP_9(f,__VA_ARGS__)
|
|
#define NPF__MAP_11(f,a,...) f(a), NPF__MAP_10(f,__VA_ARGS__)
|
|
#define NPF__MAP_12(f,a,...) f(a), NPF__MAP_11(f,__VA_ARGS__)
|
|
#define NPF__MAP_13(f,a,...) f(a), NPF__MAP_12(f,__VA_ARGS__)
|
|
#define NPF__MAP_14(f,a,...) f(a), NPF__MAP_13(f,__VA_ARGS__)
|
|
#define NPF__MAP_15(f,a,...) f(a), NPF__MAP_14(f,__VA_ARGS__)
|
|
#define NPF__MAP_16(f,a,...) f(a), NPF__MAP_15(f,__VA_ARGS__)
|
|
#define NPF__MAP_17(f,a,...) f(a), NPF__MAP_16(f,__VA_ARGS__)
|
|
#define NPF__MAP_18(f,a,...) f(a), NPF__MAP_17(f,__VA_ARGS__)
|
|
#define NPF__MAP_19(f,a,...) f(a), NPF__MAP_18(f,__VA_ARGS__)
|
|
#define NPF__MAP_20(f,a,...) f(a), NPF__MAP_19(f,__VA_ARGS__)
|
|
#define NPF__MAP_21(f,a,...) f(a), NPF__MAP_20(f,__VA_ARGS__)
|
|
#define NPF__MAP_22(f,a,...) f(a), NPF__MAP_21(f,__VA_ARGS__)
|
|
#define NPF__MAP_23(f,a,...) f(a), NPF__MAP_22(f,__VA_ARGS__)
|
|
#define NPF__MAP_24(f,a,...) f(a), NPF__MAP_23(f,__VA_ARGS__)
|
|
#define NPF__MAP_25(f,a,...) f(a), NPF__MAP_24(f,__VA_ARGS__)
|
|
#define NPF__MAP_26(f,a,...) f(a), NPF__MAP_25(f,__VA_ARGS__)
|
|
#define NPF__MAP_27(f,a,...) f(a), NPF__MAP_26(f,__VA_ARGS__)
|
|
#define NPF__MAP_28(f,a,...) f(a), NPF__MAP_27(f,__VA_ARGS__)
|
|
#define NPF__MAP_29(f,a,...) f(a), NPF__MAP_28(f,__VA_ARGS__)
|
|
#define NPF__MAP_30(f,a,...) f(a), NPF__MAP_29(f,__VA_ARGS__)
|
|
#define NPF__MAP_31(f,a,...) f(a), NPF__MAP_30(f,__VA_ARGS__)
|
|
#define NPF__MAP_32(f,a,...) f(a), NPF__MAP_31(f,__VA_ARGS__)
|
|
#define NPF__MAP_33(f,a,...) f(a), NPF__MAP_32(f,__VA_ARGS__)
|
|
#define NPF__MAP_34(f,a,...) f(a), NPF__MAP_33(f,__VA_ARGS__)
|
|
#define NPF__MAP_35(f,a,...) f(a), NPF__MAP_34(f,__VA_ARGS__)
|
|
#define NPF__MAP_36(f,a,...) f(a), NPF__MAP_35(f,__VA_ARGS__)
|
|
#define NPF__MAP_37(f,a,...) f(a), NPF__MAP_36(f,__VA_ARGS__)
|
|
#define NPF__MAP_38(f,a,...) f(a), NPF__MAP_37(f,__VA_ARGS__)
|
|
#define NPF__MAP_39(f,a,...) f(a), NPF__MAP_38(f,__VA_ARGS__)
|
|
#define NPF__MAP_40(f,a,...) f(a), NPF__MAP_39(f,__VA_ARGS__)
|
|
#define NPF__MAP_41(f,a,...) f(a), NPF__MAP_40(f,__VA_ARGS__)
|
|
#define NPF__MAP_42(f,a,...) f(a), NPF__MAP_41(f,__VA_ARGS__)
|
|
#define NPF__MAP_43(f,a,...) f(a), NPF__MAP_42(f,__VA_ARGS__)
|
|
#define NPF__MAP_44(f,a,...) f(a), NPF__MAP_43(f,__VA_ARGS__)
|
|
#define NPF__MAP_45(f,a,...) f(a), NPF__MAP_44(f,__VA_ARGS__)
|
|
#define NPF__MAP_46(f,a,...) f(a), NPF__MAP_45(f,__VA_ARGS__)
|
|
#define NPF__MAP_47(f,a,...) f(a), NPF__MAP_46(f,__VA_ARGS__)
|
|
#define NPF__MAP_48(f,a,...) f(a), NPF__MAP_47(f,__VA_ARGS__)
|
|
#define NPF__MAP_49(f,a,...) f(a), NPF__MAP_48(f,__VA_ARGS__)
|
|
#define NPF__MAP_50(f,a,...) f(a), NPF__MAP_49(f,__VA_ARGS__)
|
|
#define NPF__MAP_51(f,a,...) f(a), NPF__MAP_50(f,__VA_ARGS__)
|
|
#define NPF__MAP_52(f,a,...) f(a), NPF__MAP_51(f,__VA_ARGS__)
|
|
#define NPF__MAP_53(f,a,...) f(a), NPF__MAP_52(f,__VA_ARGS__)
|
|
#define NPF__MAP_54(f,a,...) f(a), NPF__MAP_53(f,__VA_ARGS__)
|
|
#define NPF__MAP_55(f,a,...) f(a), NPF__MAP_54(f,__VA_ARGS__)
|
|
#define NPF__MAP_56(f,a,...) f(a), NPF__MAP_55(f,__VA_ARGS__)
|
|
#define NPF__MAP_57(f,a,...) f(a), NPF__MAP_56(f,__VA_ARGS__)
|
|
#define NPF__MAP_58(f,a,...) f(a), NPF__MAP_57(f,__VA_ARGS__)
|
|
#define NPF__MAP_59(f,a,...) f(a), NPF__MAP_58(f,__VA_ARGS__)
|
|
#define NPF__MAP_60(f,a,...) f(a), NPF__MAP_59(f,__VA_ARGS__)
|
|
#define NPF__MAP_61(f,a,...) f(a), NPF__MAP_60(f,__VA_ARGS__)
|
|
#define NPF__MAP_62(f,a,...) f(a), NPF__MAP_61(f,__VA_ARGS__)
|
|
#define NPF__MAP_63(f,a,...) f(a), NPF__MAP_62(f,__VA_ARGS__)
|
|
#define NPF__MAP_64(f,a,...) f(a), NPF__MAP_63(f,__VA_ARGS__)
|
|
|
|
#endif // NANOPRINTF_USE_FLOAT_SINGLE_PRECISION
|
|
|
|
#endif // NPF_MAP_INCLUDED
|
|
|
|
/*
|
|
nanoprintf is dual-licensed under both the "Unlicense" and the
|
|
"Zero-Clause BSD" (0BSD) licenses. The intent of this dual-licensing
|
|
structure is to make nanoprintf as consumable as possible in as many
|
|
environments / countries / companies as possible without any
|
|
encumberances.
|
|
|
|
The text of the two licenses follows below:
|
|
|
|
============================== UNLICENSE ==============================
|
|
|
|
This is free and unencumbered software released into the public domain.
|
|
|
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
distribute this software, either in source code form or as a compiled
|
|
binary, for any purpose, commercial or non-commercial, and by any
|
|
means.
|
|
|
|
In jurisdictions that recognize copyright laws, the author or authors
|
|
of this software dedicate any and all copyright interest in the
|
|
software to the public domain. We make this dedication for the benefit
|
|
of the public at large and to the detriment of our heirs and
|
|
successors. We intend this dedication to be an overt act of
|
|
relinquishment in perpetuity of all present and future rights to this
|
|
software under copyright law.
|
|
|
|
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 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.
|
|
|
|
For more information, please refer to <http://unlicense.org>
|
|
|
|
================================ 0BSD =================================
|
|
|
|
Copyright (C) 2019- by Charles Nicholson <charles.nicholson+nanoprintf@gmail.com>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for
|
|
any purpose with or without fee is hereby granted.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/ |