Files
c3c/lib/std/io/formatter_private.c3
2026-02-21 21:10:08 +01:00

978 lines
23 KiB
Plaintext

module std::io;
import std::math;
const char[16] XDIGITS_H = "0123456789ABCDEF";
const char[16] XDIGITS_L = "0123456789abcdef";
faultdef BAD_FORMAT;
fn usz? print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
{
char past_10 = (uppercase ? 'A' : 'a') - 10;
usz len = 0;
foreach (c : out)
{
char digit = c >> 4;
f.print_char(digit + (digit < 10 ? '0' : past_10))!;
len++;
digit = c & 0xf;
f.print_char(digit + (digit < 10 ? '0' : past_10))!;
len++;
}
return len;
}
macro fault Formatter.first_err(&self, fault f)
{
if (self.first_fault) return self.first_fault;
self.first_fault = f;
return f;
}
fn usz? formatter_adjust(Formatter* f, usz len) @local
{
if (!f.flags.left) return 0;
return formatter_pad(f, ' ', f.width, len);
}
fn uint128? int_from_any(any arg, bool *is_neg) @private
{
switch (arg.type.kindof)
{
case FUNC:
case POINTER:
*is_neg = false;
return (uint128)(uptr)*(void**)arg.ptr;
case DISTINCT:
case CONST_ENUM:
return int_from_any(arg.as_inner(), is_neg);
default:
break;
}
*is_neg = false;
switch (arg.type)
{
case bool:
return (uint128)*(bool*)arg;
case ichar:
int val = *(ichar*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case short:
int val = *(short*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int:
int val = *(int*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case long:
long val = *(long*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int128:
int128 val = *(int128*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case char:
return *(char*)arg;
case ushort:
return *(ushort*)arg;
case uint:
return *(uint*)arg;
case ulong:
return *(ulong*)arg;
case uint128:
return *(uint128*)arg;
case float:
float f = *(float*)arg;
return (uint128)((*is_neg = f < 0) ? -f : f);
case double:
double d = *(double*)arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return BAD_FORMAT~;
}
}
fn FloatType? float_from_any(any arg) @private
{
$if env::F128_SUPPORT:
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
$endif
if (arg.type.kindof == DISTINCT || arg.type.kindof == CONST_ENUM)
{
return float_from_any(arg.as_inner());
}
switch (arg.type)
{
case bool:
return (FloatType)*(bool*)arg;
case ichar:
return *(ichar*)arg;
case short:
return *(short*)arg;
case int:
return *(int*)arg;
case long:
return *(long*)arg;
case int128:
return *(int128*)arg;
case char:
return *(char*)arg;
case ushort:
return *(ushort*)arg;
case uint:
return *(uint*)arg;
case ulong:
return *(ulong*)arg;
case uint128:
return *(uint128*)arg;
case float:
return (FloatType)*(float*)arg;
case double:
return (FloatType)*(double*)arg;
default:
return BAD_FORMAT~;
}
}
<*
Read a simple integer value, typically for formatting.
@param [inout] len_ptr : "the length remaining."
@param [in] buf : "the buf to read from."
@param maxlen : "the maximum len that can be read."
@return "The result of the atoi."
*>
fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
{
uint i = 0;
usz len = *len_ptr;
while (len < maxlen)
{
char c = buf[len];
if (!c.is_digit()) break;
i = i * 10 + c - '0';
len++;
}
*len_ptr = len;
return i;
}
fn usz? formatter_out_substr(Formatter* f, String str) @private
{
usz l = conv::utf8_codepoints(str);
uint prec = f.prec;
if (f.flags.precision && l < prec) l = prec;
usz index = 0;
usz chars = str.len;
char* ptr = str.ptr;
while (index < chars)
{
char c = ptr[index];
// Break if we have precision set and we ran out...
if (c & 0xC0 != 0x80 && f.flags.precision && !prec--) break;
f.print_char(c)!;
index++;
}
return index;
}
fn usz? formatter_pad(Formatter* f, char c, isz width, isz len) @inline
{
isz delta = width - len;
for (isz i = 0; i < delta; i++) f.print_char(c)!;
return max(0, delta);
}
fn char* fmt_u(uint128 x, char* s)
{
for (; x > ulong.max; x /= 10) *--s = '0' + (char)(x % 10);
for (ulong y = (ulong)x; y; y /= 10) *--s = '0' + (char)(y % 10);
return s;
}
fn usz? Formatter.out_chars(&self, char[] s)
{
foreach (c : s) self.print_char(c)!;
return s.len;
}
enum FloatFormatting
{
FLOAT,
EXPONENTIAL,
ADAPTIVE,
HEX
}
fn usz? formatter_etoa(Formatter* self, double y) => formatter_floatformat(self, EXPONENTIAL, y);
fn usz? formatter_ftoa(Formatter* self, double y) => formatter_floatformat(self, FLOAT, y);
fn usz? formatter_gtoa(Formatter* self, double y) => formatter_floatformat(self, ADAPTIVE, y);
fn usz? formatter_atoa(Formatter* self, double y) => formatter_floatformat(self, HEX, y);
fn usz? formatter_floatformat_hex(Formatter* self, double y, bool is_neg, isz pl, isz p) @private @inline
{
double round = 8.0;
// 0x / 0X
pl += 2;
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
{
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
while (re--) round *= 16;
if (is_neg)
{
y = -y;
y -= round;
y += round;
y = -y;
}
else
{
y += round;
y -= round;
}
}
int e2;
y = math::frexp(y, &e2) * 2;
if (y) e2--;
char[12] ebuf0;
char* ebuf = &ebuf0[0] + 12;
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
char* buf = &buf_array[0];
// Reverse print
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
if (estr == ebuf) *--estr = '0';
*--estr = (e2 < 0 ? '-' : '+');
*--estr = self.flags.uppercase ? 'P' : 'p';
char* s = buf;
char* xdigits = self.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
do
{
int x = (int)y;
*s++ = xdigits[x];
y = 16 * (y - x);
if (s - buf == 1 && (y || p > 0 || self.flags.hash)) *s++ = '.';
if (p >= 0 && (s - buf) >= (2 + p)) break;
} while (y);
isz outlen = s - buf;
isz explen = ebuf - estr;
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED~;
usz len;
usz l = (usz)(p && outlen - 2 < p
? p + 2 + explen
: outlen + explen);
if (!self.flags.left && !self.flags.zeropad) len += formatter_pad(self, ' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.print_char(is_neg ? '-' : '+')!;
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
if (self.flags.zeropad) len += formatter_pad(self, '0', self.width, pl + l)!;
len += self.out_chars(buf[:outlen])!;
len += formatter_pad(self, '0', (isz)l - outlen - explen, 0)!;
len += self.out_chars(estr[:explen])!;
if (self.flags.left) len += formatter_pad(self, ' ', self.width, pl + (isz)l)!;
return len;
}
fn usz? formatter_floatformat(Formatter* self, FloatFormatting formatting, double y) @private
{
// This code is heavily based on musl's printf code
const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1
+ (math::DOUBLE_MAX_EXP + math::DOUBLE_MANT_DIG + 28 + 8) / 9;
uint[BUF_SIZE] big;
bool is_neg = false;
if (math::signbit(y))
{
is_neg = true;
y = -y;
}
isz pl = is_neg || self.flags.plus ? 1 : 0;
// Print inf/nan
if (!math::is_finite(y))
{
usz len;
// Add padding
if (!self.flags.left) len += formatter_pad(self, ' ', self.width, 3 + pl)!;
String s = self.flags.uppercase ? "INF" : "inf";
if (math::is_nan(y)) s = self.flags.uppercase ? "NAN" : "nan";
if (pl) len += self.print_char(is_neg ? '-' : '+')!;
len += self.out_chars(s)!;
if (self.flags.left) len += formatter_pad(self, ' ', self.width, 3 + pl)!;
return len;
}
isz p = self.flags.precision ? self.prec : -1;
if (formatting == HEX) return formatter_floatformat_hex(self, y, is_neg, pl, p);
// Rescale
int e2;
y = math::frexp(y, &e2) * 2;
if (y) e2--;
if (p < 0) p = 6;
if (y)
{
y *= 0x1p28;
e2 -= 28;
}
uint* a, z, r;
if (e2 < 0)
{
a = r = z = &big;
}
else
{
a = r = z = (uint*)&big + big.len - math::DOUBLE_MANT_DIG - 1;
}
do
{
uint v = z++[0] = (uint)y;
y = 1000000000 * (y - v);
} while (y);
while (e2 > 0)
{
uint carry = 0;
int sh = math::min(29, e2);
for (uint* d = z - 1; d >= a; d--)
{
ulong x = (ulong)*d << sh + carry;
*d = (uint)(x % 1000000000);
carry = (uint)(x / 1000000000);
}
if (carry) *--a = carry;
while (z > a && !z[-1]) z--;
e2 -= sh;
}
while (e2 < 0)
{
uint carry = 0;
uint* b;
int sh = math::min(9, -e2);
int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9);
for (uint* d = a; d < z; d++)
{
uint rm = *d & ((1 << sh) - 1);
*d = (*d >> sh) + carry;
carry = (1000000000 >> sh) * rm;
}
if (!a[0]) a++;
if (carry) z++[0] = carry;
// Avoid (slow!) computation past requested precision
b = formatting == FLOAT ? r : a;
if (z - b > need) z = b + need;
e2 += sh;
}
int e;
if (a < z)
{
for (int i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
}
// Perform rounding: j is precision after the radix (possibly neg)
int j = (int)p;
if (formatting != FLOAT) j -= e;
if (formatting == ADAPTIVE && p != 0) j -= 1;
if (j < 9 * (z - r - 1))
{
uint x;
// We avoid C's broken division of negative numbers
uint* d = r + 1 + ((j + 9 * math::DOUBLE_MAX_EXP) / 9 - math::DOUBLE_MAX_EXP);
j += 9 * math::DOUBLE_MAX_EXP;
j %= 9;
int i;
for (i = 10, j++; j < 9; i *= 10, j++);
x = *d % (uint)i;
// Are there any significant digits past j?
if (x || (d + 1) != z)
{
double round = 2 / math::DOUBLE_EPSILON;
double small;
if (((*d / (uint)i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1)))
{
round += 2;
}
switch
{
case x < i / 2:
small = 0x0.8p0;
case x == i / 2 && d + 1 == z:
small = 0x1.0p0;
default:
small = 0x1.8p0;
}
if (pl && is_neg)
{
round *= -1;
small *= -1;
}
*d -= x;
// Decide whether to round by probing round+small
if (round + small != round)
{
*d = *d + i;
while (*d > 999999999)
{
*d-- = 0;
if (d < a) *--a = 0;
(*d)++;
}
for (i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
}
}
if (z > d + 1) z = d + 1;
}
for (; z>a && !z[-1]; z--);
if (formatting == ADAPTIVE)
{
if (!p) p++;
if (p > e && e >= -4)
{
formatting = FLOAT;
p -= (isz)e + 1;
}
else
{
formatting = EXPONENTIAL;
p--;
}
if (!self.flags.hash)
{
// Count trailing zeros in last place
if (z > a && z[-1])
{
for (int i = 10, j = 0; z[-1] % (uint)i == 0; i *= 10, j++);
}
else
{
j = 9;
}
if (formatting == FLOAT)
{
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) - j));
}
else
{
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) + e - j));
}
}
}
if (p > int.max - 1 - (isz)(p || self.flags.hash)) return INTERNAL_BUFFER_EXCEEDED~;
int l = (int)(1 + p + (isz)(p || self.flags.hash));
char[12] ebuf0;
char* ebuf = &ebuf0[0] + 12;
char* estr @noinit;
if (formatting == FLOAT)
{
if (e > int.max - l) return INTERNAL_BUFFER_EXCEEDED~;
if (e > 0) l += e;
}
else
{
estr = fmt_u((uint128)(e < 0 ? -e : e), ebuf);
while (ebuf - estr < 2) (--estr)[0] = '0';
*--estr = (e < 0 ? '-' : '+');
*--estr = self.flags.uppercase ? 'E' : 'e';
if (ebuf - estr > (isz)int.max - l) return INTERNAL_BUFFER_EXCEEDED~;
l += (int)(ebuf - estr);
}
if (l > int.max - pl) return INTERNAL_BUFFER_EXCEEDED~;
usz len;
if (!self.flags.left && !self.flags.zeropad) len += formatter_pad(self, ' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.print_char(is_neg ? '-' : '+')!;
if (self.flags.zeropad) len += formatter_pad(self, '0', self.width, pl + l)!;
char[9] buf_array;
char* buf = &buf_array[0];
if (formatting == FLOAT)
{
if (a > r) a = r;
uint* d = a;
for (; d <= r; d++)
{
char* s = fmt_u(*d, buf + 9);
switch
{
case d != a:
while (s > buf) (--s)[0] = '0';
case s == buf + 9:
*--s = '0';
}
len += self.out_chars(s[:buf + 9 - s])!;
}
if (p || self.flags.hash) len += self.print_char('.')!;
for (; d < z && p > 0; d++, p -= 9)
{
char* s = fmt_u(*d, buf + 9);
while (s > buf) *--s = '0';
len += self.out_chars(s[:math::min((isz)9, p)])!;
}
len += formatter_pad(self, '0', p + 9, 9)!;
}
else
{
if (z <= a) z = a + 1;
for (uint* d = a; d < z && p >= 0; d++)
{
char* s = fmt_u(*d, buf + 9);
if (s == buf + 9) (--s)[0] = '0';
if (d != a)
{
while (s > buf) (--s)[0] = '0';
}
else
{
len += self.print_char(s++[0])!;
if (p > 0 || self.flags.hash) len += self.print_char('.')!;
}
len += self.out_chars(s[:math::min(buf + 9 - s, p)])!;
p -= buf + 9 - s;
}
len += formatter_pad(self, '0', p + 18, 18)!;
len += self.out_chars(estr[:ebuf - estr])!;
}
if (self.flags.left) len += formatter_pad(self, ' ', self.width, pl + l)!;
return len;
}
fn usz? formatter_out_str_pad(Formatter* self, any arg) @private @inline
{
usz total;
if (self.width && !self.flags.left)
{
OutputFn out_fn = self.out_fn;
self.out_fn = (OutputFn)&out_null_fn;
usz len = formatter_out_str(self, arg)!;
self.out_fn = out_fn;
total += formatter_pad(self, ' ', self.width, (isz)len)!;
}
usz len = formatter_out_str(self, arg)!;
total += len;
if (self.flags.left)
{
total += formatter_pad(self, ' ', self.width, (isz)len)!;
}
return total;
}
const char[201] DIGIT_PAIRS @private =
"00102030405060708090"
"01112131415161718191"
"02122232425262728292"
"03132333435363738393"
"04142434445464748494"
"05152535455565758595"
"06162636465666768696"
"07172737475767778797"
"08182838485868788898"
"09192939495969798999";
fn usz? formatter_ntoa(Formatter* f, uint128 value, bool negative, uint base) @private
{
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
usz len;
// no hash for 0 values
if (!value) f.flags.hash = false;
// write if precision != 0 or value is != 0
if (!f.flags.precision || value)
{
char past_10 = (f.flags.uppercase ? 'A' : 'a') - 10;
switch (base)
{
case 2:
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0' + (char)value & 1;
value >>= 1;
}
while (value);
case 10:
if (!value)
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
break;
}
while (value >= 10)
{
if (len + 1 >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
char digit = (char)(value % 100U);
buf[len:2] = DIGIT_PAIRS[2 * digit:2];
len += 2;
value /= 100U;
}
if (value > 0)
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0' + (char)value;
}
case 16:
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
char digit = (char)value & 0xF;
buf[len++] = digit + (digit < 10 ? '0' : past_10);
value >>= 4;
}
while (value);
case 8:
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0' + (char)value & 0x7;
value >>= 3;
} while (value);
default:
unreachable();
}
}
return formatter_ntoa_format(f, (String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
}
fn usz? formatter_ntoa_format(Formatter* f, String buf, usz len, bool negative, uint base) @private
{
// pad leading zeros
if (!f.flags.left)
{
if (f.width && f.flags.zeropad && (negative || f.flags.plus || f.flags.space)) f.width--;
while (len < f.prec)
{
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
}
while (f.flags.zeropad && len < f.width)
{
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
}
}
// handle hash
if (f.flags.hash && base != 10)
{
if (!f.flags.precision && len && len == f.prec && len == f.width)
{
len--;
if (len) len--;
}
if (base != 10)
{
if (len + 1 >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
switch (base)
{
case 16:
buf[len++] = f.flags.uppercase ? 'X' : 'x';
case 8:
buf[len++] = f.flags.uppercase ? 'O' : 'o';
case 2:
buf[len++] = f.flags.uppercase ? 'B' : 'b';
default:
unreachable();
}
buf[len++] = '0';
}
}
switch (true)
{
case negative:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '-';
case f.flags.plus:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '+';
case f.flags.space:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = ' ';
}
if (len) formatter_out_reverse(f, buf[:len])!;
return len;
}
fn usz? formatter_ntoa_any(Formatter* f, any arg, uint base) @private
{
bool is_neg;
return formatter_ntoa(f, int_from_any(arg, &is_neg)!!, is_neg, base) @inline;
}
fn usz? formatter_out_char(Formatter* f, any arg) @private
{
if (!arg.type.kindof.is_int())
{
return formatter_out_substr(f, "<NOT CHAR>");
}
usz len = 1;
// pre padding
if (!f.flags.left)
{
len += formatter_pad(f, ' ', f.width, len)!;
}
// char output
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
switch (true)
{
case c < 0x7f:
f.print_char((char)c)!;
case c < 0x7ff:
f.print_char((char)(0xC0 | c >> 6))!;
f.print_char((char)(0x80 | (c & 0x3F)))!;
case c < 0xffff:
f.print_char((char)(0xE0 | c >> 12))!;
f.print_char((char)(0x80 | (c >> 6 & 0x3F)))!;
f.print_char((char)(0x80 | (c & 0x3F)))!;
default:
f.print_char((char)(0xF0 | c >> 18))!;
f.print_char((char)(0x80 | (c >> 12 & 0x3F)))!;
f.print_char((char)(0x80 | (c >> 6 & 0x3F)))!;
f.print_char((char)(0x80 | (c & 0x3F)))!;
}
if (f.flags.left)
{
len += formatter_pad(f, ' ', f.width, len)!;
}
return len;
}
fn usz? formatter_out_reverse(Formatter* f, char[] buf) @private
{
usz n;
usz len = buf.len;
// pad spaces up to given width
if (!f.flags.zeropad && !f.flags.left)
{
n += formatter_pad(f, ' ', f.width, len)!;
}
// reverse string
while (len) n += f.print_char(buf[--len])!;
// append pad spaces up to given width
n += formatter_adjust(f, n)!;
return n;
}
fn int? printf_parse_format_field(
any* args_ptr, usz args_len, usz* args_index_ptr,
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
{
char c = format_ptr[*index_ptr];
if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr);
if (c != '*') return 0;
usz len = ++(*index_ptr);
if (len >= format_len) return BAD_FORMAT~;
if (*args_index_ptr >= args_len) return BAD_FORMAT~;
any val = args_ptr[(*args_index_ptr)++];
if (!val.type.kindof.is_int()) return BAD_FORMAT~;
uint? intval = types::any_to_int(val, int);
return intval ?? BAD_FORMAT~;
}
fn usz? formatter_out_hex_buffer(Formatter* self, any arg) @private @inline
{
char[] out @noinit;
switch (arg.type)
{
case char[]:
case ichar[]:
out = *(char[]*)arg;
default:
if (arg.type.kindof == ARRAY && (arg.type.inner == char.typeid || arg.type.inner == ichar.typeid))
{
out = ((char*)arg.ptr)[:arg.type.sizeof];
break;
}
if (arg.type.kindof == POINTER)
{
// Maybe there is a more idiomatic way here
out = ((*(char**)arg.ptr))[:arg.type.inner.sizeof];
break;
}
return formatter_out_substr(self, "<INVALID>");
}
usz len = out.len * 2;
usz total;
if (self.flags.left)
{
total += print_hex_chars(self, out, self.flags.uppercase)!;
total += formatter_pad(self, ' ', self.width, (isz)total)!;
}
else
{
if (self.width) total += formatter_pad(self, ' ', self.width, (isz)len)!;
total += print_hex_chars(self, out, self.flags.uppercase)!;
}
return total;
}
fn usz? formatter_out_unknown(Formatter* self, String category, any arg) @private
{
return formatter_out_substr(self, "<")
+ formatter_out_substr(self, category)
+ formatter_out_substr(self, " type:")
+ formatter_ntoa(self, (iptr)arg.type, false, 16)
+ formatter_out_substr(self, ", addr:")
+ formatter_ntoa(self, (iptr)arg.ptr, false, 16)
+ formatter_out_substr(self, ">");
}
fn usz? formatter_out_collection(Formatter* self, any arg, String open, String close) @private
{
typeid inner = arg.type.inner;
if (inner == void.typeid) inner = char.typeid;
usz size = inner.sizeof;
usz alen;
void* data_ptr;
if (arg.type.kindof == SLICE)
{
String* temp = arg.ptr;
data_ptr = temp.ptr;
alen = temp.len;
}
else
{
data_ptr = arg.ptr;
alen = arg.type.len;
}
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
usz len = formatter_out_substr(self, open)!;
for (usz i = 0; i < alen; i++)
{
if (i != 0) len += formatter_out_substr(self, ", ")!;
len += formatter_out_str(self, any_make(data_ptr, inner))!;
data_ptr += size;
}
len += formatter_out_substr(self, close)!;
return len;
}
fn usz? formatter_out_str(Formatter* self, any arg) @private
{
switch (arg.type.kindof)
{
case VOID:
return formatter_out_substr(self, "void");
case FAULT:
fault f = *(fault*)arg.ptr;
return formatter_out_substr(self, f ? f.nameof : "(empty-fault)");
case INTERFACE:
any a = *(any*)arg;
return a ? formatter_out_str(self, a) : formatter_out_substr(self, "(empty-interface)");
case ANY:
any a = *(any*)arg;
return a ? formatter_out_str(self, a) : formatter_out_substr(self, "(empty-any)");
case OPTIONAL:
unreachable();
case SIGNED_INT:
case UNSIGNED_INT:
case FLOAT:
case FUNC:
case POINTER:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
switch (arg.type.kindof)
{
case SIGNED_INT:
case UNSIGNED_INT:
return formatter_ntoa_any(self, arg, 10) ?? formatter_out_substr(self, "<INVALID>");
case FLOAT:
return formatter_ftoa(self, float_from_any(arg)) ?? formatter_out_substr(self, "ERR");
case FUNC:
case POINTER:
if (arg.type.kindof == POINTER && arg.type.inner != void.typeid)
{
void** pointer = arg.ptr;
any deref = any_make(*pointer, arg.type.inner);
usz? n = self.print_with_function((Printable)deref);
if (try n) return n;
if (@catch(n) != NOT_FOUND) n!;
}
return formatter_out_substr(self, "0x")! + formatter_ntoa_any(self, arg, 16);
default:
unreachable();
}
case BOOL:
return formatter_out_substr(self, *(bool*)arg.ptr ? "true" : "false");
default:
}
usz? n = self.print_with_function((Printable)arg);
if (try n) return n;
if (@catch(n) != NOT_FOUND) n!;
switch (arg.type.kindof)
{
case TYPEID:
return formatter_out_substr(self, "typeid[")! + formatter_ntoa(self, (iptr)*(typeid*)arg, false, 16)! + formatter_out_substr(self, "]")!;
case ENUM:
usz i = types::any_to_enum_ordinal(arg, usz)!!;
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
return formatter_out_substr(self, arg.type.names[i]);
case STRUCT:
return formatter_out_unknown(self, "struct", arg);
case UNION:
return formatter_out_unknown(self, "union", arg);
case BITSTRUCT:
return formatter_out_unknown(self, "bitstruct", arg);
case CONST_ENUM:
case DISTINCT:
if (arg.type == String.typeid)
{
return formatter_out_substr(self, *(String*)arg);
}
if (arg.type == ZString.typeid)
{
return formatter_out_substr(self, *(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
}
if (arg.type == DString.typeid)
{
return formatter_out_substr(self, *(DString*)arg ? ((DString*)arg).str_view() : "(null)");
}
return formatter_out_str(self, arg.as_inner());
case ARRAY:
return formatter_out_collection(self, arg, "[", "]");
case VECTOR:
return formatter_out_collection(self, arg, "[<", ">]");
case SLICE:
return formatter_out_collection(self, arg, "[", "]");
case ANY:
case INTERFACE:
unreachable("Already handled");
default:
}
return formatter_out_substr(self, "Invalid type");
}