From 7ae4c5a1abff7ca4f7360ea7ee4a9192e16689da Mon Sep 17 00:00:00 2001 From: Manuel Barrio Linares Date: Mon, 16 Feb 2026 17:27:43 -0300 Subject: [PATCH] some refactoring of io formatter internals extracted logic for hex-buffers (%h), string-padding (%s), hex-floats (%a), and collection printing into helper functions. --- lib/std/io/formatter.c3 | 295 ++++++++++++++------------------ lib/std/io/formatter_private.c3 | 136 ++++++++------- 2 files changed, 203 insertions(+), 228 deletions(-) diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 7571133c1..8909b3a93 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -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(""); 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(""); + 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(""); + } + 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, ""); int? prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i); if (catch prec) return @report_fault(self, ""); - 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("")!; - 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; } diff --git a/lib/std/io/formatter_private.c3 b/lib/std/io/formatter_private.c3 index 82cae987a..9de436709 100644 --- a/lib/std/io/formatter_private.c3 +++ b/lib/std/io/formatter_private.c3 @@ -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;