792 lines
28 KiB
C
792 lines
28 KiB
C
// Buffer-formatting siblings of printf — kept in their own translation
|
|
// unit so the shared writeXxx helpers don't have to take a function-
|
|
// pointer sink (indirect call cost on this target) and so adding the
|
|
// formatter to libc.c can't shift vprintf's branch distances out of
|
|
// range (per the strtol.c precedent).
|
|
//
|
|
// Functions:
|
|
// int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap);
|
|
// int snprintf (char *buf, size_t n, const char *fmt, ...);
|
|
// int vsprintf (char *buf, const char *fmt, va_list ap);
|
|
// int sprintf (char *buf, const char *fmt, ...);
|
|
//
|
|
// Format support:
|
|
// conversions %d %i %u %x %X %o %c %s %p %f %F %e %E %g %G %n %%
|
|
// flags - + (space) # 0
|
|
// width decimal or `*` (from va_arg int)
|
|
// precision .N or .*
|
|
// length hh, h, l, ll, j, z, t
|
|
//
|
|
// Floats are soft-double (double + float promote-to-double via va_arg);
|
|
// precision capped at 9 fractional digits. Hex-float (%a / %A) is
|
|
// fully supported: IEEE-754 double bits decoded into 4 u16 words (no
|
|
// i64 shift libcalls), emitted as `0x1.{13-hex}p{signed-decimal}` with
|
|
// glibc-style trailing-zero stripping when precision is unspecified.
|
|
// Subnormals canonicalize as `0x0.{mantissa}p-1022`. Inf/NaN parity
|
|
// across %f / %F / %g / %G / %e / %E / %a / %A. Multibyte / wide-char
|
|
// specifiers (%lc, %ls) fall through and emit `%lc` literally.
|
|
//
|
|
// Return value: number of characters that would have been written had
|
|
// the buffer been unbounded (C99 vsnprintf semantics), not just the
|
|
// number actually written. This lets callers detect truncation.
|
|
//
|
|
// Sink state lives in file-static globals (gCur/gEnd/gTotal) rather
|
|
// than a per-call context. Single-threaded use only, but that matches
|
|
// the rest of this runtime.
|
|
|
|
typedef unsigned long size_t;
|
|
typedef __builtin_va_list va_list;
|
|
#define va_start(ap, last) __builtin_va_start(ap, last)
|
|
#define va_arg(ap, ty) __builtin_va_arg(ap, ty)
|
|
#define va_end(ap) __builtin_va_end(ap)
|
|
|
|
// Unbounded sink sentinel used by sprintf/vsprintf. Setting gEnd to
|
|
// `buf + 0xFFFE` looks innocuous but clang lowers the +0xFFFE to a
|
|
// `dec a; dec a` peephole (0xFFFE is -2 in 16-bit), giving gEnd =
|
|
// buf - 2 -- the `cur < end` bounds test then always fails. Use the
|
|
// absolute top-of-bank sentinel instead.
|
|
#define SPRINTF_END_SENTINEL ((char *)0xFFFF)
|
|
|
|
|
|
static char *gCur;
|
|
static char *gEnd;
|
|
static size_t gTotal;
|
|
|
|
|
|
static void emit(char c) {
|
|
if (gCur < gEnd) {
|
|
*gCur++ = c;
|
|
}
|
|
gTotal++;
|
|
}
|
|
|
|
|
|
static void emitStr(const char *p) {
|
|
if (!p) {
|
|
p = "(null)";
|
|
}
|
|
while (*p) {
|
|
emit(*p++);
|
|
}
|
|
}
|
|
|
|
|
|
// ---- Number-to-buffer helpers (reverse order, returns digit count) -----
|
|
|
|
// uint64 -> decimal digits in reverse order. buf must be >= 20 bytes
|
|
// (UINT64_MAX = 18446744073709551615 = 20 digits).
|
|
static int u64ToDec(unsigned long long n, char *buf) {
|
|
int i = 0;
|
|
if (n == 0) {
|
|
buf[i++] = '0';
|
|
return i;
|
|
}
|
|
while (n > 0) {
|
|
buf[i++] = (char)('0' + (n % 10ull));
|
|
n /= 10ull;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
// uint64 -> hex digits in reverse order. Returns digit count. Buf
|
|
// must be >= 16 bytes (UINT64_MAX = 16 hex digits).
|
|
static int u64ToHex(unsigned long long n, int upper, char *buf) {
|
|
const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
|
|
int i = 0;
|
|
if (n == 0) {
|
|
buf[i++] = '0';
|
|
return i;
|
|
}
|
|
while (n > 0) {
|
|
buf[i++] = digits[n & 0xFull];
|
|
n >>= 4;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
// uint64 -> octal digits in reverse order. Returns digit count.
|
|
static int u64ToOct(unsigned long long n, char *buf) {
|
|
int i = 0;
|
|
if (n == 0) {
|
|
buf[i++] = '0';
|
|
return i;
|
|
}
|
|
while (n > 0) {
|
|
buf[i++] = (char)('0' + (n & 7ull));
|
|
n >>= 3;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
// Emit n copies of c (used for width / precision padding).
|
|
static void emitPad(int n, char c) {
|
|
while (n-- > 0) emit(c);
|
|
}
|
|
|
|
|
|
// Emit a reversed buffer in forward order.
|
|
static void emitRev(const char *buf, int n) {
|
|
while (n-- > 0) emit(buf[n]);
|
|
}
|
|
|
|
|
|
// ---- Integer formatting with full flag/width/prec/length surface ----
|
|
|
|
typedef struct {
|
|
int leftAlign; // '-'
|
|
int signPlus; // '+'
|
|
int signSpace; // ' '
|
|
int altForm; // '#'
|
|
int zeroPad; // '0'
|
|
int width;
|
|
int prec; // -1 if unset
|
|
} Spec;
|
|
|
|
|
|
// Emit an integer with all the conversion flags. `value` is the
|
|
// magnitude (always non-negative); `isNeg` says whether to prefix '-'.
|
|
// `base` is 8 / 10 / 16. `upper` selects A-F vs a-f for base 16.
|
|
static void emitNumber(unsigned long long value,
|
|
int isSigned, int isNeg,
|
|
int base, int upper,
|
|
const Spec *s) {
|
|
char buf[20];
|
|
int len;
|
|
if (base == 16) {
|
|
len = u64ToHex(value, upper, buf);
|
|
} else if (base == 8) {
|
|
len = u64ToOct(value, buf);
|
|
} else {
|
|
len = u64ToDec(value, buf);
|
|
}
|
|
// Sign / alt-form prefix.
|
|
char prefix1 = 0; // '-', '+', or ' '
|
|
char prefix2 = 0; // 'x' / 'X' / '0' for # alt-form
|
|
char prefix0 = 0; // '0' before the 'x' for hex alt
|
|
if (isSigned) {
|
|
if (isNeg) prefix1 = '-';
|
|
else if (s->signPlus) prefix1 = '+';
|
|
else if (s->signSpace) prefix1 = ' ';
|
|
}
|
|
if (s->altForm && base == 16 && value != 0ull) {
|
|
prefix0 = '0';
|
|
prefix2 = upper ? 'X' : 'x';
|
|
} else if (s->altForm && base == 8 && (s->prec < 0 || s->prec < len + 1)) {
|
|
// Octal alt-form: ensure leading 0. Easiest: bump precision.
|
|
if (buf[len - 1] != '0') {
|
|
buf[len++] = '0';
|
|
}
|
|
}
|
|
// Precision (min number of digits — left-pad with '0').
|
|
int digitPad = 0;
|
|
if (s->prec >= 0 && len < s->prec) {
|
|
digitPad = s->prec - len;
|
|
}
|
|
int prefixLen = (prefix1 ? 1 : 0) + (prefix0 ? 1 : 0) + (prefix2 ? 1 : 0);
|
|
int contentLen = prefixLen + digitPad + len;
|
|
int fieldPad = s->width > contentLen ? s->width - contentLen : 0;
|
|
// When zero-padding is requested AND no precision, the zero pad
|
|
// counts toward digitPad (so '+' / sign goes first, then zeros,
|
|
// then digits). Precision specified disables zero pad per C99.
|
|
if (s->zeroPad && !s->leftAlign && s->prec < 0) {
|
|
digitPad += fieldPad;
|
|
fieldPad = 0;
|
|
}
|
|
if (!s->leftAlign) {
|
|
emitPad(fieldPad, ' ');
|
|
}
|
|
if (prefix1) emit(prefix1);
|
|
if (prefix0) emit(prefix0);
|
|
if (prefix2) emit(prefix2);
|
|
emitPad(digitPad, '0');
|
|
emitRev(buf, len);
|
|
if (s->leftAlign) {
|
|
emitPad(fieldPad, ' ');
|
|
}
|
|
}
|
|
|
|
|
|
// Emit a string with width + precision honored (precision = max chars).
|
|
static void emitStrField(const char *p, const Spec *s) {
|
|
if (!p) p = "(null)";
|
|
int len = 0;
|
|
while (p[len] && (s->prec < 0 || len < s->prec)) len++;
|
|
int fieldPad = s->width > len ? s->width - len : 0;
|
|
if (!s->leftAlign) emitPad(fieldPad, ' ');
|
|
for (int i = 0; i < len; i++) emit(p[i]);
|
|
if (s->leftAlign) emitPad(fieldPad, ' ');
|
|
}
|
|
|
|
|
|
// IEEE-754 double decoded into a sign bit + 11-bit exponent + four
|
|
// 16-bit mantissa words. Mantissa is laid out LSB-first: m[0] is
|
|
// bits[15:0], m[1] bits[31:16], m[2] bits[47:32], m[3] bits[51:48]
|
|
// (only the low 4 bits of m[3] are used). Reading the bits as 4 u16
|
|
// words avoids the >>52 / 12-bit-mask paths that drag i64 libcalls in.
|
|
#ifndef LLVM816_NO_FLOAT_PRINTF
|
|
typedef struct {
|
|
unsigned short m[4]; // mantissa: low-to-high, m[3] only 4 LSBs
|
|
unsigned short exp; // 11-bit biased exponent (0..0x7FF)
|
|
unsigned char sign; // 0 / 1
|
|
} DblBits;
|
|
|
|
|
|
static void decodeDouble(double v, DblBits *d) {
|
|
unsigned short w[4];
|
|
__builtin_memcpy(w, &v, 8);
|
|
// Little-endian byte order: w[0] = bytes 0-1 (mantissa LSB),
|
|
// w[3] = bytes 6-7 (sign + exp + mantissa MSB-nibble).
|
|
d->m[0] = w[0];
|
|
d->m[1] = w[1];
|
|
d->m[2] = w[2];
|
|
d->m[3] = (unsigned short)(w[3] & 0x000F);
|
|
d->exp = (unsigned short)((w[3] >> 4) & 0x07FF);
|
|
d->sign = (unsigned char)((w[3] >> 15) & 1);
|
|
}
|
|
|
|
|
|
// If v is +/-Inf or NaN, emit the canonical glibc-style spelling and
|
|
// return 1. Otherwise return 0 (caller continues with finite path).
|
|
// `upper` selects "INF"/"NAN" vs "inf"/"nan". Width/left-align/space/
|
|
// '+' flags are honored exactly like glibc.
|
|
static int emitInfNan(const DblBits *d, int upper, const Spec *s) {
|
|
if (d->exp != 0x7FF) {
|
|
return 0;
|
|
}
|
|
int isNan = (d->m[0] | d->m[1] | d->m[2] | d->m[3]) != 0;
|
|
const char *body = isNan ? (upper ? "NAN" : "nan")
|
|
: (upper ? "INF" : "inf");
|
|
char prefix = 0;
|
|
if (!isNan) {
|
|
if (d->sign) prefix = '-';
|
|
else if (s->signPlus) prefix = '+';
|
|
else if (s->signSpace)prefix = ' ';
|
|
}
|
|
int bodyLen = 3;
|
|
int total = bodyLen + (prefix ? 1 : 0);
|
|
int fieldPad = s->width > total ? s->width - total : 0;
|
|
// C99: zero-padding is undefined / ignored for Inf/NaN; glibc uses
|
|
// spaces. We follow glibc.
|
|
if (!s->leftAlign) {
|
|
emitPad(fieldPad, ' ');
|
|
}
|
|
if (prefix) {
|
|
emit(prefix);
|
|
}
|
|
emitStr(body);
|
|
if (s->leftAlign) {
|
|
emitPad(fieldPad, ' ');
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
// Emit %a / %A hex-float. Local width/leftAlign/zeroPad handling --
|
|
// emitNumber's monolithic numeric body can only honor one prefix at a
|
|
// time, and hex-float needs prefix = sign + "0x" + content. We do use
|
|
// emitNumber for the exponent tail (sign + decimal digits, no prefix).
|
|
//
|
|
// Format: [-]0x{H}.{F}p{SE} where H is 0 or 1, F is up to 13 hex digits
|
|
// (52 mantissa bits / 4), SE is signed decimal exponent. Subnormals
|
|
// canonicalize as 0x0.{F}p-1022 (matching glibc). Trailing-zero
|
|
// stripping for the fractional part fires when precision is unspecified.
|
|
static void emitHexFloat(double v, char spec, const Spec *s) {
|
|
DblBits d;
|
|
decodeDouble(v, &d);
|
|
int upper = (spec == 'A');
|
|
if (emitInfNan(&d, upper, s)) {
|
|
return;
|
|
}
|
|
// Pull the 13 fractional hex nibbles of the mantissa (high-to-low).
|
|
// The 52-bit mantissa = 13 hex digits. All of n[0..12] are
|
|
// FRACTIONAL nibbles; the integral digit (0 or 1) is implicit
|
|
// (set by the exp == 0 subnormal-vs-zero split below).
|
|
// n[0] is the most significant nibble (m[3] LSBs); n[12] is the
|
|
// least significant nibble (m[0] LSBs).
|
|
unsigned char n[13];
|
|
n[0] = (unsigned char)(d.m[3] & 0x0F);
|
|
n[1] = (unsigned char)((d.m[2] >> 12) & 0x0F);
|
|
n[2] = (unsigned char)((d.m[2] >> 8) & 0x0F);
|
|
n[3] = (unsigned char)((d.m[2] >> 4) & 0x0F);
|
|
n[4] = (unsigned char)( d.m[2] & 0x0F);
|
|
n[5] = (unsigned char)((d.m[1] >> 12) & 0x0F);
|
|
n[6] = (unsigned char)((d.m[1] >> 8) & 0x0F);
|
|
n[7] = (unsigned char)((d.m[1] >> 4) & 0x0F);
|
|
n[8] = (unsigned char)( d.m[1] & 0x0F);
|
|
n[9] = (unsigned char)((d.m[0] >> 12) & 0x0F);
|
|
n[10] = (unsigned char)((d.m[0] >> 8) & 0x0F);
|
|
n[11] = (unsigned char)((d.m[0] >> 4) & 0x0F);
|
|
n[12] = (unsigned char)( d.m[0] & 0x0F);
|
|
// Determine integral hex digit + biased-to-unbiased exponent.
|
|
// C99 canonical: normal -> 1.fp{e-1023}, subnormal -> 0.fp-1022,
|
|
// zero -> 0x0p+0 (glibc prints with prec digits if requested).
|
|
char integral; // '0' or '1'
|
|
int expVal; // exponent of 2 (already accounting for the
|
|
// implicit-1 / subnormal split)
|
|
int zero = (d.exp == 0)
|
|
&& (d.m[0] | d.m[1] | d.m[2] | d.m[3]) == 0;
|
|
if (d.exp == 0) {
|
|
integral = '0';
|
|
expVal = zero ? 0 : -1022; // subnormals all share -1022
|
|
} else {
|
|
integral = '1';
|
|
expVal = (int)d.exp - 1023;
|
|
}
|
|
// Decide how many fractional hex digits to emit. fracLen is the
|
|
// count of nibbles to emit from n[0..fracLen-1]. When prec is
|
|
// unspecified (s->prec < 0): emit exact representation, strip
|
|
// trailing zeros (glibc style). Otherwise: emit `prec` digits
|
|
// (zero-pad or round if needed).
|
|
int fracLen;
|
|
if (s->prec < 0) {
|
|
// Trailing-zero strip: find the largest index < 13 with a
|
|
// non-zero nibble; fracLen = (idx + 1). If all zero,
|
|
// fracLen = 0.
|
|
fracLen = 13;
|
|
while (fracLen > 0 && n[fracLen - 1] == 0) {
|
|
fracLen--;
|
|
}
|
|
} else if (s->prec > 13) {
|
|
fracLen = 13; // We have at most 13 nibbles of real data;
|
|
// pad below with '0' up to s->prec.
|
|
} else {
|
|
fracLen = s->prec;
|
|
// Round-half-even at fracLen. When fracLen < 13, the first
|
|
// discarded nibble is n[fracLen]. Half = 8. Round up if >8;
|
|
// round to even on exactly 8 with no remainder; round down if <8.
|
|
if (fracLen < 13) {
|
|
int round = 0;
|
|
unsigned char first = n[fracLen];
|
|
if (first > 8) {
|
|
round = 1;
|
|
} else if (first == 8) {
|
|
// Any remaining non-zero nibble after first -> round up.
|
|
int sticky = 0;
|
|
for (int i = fracLen + 1; i < 13; i++) {
|
|
if (n[i] != 0) { sticky = 1; break; }
|
|
}
|
|
if (sticky) {
|
|
round = 1;
|
|
} else {
|
|
// Half: round to even (last kept nibble even -> down).
|
|
unsigned char last = (fracLen > 0) ? n[fracLen - 1]
|
|
: (unsigned char)(integral - '0');
|
|
round = (last & 1);
|
|
}
|
|
}
|
|
if (round) {
|
|
int i = fracLen - 1;
|
|
while (i >= 0) {
|
|
n[i] = (unsigned char)((n[i] + 1) & 0x0F);
|
|
if (n[i] != 0) break;
|
|
i--;
|
|
}
|
|
if (i < 0) {
|
|
// Carry propagated into the integral digit. glibc
|
|
// does NOT re-normalize on overflow here: `%.0a` of
|
|
// 1.5 (0x1.8p+0) emits `0x2p+0`, not `0x1p+1`. We
|
|
// match that. Subnormal rounding up to 0x1 keeps
|
|
// the -1022 exponent (subnormal-to-smallest-normal).
|
|
unsigned char ih = (unsigned char)(integral - '0');
|
|
ih = (unsigned char)(ih + 1);
|
|
integral = (char)('0' + ih);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Build the body in a local buffer so we can apply width padding
|
|
// without reusing emitNumber's prefix logic. Body layout:
|
|
// [sign] 0x H . F p SE
|
|
// Worst case: sign(1) + "0x"(2) + integral(1) + "."(1) +
|
|
// 13 hex digits + "p"(1) + sign(1) + 5 decimal = 25.
|
|
// We allow up to 32 to give the prec>13 padding case headroom.
|
|
char body[40];
|
|
int bi = 0;
|
|
if (d.sign) body[bi++] = '-';
|
|
else if (s->signPlus) body[bi++] = '+';
|
|
else if (s->signSpace) body[bi++] = ' ';
|
|
body[bi++] = '0';
|
|
body[bi++] = upper ? 'X' : 'x';
|
|
body[bi++] = integral;
|
|
// The '.' is emitted IFF we will emit at least one fractional digit
|
|
// OR alt-form is set (# forces the radix point).
|
|
int emitDot = (fracLen > 0) || (s->prec > 0) || s->altForm;
|
|
if (emitDot) {
|
|
body[bi++] = '.';
|
|
}
|
|
{
|
|
const char *digits = upper ? "0123456789ABCDEF"
|
|
: "0123456789abcdef";
|
|
int written = 0;
|
|
for (int i = 0; i < fracLen && i < 13; i++) {
|
|
body[bi++] = digits[n[i]];
|
|
written++;
|
|
}
|
|
// Zero-pad up to s->prec when prec exceeds available nibbles.
|
|
if (s->prec > written) {
|
|
int pad = s->prec - written;
|
|
while (pad-- > 0) {
|
|
body[bi++] = '0';
|
|
}
|
|
}
|
|
}
|
|
body[bi++] = upper ? 'P' : 'p';
|
|
// Exponent: ALWAYS prints a sign ('+' or '-') and at least one digit.
|
|
int eAbs = expVal < 0 ? -expVal : expVal;
|
|
char ebuf[8]; // up to 4-5 digits
|
|
int elen = u64ToDec((unsigned long long)eAbs, ebuf);
|
|
body[bi++] = (expVal < 0) ? '-' : '+';
|
|
while (elen-- > 0) {
|
|
body[bi++] = ebuf[elen];
|
|
}
|
|
// Field-width + zero-pad logic (local, NOT via emitNumber).
|
|
int contentLen = bi;
|
|
int fieldPad = s->width > contentLen ? s->width - contentLen : 0;
|
|
if (s->zeroPad && !s->leftAlign) {
|
|
// Zero pad goes BETWEEN the "0x" prefix (incl. any sign) and
|
|
// the integral digit, matching glibc / C99 for %a.
|
|
int prefixEnd = 0;
|
|
if (body[0] == '-' || body[0] == '+' || body[0] == ' ') {
|
|
prefixEnd = 3; // sign + 0x
|
|
} else {
|
|
prefixEnd = 2; // 0x
|
|
}
|
|
// Emit the leading prefix, then the zeros, then the rest.
|
|
for (int i = 0; i < prefixEnd; i++) emit(body[i]);
|
|
emitPad(fieldPad, '0');
|
|
for (int i = prefixEnd; i < bi; i++) emit(body[i]);
|
|
return;
|
|
}
|
|
if (!s->leftAlign) {
|
|
emitPad(fieldPad, ' ');
|
|
}
|
|
for (int i = 0; i < bi; i++) emit(body[i]);
|
|
if (s->leftAlign) {
|
|
emitPad(fieldPad, ' ');
|
|
}
|
|
}
|
|
|
|
|
|
static void emitDouble(double v, int prec, char spec, const Spec *s) {
|
|
// For %g / %G, "precision" is total significant digits. Real glibc
|
|
// would compute exponent and choose between %e and %f styles, but
|
|
// we keep things simple and just emit `X.YYY` with trailing zeros
|
|
// stripped at the end. For %f / %e, prec is decimal places.
|
|
int isG = (spec == 'g' || spec == 'G');
|
|
// Inf/NaN parity with %a (must precede prec clamp and sign strip
|
|
// since those don't make sense on non-finite values). `upper` for
|
|
// %F/%E/%G follows the same caps convention as %A.
|
|
{
|
|
DblBits d;
|
|
decodeDouble(v, &d);
|
|
int upper = (spec == 'F' || spec == 'E' || spec == 'G');
|
|
if (emitInfNan(&d, upper, s)) {
|
|
return;
|
|
}
|
|
}
|
|
if (prec < 0) {
|
|
prec = 6;
|
|
}
|
|
if (prec > 9) {
|
|
prec = 9;
|
|
}
|
|
// Avoid `if (v < 0)` (which calls __ltdf2) — the W65816 codegen
|
|
// for that comparison passes its double arg with a missing word,
|
|
// and the test silently returns false for negatives. Read the
|
|
// IEEE-754 sign bit and clear it inline instead.
|
|
unsigned long long bits;
|
|
__builtin_memcpy(&bits, &v, 8);
|
|
if (bits & ((unsigned long long)1 << 63)) {
|
|
emit('-');
|
|
bits &= ~((unsigned long long)1 << 63);
|
|
__builtin_memcpy(&v, &bits, 8);
|
|
}
|
|
// Split int part first, then scale only the fractional part. The
|
|
// earlier "multiply v by 10^prec then split via integer divide"
|
|
// approach silently overflowed long for v*10^prec > 2^31 (e.g. any
|
|
// value ≥ 2.15 with prec=9 in `%.12g`). We've since reworked the
|
|
// libcall ABI, so the previously-buggy `v - (double)ipart` chain
|
|
// works now — smoke catches a regression of either bug.
|
|
unsigned long intPart = (unsigned long)(long)v;
|
|
double frac = v - (double)intPart;
|
|
unsigned long mul = 1;
|
|
for (int i = 0; i < prec; i++) {
|
|
frac = frac * 10.0;
|
|
mul *= 10;
|
|
}
|
|
// Round-half-up before truncation: 0.314 * 100 = 31.3999... in
|
|
// soft-double, but `%.2f` of 3.14 should print "3.14". Adding 0.5
|
|
// then truncating is round-half-up for the non-negative frac here.
|
|
frac = frac + 0.5;
|
|
unsigned long frcPart = (unsigned long)(long)frac;
|
|
// Carry-up if rounding pushed frac to a full integer (e.g. 0.9995
|
|
// → 0.9995*1000+0.5 = 1000 = mul; the "0.9995" wanted to become
|
|
// "1.000", not "0.1000").
|
|
if (frcPart >= mul) {
|
|
intPart += 1;
|
|
frcPart = 0;
|
|
}
|
|
{
|
|
char ibuf[20];
|
|
int ilen = u64ToDec(intPart, ibuf);
|
|
emitRev(ibuf, ilen);
|
|
}
|
|
if (prec == 0) {
|
|
return;
|
|
}
|
|
// Build fractional digits into a local buffer (reverse order to
|
|
// forward) so we can trim trailing zeros for %g before emitting.
|
|
char buf[10];
|
|
for (int i = prec - 1; i >= 0; i--) {
|
|
buf[i] = (char)('0' + (frcPart % 10));
|
|
frcPart /= 10;
|
|
}
|
|
int emitCount = prec;
|
|
if (isG) {
|
|
// Strip trailing zeros. If the whole fractional part is
|
|
// zeros, skip the '.' too.
|
|
while (emitCount > 0 && buf[emitCount - 1] == '0') {
|
|
emitCount -= 1;
|
|
}
|
|
}
|
|
if (emitCount == 0) {
|
|
return; // No fractional digits to emit → no '.' either.
|
|
}
|
|
emit('.');
|
|
for (int i = 0; i < emitCount; i++) {
|
|
emit(buf[i]);
|
|
}
|
|
}
|
|
#endif // LLVM816_NO_FLOAT_PRINTF
|
|
|
|
|
|
// Length modifiers — encoded as small ints to keep the dispatch flat.
|
|
enum {
|
|
LEN_NONE = 0,
|
|
LEN_HH, // hh: char-promoted-to-int
|
|
LEN_H, // h: short-promoted-to-int
|
|
LEN_L, // l: long
|
|
LEN_LL, // ll: long long
|
|
LEN_J, // j: intmax_t (= long long)
|
|
LEN_Z, // z: size_t (= unsigned long)
|
|
LEN_T // t: ptrdiff_t (= int)
|
|
};
|
|
|
|
|
|
// fmt is arg0 (A register); see banner comment for why the order matters.
|
|
static int format(const char *fmt, va_list ap) {
|
|
while (*fmt) {
|
|
char c = *fmt++;
|
|
if (c != '%') {
|
|
emit(c);
|
|
continue;
|
|
}
|
|
Spec s;
|
|
s.leftAlign = 0;
|
|
s.signPlus = 0;
|
|
s.signSpace = 0;
|
|
s.altForm = 0;
|
|
s.zeroPad = 0;
|
|
s.width = 0;
|
|
s.prec = -1;
|
|
// Flags (any subset, any order).
|
|
for (;;) {
|
|
char f = *fmt;
|
|
if (f == '-') s.leftAlign = 1;
|
|
else if (f == '+') s.signPlus = 1;
|
|
else if (f == ' ') s.signSpace = 1;
|
|
else if (f == '#') s.altForm = 1;
|
|
else if (f == '0') s.zeroPad = 1;
|
|
else break;
|
|
fmt++;
|
|
}
|
|
// Width: decimal or `*`.
|
|
if (*fmt == '*') {
|
|
int w = va_arg(ap, int);
|
|
if (w < 0) { s.leftAlign = 1; w = -w; }
|
|
s.width = w;
|
|
fmt++;
|
|
} else {
|
|
while (*fmt >= '0' && *fmt <= '9') {
|
|
s.width = s.width * 10 + (*fmt - '0');
|
|
fmt++;
|
|
}
|
|
}
|
|
// Precision: `.N` or `.*` (presence enables, default 0 if no digits).
|
|
if (*fmt == '.') {
|
|
fmt++;
|
|
if (*fmt == '*') {
|
|
s.prec = va_arg(ap, int);
|
|
fmt++;
|
|
} else {
|
|
s.prec = 0;
|
|
while (*fmt >= '0' && *fmt <= '9') {
|
|
s.prec = s.prec * 10 + (*fmt - '0');
|
|
fmt++;
|
|
}
|
|
}
|
|
}
|
|
// Length modifier (one of hh / h / l / ll / j / z / t).
|
|
int len = LEN_NONE;
|
|
if (*fmt == 'h') {
|
|
fmt++;
|
|
if (*fmt == 'h') { fmt++; len = LEN_HH; }
|
|
else len = LEN_H;
|
|
} else if (*fmt == 'l') {
|
|
fmt++;
|
|
if (*fmt == 'l') { fmt++; len = LEN_LL; }
|
|
else len = LEN_L;
|
|
} else if (*fmt == 'j') { fmt++; len = LEN_J; }
|
|
else if (*fmt == 'z') { fmt++; len = LEN_Z; }
|
|
else if (*fmt == 't') { fmt++; len = LEN_T; }
|
|
char spec = *fmt++;
|
|
// Signed integers.
|
|
if (spec == 'd' || spec == 'i') {
|
|
long long v;
|
|
switch (len) {
|
|
case LEN_HH: v = (signed char)va_arg(ap, int); break;
|
|
case LEN_H: v = (short)va_arg(ap, int); break;
|
|
case LEN_L: v = (long)va_arg(ap, long); break;
|
|
case LEN_LL:
|
|
case LEN_J: v = va_arg(ap, long long); break;
|
|
case LEN_Z: v = (long)va_arg(ap, unsigned long); break;
|
|
case LEN_T: v = va_arg(ap, int); break;
|
|
default: v = va_arg(ap, int); break;
|
|
}
|
|
int isNeg = v < 0;
|
|
unsigned long long mag = isNeg ? (0ull - (unsigned long long)v)
|
|
: (unsigned long long)v;
|
|
emitNumber(mag, /*isSigned=*/1, isNeg, 10, 0, &s);
|
|
}
|
|
// Unsigned bases: %u %x %X %o.
|
|
else if (spec == 'u' || spec == 'x' || spec == 'X' || spec == 'o') {
|
|
unsigned long long v;
|
|
switch (len) {
|
|
case LEN_HH: v = (unsigned char)va_arg(ap, unsigned int); break;
|
|
case LEN_H: v = (unsigned short)va_arg(ap, unsigned int); break;
|
|
case LEN_L: v = va_arg(ap, unsigned long); break;
|
|
case LEN_LL:
|
|
case LEN_J: v = va_arg(ap, unsigned long long); break;
|
|
case LEN_Z: v = va_arg(ap, unsigned long); break;
|
|
case LEN_T: v = (unsigned int)va_arg(ap, int); break;
|
|
default: v = va_arg(ap, unsigned int); break;
|
|
}
|
|
int base = (spec == 'u') ? 10 : (spec == 'o') ? 8 : 16;
|
|
int upper = (spec == 'X');
|
|
emitNumber(v, /*isSigned=*/0, 0, base, upper, &s);
|
|
}
|
|
else if (spec == 'c') {
|
|
char ch = (char)va_arg(ap, int);
|
|
int fieldPad = s.width > 1 ? s.width - 1 : 0;
|
|
if (!s.leftAlign) emitPad(fieldPad, ' ');
|
|
emit(ch);
|
|
if (s.leftAlign) emitPad(fieldPad, ' ');
|
|
}
|
|
else if (spec == 's') {
|
|
emitStrField(va_arg(ap, const char *), &s);
|
|
}
|
|
#ifndef LLVM816_NO_FLOAT_PRINTF
|
|
else if (spec == 'f' || spec == 'F' ||
|
|
spec == 'g' || spec == 'G' ||
|
|
spec == 'e' || spec == 'E') {
|
|
emitDouble(va_arg(ap, double), s.prec, spec, &s);
|
|
}
|
|
else if (spec == 'a' || spec == 'A') {
|
|
emitHexFloat(va_arg(ap, double), spec, &s);
|
|
}
|
|
#endif
|
|
else if (spec == 'p') {
|
|
// ptr32 — print as "0xBBBBOOOO" (8 hex digits, bank + offset).
|
|
unsigned long pp = (unsigned long)(unsigned long)va_arg(ap, void *);
|
|
emit('0');
|
|
emit('x');
|
|
Spec p = s;
|
|
p.prec = 8;
|
|
p.width = 0;
|
|
emitNumber(pp, 0, 0, 16, 0, &p);
|
|
}
|
|
else if (spec == 'n') {
|
|
// Store the number of chars emitted so far through the
|
|
// pointer arg. Length modifier picks the integer width.
|
|
int count = (int)gTotal;
|
|
switch (len) {
|
|
case LEN_HH: *va_arg(ap, signed char *) = (signed char)count; break;
|
|
case LEN_H: *va_arg(ap, short *) = (short)count; break;
|
|
case LEN_L: *va_arg(ap, long *) = (long)count; break;
|
|
case LEN_LL:
|
|
case LEN_J: *va_arg(ap, long long *) = (long long)count; break;
|
|
case LEN_Z: *va_arg(ap, unsigned long *) = (unsigned long)count; break;
|
|
case LEN_T: *va_arg(ap, int *) = count; break;
|
|
default: *va_arg(ap, int *) = count; break;
|
|
}
|
|
}
|
|
else if (spec == '%') {
|
|
emit('%');
|
|
}
|
|
else {
|
|
// Unknown conversion — echo `%spec` literally.
|
|
emit('%');
|
|
emit(spec);
|
|
}
|
|
}
|
|
if (gCur < gEnd) {
|
|
*gCur = '\0';
|
|
} else if (gEnd > (char *)0) {
|
|
// Truncated, but n > 0: overwrite the last byte with NUL so
|
|
// the result is a valid C string. snprintf with n=0 sets
|
|
// gEnd = NULL up front so this branch correctly skips —
|
|
// previously it wrote `gEnd[-1]` to `buf[-1]`, clobbering
|
|
// memory before the buffer.
|
|
gEnd[-1] = '\0';
|
|
}
|
|
return (int)gTotal;
|
|
}
|
|
|
|
|
|
|
|
int snprintf(char *buf, size_t n, const char *fmt, ...) {
|
|
gCur = buf;
|
|
// n == 0 must NOT touch the buffer (C99 7.19.6.5). Setting
|
|
// gEnd = NULL here makes both `gCur < gEnd` and `gEnd > 0`
|
|
// false, so no NUL terminator gets written.
|
|
gEnd = n ? buf + n : (char *)0;
|
|
gTotal = 0;
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
int r = format(fmt, ap);
|
|
va_end(ap);
|
|
return r;
|
|
}
|
|
|
|
|
|
int sprintf(char *buf, const char *fmt, ...) {
|
|
gCur = buf;
|
|
// sprintf is unbounded; see SPRINTF_END_SENTINEL above for the
|
|
// reason we don't use buf + 0xFFFE.
|
|
gEnd = SPRINTF_END_SENTINEL;
|
|
gTotal = 0;
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
int r = format(fmt, ap);
|
|
va_end(ap);
|
|
return r;
|
|
}
|
|
|
|
|
|
int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap) {
|
|
gCur = buf;
|
|
gEnd = n ? buf + n : (char *)0;
|
|
gTotal = 0;
|
|
return format(fmt, ap);
|
|
}
|
|
|
|
|
|
int vsprintf(char *buf, const char *fmt, va_list ap) {
|
|
gCur = buf;
|
|
gEnd = SPRINTF_END_SENTINEL;
|
|
gTotal = 0;
|
|
return format(fmt, ap);
|
|
}
|