b0e9bb1b91
The Quake plugin is the only code that actually relies on float formatting. Because Quake only runs on targets with huge memory anyway, limiting their formatting to just those targets will minimize the increased memory use in the core. Change-Id: Icdbe26ec6ede564861cc01ac4add76a12b00ecd5
1442 lines
44 KiB
C
1442 lines
44 KiB
C
/***************************************************************************
|
||
* __________ __ ___.
|
||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||
* \/ \/ \/ \/ \/
|
||
* $Id$
|
||
*
|
||
* Copyright (C) 2002 by Gary Czvitkovicz
|
||
* Copyright (C) 2017 by Michael A. Sevakis
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License
|
||
* as published by the Free Software Foundation; either version 2
|
||
* of the License, or (at your option) any later version.
|
||
*
|
||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||
* KIND, either express or implied.
|
||
*
|
||
****************************************************************************/
|
||
#include <sys/types.h>
|
||
#include <limits.h>
|
||
#include <string.h>
|
||
#include <stddef.h>
|
||
#include <stdlib.h>
|
||
#include "system.h"
|
||
#include "vuprintf.h"
|
||
#include "ap_int.h"
|
||
|
||
#ifndef BOOTLOADER
|
||
|
||
/* Only the Quake plugin needs float formatting */
|
||
#if defined(HAVE_LCD_COLOR) && \
|
||
(!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE)) && \
|
||
(PLUGIN_BUFFER_SIZE > 0x14000) && (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM)
|
||
/* turn everything on */
|
||
#define FMT_LENMOD (0xffffffff)
|
||
#define FMT_RADIX (0xffffffff)
|
||
#endif
|
||
|
||
#endif
|
||
|
||
/* these are the defaults if no other preference is given */
|
||
#ifndef FMT_LENMOD
|
||
#define FMT_LENMOD (FMT_LENMOD_l | \
|
||
FMT_LENMOD_z)
|
||
#endif /* FMT_LENMOD */
|
||
|
||
#ifndef FMT_RADIX
|
||
#define FMT_RADIX (FMT_RADIX_c | \
|
||
FMT_RADIX_d | \
|
||
FMT_RADIX_p | \
|
||
FMT_RADIX_s | \
|
||
FMT_RADIX_u | \
|
||
FMT_RADIX_x)
|
||
#endif /* FMT_RADIX */
|
||
|
||
/** Length modifier and radix flags **/
|
||
|
||
/* compulsory length modifiers: NONE
|
||
* however a compatible 'l' or 'll' must be defined if another requires it */
|
||
#define FMT_LENMOD_h 0x001 /* signed/unsigned short (%h<radix>) */
|
||
#define FMT_LENMOD_hh 0x002 /* signed/unsigned char (%hh<radix>) */
|
||
#define FMT_LENMOD_j 0x004 /* intmax_t/uintmax_t (%j<radix>) */
|
||
#define FMT_LENMOD_l 0x008 /* signed/unsigned long (%l<radix>) */
|
||
#define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */
|
||
#define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */
|
||
#define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */
|
||
#if 0
|
||
#define FMT_LENMOD_L 0x080 /* long double (instead of double) */
|
||
#else
|
||
#define FMT_LENMOD_L 0x000
|
||
#endif
|
||
|
||
/* compulsory radixes: c, d, i, u, s */
|
||
#define FMT_RADIX_c 0x001 /* single character (%c) */
|
||
#define FMT_RADIX_d 0x002 /* signed integer type, decimal (%d %i) */
|
||
#define FMT_RADIX_n 0x004 /* bytes output so far (%n) */
|
||
#define FMT_RADIX_o 0x008 /* unsigned integer type, octal (%o) */
|
||
#define FMT_RADIX_p 0x010 /* pointer (%p %P) */
|
||
#define FMT_RADIX_s 0x020 /* string (%s) */
|
||
#define FMT_RADIX_u 0x040 /* unsigned integer type, decimal (%u) */
|
||
#define FMT_RADIX_x 0x080 /* unsigned integer type, hex (%x %X) */
|
||
#define FMT_RADIX_a 0x100 /* hex floating point "[-]0xh.hhhhp<68>d" */
|
||
#define FMT_RADIX_e 0x200 /* floating point with exponent "[-]d.ddde<64>dd" */
|
||
#define FMT_RADIX_f 0x400 /* floating point "[-]ddd.ddd" */
|
||
#define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending
|
||
upon value and precision */
|
||
|
||
/* TODO: 'a' 'A' */
|
||
#define FMT_RADIX_floats (FMT_RADIX_e|FMT_RADIX_f|FMT_RADIX_g)
|
||
|
||
#if (FMT_RADIX & FMT_RADIX_floats)
|
||
/* Assumes IEEE 754 double-precision, native-endian; replace to parse and init
|
||
for some other format */
|
||
#define parse_double parse_ieee754_double
|
||
#define init_double_chunks init_ieee754_double_chunks
|
||
#define format_double_int10 format_ap_int10
|
||
#define format_double_frac10 format_ap_frac10
|
||
#endif
|
||
|
||
/* avoid defining redundant functions if two or more types can use the same
|
||
* something not getting a macro means it gets assigned its own value and
|
||
* formatter */
|
||
|
||
/* l */
|
||
#if LONG_MIN == INT_MIN && LONG_MAX == INT_MAX
|
||
#define val_ld val_d
|
||
#define format_ld format_d
|
||
#define branch_fmt_ld branch_fmt_d
|
||
#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
|
||
#define val_ld
|
||
#endif /* LONG_ */
|
||
|
||
#if ULONG_MAX == UINT_MAX
|
||
#define val_lu val_u
|
||
#define format_lu format_u
|
||
#define branch_fmt_lu branch_fmt_u
|
||
#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
|
||
#define val_lu
|
||
#endif /* ULONG_ */
|
||
|
||
/* ll */
|
||
#if LLONG_MIN == INT_MIN && LLONG_MAX == INT_MAX
|
||
#define val_lld val_d
|
||
#define format_lld format_d
|
||
#define branch_fmt_lld branch_fmt_d
|
||
#elif LLONG_MIN == LONG_MIN && LLONG_MAX == LONG_MAX
|
||
#define val_lld val_ld
|
||
#define format_lld format_ld
|
||
#define branch_fmt_lld branch_fmt_ld
|
||
#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
|
||
#define val_lld
|
||
#endif /* LLONG_ */
|
||
|
||
#if ULLONG_MAX == UINT_MAX
|
||
#define val_llu val_u
|
||
#define format_llu format_u
|
||
#define branch_fmt_llu branch_fmt_u
|
||
#elif ULLONG_MAX == ULONG_MAX
|
||
#define val_llu val_lu
|
||
#define format_llu format_lu
|
||
#define branch_fmt_llu branch_fmt_lu
|
||
#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
|
||
#define val_llu
|
||
#endif /* ULLONG_ */
|
||
|
||
/* char/short parameter type promotions */
|
||
#define SCHAR_INT_ARG int
|
||
#define UCHAR_INT_ARG int
|
||
#define SSHRT_INT_ARG int
|
||
#if USHRT_MAX == UINT_MAX
|
||
#define USHRT_INT_ARG unsigned int
|
||
#else
|
||
#define USHRT_INT_ARG int
|
||
#endif
|
||
|
||
/* some macros to have conditional work inside macros */
|
||
#if (FMT_LENMOD & FMT_LENMOD_l)
|
||
#define IF_FMT_LENMOD_l(...) __VA_ARGS__
|
||
#else
|
||
#define IF_FMT_LENMOD_l(...)
|
||
#endif
|
||
|
||
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
||
#define IF_FMT_LENMOD_ll(...) __VA_ARGS__
|
||
#else
|
||
#define IF_FMT_LENMOD_ll(...)
|
||
#endif
|
||
|
||
#if (FMT_RADIX & FMT_RADIX_o)
|
||
#define IF_FMT_RADIX_o(...) __VA_ARGS__
|
||
#else
|
||
#define IF_FMT_RADIX_o(...)
|
||
#endif
|
||
|
||
#if (FMT_RADIX & FMT_RADIX_x)
|
||
#define IF_FMT_RADIX_x(...) __VA_ARGS__
|
||
#else
|
||
#define IF_FMT_RADIX_x(...)
|
||
#endif
|
||
|
||
/* synthesize multicharacter constant */
|
||
#define LENMOD2(cv, ch) \
|
||
(((cv) << CHAR_BIT) | (ch))
|
||
|
||
#define LENMOD_NONE 0
|
||
|
||
#if (FMT_LENMOD & FMT_LENMOD_h)
|
||
#define LENMOD_h 'h'
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_hh)
|
||
#define LENMOD_hh LENMOD2('h', 'h') /* 'hh' */
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_j)
|
||
#define LENMOD_j 'j'
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_l)
|
||
#define LENMOD_l 'l'
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
||
#undef FMT_MAX_L
|
||
#define LENMOD_ll LENMOD2('l', 'l') /* 'll' */
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_t)
|
||
#define LENMOD_t 't'
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_z)
|
||
#define LENMOD_z 'z'
|
||
#endif
|
||
|
||
/* select type-compatible length modifier
|
||
* (a bit hacky; it should be range-based) */
|
||
#define LENMOD_INTCOMPAT_SEL(type, signd) \
|
||
({ int __lenmod; \
|
||
size_t __size = sizeof (type); \
|
||
if (__size == ((signd) ? sizeof (int) : \
|
||
sizeof (unsigned int))) { \
|
||
__lenmod = LENMOD_NONE; \
|
||
} \
|
||
else if (__size == ((signd) ? sizeof (long) : \
|
||
sizeof (unsigned long))) { \
|
||
IF_FMT_LENMOD_l(__lenmod = LENMOD_l;) \
|
||
} \
|
||
else if (__size == ((signd) ? sizeof (long long) : \
|
||
sizeof (unsigned long long))) { \
|
||
IF_FMT_LENMOD_ll(__lenmod = LENMOD_ll;) \
|
||
} \
|
||
__lenmod; })
|
||
|
||
/* call formatting function for the compatible integer type */
|
||
#define LENMOD_INTCOMPAT_CALL(inteqv, val, fmt_buf, x, signd) \
|
||
({ const char *__buf; \
|
||
switch (inteqv) { \
|
||
case LENMOD_NONE: \
|
||
__buf = (signd) ? \
|
||
format_d((val), (fmt_buf), (x)) : \
|
||
format_u((val), (fmt_buf), (x)); \
|
||
break; \
|
||
IF_FMT_LENMOD_l( \
|
||
case LENMOD_l: \
|
||
__buf = (signd) ? \
|
||
format_ld((val), (fmt_buf), (x)) : \
|
||
format_lu((val), (fmt_buf), (x)); \
|
||
break; \
|
||
) \
|
||
IF_FMT_LENMOD_ll( \
|
||
case LENMOD_ll: \
|
||
__buf = (signd) ? \
|
||
format_lld((val), (fmt_buf), (x)) : \
|
||
format_llu((val), (fmt_buf), (x)); \
|
||
break; \
|
||
) \
|
||
} \
|
||
__buf; \
|
||
})
|
||
|
||
/* execute formatting branch for the compatible integer type */
|
||
#define LENMOD_INTCOMPAT_BRANCH(inteqv, val, signd) \
|
||
({ switch (inteqv) { \
|
||
case LENMOD_NONE: \
|
||
if (signd) { \
|
||
val_d = (val); \
|
||
goto branch_fmt_d; \
|
||
} \
|
||
else { \
|
||
val_u = (val); \
|
||
goto branch_fmt_u; \
|
||
} \
|
||
IF_FMT_LENMOD_l( \
|
||
case LENMOD_l: \
|
||
if (signd) { \
|
||
val_ld = (val); \
|
||
goto branch_fmt_ld; \
|
||
} \
|
||
else { \
|
||
val_lu = (val); \
|
||
goto branch_fmt_lu; \
|
||
} \
|
||
) \
|
||
IF_FMT_LENMOD_ll( \
|
||
case LENMOD_ll: \
|
||
if (signd) { \
|
||
val_lld = (val); \
|
||
goto branch_fmt_lld; \
|
||
} \
|
||
else { \
|
||
val_llu = (val); \
|
||
goto branch_fmt_llu; \
|
||
} \
|
||
) \
|
||
} \
|
||
})
|
||
|
||
#define CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, type) \
|
||
do { \
|
||
if (val) { \
|
||
unsigned type v; \
|
||
\
|
||
if (val < 0) { \
|
||
v = (typeof (v))-(val + 1) + 1; \
|
||
signchar = '-'; \
|
||
} \
|
||
else { \
|
||
v = val; \
|
||
} \
|
||
\
|
||
do { \
|
||
*--p = (v % 10) + '0'; \
|
||
v /= 10; \
|
||
} while (v); \
|
||
} \
|
||
\
|
||
if (signchar) { \
|
||
p[-1] = signchar; \
|
||
fmt_buf->length = 1; \
|
||
break; \
|
||
} \
|
||
\
|
||
fmt_buf->length = 0; \
|
||
} while (0)
|
||
|
||
#define CONVERT_RADIX_8(val, fmt_buf, p) \
|
||
do { \
|
||
if (val) { \
|
||
typeof (val) v = val; \
|
||
\
|
||
do { \
|
||
*--p = (v % 010) + '0'; \
|
||
v /= 010; \
|
||
} while (v); \
|
||
} \
|
||
\
|
||
if (fmt_buf->length) { \
|
||
*--p = '0'; \
|
||
fmt_buf->length = 0; \
|
||
} \
|
||
} while (0)
|
||
|
||
#define CONVERT_RADIX_10(val, fmt_buf, p) \
|
||
do { \
|
||
if (val) { \
|
||
typeof (val) v = val; \
|
||
\
|
||
do { \
|
||
*--p = (v % 10) + '0'; \
|
||
v /= 10; \
|
||
} while (v); \
|
||
} \
|
||
\
|
||
fmt_buf->length = 0; \
|
||
} while (0)
|
||
|
||
#define CONVERT_RADIX_16(val, fmt_buf, p, x) \
|
||
do { \
|
||
if (val) { \
|
||
const int h = x - 'X' - 0xA \
|
||
+ 'A' - '0'; \
|
||
typeof (val) v = val; \
|
||
\
|
||
do { \
|
||
unsigned int d = v % 0x10; \
|
||
if (d >= 0xA) { \
|
||
d += h; \
|
||
} \
|
||
*--p = d + '0'; \
|
||
v /= 0x10; \
|
||
} while (v); \
|
||
\
|
||
if (fmt_buf->length) { \
|
||
p[-1] = x; \
|
||
p[-2] = '0'; \
|
||
fmt_buf->length = 2; \
|
||
break; \
|
||
} \
|
||
} \
|
||
\
|
||
fmt_buf->length = 0; \
|
||
} while (0)
|
||
|
||
#define CONVERT_RADIX_NOSIGN(val, fmt_buf, p, x) \
|
||
switch (x) \
|
||
{ \
|
||
IF_FMT_RADIX_o( case 'o': \
|
||
CONVERT_RADIX_8(val, fmt_buf, p); \
|
||
break; ) \
|
||
case 'u': \
|
||
CONVERT_RADIX_10(val, fmt_buf, p); \
|
||
break; \
|
||
IF_FMT_RADIX_x( default: \
|
||
CONVERT_RADIX_16(val, fmt_buf, p, x); \
|
||
break; ) \
|
||
}
|
||
|
||
struct fmt_buf {
|
||
const char *fmt_start; /* second character of formatter after '%' */
|
||
size_t length; /* length of formatted text (non-numeric)
|
||
or prefix (numeric) */
|
||
char buf[24]; /* work buffer */
|
||
char bufend[1]; /* buffer end marker and guard '0' */
|
||
#if (FMT_RADIX & FMT_RADIX_floats)
|
||
int lenmod;
|
||
int radixchar;
|
||
int signchar;
|
||
int alignchar;
|
||
int width;
|
||
int precision;
|
||
char *p;
|
||
#endif
|
||
};
|
||
|
||
#define PUSHCHAR(ch) \
|
||
({ int __rc = push(userp, (ch)); \
|
||
count += __rc >= 0; \
|
||
if (__rc <= 0) { \
|
||
goto done; \
|
||
} })
|
||
|
||
/* %d %i */
|
||
static inline const char * format_d(int val,
|
||
struct fmt_buf *fmt_buf,
|
||
int signchar)
|
||
{
|
||
char *p = fmt_buf->bufend;
|
||
CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, int);
|
||
return p;
|
||
}
|
||
|
||
/* %o %u %x %X */
|
||
static inline const char * format_u(unsigned int val,
|
||
struct fmt_buf *fmt_buf,
|
||
int radixchar)
|
||
{
|
||
char *p = fmt_buf->bufend;
|
||
CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
|
||
return p;
|
||
}
|
||
|
||
#if (FMT_LENMOD & FMT_LENMOD_l)
|
||
#ifndef format_ld
|
||
/* %ld %li */
|
||
static inline const char * format_ld(long val,
|
||
struct fmt_buf *fmt_buf,
|
||
int signchar)
|
||
{
|
||
char *p = fmt_buf->bufend;
|
||
CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long);
|
||
return p;
|
||
}
|
||
#endif /* format_ld */
|
||
|
||
#ifndef format_lu
|
||
/* %lo %lu %lx %lX */
|
||
static inline const char * format_lu(unsigned long val,
|
||
struct fmt_buf *fmt_buf,
|
||
int radixchar)
|
||
{
|
||
char *p = fmt_buf->bufend;
|
||
CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
|
||
return p;
|
||
}
|
||
#endif /* format_lu */
|
||
#endif /* FMT_LENMOD_l */
|
||
|
||
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
||
#ifndef format_lld
|
||
/* %lld %lli */
|
||
static inline const char * format_lld(long long val,
|
||
struct fmt_buf *fmt_buf,
|
||
int signchar)
|
||
{
|
||
char *p = fmt_buf->bufend;
|
||
CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long long);
|
||
return p;
|
||
}
|
||
#endif /* format_lld */
|
||
|
||
#ifndef format_llu
|
||
/* %llo %llu %llx %llX */
|
||
static inline const char * format_llu(unsigned long long val,
|
||
struct fmt_buf *fmt_buf,
|
||
int radixchar)
|
||
{
|
||
char *p = fmt_buf->bufend;
|
||
CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
|
||
return p;
|
||
}
|
||
#endif /* format_llu */
|
||
#endif /* FMT_LENMOD_ll */
|
||
|
||
/* %c */
|
||
static inline const char * format_c(int c,
|
||
struct fmt_buf *fmt_buf,
|
||
int lenmod)
|
||
{
|
||
if (lenmod != LENMOD_NONE) {
|
||
return NULL; /* wchar_t support for now */
|
||
}
|
||
|
||
char *p = fmt_buf->bufend;
|
||
fmt_buf->length = 1;
|
||
*--p = (unsigned char)c;
|
||
return p;
|
||
}
|
||
|
||
/* %s */
|
||
static inline const char * format_s(const void *str,
|
||
struct fmt_buf *fmt_buf,
|
||
int precision,
|
||
int lenmod)
|
||
{
|
||
if (lenmod != LENMOD_NONE) {
|
||
return NULL; /* wchar_t support for now */
|
||
}
|
||
|
||
const char *s = str;
|
||
size_t len;
|
||
/* string length may be specified by precision instead of \0-
|
||
terminated; however, don't go past a \0 if one is there */
|
||
if (precision >= 0) {
|
||
const char *nil = memchr(s, '\0', (size_t) precision);
|
||
|
||
if (nil != NULL && (nil - s) < precision)
|
||
len = nil - s;
|
||
else
|
||
len = precision;
|
||
}
|
||
else
|
||
len = strlen(s);
|
||
|
||
fmt_buf->length = len;
|
||
return s;
|
||
}
|
||
|
||
#if (FMT_RADIX & FMT_RADIX_n)
|
||
/* %n */
|
||
static inline bool format_n(void *np,
|
||
int count,
|
||
int lenmod)
|
||
{
|
||
if (lenmod != LENMOD_NONE) {
|
||
return false; /* int only for now */
|
||
}
|
||
|
||
*(int *)np = count;
|
||
return true;
|
||
}
|
||
#endif /* FMT_RADIX_n */
|
||
|
||
#if (FMT_RADIX & FMT_RADIX_p)
|
||
/* %p %P */
|
||
static inline const char * format_p(const void *p,
|
||
struct fmt_buf *fmt_buf,
|
||
int radixchar,
|
||
bool *numericp)
|
||
{
|
||
if (p) {
|
||
/* format as %#x or %#X */
|
||
*numericp = true;
|
||
radixchar -= 'P' - 'X';
|
||
fmt_buf->length = 2;
|
||
return LENMOD_INTCOMPAT_CALL(LENMOD_INTCOMPAT_SEL(uintptr_t, false),
|
||
(uintptr_t)p, fmt_buf, radixchar, false);
|
||
}
|
||
else {
|
||
/* format as %s */
|
||
fmt_buf->length = 5;
|
||
return "(nil)";
|
||
}
|
||
}
|
||
#endif /* FMT_RADIX_p */
|
||
|
||
#if (FMT_RADIX & FMT_RADIX_floats)
|
||
/* find out how many uint32_t chunks need to be allocated, if any
|
||
* if none are needed, finish the init for the number here */
|
||
static long parse_ieee754_double(double f,
|
||
struct ap_int *ia,
|
||
struct ap_int *fa,
|
||
struct fmt_buf *fmt_buf)
|
||
{
|
||
long rc = 0;
|
||
|
||
union {
|
||
double f;
|
||
uint64_t f64;
|
||
} u = { .f = f };
|
||
|
||
int e = ((int)(u.f64 >> 52) & 0x7ff) - 1023; /* -1023..1024 */
|
||
uint64_t mantissa = u.f64 & 0x000fffffffffffffull;
|
||
|
||
if (u.f64 >> 63) {
|
||
fmt_buf->signchar = '-';
|
||
}
|
||
|
||
if (LIKELY(e >= -8 && e <= 63)) { /* -8 to +63 */
|
||
/* integer, fraction and manipulations fit in uint64_t */
|
||
mantissa |= 0x0010000000000000ull;
|
||
ia->numchunks = 0;
|
||
ia->shift = 0;
|
||
fa->numchunks = 0;
|
||
|
||
if (e < 0) { /* -8 to -1 - fraction */
|
||
long fracbits = 52 - e;
|
||
/* int - none */
|
||
ia->len = 0;
|
||
ia->val = 0;
|
||
/* frac */
|
||
fa->len = fracbits - __builtin_ctzll(mantissa);
|
||
fa->shift = fracbits;
|
||
fa->val = mantissa;
|
||
}
|
||
else if (e <= 51) { /* 0 to +51 - integer|fraction */
|
||
long fracbits = 52 - e;
|
||
/* int */
|
||
ia->len = base10exp(e) + 2; /* go up + possibly 1 longer */
|
||
ia->val = mantissa >> fracbits;
|
||
/* frac */
|
||
fa->shift = fracbits;
|
||
fa->val = mantissa ^ (ia->val << fracbits);
|
||
fa->len = fa->val ? fracbits - __builtin_ctzll(mantissa) : 0;
|
||
}
|
||
else { /* +52 to +63 - integer */
|
||
/* int */
|
||
ia->len = base10exp(e) + 2;
|
||
ia->val = mantissa << (e - 52);
|
||
/* frac - none */
|
||
fa->len = 0;
|
||
fa->shift = 0;
|
||
fa->val = 0;
|
||
}
|
||
}
|
||
else if (e < 0) { /* -1023 to -9 - fraction */
|
||
/* int - none */
|
||
ia->numchunks = 0;
|
||
ia->len = 0;
|
||
ia->shift = 0;
|
||
ia->val = 0;
|
||
/* frac - left-justify on bit 31 of the chunk of the MSb */
|
||
if (e >= -1022) { /* normal */
|
||
mantissa |= 0x0010000000000000ull;
|
||
}
|
||
else { /* subnormal (including zero) */
|
||
e = -1022;
|
||
}
|
||
|
||
if (mantissa) {
|
||
long fracbits = 52 - e;
|
||
fa->len = fracbits - __builtin_ctzll(mantissa);
|
||
fa->shift = 31 - ((51 - e) % 32);
|
||
fa->val = mantissa;
|
||
fa->basechunk = (fa->shift + 52) / 32;
|
||
fa->numchunks = (51 - e + fa->shift) / 32 + 1;
|
||
rc = fa->numchunks;
|
||
}
|
||
else { /* zero */
|
||
fa->numchunks = 0;
|
||
fa->len = 0;
|
||
fa->shift = 0;
|
||
fa->val = 0;
|
||
}
|
||
}
|
||
else if (e <= 1023) { /* +64 to +1023 - integer */
|
||
/* int - right-justify on bit 0 of the first chunk */
|
||
ia->val = mantissa | 0x0010000000000000ull;
|
||
ia->len = base10exp(e) + 2;
|
||
ia->shift = (e - 52) % 32;
|
||
ia->basechunk = e / 32;
|
||
ia->numchunks = ia->basechunk + 1;
|
||
rc = ia->numchunks;
|
||
/* frac - none */
|
||
fa->numchunks = 0;
|
||
fa->len = 0;
|
||
fa->shift = 0;
|
||
fa->val = 0;
|
||
}
|
||
else { /* +1024: INF, NAN */
|
||
rc = -1 - !!mantissa;
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
/* construct the arbitrary-precision value in the provided allocation */
|
||
static void init_ieee754_double_chunks(struct ap_int *a,
|
||
uint32_t *a_chunks)
|
||
{
|
||
long basechunk = a->basechunk;
|
||
long shift = a->shift;
|
||
uint64_t val = a->val;
|
||
|
||
a->chunks = a_chunks;
|
||
|
||
memset(a_chunks, 0, a->numchunks*sizeof (uint32_t));
|
||
|
||
if (shift < 12) {
|
||
a_chunks[basechunk - 1] = val << shift;
|
||
a_chunks[basechunk - 0] = val >> (32 - shift);
|
||
}
|
||
else {
|
||
a_chunks[basechunk - 2] = val << shift;
|
||
a_chunks[basechunk - 1] = val >> (32 - shift);
|
||
a_chunks[basechunk - 0] = val >> (64 - shift);
|
||
}
|
||
}
|
||
|
||
/* format inf, nan strings */
|
||
static void format_inf_nan(struct fmt_buf *fmt_buf, long type)
|
||
{
|
||
/* certain special values */
|
||
static const char text[2][2][3] =
|
||
{
|
||
{ { 'I', 'N', 'F' }, { 'i', 'n', 'f' } },
|
||
{ { 'N', 'A', 'N' }, { 'n', 'a', 'n' } },
|
||
};
|
||
|
||
char *p = fmt_buf->buf;
|
||
fmt_buf->p = p;
|
||
fmt_buf->length = 3;
|
||
|
||
/* they also have a sign */
|
||
if (fmt_buf->signchar) {
|
||
*p++ = fmt_buf->signchar;
|
||
fmt_buf->length++;
|
||
}
|
||
|
||
memcpy(p, &text[type][(fmt_buf->radixchar >> 5) & 0x1], 3);
|
||
}
|
||
|
||
/* %e %E %f %F %g %G */
|
||
static int format_double_radix(double f,
|
||
struct fmt_buf *fmt_buf,
|
||
vuprintf_push_cb push,
|
||
void *userp)
|
||
{
|
||
struct ap_int ia, fa;
|
||
long rc = parse_double(f, &ia, &fa, fmt_buf);
|
||
|
||
if (UNLIKELY(rc < 0)) {
|
||
format_inf_nan(fmt_buf, -rc - 1);
|
||
return 0;
|
||
}
|
||
|
||
int count = 0;
|
||
|
||
/* default precision is 6 for all formats */
|
||
int prec_rem = fmt_buf->precision < 0 ? 6 : fmt_buf->precision;
|
||
|
||
int exp = exp;
|
||
int explen = 0;
|
||
|
||
switch (fmt_buf->radixchar & 0x3)
|
||
{
|
||
case 3: /* %g, %G */
|
||
fmt_buf->precision = prec_rem;
|
||
if (prec_rem) {
|
||
prec_rem--;
|
||
}
|
||
case 1: /* %e, %E */
|
||
explen = 2;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (rc > 0 && ia.numchunks > 0) {
|
||
/* large integer required */
|
||
init_double_chunks(&ia, alloca(rc*sizeof(*ia.chunks)));
|
||
rc = 0;
|
||
}
|
||
|
||
const int bufoffs = 6; /* log rollover + round rollover + leading zeros (%g) */
|
||
long f_prec = MIN(fa.len, prec_rem + 1);
|
||
char buf[bufoffs + ia.len + f_prec + 1];
|
||
char *p_last = &buf[bufoffs + ia.len];
|
||
char *p_dec = p_last;
|
||
char *p_first = format_double_int10(&ia, p_last);
|
||
|
||
if (explen) {
|
||
if (!ia.val && fa.len) {
|
||
p_first = p_last = &buf[bufoffs];
|
||
f_prec = -f_prec - 1; /* no lead zeros */
|
||
}
|
||
else { /* handles 0e+0 too */
|
||
exp = ia.len - 1;
|
||
|
||
if (exp) {
|
||
prec_rem -= exp;
|
||
|
||
if (prec_rem < 0) {
|
||
p_last += prec_rem + 1;
|
||
f_prec = 0;
|
||
}
|
||
else {
|
||
f_prec = MIN(fa.len, prec_rem + 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
p_dec = p_first + 1;
|
||
}
|
||
|
||
if (f_prec) {
|
||
if (rc > 0) {
|
||
/* large integer required */
|
||
init_double_chunks(&fa, alloca(rc*sizeof(*fa.chunks)));
|
||
}
|
||
|
||
p_last = format_double_frac10(&fa, p_last, f_prec);
|
||
|
||
if (f_prec < 0) {
|
||
f_prec = -f_prec - 1;
|
||
exp = f_prec - fa.len;
|
||
}
|
||
|
||
prec_rem -= f_prec;
|
||
}
|
||
|
||
if (prec_rem < 0) {
|
||
prec_rem = 0;
|
||
p_last--;
|
||
|
||
if (round_number_string10(p_last, p_last - p_first)) {
|
||
/* carried left */
|
||
p_first--;
|
||
|
||
if (explen) {
|
||
/* slide everything left by 1 */
|
||
exp++;
|
||
p_dec--;
|
||
p_last--;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (explen) {
|
||
if ((fmt_buf->radixchar & 0x3) == 0x3) { /* g, G */
|
||
/* 'g' is some weird crap */
|
||
/* now that the final exponent is known and everything rounded,
|
||
it is possible to decide whether to format similarly to
|
||
'e' or 'f' */
|
||
if (fmt_buf->precision > exp && exp >= -4) { /* P > X >= -4 */
|
||
if (exp >= 0) {
|
||
/* integer digits will be in the buffer */
|
||
p_dec = p_first + exp + 1;
|
||
}
|
||
else {
|
||
/* we didn't keep leading zeros and need to regenerate
|
||
them; space was reserved just in case */
|
||
p_first = memset(p_dec + exp - 1, '0', -exp);
|
||
p_dec = p_first + 1;
|
||
}
|
||
|
||
/* suppress exponent */
|
||
explen = 0;
|
||
}
|
||
|
||
if (!fmt_buf->length) {
|
||
/* strip any trailing zeros from the fraction */
|
||
while (p_last > p_dec && p_last[-1] == '0') {
|
||
p_last--;
|
||
}
|
||
|
||
/* suppress trailing precision fill */
|
||
prec_rem = 0;
|
||
}
|
||
}
|
||
|
||
if (explen) {
|
||
/* build exponent string: 'e<>dd' */
|
||
char *p = fmt_buf->bufend;
|
||
int signchar = '+';
|
||
|
||
if (exp < 0) {
|
||
signchar = '-';
|
||
exp = -exp;
|
||
}
|
||
|
||
while (exp || explen < 4) {
|
||
*--p = exp % 10 + '0';
|
||
exp /= 10;
|
||
explen++;
|
||
}
|
||
|
||
*--p = signchar;
|
||
*--p = fmt_buf->radixchar & ~0x2;
|
||
}
|
||
}
|
||
|
||
int width = fmt_buf->width;
|
||
int point = p_last > p_dec || prec_rem || fmt_buf->length;
|
||
int length = p_last - p_first + !!fmt_buf->signchar + point + explen;
|
||
|
||
if (width) {
|
||
if (width - length <= prec_rem) {
|
||
width = 0;
|
||
}
|
||
else {
|
||
width -= length + prec_rem;
|
||
}
|
||
}
|
||
|
||
rc = -1;
|
||
|
||
/* left padding */
|
||
if (fmt_buf->alignchar > '0') {
|
||
/* space-padded width -- before sign */
|
||
while (width > 0) {
|
||
PUSHCHAR(' ');
|
||
width--;
|
||
}
|
||
}
|
||
|
||
if (fmt_buf->signchar) {
|
||
PUSHCHAR(fmt_buf->signchar);
|
||
}
|
||
|
||
if (fmt_buf->alignchar == '0') {
|
||
/* zero-padded width -- after sign */
|
||
while (width > 0) {
|
||
PUSHCHAR('0');
|
||
width--;
|
||
}
|
||
}
|
||
|
||
/* integer part */
|
||
while (p_first < p_dec) {
|
||
PUSHCHAR(*p_first++);
|
||
}
|
||
|
||
/* decimal point */
|
||
if (point) {
|
||
PUSHCHAR('.');
|
||
}
|
||
|
||
/* fractional part */
|
||
while (p_first < p_last) {
|
||
PUSHCHAR(*p_first++);
|
||
}
|
||
|
||
/* precision 0-padding */
|
||
while (prec_rem > 0) {
|
||
PUSHCHAR('0');
|
||
prec_rem--;
|
||
}
|
||
|
||
/* exponent */
|
||
if (explen > 0) {
|
||
char *p = fmt_buf->bufend;
|
||
while (explen > 0) {
|
||
PUSHCHAR(p[-explen--]);
|
||
}
|
||
}
|
||
|
||
/* right padding */
|
||
while (width > 0) {
|
||
PUSHCHAR(' ');
|
||
width--;
|
||
}
|
||
|
||
rc = 1;
|
||
done:
|
||
fmt_buf->length = count;
|
||
return rc;
|
||
}
|
||
#endif /* FMT_RADIX_floats */
|
||
|
||
/* parse fixed width or precision field */
|
||
static const char * parse_number_spec(const char *fmt,
|
||
int ch,
|
||
int *out)
|
||
{
|
||
int i = ch - '0';
|
||
|
||
while (1) {
|
||
ch = *fmt - '0';
|
||
|
||
if (ch < 0 || ch > 9 || i > INT_MAX / 10 ||
|
||
(i == INT_MAX / 10 && ch > INT_MAX % 10)) {
|
||
break;
|
||
}
|
||
|
||
i = i * 10 + ch;
|
||
fmt++;
|
||
}
|
||
|
||
*out = i;
|
||
return fmt;
|
||
}
|
||
|
||
int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
|
||
void *userp,
|
||
const char *fmt,
|
||
va_list ap)
|
||
{
|
||
int count = 0;
|
||
int ch;
|
||
|
||
/* macrofied identifiers share a variable with another */
|
||
unsigned int val_d;
|
||
unsigned int val_u;
|
||
#ifndef val_ld
|
||
unsigned long val_ld;
|
||
#endif
|
||
#ifndef val_lu
|
||
unsigned long val_lu;
|
||
#endif
|
||
#ifndef val_lld
|
||
unsigned long long val_lld;
|
||
#endif
|
||
#ifndef val_llu
|
||
unsigned long long val_llu;
|
||
#endif
|
||
|
||
struct fmt_buf fmt_buf;
|
||
fmt_buf.bufend[0] = '0';
|
||
|
||
while (1) {
|
||
while (1) {
|
||
if ((ch = *fmt++) == '\0') {
|
||
goto done;
|
||
}
|
||
|
||
if (ch == '%' && (ch = *fmt++) != '%') {
|
||
break;
|
||
}
|
||
|
||
PUSHCHAR(ch);
|
||
}
|
||
|
||
/* set to defaults */
|
||
fmt_buf.fmt_start = fmt;
|
||
|
||
int signchar = 0;
|
||
unsigned int width = 0;
|
||
int lenmod = LENMOD_NONE;
|
||
size_t length = 0;
|
||
size_t pfxlen = 0;
|
||
bool numeric = false;
|
||
int alignchar = '0' + 1;
|
||
int precision = -1;
|
||
const char *buf = NULL;
|
||
|
||
/*** flags ***/
|
||
while (1) {
|
||
switch (ch)
|
||
{
|
||
case ' ': /* <space> before non-negative value (signed conversion) */
|
||
case '+': /* '+' before non-negative value (signed conversion) */
|
||
/* '+' overrides ' ' */
|
||
if (ch > signchar) {
|
||
signchar = ch;
|
||
}
|
||
break;
|
||
case '-': /* left-justify in field */
|
||
case '0': /* zero-pad to fill field */
|
||
/* '-' overrides '0' */
|
||
if (ch < alignchar) {
|
||
alignchar = ch;
|
||
}
|
||
break;
|
||
case '#': /* number prefix (nonzero %o:'0' %x/%X:'0x') */
|
||
/* indicate; formatter updates with actual length */
|
||
pfxlen = 1;
|
||
break;
|
||
#if 0
|
||
case '\'': /* digit grouping (non-monetary) */
|
||
break;
|
||
#endif
|
||
default:
|
||
goto flags_done;
|
||
}
|
||
|
||
ch = *fmt++;
|
||
}
|
||
flags_done:
|
||
|
||
/*** width ***/
|
||
if (ch == '*') {
|
||
/* variable width */
|
||
int w = va_arg(ap, int);
|
||
if (w < 0) {
|
||
/* negative width is width with implied '-' */
|
||
width = (unsigned int)-(w + 1) + 1;
|
||
alignchar = '-';
|
||
}
|
||
else {
|
||
width = w;
|
||
}
|
||
|
||
ch = *fmt++;
|
||
}
|
||
else if (ch >= '1' && ch <= '9') {
|
||
/* fixed width */
|
||
fmt = parse_number_spec(fmt, ch, &width);
|
||
ch = *fmt++;
|
||
}
|
||
|
||
/*** precision ***/
|
||
if (ch == '.') {
|
||
ch = *fmt++;
|
||
|
||
if (ch == '*') {
|
||
/* variable precision; negative precision is ignored */
|
||
precision = va_arg (ap, int);
|
||
ch = *fmt++;
|
||
}
|
||
else if (ch >= '0' && ch <= '9') {
|
||
/* fixed precision */
|
||
fmt = parse_number_spec(fmt, ch, &precision);
|
||
ch = *fmt++;
|
||
}
|
||
}
|
||
|
||
/*** length modifier ***/
|
||
#if FMT_LENMOD
|
||
switch (ch)
|
||
{
|
||
#if (FMT_LENMOD & (FMT_LENMOD_h | FMT_LENMOD_hh))
|
||
case 'h':
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_j)
|
||
case 'j':
|
||
#endif
|
||
#if (FMT_LENMOD & (FMT_LENMOD_l | FMT_LENMOD_ll))
|
||
case 'l':
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_t)
|
||
case 't':
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_z)
|
||
case 'z':
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_L)
|
||
case 'L':
|
||
#endif
|
||
lenmod = ch;
|
||
ch = *fmt++;
|
||
#if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll))
|
||
/* doesn't matter if jj, tt or zz happen; they will be rejected
|
||
by the radix handler */
|
||
if (ch == lenmod) {
|
||
lenmod = LENMOD2(lenmod, ch);
|
||
ch = *fmt++;
|
||
}
|
||
#endif
|
||
}
|
||
#endif /* FMT_LENMOD */
|
||
|
||
/*** radix ***/
|
||
switch (ch)
|
||
{
|
||
/** non-numeric **/
|
||
case 'c':
|
||
buf = format_c(va_arg(ap, int), &fmt_buf, lenmod);
|
||
break;
|
||
#if (FMT_RADIX & FMT_RADIX_n)
|
||
case 'n':
|
||
if (format_n(va_arg(ap, void *), count, lenmod)) {
|
||
continue; /* no output */
|
||
}
|
||
break;
|
||
#endif
|
||
case 's':
|
||
buf = format_s(va_arg(ap, const void *), &fmt_buf,
|
||
precision, lenmod);
|
||
break;
|
||
|
||
/** non-integer **/
|
||
#if (FMT_RADIX & FMT_RADIX_p)
|
||
case 'p':
|
||
case 'P':
|
||
buf = format_p(va_arg(ap, void *), &fmt_buf, ch,
|
||
&numeric);
|
||
break;
|
||
#endif
|
||
|
||
#if (FMT_RADIX & FMT_RADIX_floats)
|
||
/* any floats gets all of them (except with 'L' and %a, %A for now) */
|
||
case 'e':
|
||
case 'E':
|
||
case 'f':
|
||
case 'F':
|
||
case 'g':
|
||
case 'G':
|
||
/* LENMOD_L isn't supported for now and will be rejected automatically */
|
||
|
||
/* floating point has very different spec interpretations to other
|
||
formats and requires special handling */
|
||
fmt_buf.length = pfxlen;
|
||
fmt_buf.lenmod = lenmod;
|
||
fmt_buf.radixchar = ch;
|
||
fmt_buf.signchar = signchar;
|
||
fmt_buf.alignchar = alignchar;
|
||
fmt_buf.width = width;
|
||
fmt_buf.precision = precision;
|
||
|
||
ch = format_double_radix(va_arg(ap, double), &fmt_buf, push, userp);
|
||
if (ch) {
|
||
count += fmt_buf.length;
|
||
if (ch > 0) {
|
||
continue;
|
||
}
|
||
|
||
goto done;
|
||
}
|
||
|
||
buf = fmt_buf.p;
|
||
break;
|
||
#endif
|
||
|
||
/** signed integer **/
|
||
case 'd':
|
||
case 'i':
|
||
fmt_buf.length = pfxlen;
|
||
|
||
switch (lenmod)
|
||
{
|
||
case LENMOD_NONE:
|
||
val_d = va_arg(ap, signed int);
|
||
goto branch_fmt_d;
|
||
#if (FMT_LENMOD & FMT_LENMOD_h)
|
||
case LENMOD_h:
|
||
val_d = (signed short)va_arg(ap, SSHRT_INT_ARG);
|
||
goto branch_fmt_d;
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_hh)
|
||
case LENMOD_hh:
|
||
val_d = (signed char)va_arg(ap, SCHAR_INT_ARG);
|
||
goto branch_fmt_d;
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_j)
|
||
case LENMOD_j:
|
||
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(intmax_t, true),
|
||
va_arg(ap, intmax_t), true);
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_l)
|
||
case LENMOD_l:
|
||
val_ld = va_arg(ap, signed long);
|
||
goto branch_fmt_ld;
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
||
case LENMOD_ll:
|
||
val_lld = va_arg(ap, signed long long);
|
||
goto branch_fmt_lld;
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_t)
|
||
case LENMOD_t:
|
||
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ptrdiff_t, true),
|
||
va_arg(ap, ptrdiff_t), true);
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_z)
|
||
case LENMOD_z:
|
||
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ssize_t, true),
|
||
va_arg(ap, ssize_t), true);
|
||
#endif
|
||
}
|
||
|
||
/* macrofied labels share a formatter with another */
|
||
if (0) {
|
||
branch_fmt_d:
|
||
buf = format_d(val_d, &fmt_buf, signchar);
|
||
} else if (0) {
|
||
#ifndef val_ld
|
||
branch_fmt_ld:
|
||
buf = format_ld(val_ld, &fmt_buf, signchar);
|
||
#endif
|
||
} else if (0) {
|
||
#ifndef val_lld
|
||
branch_fmt_lld:
|
||
buf = format_lld(val_lld, &fmt_buf, signchar);
|
||
#endif
|
||
}
|
||
|
||
numeric = true;
|
||
break;
|
||
|
||
/** unsigned integer **/
|
||
#if (FMT_RADIX & FMT_RADIX_o)
|
||
case 'o':
|
||
#endif
|
||
case 'u':
|
||
#if (FMT_RADIX & FMT_RADIX_x)
|
||
case 'x':
|
||
case 'X':
|
||
#endif
|
||
fmt_buf.length = pfxlen;
|
||
|
||
switch (lenmod)
|
||
{
|
||
case LENMOD_NONE:
|
||
val_u = va_arg(ap, unsigned int);
|
||
goto branch_fmt_u;
|
||
#if (FMT_LENMOD & FMT_LENMOD_h)
|
||
case LENMOD_h:
|
||
val_u = (unsigned short)va_arg(ap, USHRT_INT_ARG);
|
||
goto branch_fmt_u;
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_hh)
|
||
case LENMOD_hh:
|
||
val_u = (unsigned char)va_arg(ap, UCHAR_INT_ARG);
|
||
goto branch_fmt_u;
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_j)
|
||
case LENMOD_j:
|
||
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(uintmax_t, false),
|
||
va_arg(ap, uintmax_t), false);
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_l)
|
||
case LENMOD_l:
|
||
val_lu = va_arg(ap, unsigned long);
|
||
goto branch_fmt_lu;
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
||
case LENMOD_ll:
|
||
val_llu = va_arg(ap, unsigned long long);
|
||
goto branch_fmt_llu;
|
||
#endif
|
||
#if (FMT_LENMOD & (FMT_LENMOD_t | FMT_LENMOD_z))
|
||
/* format "uptrdiff_t" as size_t (unless it becomes standard) */
|
||
#if (FMT_LENMOD & FMT_LENMOD_t)
|
||
case LENMOD_t:
|
||
#endif
|
||
#if (FMT_LENMOD & FMT_LENMOD_z)
|
||
case LENMOD_z:
|
||
#endif
|
||
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(size_t, false),
|
||
va_arg(ap, size_t), false);
|
||
#endif
|
||
}
|
||
|
||
/* macrofied labels share a formatter with another */
|
||
if (0) {
|
||
branch_fmt_u:
|
||
buf = format_u(val_u, &fmt_buf, ch);
|
||
} else if (0) {
|
||
#ifndef val_lu
|
||
branch_fmt_lu:
|
||
buf = format_lu(val_lu, &fmt_buf, ch);
|
||
#endif
|
||
} else if (0) {
|
||
#ifndef val_llu
|
||
branch_fmt_llu:
|
||
buf = format_llu(val_llu, &fmt_buf, ch);
|
||
#endif
|
||
}
|
||
|
||
numeric = true;
|
||
break;
|
||
}
|
||
|
||
if (buf) {
|
||
/** padding **/
|
||
if (numeric) {
|
||
/* numeric formats into fmt_buf.buf */
|
||
pfxlen = fmt_buf.length;
|
||
length = fmt_buf.bufend - buf;
|
||
|
||
size_t size = pfxlen + length;
|
||
|
||
if (precision >= 0) {
|
||
/* explicit precision */
|
||
precision -= (int)length;
|
||
|
||
if (precision > 0) {
|
||
size += precision;
|
||
}
|
||
|
||
width -= MIN(width, size);
|
||
}
|
||
else {
|
||
/* default precision */
|
||
if (!length) {
|
||
length = 1;
|
||
size++;
|
||
}
|
||
|
||
width -= MIN(width, size);
|
||
|
||
if (alignchar == '0') {
|
||
/* width zero-fill */
|
||
precision = width;
|
||
width = 0;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
/* non-numeric: supress prefix and precision; keep length and
|
||
width */
|
||
pfxlen = 0;
|
||
precision = 0;
|
||
length = fmt_buf.length;
|
||
width -= MIN(width, length);
|
||
}
|
||
}
|
||
else {
|
||
/* format not accepted; print it literally */
|
||
buf = fmt_buf.fmt_start - 2;
|
||
length = fmt - buf;
|
||
width = 0;
|
||
pfxlen = 0;
|
||
precision = 0;
|
||
}
|
||
|
||
/** push all the stuff **/
|
||
|
||
if (alignchar != '-') {
|
||
/* left padding */
|
||
while (width > 0) {
|
||
PUSHCHAR(' ');
|
||
width--;
|
||
}
|
||
}
|
||
|
||
/* prefix */
|
||
while (pfxlen > 0) {
|
||
PUSHCHAR(buf[-pfxlen]);
|
||
pfxlen--;
|
||
}
|
||
|
||
/* 0-padding */
|
||
while (precision > 0) {
|
||
PUSHCHAR('0');
|
||
precision--;
|
||
}
|
||
|
||
/* field */
|
||
while (length > 0) {
|
||
PUSHCHAR(*buf++);
|
||
length--;
|
||
}
|
||
|
||
/* right padding */
|
||
while (width > 0) {
|
||
PUSHCHAR(' ');
|
||
width--;
|
||
}
|
||
}
|
||
|
||
done:
|
||
return count;
|
||
}
|