some refactoring of io formatter internals

extracted logic for hex-buffers (%h), string-padding (%s), hex-floats
(%a), and collection printing into helper functions.
This commit is contained in:
Manuel Barrio Linares
2026-02-16 17:27:43 -03:00
committed by Christoffer Lerno
parent 9fbf6bc213
commit 7ae4c5a1ab
2 changed files with 203 additions and 228 deletions

View File

@@ -139,6 +139,47 @@ fn usz? Formatter.out_unknown(&self, String category, any arg) @private
{
return self.out_substr("<") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr(">");
}
fn usz? Formatter.out_collection(&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 = self.out_substr(open)!;
for (usz i = 0; i < alen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(data_ptr, inner))!;
data_ptr += size;
}
len += self.out_substr(close)!;
return len;
}
fn usz? Formatter.out_str(&self, any arg) @private
{
switch (arg.type.kindof)
@@ -158,17 +199,9 @@ fn usz? Formatter.out_str(&self, any arg) @private
unreachable();
case SIGNED_INT:
case UNSIGNED_INT:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
case FLOAT:
case FUNC:
case POINTER:
PrintFlags flags = self.flags;
uint width = self.width;
defer
@@ -178,7 +211,27 @@ fn usz? Formatter.out_str(&self, any arg) @private
}
self.flags = {};
self.width = 0;
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
switch (arg.type.kindof)
{
case SIGNED_INT:
case UNSIGNED_INT:
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
case FLOAT:
return self.ftoa(float_from_any(arg)) ?? self.out_substr("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 self.out_substr("0x")! + self.ntoa_any(arg, 16);
default:
unreachable();
}
case BOOL:
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
default:
@@ -200,16 +253,6 @@ fn usz? Formatter.out_str(&self, any arg) @private
return self.out_unknown("union", arg);
case BITSTRUCT:
return self.out_unknown("bitstruct", arg);
case FUNC:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.width = 0;
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
case CONST_ENUM:
case DISTINCT:
if (arg.type == String.typeid)
@@ -225,102 +268,12 @@ fn usz? Formatter.out_str(&self, any arg) @private
return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)");
}
return self.out_str(arg.as_inner());
case POINTER:
typeid inner = arg.type.inner;
void** pointer = arg.ptr;
if (arg.type.inner != void.typeid)
{
any deref = any_make(*pointer, inner);
n = self.print_with_function((Printable)deref);
if (try n) return n;
if (@catch(n) != NOT_FOUND) n!;
}
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.width = 0;
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
case ARRAY:
// this is SomeType[*] so grab the "SomeType"
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz alen = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
usz len = self.out('[')!;
for (usz i = 0; i < alen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(ptr, inner))!;
ptr += size;
}
len += self.out(']')!;
return len;
return self.out_collection(arg, "[", "]");
case VECTOR:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz vlen = arg.type.len;
// Pretend this is a String
void* ptr = (void*)arg.ptr;
usz len = self.out_substr("[<")!;
for (usz i = 0; i < vlen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(ptr, inner))!;
ptr += size;
}
len += self.out_substr(">]")!;
return len;
return self.out_collection(arg, "[<", ">]");
case SLICE:
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == void.typeid) inner = char.typeid;
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
usz size = inner.sizeof;
// Pretend this is a String
String* temp = (void*)arg.ptr;
void* ptr = (void*)temp.ptr;
usz slen = temp.len;
usz len = self.out('[')!;
for (usz i = 0; i < slen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(ptr, inner))!;
ptr += size;
}
len += self.out(']')!;
return len;
return self.out_collection(arg, "[", "]");
case ANY:
case INTERFACE:
unreachable("Already handled");
@@ -361,6 +314,63 @@ macro usz? @wrap_bad(Formatter* f, #action)
return len;
}
fn usz? Formatter.out_hex_buffer(&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 self.out_substr("<INVALID>");
}
usz len = out.len * 2;
usz total;
if (self.flags.left)
{
total += print_hex_chars(self, out, self.flags.uppercase)!;
total += self.pad(' ', self.width, (isz)total)!;
}
else
{
if (self.width) total += self.pad(' ', self.width, (isz)len)!;
total += print_hex_chars(self, out, self.flags.uppercase)!;
}
return total;
}
fn usz? Formatter.out_str_pad(&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 = self.out_str(arg)!;
self.out_fn = out_fn;
total += self.pad(' ', self.width, (isz)len)!;
}
usz len = self.out_str(arg)!;
total += len;
if (self.flags.left)
{
total += self.pad(' ', self.width, (isz)len)!;
}
return total;
}
fn usz? Formatter.vprintf(&self, String format, any[] anys)
{
self.first_fault = {};
@@ -415,7 +425,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
self.flags.left = true;
w = -w;
}
self.width = w;
self.width = (uint)w;
// evaluate precision field
self.prec = 0;
if (c == '.')
@@ -424,7 +434,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
if (++i >= format_len) return @report_fault(self, "<BAD FORMAT>");
int? prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
if (catch prec) return @report_fault(self, "<BAD FORMAT>");
self.prec = prec < 0 ? 0 : prec;
self.prec = (uint)(prec < 0 ? 0 : prec);
c = format[i];
}
@@ -488,56 +498,10 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
self.flags.uppercase = true;
nextcase;
case 'h':
char[] out @noinit;
switch (current.type)
{
case char[]:
case ichar[]:
out = *(char[]*)current;
default:
if (current.type.kindof == ARRAY && (current.type.inner == char.typeid || current.type.inner == ichar.typeid))
{
out = ((char*)current.ptr)[:current.type.sizeof];
break;
}
if (current.type.kindof == POINTER)
{
out = ((*(char**)current.ptr))[:current.type.inner.sizeof];
break;
}
total_len += self.out_substr("<INVALID>")!;
continue;
}
if (self.flags.left)
{
usz len = print_hex_chars(self, out, self.flags.uppercase)!;
total_len += len;
total_len += self.pad(' ', self.width, len)!;
continue;
}
if (self.width)
{
total_len += self.pad(' ', self.width, out.len * 2)!;
}
total_len += print_hex_chars(self, out, self.flags.uppercase)!;
total_len += self.out_hex_buffer(current)!;
continue;
case 's':
if (self.flags.left)
{
usz len = self.out_str(current)!;
total_len += len;
total_len += self.pad(' ', self.width, len)!;
continue;
}
if (self.width)
{
OutputFn out_fn = self.out_fn;
self.out_fn = (OutputFn)&out_null_fn;
usz len = self.out_str(current)!;
self.out_fn = out_fn;
total_len += self.pad(' ', self.width, len)!;
}
total_len += self.out_str(current)!;
total_len += self.out_str_pad(current)!;
continue;
case 'p':
self.flags.zeropad = true;
@@ -559,10 +523,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
bool is_neg;
total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
if (self.first_fault) return self.first_fault~;
return total_len;
}

View File

@@ -208,6 +208,70 @@ fn usz? Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y);
fn usz? Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y);
fn usz? Formatter.atoa(&self, double y) => self.floatformat(HEX, y);
fn usz? Formatter.floatformat_hex(&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 += self.pad(' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
len += self.out_chars(buf[:outlen])!;
len += self.pad('0', (isz)l - outlen - explen, 0)!;
len += self.out_chars(estr[:explen])!;
if (self.flags.left) len += self.pad(' ', self.width, pl + (isz)l)!;
return len;
}
fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @private
{
// This code is heavily based on musl's printf code
@@ -234,70 +298,15 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
return len;
}
isz p = self.flags.precision ? self.prec : -1;
if (formatting == HEX) return self.floatformat_hex(y, is_neg, pl, p);
// Rescale
int e2;
y = math::frexp(y, &e2) * 2;
if (y) e2--;
char[12] ebuf0;
char* ebuf = 12 + (char*)&ebuf0;
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
char* buf = &buf_array;
isz p = self.flags.precision ? self.prec : -1;
if (formatting == HEX)
{
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;
}
}
// 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++ = '.';
} 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 = p && outlen - 2 < p
? p + 2 + explen
: outlen + explen;
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
len += self.out_chars(buf[:outlen])!;
len += self.pad('0', l - outlen - explen, 0)!;
len += self.out_chars(estr[:explen])!;
if (self.flags.left) len += self.pad(' ', self.width, pl + l)!;
return len;
}
if (p < 0) p = 6;
if (y)
{
@@ -344,7 +353,6 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9);
for (uint* d = a; d < z; d++)
{
// CHECK THIS
uint rm = *d & ((1 << sh) - 1);
*d = (*d >> sh) + carry;
carry = (1000000000 >> sh) * rm;
@@ -456,6 +464,8 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
}
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)
{
@@ -476,6 +486,10 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
char[9] buf_array;
char* buf = &buf_array[0];
if (formatting == FLOAT)
{
if (a > r) a = r;