Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 20 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
[![](https://img.shields.io/badge/license-public_domain-brightgreen.svg)](https://github.com/charlesnicholson/nanoprintf/blob/master/LICENSE)
[![](https://img.shields.io/badge/license-0BSD-brightgreen)](https://github.com/charlesnicholson/nanoprintf/blob/master/LICENSE)

nanoprintf is an unencumbered implementation of snprintf and vsnprintf for embedded systems that, when fully enabled, aim for C11 standard compliance. The primary exceptions are scientific notation (`%e`, `%g`, `%a`), and locale conversions that require `wcrtomb` to exist. C23 binary integer output is optionally supported as per [N2630](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2630.pdf). Safety extensions for snprintf and vsnprintf can be optionally configured to return trimmed or fully-empty strings on buffer overflow events.
nanoprintf is an unencumbered implementation of snprintf and vsnprintf for embedded systems that, when fully enabled, aim for C11 standard compliance. The primary exceptions are scientific notation (`%e`, `%g`), and locale conversions that require `wcrtomb` to exist. C23 binary integer output is optionally supported as per [N2630](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2630.pdf). Safety extensions for snprintf and vsnprintf can be optionally configured to return trimmed or fully-empty strings on buffer overflow events.

Additionally, nanoprintf can be used to parse printf-style format strings to extract the various parameters and conversion specifiers, without doing any actual text formatting.

nanoprintf makes no memory allocations and uses less than 100 bytes of stack. It compiles to between *~580-2800 bytes of object code* on a Cortex-M0 architecture, depending on configuration.
nanoprintf makes no memory allocations and uses less than 100 bytes of stack. It compiles to between *~610-3160 bytes of object code* on a Cortex-M4 architecture, depending on configuration.

All code is written in a minimal dialect of C99 for maximal compiler compatibility, compiles cleanly at the highest warning levels on clang + gcc + msvc, raises no issues from UBsan or Asan, and is exhaustively tested on 32-bit and 64-bit architectures. nanoprintf does include C standard headers but only uses them for C99 types and argument lists; no calls are made into stdlib / libc, with the exception of any internal large integer arithmetic calls your compiler might emit. As usual, some Windows-specific headers are required if you're compiling natively for msvc.

Expand Down Expand Up @@ -83,7 +83,8 @@ nanoprintf has the following static configuration flags.

* `NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS`: Set to `0` or `1`. Enables field width specifiers.
* `NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS`: Set to `0` or `1`. Enables precision specifiers.
* `NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS`: Set to `0` or `1`. Enables floating-point specifiers.
* `NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS`: Set to `0` or `1`. Enables floating-point specifiers (`%f`/`%F`).
* `NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER`: Set to `0` or `1`. Enables hex float specifier (`%a`/`%A`). Requires `NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=1`.
* `NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS`: Set to `0` or `1`. Enables small modifiers.
* `NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS`: Set to `0` or `1`. Enables oversized modifiers.
* `NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS`: Set to `0` or `1`. Enables binary specifiers.
Expand Down Expand Up @@ -159,7 +160,7 @@ Like `printf`, `nanoprintf` expects a conversion specification string of the fol
* `f`/`F`: Floating-point decimal
* `e`/`E`: Floating-point scientific (unimplemented, prints float decimal)
* `g`/`G`: Floating-point shortest (unimplemented, prints float decimal)
* `a`/`A`: Floating-point hex (unimplemented, prints float decimal)
* `a`/`A`: Floating-point hex
* `b`/`B`: Binary integers

## Floating-Point
Expand All @@ -168,98 +169,32 @@ Floating-point conversion is performed by extracting the integer and fraction pa

Because the float -> fixed code operates on the raw float value bits, no floating-point operations are performed. This allows nanoprintf to efficiently format floats on soft-float architectures like Cortex-M0, to function identically with or without optimizations like "fast math", and to minimize the code footprint.

The `%e`/`%E`, `%a`/`%A`, and `%g`/`%G` specifiers are parsed but not formatted. If used, the output will be identical to if `%f`/`%F` was used. Pull requests welcome! :)
The `%a`/`%A` hex float specifier is optionally supported via `NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER`. It operates directly on the IEEE 754 binary representation, emitting the mantissa as hex nibbles and the exponent in decimal. No floating-point arithmetic is performed. Rounding uses a nibble-at-a-time carry loop with only constant 3- and 4-bit shifts, keeping the code compact on architectures without a barrel shifter (e.g. Cortex-M0).

The `%e`/`%E` and `%g`/`%G` specifiers are parsed but not formatted. If used, the output will be identical to if `%f`/`%F` was used. Pull requests welcome! :)

## Limitations

No wide-character support exists: the `%lc` and `%ls` fields require that the arg be converted to a char array as if by a call to [wcrtomb](http://man7.org/linux/man-pages/man3/wcrtomb.3.html). When locale and character set conversions get involved, it's hard to keep the name "nano". Accordingly, `%lc` and `%ls` behave like `%c` and `%s`, respectively.

Currently the only supported float conversions are the decimal forms: `%f` and `%F`. Pull requests welcome!
Currently the supported float conversions are `%f`/`%F` and `%a`/`%A`. Pull requests for `%e`/`%g` welcome!

## Measurement

The CI build is set up to use gcc and nm to measure the compiled size of every pull request. See the [Presubmit Checks](https://github.com/charlesnicholson/nanoprintf/actions/workflows/presubmit.yml) "size reports" job output for recent runs.

The following size measurements are taken against the Cortex-M0 build.
All measurements compiled with `arm-none-eabi-gcc -Os` and `-ffunction-sections`.

| Configuration | Cortex-M0 | Cortex-M4 |
|---|--:|--:|
| Minimal | 610 | 678 |
| Binary | 674 | 738 |
| Field Width + Precision | 1494 | 1570 |
| Field Width + Precision + Binary | 1698 | 1706 |
| Float | 1826 | 1942 |
| Float + Hex Float | 2254 | 2378 |
| Everything | 3142 | 3158 |

```
Configuration "Minimal":
arm-none-eabi-gcc -c -x c -Os -I/__w/nanoprintf/nanoprintf -o npf.o -mcpu=cortex-m0 -DNANOPRINTF_IMPLEMENTATION -DNANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_ALT_FORM_FLAG=0 -
arm-none-eabi-nm --print-size --size-sort npf.o
00000046 00000002 t npf_bufputc_nop
00000032 00000014 t npf_bufputc
000001ce 00000016 T npf_pprintf
0000022c 00000016 T npf_snprintf
00000000 00000032 t npf_utoa_rev
000001e4 00000048 T npf_vsnprintf
00000048 00000186 T npf_vpprintf
Total size: 0x242 (578) bytes

Configuration "Binary":
arm-none-eabi-gcc -c -x c -Os -I/__w/nanoprintf/nanoprintf -o npf.o -mcpu=cortex-m0 -DNANOPRINTF_IMPLEMENTATION -DNANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_ALT_FORM_FLAG=0 -
arm-none-eabi-nm --print-size --size-sort npf.o
00000046 00000002 t npf_bufputc_nop
00000048 00000010 t npf_putc_cnt
00000032 00000014 t npf_bufputc
00000228 00000016 T npf_pprintf
00000288 00000016 T npf_snprintf
00000000 00000032 t npf_utoa_rev
0000023e 0000004a T npf_vsnprintf
00000058 000001d0 T npf_vpprintf
Total size: 0x29e (670) bytes

Configuration "Field Width + Precision":
arm-none-eabi-gcc -c -x c -Os -I/__w/nanoprintf/nanoprintf -o npf.o -mcpu=cortex-m0 -DNANOPRINTF_IMPLEMENTATION -DNANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_ALT_FORM_FLAG=1 -
arm-none-eabi-nm --print-size --size-sort npf.o
00000046 00000002 t npf_bufputc_nop
00000048 00000010 t npf_putc_cnt
00000032 00000014 t npf_bufputc
00000532 00000016 T npf_pprintf
00000590 00000016 T npf_snprintf
00000000 00000032 t npf_utoa_rev
00000548 00000048 T npf_vsnprintf
00000058 000004da T npf_vpprintf
Total size: 0x5a6 (1446) bytes

Configuration "Field Width + Precision + Binary":
arm-none-eabi-gcc -c -x c -Os -I/__w/nanoprintf/nanoprintf -o npf.o -mcpu=cortex-m0 -DNANOPRINTF_IMPLEMENTATION -DNANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_ALT_FORM_FLAG=1 -
arm-none-eabi-nm --print-size --size-sort npf.o
00000046 00000002 t npf_bufputc_nop
00000048 00000010 t npf_putc_cnt
00000032 00000014 t npf_bufputc
000005ba 00000016 T npf_pprintf
00000618 00000016 T npf_snprintf
00000000 00000032 t npf_utoa_rev
000005d0 00000048 T npf_vsnprintf
00000058 00000562 T npf_vpprintf
Total size: 0x62e (1582) bytes

Configuration "Float":
arm-none-eabi-gcc -c -x c -Os -I/__w/nanoprintf/nanoprintf -o npf.o -mcpu=cortex-m0 -DNANOPRINTF_IMPLEMENTATION -DNANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=0 -DNANOPRINTF_USE_ALT_FORM_FLAG=1 -
arm-none-eabi-nm --print-size --size-sort npf.o
00000046 00000002 t npf_bufputc_nop
00000048 00000010 t npf_putc_cnt
00000032 00000014 t npf_bufputc
00000650 00000016 T npf_pprintf
000006b0 00000016 T npf_snprintf
00000000 00000032 t npf_utoa_rev
00000666 0000004a T npf_vsnprintf
00000058 000005f8 T npf_vpprintf
Total size: 0x6c6 (1734) bytes

Configuration "Everything":
arm-none-eabi-gcc -c -x c -Os -I/__w/nanoprintf/nanoprintf -o npf.o -mcpu=cortex-m0 -DNANOPRINTF_IMPLEMENTATION -DNANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=1 -DNANOPRINTF_USE_ALT_FORM_FLAG=1 -
arm-none-eabi-nm --print-size --size-sort npf.o
0000005a 00000002 t npf_bufputc_nop
0000005c 00000010 t npf_putc_cnt
00000046 00000014 t npf_bufputc
00000ab4 00000016 T npf_pprintf
00000b14 00000016 T npf_snprintf
00000000 00000046 t npf_utoa_rev
00000aca 0000004a T npf_vsnprintf
0000006c 00000a48 T npf_vpprintf
Total size: 0xb2a (2858) bytes
```

## Development

Expand Down
114 changes: 108 additions & 6 deletions nanoprintf.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ NPF_VISIBILITY int npf_vpprintf(npf_putc pc,
#define NANOPRINTF_USE_ALT_FORM_FLAG 1
#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
Expand Down Expand Up @@ -136,6 +141,11 @@ NPF_VISIBILITY int npf_vpprintf(npf_putc pc,
#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

// intmax_t / uintmax_t require stdint from c99 / c++11
#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1
#ifndef _MSC_VER
Expand Down Expand Up @@ -281,8 +291,10 @@ enum {
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 {
Expand Down Expand Up @@ -466,11 +478,13 @@ static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec
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':
Expand Down Expand Up @@ -718,6 +732,62 @@ static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) {
return -(int)i;
}

#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1

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
Expand Down Expand Up @@ -825,9 +895,13 @@ int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) {
case NPF_FMT_SPEC_CONV_FLOAT_DEC:
case NPF_FMT_SPEC_CONV_FLOAT_SCI:
case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST:
case NPF_FMT_SPEC_CONV_FLOAT_HEX:
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;
}
Expand Down Expand Up @@ -1023,19 +1097,36 @@ int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) {
case NPF_FMT_SPEC_CONV_FLOAT_DEC:
case NPF_FMT_SPEC_CONV_FLOAT_SCI:
case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST:
case NPF_FMT_SPEC_CONV_FLOAT_HEX: {
#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
case NPF_FMT_SPEC_CONV_FLOAT_HEX:
#endif
{
double val;
if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) {
val = (double)va_arg(args, long double);
} else {
val = va_arg(args, double);
}

#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1
{ npf_double_bin_t const b = npf_double_to_int_rep(val);
sign_c = (b >> NPF_DOUBLE_SIGN_POS) ? '-' : fs.prepend;
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
zero = !(b & ~((npf_double_bin_t)1 << NPF_DOUBLE_SIGN_POS));
#endif
}
if ((fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_HEX) &&
((cbuf_len = npf_atoa_rev(cbuf, &fs, val)) > 0)) {
need_0x = (char)('X' + fs.case_adjust);
} else
{ cbuf_len = npf_ftoa_rev(cbuf, &fs, val); }
#else
sign_c = (npf_double_to_int_rep(val) >> NPF_DOUBLE_SIGN_POS) ? '-' : fs.prepend;
#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1
zero = (val == 0.);
#endif
cbuf_len = npf_ftoa_rev(cbuf, &fs, val);
#endif
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
Expand Down Expand Up @@ -1069,8 +1160,11 @@ int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) {
// 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) &&
(fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_HEX))
(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); }
}
Expand All @@ -1094,10 +1188,18 @@ int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) {
}
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) { NPF_PUTC('0'); NPF_PUTC(need_0x); }
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
{ if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } } // no pad, '0x' requested.
{ // 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) {
Expand Down
Loading