in doom_py/src/vizdoom/src/zstrformat.cpp [139:889]
int VWorker (OutputFunc output, void *outputData, const char *fmt, va_list arglist)
{
const char *c;
const char *base;
int len = 0;
int width;
int precision;
int flags;
base = c = fmt;
for (;;)
{
while (*c && *c != '%')
{
++c;
}
if (*c == '\0')
{
return len + output (outputData, base, int(c - base));
}
if (c - base > 0)
{
len += output (outputData, base, int(c - base));
}
c++;
// Gather the flags, if any
for (flags = 0;; ++c)
{
if (*c == '-')
{
flags |= F_MINUS; // bit 0
}
else if (*c == '+')
{
flags |= F_PLUS; // bit 1
}
else if (*c == '0')
{
flags |= F_ZERO; // bit 2
}
else if (*c == ' ')
{
flags |= F_BLANK; // bit 3
}
else if (*c == '#')
{
flags |= F_HASH; // bit 4
}
else
{
break;
}
}
width = precision = -1;
// Read the width, if any
if (*c == '*')
{
++c;
width = va_arg (arglist, int);
if (width < 0)
{ // Negative width means minus flag and positive width
flags |= F_MINUS;
width = -width;
}
}
else if (*c >= '0' && *c <= '9')
{
width = *c++ - '0';
while (*c >= '0' && *c <= '9')
{
width = width * 10 + *c++ - '0';
}
}
// If 0 and - both appear, 0 is ignored.
// If the blank and + both appear, the blank is ignored.
flags &= ~((flags & 3) << 2);
// Read the precision, if any
if (*c == '.')
{
precision = 0;
if (*++c == '*')
{
++c;
precision = va_arg (arglist, int);
}
else if (*c >= '0' && *c <= '9')
{
precision = *c++ - '0';
while (*c >= '0' && *c <= '9')
{
precision = precision * 10 + *c++ - '0';
}
}
}
// Read the size prefix, if any
if (*c == 'h')
{
if (*++c == 'h')
{
flags |= F_HALFHALF;
++c;
}
else
{
flags |= F_HALF;
}
}
else if (*c == 'l')
{
if (*++c == 'l')
{
flags |= F_LONGLONG;
++c;
}
else
{
flags |= F_LONG;
}
}
else if (*c == 'I')
{
if (*++c == '6')
{
if (*++c == '4')
{
flags |= F_LONGLONG;
++c;
}
}
else
{
flags |= F_BIGI;
}
}
else if (*c == 't')
{
flags |= F_PTRDIFF;
++c;
}
else if (*c == 'z')
{
flags |= F_SIZE;
++c;
}
base = c+1;
// Now that that's all out of the way, we should be pointing at the type specifier
{
char prefix[3];
int prefixlen;
char hexprefix = '\0';
char sign = '\0';
int postprefixzeros = 0;
int size = flags & 0xF000;
char buffer[80], *ibuff;
const char *obuff = 0;
char type = *c++;
int bufflen = 0;
int outlen = 0;
unsigned int intarg = 0;
uint64_t int64arg = 0;
const void *voidparg;
const char *charparg;
double dblarg;
const char *xits = hexits;
int inlen = len;
/*
* We can decompose the printed representation of floating
* point numbers into several parts, some of which may be empty:
*
* [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
* A B ---C--- D E F
*
* A: 'sign' holds this value if present; '\0' otherwise
* B: hexprefix holds the 'x' or 'X'; '\0' if not hexadecimal
* C: obuff points to the string MMMNNN. Leading and trailing
* zeros are not in the string and must be added.
* D: expchar holds this character; '\0' if no exponent, e.g. %f
* F: at least two digits for decimal, at least one digit for hex
*/
const char *decimal_point = ".";/* locale specific decimal point */
int signflag; /* true if float is negative */
int expt; /* integer value of exponent */
char expchar = 'e'; /* exponent character: [eEpP\0] */
char *dtoaend; /* pointer to end of converted digits */
int expsize = 0; /* character count for expstr */
int ndig = 0; /* actual number of digits returned by dtoa */
char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */
char *dtoaresult = NULL; /* buffer allocated by dtoa */
// Using a bunch of if/else if statements is faster than a switch, because a switch generates
// a jump table. A jump table means a possible data cache miss and a hefty penalty while the
// cache line is loaded.
if (type == 'x' || type == 'X' ||
type == 'p' ||
type == 'd' || type == 'u' || type == 'i' ||
type == 'o' ||
type == 'B')
{
if (type == 'X' || type == 'p')
{
xits = HEXits;
}
if (type == 'p')
{
type = 'X';
voidparg = va_arg (arglist, void *);
if (sizeof(void*) == sizeof(int))
{
intarg = (unsigned int)(size_t)voidparg;
precision = 8;
size = 0;
}
else
{
int64arg = (uint64_t)(size_t)voidparg;
precision = 16;
size = F_LONGLONG;
}
}
else
{
if (size == 0)
{
intarg = va_arg (arglist, int);
}
else if (size == F_HALFHALF)
{
intarg = va_arg (arglist, int);
intarg = (signed char)intarg;
}
else if (size == F_HALF)
{
intarg = va_arg (arglist, int);
intarg = (short)intarg;
}
else if (size == F_LONG)
{
if (sizeof(long) == sizeof(int)) intarg = va_arg (arglist, int);
else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
}
else if (size == F_BIGI)
{
if (sizeof(void*) == sizeof(int)) intarg = va_arg (arglist, int);
else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
}
else if (size == F_LONGLONG)
{
int64arg = va_arg (arglist, int64_t);
}
else if (size == F_PTRDIFF)
{
if (sizeof(ptrdiff_t) == sizeof(int)) intarg = va_arg (arglist, int);
else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
}
else if (size == F_SIZE)
{
if (sizeof(size_t) == sizeof(int)) intarg = va_arg (arglist, int);
else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
}
else
{
intarg = va_arg (arglist, int);
}
}
if (precision < 0) precision = 1;
ibuff = &buffer[sizeof(buffer)];
if (size == F_LONGLONG)
{
if (int64arg == 0)
{
flags |= F_ZEROVALUE;
}
else
{
if (type == 'o')
{ // Octal: Dump digits until it fits in an unsigned int
while (int64arg > UINT_MAX)
{
*--ibuff = char(int64arg & 7) + '0'; int64arg >>= 3;
}
intarg = int(int64arg);
}
else if (type == 'x' || type == 'X')
{ // Hexadecimal: Dump digits until it fits in an unsigned int
while (int64arg > UINT_MAX)
{
*--ibuff = xits[int64arg & 15]; int64arg >>= 4;
}
intarg = int(int64arg);
}
else if (type == 'B')
{ // Binary: Dump digits until it fits in an unsigned int
while (int64arg > UINT_MAX)
{
*--ibuff = char(int64arg & 1) + '0'; int64arg >>= 1;
}
intarg = int(int64arg);
}
else
{
if (type != 'u')
{
// If a signed number is negative, set the negative flag and make it positive.
int64_t sint64arg = (int64_t)int64arg;
if (sint64arg < 0)
{
flags |= F_NEGATIVE;
sint64arg = -sint64arg;
int64arg = sint64arg;
}
flags |= F_SIGNED;
type = 'u';
}
// If an unsigned int64 is too big to fit in an unsigned int, dump out
// digits until it is sufficiently small.
while (int64arg > INT_MAX)
{
*--ibuff = char(int64arg % 10) + '0'; int64arg /= 10;
}
intarg = (unsigned int)(int64arg);
}
}
}
else
{
if (intarg == 0)
{
flags |= F_ZEROVALUE;
}
else if (type == 'i' || type == 'd')
{ // If a signed int is negative, set the negative flag and make it positive.
signed int sintarg = (signed int)intarg;
if (sintarg < 0)
{
flags |= F_NEGATIVE;
sintarg = -sintarg;
intarg = sintarg;
}
flags |= F_SIGNED;
type = 'u';
}
}
if (flags & F_ZEROVALUE)
{
if (precision != 0)
{
*--ibuff = '0';
}
}
else if (type == 'u')
{ // Decimal
int i;
// Unsigned division is typically slower than signed division.
// Do it at most once.
if (intarg > INT_MAX)
{
*--ibuff = char(intarg % 10) + '0'; intarg /= 10;
}
i = (int)intarg;
while (i != 0)
{
*--ibuff = char(i % 10) + '0'; i /= 10;
}
}
else if (type == 'o')
{ // Octal
while (intarg != 0)
{
*--ibuff = char(intarg & 7) + '0'; intarg >>= 3;
}
}
else if (type == 'B')
{ // Binary
while (intarg != 0)
{
*--ibuff = char(intarg & 1) + '0'; intarg >>= 1;
}
}
else
{ // Hexadecimal
while (intarg != 0)
{
*--ibuff = xits[intarg & 15]; intarg >>= 4;
}
}
// Check for prefix (only for non-decimal, which are always unsigned)
if ((flags & (F_HASH|F_ZEROVALUE)) == F_HASH)
{
if (type == 'o')
{
if (bufflen >= precision)
{
sign = '0';
}
}
else if (type == 'x' || type == 'X')
{
hexprefix = type;
}
else if (type == 'B')
{
hexprefix = '!';
}
}
bufflen = (int)(ptrdiff_t)(&buffer[sizeof(buffer)] - ibuff);
obuff = ibuff;
if (precision >= 0)
{
postprefixzeros = precision - bufflen;
if (postprefixzeros < 0) postprefixzeros = 0;
// flags &= ~F_ZERO;
}
}
else if (type == 'c')
{
intarg = va_arg (arglist, int);
buffer[0] = char(intarg);
bufflen = 1;
obuff = buffer;
}
else if (type == 's')
{
charparg = va_arg (arglist, const char *);
if (charparg == NULL)
{
obuff = "(null)";
bufflen = 6;
}
else
{
obuff = charparg;
if (precision < 0)
{
bufflen = (int)strlen (charparg);
}
else
{
for (bufflen = 0; bufflen < precision && charparg[bufflen] != '\0'; ++bufflen)
{ /* empty */ }
}
}
}
else if (type == '%')
{ // Just print a '%': Output it with the next stage.
base--;
continue;
}
else if (type == 'n')
{
if (size == F_HALFHALF)
{
*va_arg (arglist, char *) = (char)inlen;
}
else if (size == F_HALF)
{
*va_arg (arglist, short *) = (short)inlen;
}
else if (size == F_LONG)
{
*va_arg (arglist, long *) = inlen;
}
else if (size == F_LONGLONG)
{
*va_arg (arglist, int64_t *) = inlen;
}
else if (size == F_BIGI)
{
*va_arg (arglist, ptrdiff_t *) = inlen;
}
else
{
*va_arg (arglist, int *) = inlen;
}
}
else if (type == 'f' || type == 'F')
{
expchar = '\0';
goto fp_begin;
}
else if (type == 'g' || type == 'G')
{
expchar = type - ('g' - 'e');
if (precision == 0)
{
precision = 1;
}
goto fp_begin;
}
#if 0
// The hdtoa function provided with FreeBSD uses a hexadecimal FP constant.
// Microsoft's compiler does not support these, so I would need to hack it
// together with ints instead. It's very do-able, but until I actually have
// some reason to print hex FP numbers, I won't bother.
else if (type == 'a' || type == 'A')
{
if (type == 'A')
{
xits = HEXits;
hexprefix = 'X';
expchar = 'P';
}
else
{
hexprefix = 'x';
expchar = 'p';
}
if (precision >= 0)
{
precision++;
}
dblarg = va_arg(arglist, double);
dtoaresult = obuff = hdtoa(dblarg, xits, precision, &expt, &signflag, &dtoaend);
if (precision < 0)
{
precision = (int)(dtoaend - obuff);
}
if (expt == INT_MAX)
{
hexprefix = '\0';
}
goto fp_common;
}
#endif
else if (type == 'e' || type == 'E')
{
expchar = type;
if (precision < 0) // account for digit before decpt
{
precision = DEFPREC + 1;
}
else
{
precision++;
}
fp_begin:
if (precision < 0)
{
precision = DEFPREC;
}
dblarg = va_arg(arglist, double);
obuff = dtoaresult = dtoa(dblarg, expchar ? 2 : 3, precision, &expt, &signflag, &dtoaend);
//fp_common:
decimal_point = localeconv()->decimal_point;
flags |= F_SIGNED;
if (signflag)
{
flags |= F_NEGATIVE;
}
if (expt == INT_MAX) // inf or nan
{
if (*obuff == 'N')
{
obuff = (type >= 'a') ? "nan" : "NAN";
flags &= ~F_SIGNED;
}
else
{
obuff = (type >= 'a') ? "inf" : "INF";
}
bufflen = 3;
flags &= ~F_ZERO;
}
else
{
flags |= F_FPT;
ndig = (int)(dtoaend - obuff);
if (type == 'g' || type == 'G')
{
if (expt > -4 && expt <= precision)
{ // Make %[gG] smell like %[fF].
expchar = '\0';
if (flags & F_HASH)
{
precision -= expt;
}
else
{
precision = ndig - expt;
}
if (precision < 0)
{
precision = 0;
}
}
else
{ // Make %[gG] smell like %[eE], but trim trailing zeroes if no # flag.
if (!(flags & F_HASH))
{
precision = ndig;
}
}
}
if (expchar)
{
expsize = exponent(expstr, expt - 1, expchar);
bufflen = expsize + precision;
if (precision > 1 || (flags & F_HASH))
{
++bufflen;
}
}
else
{ // space for digits before decimal point
if (expt > 0)
{
bufflen = expt;
}
else // "0"
{
bufflen = 1;
}
// space for decimal pt and following digits
if (precision != 0 || (flags & F_HASH))
{
bufflen += precision + 1;
}
}
}
}
// Check for sign prefix (only for signed numbers)
if (flags & F_SIGNED)
{
if (flags & F_NEGATIVE)
{
sign = '-';
}
else if (flags & F_PLUS)
{
sign = '+';
}
else if (flags & F_BLANK)
{
sign = ' ';
}
}
// Construct complete prefix from sign and hex prefix character
prefixlen = 0;
if (sign != '\0')
{
prefix[0] = sign;
prefixlen = 1;
}
if (hexprefix != '\0')
{
prefix[prefixlen] = '0';
prefix[prefixlen + 1] = hexprefix;
prefixlen += 2;
}
// Pad the output to the field width, if needed
int fieldlen = prefixlen + postprefixzeros + bufflen;
const char *pad = (flags & F_ZERO) ? zeroes : spaces;
// If the output is right aligned and zero-padded, then the prefix must come before the padding.
if ((flags & (F_ZERO|F_MINUS)) == F_ZERO && prefixlen > 0)
{
outlen += output (outputData, prefix, prefixlen);
prefixlen = 0;
}
if (!(flags & F_MINUS) && fieldlen < width)
{ // Field is right-justified, so padding comes first
outlen += writepad (output, outputData, pad, sizeof(spaces), width - fieldlen);
width = -1;
}
// Output field: Prefix, post-prefix zeros, buffer text
if (prefixlen > 0)
{
outlen += output (outputData, prefix, prefixlen);
}
outlen += writepad (output, outputData, zeroes, sizeof(spaces), postprefixzeros);
if (!(flags & F_FPT))
{
if (bufflen > 0)
{
outlen += output (outputData, obuff, bufflen);
}
}
else
{
if (expchar == '\0') // %[fF] or sufficiently short %[gG]
{
if (expt <= 0)
{
outlen += output (outputData, zeroes, 1);
if (precision != 0 || (flags & F_HASH))
{
outlen += output (outputData, decimal_point, 1);
}
outlen += writepad (output, outputData, zeroes, sizeof(zeroes), -expt);
// already handled initial 0's
precision += expt;
}
else
{
outlen += printandpad (output, outputData, obuff, dtoaend, expt, zeroes, sizeof(zeroes));
obuff += expt;
if (precision || (flags & F_HASH))
{
outlen += output (outputData, decimal_point, 1);
}
}
outlen += printandpad (output, outputData, obuff, dtoaend, precision, zeroes, sizeof(zeroes));
}
else // %[eE] or sufficiently long %[gG]
{
if (precision > 1 || (flags & F_HASH))
{
buffer[0] = *obuff++;
buffer[1] = *decimal_point;
outlen += output (outputData, buffer, 2);
outlen += output (outputData, obuff, ndig - 1);
outlen += writepad (output, outputData, zeroes, sizeof(zeroes), precision - ndig);
}
else // XeYY
{
outlen += output (outputData, obuff, 1);
}
outlen += output (outputData, expstr, expsize);
}
}
if ((flags & F_MINUS) && fieldlen < width)
{ // Field is left-justified, so padding comes last
outlen += writepad (output, outputData, pad, sizeof(spaces), width - fieldlen);
}
len += outlen;
if (dtoaresult != NULL)
{
freedtoa(dtoaresult);
dtoaresult = NULL;
}
}
}
}