diff --git a/lib/std/collections/enummap.c3 b/lib/std/collections/enummap.c3 index 7d411a619..4a8a51360 100644 --- a/lib/std/collections/enummap.c3 +++ b/lib/std/collections/enummap.c3 @@ -14,15 +14,16 @@ fn void EnumMap.init(&self, ValueType init_value) } -fn void! EnumMap.to_format(&self, Formatter* formatter) @dynamic +fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic { - formatter.print("{ ")!; + usz n = formatter.print("{ ")!; foreach (i, &value : self.values) { if (i != 0) formatter.print(", ")!; - formatter.printf("%s: %s", (Enum)i, *value)!; + n += formatter.printf("%s: %s", (Enum)i, *value)!; } - formatter.print(" }")!; + n += formatter.print(" }")!; + return n; } fn String EnumMap.to_string(&self, Allocator* using = mem::heap()) @dynamic diff --git a/lib/std/collections/enumset.c3 b/lib/std/collections/enumset.c3 index 4d37966cd..3175167b4 100644 --- a/lib/std/collections/enumset.c3 +++ b/lib/std/collections/enumset.c3 @@ -125,18 +125,19 @@ fn EnumSet EnumSet.xor_of(&self, EnumSet s) $endif } -fn void! EnumSet.to_format(&set, Formatter* formatter) @dynamic +fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic { - formatter.print("[")!; - bool found = false; + usz n = formatter.print("[")!; + bool found; foreach (value : Enum.values) { if (!set.has(value)) continue; - if (found) formatter.print(", ")!; + if (found) n += formatter.print(", ")!; found = true; - formatter.printf("%s", value)!; + n += formatter.printf("%s", value)!; } - formatter.print("]")!; + n += formatter.print("]")!; + return n; } fn String EnumSet.to_string(&set, Allocator* using = mem::heap()) @dynamic diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index a862f3b4e..1162df7cd 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -41,22 +41,23 @@ fn void List.tinit(&self, usz initial_capacity = 16) self.init(initial_capacity, mem::temp()) @inline; } -fn void! List.to_format(&self, Formatter* formatter) @dynamic +fn usz! List.to_format(&self, Formatter* formatter) @dynamic { switch (self.size) { case 0: - formatter.print("[]")!; + return formatter.print("[]")!; case 1: - formatter.printf("[%s]", self.entries[0])!; + return formatter.printf("[%s]", self.entries[0])!; default: - formatter.print("[")!; + usz n = formatter.print("[")!; foreach (i, element : self.entries[:self.size]) { if (i != 0) formatter.print(", ")!; - formatter.printf("%s", element)!; + n += formatter.printf("%s", element)!; } - formatter.print("]")!; + n += formatter.print("]")!; + return n; } } diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index ad01a534c..30f40c15e 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -27,51 +27,53 @@ struct Object } -fn void! Object.to_format(&self, Formatter* formatter) @dynamic +fn usz! Object.to_format(&self, Formatter* formatter) @dynamic { switch (self.type) { case void: - formatter.printf("{}")!; + return formatter.printf("{}")!; case void*: - formatter.printf("null")!; + return formatter.printf("null")!; case String: - formatter.printf(`"%s"`, self.s)!; + return formatter.printf(`"%s"`, self.s)!; case bool: - formatter.printf(self.b ? "true" : "false")!; + return formatter.printf(self.b ? "true" : "false")!; case ObjectInternalList: - formatter.printf("[")!; + usz n = formatter.printf("[")!; foreach (i, ol : self.array) { - formatter.printf(i == 0 ? " " : ", ")!; - ol.to_format(formatter)!; + n += formatter.printf(i == 0 ? " " : ", ")!; + n += ol.to_format(formatter)!; } - formatter.printf(" ]")!; + n += formatter.printf(" ]")!; + return n; case ObjectInternalMap: - formatter.printf("{")!; + usz n = formatter.printf("{")!; @pool() { foreach (i, key : self.map.key_tlist()) { - formatter.printf(i == 0 ? " " : ", ")!; - formatter.printf(`"%s": `, key)!; - self.map.get(key).to_format(formatter)!; + n += formatter.printf(i == 0 ? " " : ", ")!; + n += formatter.printf(`"%s": `, key)!; + n += self.map.get(key).to_format(formatter)!; } }; - formatter.printf(" }")!; + n += formatter.printf(" }")!; + return n; default: switch (self.type.kindof) { case SIGNED_INT: - formatter.printf("%d", self.i)!; + return formatter.printf("%d", self.i)!; case UNSIGNED_INT: - formatter.printf("%d", (uint128)self.i)!; + return formatter.printf("%d", (uint128)self.i)!; case FLOAT: - formatter.printf("%d", self.f)!; + return formatter.printf("%d", self.f)!; case ENUM: - formatter.printf("%d", self.i)!; + return formatter.printf("%d", self.i)!; default: - formatter.printf("<>")!; + return formatter.printf("<>")!; } } } diff --git a/lib/std/collections/priorityqueue.c3 b/lib/std/collections/priorityqueue.c3 index cc42c7d8e..2360fbfc6 100644 --- a/lib/std/collections/priorityqueue.c3 +++ b/lib/std/collections/priorityqueue.c3 @@ -133,7 +133,7 @@ fn Type PrivatePriorityQueue.peek_at(&self, usz index) @operator([]) return self.heap[index]; } -fn void! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic +fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic { return self.heap.to_format(formatter); } diff --git a/lib/std/collections/range.c3 b/lib/std/collections/range.c3 index 694e4fc61..a756f7138 100644 --- a/lib/std/collections/range.c3 +++ b/lib/std/collections/range.c3 @@ -34,9 +34,9 @@ fn String Range.to_string(&self, Allocator* using = mem::heap()) @dynamic return string::printf("[%s..%s]", self.start, self.end); } -fn void! Range.to_format(&self, Formatter* formatter) @dynamic +fn usz! Range.to_format(&self, Formatter* formatter) @dynamic { - formatter.printf("[%s..%s]", self.start, self.end)!; + return formatter.printf("[%s..%s]", self.start, self.end)!; } struct ExclusiveRange @@ -56,9 +56,9 @@ fn bool ExclusiveRange.contains(&self, Type value) @inline return value >= self.start && value < self.end; } -fn void! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic +fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic { - formatter.printf("[%s..<%s]", self.start, self.end)!; + return formatter.printf("[%s..<%s]", self.start, self.end)!; } fn String ExclusiveRange.to_string(&self, Allocator* using = mem::heap()) @dynamic diff --git a/lib/std/io/bits.c3 b/lib/std/io/bits.c3 new file mode 100644 index 000000000..702c1b1b4 --- /dev/null +++ b/lib/std/io/bits.c3 @@ -0,0 +1,91 @@ +module std::io; + +struct BitReader +{ + Stream reader; + uint bits; + uint len; +} + +/** + * @require byte_reader.supports_read_byte() + **/ +fn void BitReader.init(&self, Stream byte_reader) +{ + *self = { .reader = byte_reader }; +} + +fn void BitReader.clear(&self) @inline +{ + self.len = 0; +} + +/** + * @require nbits <= 8 + * @require self.len + nbits <= uint.sizeof * 8 + **/ +fn char! BitReader.read_bits(&self, uint nbits) +{ + uint bits = self.bits; + if (self.len < nbits) + { + // New bits are pushed right. + char c = self.reader.read_byte()!; + bits <<= 8; + bits |= c; + self.bits = bits; + self.len += 8; + } + self.len -= nbits; + uint mask = (1 << nbits) - 1; + return (char)((bits >> self.len) & mask); +} + +struct BitWriter +{ + Stream writer; + uint bits; + uint len; +} + +/** + * @require byte_writer.supports_write_byte() + **/ +fn void BitWriter.init(&self, Stream byte_writer) +{ + *self = { .writer = byte_writer }; +} + +fn void! BitWriter.flush(&self) +{ + if (self.len == 0) return; + uint bits = self.bits << (32 - self.len); + uint n = (self.len + 7) / 8; + char[4] buffer; + bitorder::write(bits, &buffer, UIntBE); + self.writer.write_all(buffer[:n])!; + self.len = 0; +} + +/** + * @require nbits <= 8 + **/ +fn void! BitWriter.write_bits(&self, uint bits, uint nbits) +{ + if (nbits == 0) return; + uint n = self.len + nbits; + uint to_write = n / 8; + uint left = n % 8; + if (to_write > 0) + { + ulong lbits; + if (self.len > 0) lbits = (ulong)self.bits << (64 - self.len); + lbits |= (ulong)(bits >> left) << (64 - (n - left)); + char[8] buffer; + bitorder::write(lbits, &buffer, ULongBE); + self.writer.write_all(buffer[:to_write])!; + } + self.bits <<= left; + self.bits |= bits & ((1 << left) - 1); + self.len = left; +} \ No newline at end of file diff --git a/lib/std/io/io_formatter_private.c3 b/lib/std/io/io_formatter_private.c3 index 991497aa3..c525e728a 100644 --- a/lib/std/io/io_formatter_private.c3 +++ b/lib/std/io/io_formatter_private.c3 @@ -3,19 +3,12 @@ module std::io; const char[16] XDIGITS_H = "0123456789ABCDEF"; const char[16] XDIGITS_L = "0123456789abcdef"; -fn void! Formatter.left_adjust(&self, usz len) @local +fn usz! Formatter.adjust(&self, usz len) @local { - if (!self.flags.left) return; - for (usz l = len; l < self.width; l++) self.out(' ')!; + if (!self.flags.left) return 0; + return self.pad(' ', self.width, len); } -fn void! Formatter.right_adjust(&self, usz len) @local -{ - if (self.flags.left) return; - for (usz l = len; l < self.width; l++) self.out(' ')!; -} - - fn uint128! int_from_any(any arg, bool *is_neg) @private { *is_neg = false; @@ -136,12 +129,11 @@ fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private return i; } -fn void! Formatter.out_substr(&self, String str) @private +fn usz! Formatter.out_substr(&self, String str) @private { usz l = conv::utf8_codepoints(str); uint prec = self.prec; if (self.flags.precision && l < prec) l = prec; - self.right_adjust(l)!; usz index = 0; usz chars = str.len; char* ptr = str.ptr; @@ -153,12 +145,14 @@ fn void! Formatter.out_substr(&self, String str) @private self.out(c)!; index++; } - return self.left_adjust(l); + return index; } -fn void! Formatter.pad(&self, char c, isz width, isz len) @inline +fn usz! Formatter.pad(&self, char c, isz width, isz len) @inline { - for (isz i = len; i < width; i++) self.out(c)!; + isz delta = width - len; + for (isz i = 0; i < delta; i++) self.out(c)!; + return max(0, delta); } fn char* fmt_u(uint128 x, char* s) @@ -168,9 +162,10 @@ fn char* fmt_u(uint128 x, char* s) return s; } -fn void! Formatter.out_chars(&self, char[] s) +fn usz! Formatter.out_chars(&self, char[] s) { foreach (c : s) self.out(c)!; + return s.len; } enum FloatFormatting @@ -181,12 +176,12 @@ enum FloatFormatting HEX } -fn void! Formatter.etoa(&self, double y) => self.floatformat(EXPONENTIAL, y); -fn void! Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y); -fn void! Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y); -fn void! Formatter.atoa(&self, double y) => self.floatformat(HEX, y); +fn usz! Formatter.etoa(&self, double y) => self.floatformat(EXPONENTIAL, y); +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 void! Formatter.floatformat(&self, FloatFormatting formatting, double y) @private +fn usz! Formatter.floatformat(&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 @@ -202,14 +197,16 @@ fn void! Formatter.floatformat(&self, FloatFormatting formatting, double y) @pri // Print inf/nan if (!math::is_finite(y)) { + usz len; // Add padding - if (!self.flags.left) self.pad(' ', self.width, 3 + pl)!; + if (!self.flags.left) len += self.pad(' ', self.width, 3 + pl)!; String s = self.flags.uppercase ? "INF" : "inf"; - if (y != y) self.flags.uppercase ? "NAN" : "nan"; - if (pl) self.out(is_neg ? '-' : '+')!; - self.out_chars(s)!; - if (self.flags.left) self.pad(' ', self.width, 3 + pl)!; - return; + if (y != y) s = self.flags.uppercase ? "NAN" : "nan"; + len += s.len; + if (pl) len += self.out(is_neg ? '-' : '+')!; + len += self.out_chars(s)!; + if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!; + return len; } // Rescale int e2; @@ -261,18 +258,19 @@ fn void! Formatter.floatformat(&self, FloatFormatting formatting, double y) @pri isz outlen = s - buf; isz explen = ebuf - estr; if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; + usz len; usz l = p && outlen - 2 < p ? p + 2 + explen : outlen + explen; - if (!self.flags.left && !self.flags.zeropad) self.pad(' ', self.width, pl + l)!; - if (is_neg || self.flags.plus) self.out(is_neg ? '-' : '+')!; - self.out_chars(self.flags.uppercase ? "0X" : "0x")!; - if (self.flags.zeropad) self.pad('0', self.width, pl + l)!; - self.out_chars(buf[:outlen])!; - self.pad('0', l - outlen - explen, 0)!; - self.out_chars(estr[:explen])!; - if (self.flags.left) self.pad(' ', self.width, pl + l)!; - return; + 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) @@ -445,9 +443,10 @@ fn void! Formatter.floatformat(&self, FloatFormatting formatting, double y) @pri l += (int)(ebuf - estr); } if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - if (!self.flags.left && !self.flags.zeropad) self.pad(' ', self.width, pl + l)!; - if (is_neg || self.flags.plus) self.out(is_neg ? '-' : '+')!; - if (self.flags.zeropad) self.pad('0', self.width, pl + l)!; + usz len; + 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)!; if (formatting == FLOAT) { if (a > r) a = r; @@ -462,16 +461,16 @@ fn void! Formatter.floatformat(&self, FloatFormatting formatting, double y) @pri case s == buf + 9: *--s = '0'; } - self.out_chars(s[:buf + 9 - s])!; + len += self.out_chars(s[:buf + 9 - s])!; } - if (p || self.flags.hash) self.out('.')!; + if (p || self.flags.hash) len += self.out('.')!; for (; d < z && p > 0; d++, p -= 9) { char* s = fmt_u(*d, buf + 9); while (s > buf) *--s = '0'; - self.out_chars(s[:math::min((isz)9, p)])!; + len += self.out_chars(s[:math::min((isz)9, p)])!; } - self.pad('0', p + 9, 9)!; + len += self.pad('0', p + 9, 9)!; } else { @@ -486,25 +485,25 @@ fn void! Formatter.floatformat(&self, FloatFormatting formatting, double y) @pri } else { - self.out(s++[0])!; - if (p > 0 || self.flags.hash) self.out('.')!; + len += self.out(s++[0])!; + if (p > 0 || self.flags.hash) len += self.out('.')!; } - self.out_chars(s[:math::min(buf + 9 - s, p)])!; + len += self.out_chars(s[:math::min(buf + 9 - s, p)])!; p -= buf + 9 - s; } - self.pad('0', p + 18, 18)!; - self.out_chars(estr[:ebuf - estr])!; + len += self.pad('0', p + 18, 18)!; + len += self.out_chars(estr[:ebuf - estr])!; } - if (self.flags.left) self.pad(' ', self.width, pl + l)!; + if (self.flags.left) len += self.pad(' ', self.width, pl + l)!; - return; + return len; } -fn void! Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private +fn usz! Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private { char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit; - usz len = 0; + usz len; // no hash for 0 values if (!value) self.flags.hash = false; @@ -525,7 +524,7 @@ fn void! Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private return self.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base); } -fn void! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint base) @private +fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint base) @private { // pad leading zeros if (!self.flags.left) @@ -581,23 +580,24 @@ fn void! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint b if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; buf[len++] = ' '; } - if (!len) return; - return self.out_reverse(buf[:len]); + if (len) self.out_reverse(buf[:len])!; + return len; } -fn void! Formatter.ntoa_any(&self, any arg, uint base) @private +fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private { bool is_neg; uint128 val = int_from_any(arg, &is_neg)!!; return self.ntoa(val, is_neg, base) @inline; } -fn void! Formatter.out_char(&self, any arg) @private +fn usz! Formatter.out_char(&self, any arg) @private { + usz len = 1; uint l = 1; // pre padding - self.right_adjust(l)!; + len += self.adjust(l)!; // char output Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD; switch (true) @@ -617,27 +617,27 @@ fn void! Formatter.out_char(&self, any arg) @private self.out((char)(0x80 | (c >> 6 & 0x3F)))!; self.out((char)(0x80 | (c & 0x3F)))!; } - return self.left_adjust(l); + len += self.adjust(l)!; + return len; } -fn void! Formatter.out_reverse(&self, char[] buf) @private +fn usz! Formatter.out_reverse(&self, char[] buf) @private { + usz n; usz buffer_start_idx = self.idx; usz len = buf.len; // pad spaces up to given width - if (!self.flags.left && !self.flags.zeropad) + if (!self.flags.zeropad) { - for (usz i = len; i < self.width; i++) - { - self.out(' ')!; - } + n += self.adjust(len)!; } // reverse string - while (len) self.out(buf[--len])!; + while (len) n += self.out(buf[--len])!; // append pad spaces up to given width - return self.left_adjust(self.idx - buffer_start_idx); + n += self.adjust(self.idx - buffer_start_idx)!; + return n; } fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private diff --git a/lib/std/io/io_printf.c3 b/lib/std/io/io_printf.c3 index 096d99f25..e0ed2cfb8 100644 --- a/lib/std/io/io_printf.c3 +++ b/lib/std/io/io_printf.c3 @@ -25,7 +25,7 @@ def OutputFn = fn void!(char c, void* buffer); def FloatType = double; fn String any.to_string(void* value, Allocator *using) @interface; -fn void! any.to_format(void* value, Formatter* formatter) @interface; +fn usz! any.to_format(void* value, Formatter* formatter) @interface; fn usz! printf(String format, args...) @maydiscard { @@ -103,12 +103,13 @@ fn void Formatter.init(&self, OutputFn out_fn, void* data = null) *self = { .data = data, .out_fn = out_fn}; } -fn void! Formatter.out(&self, char c) @private +fn usz! Formatter.out(&self, char c) @private { self.out_fn(c, self.data)!; + return 1; } -macro bool! Formatter.print_with_function(&self, any arg) +macro usz! Formatter.print_with_function(&self, any arg) { if (&arg.to_format) { @@ -121,8 +122,7 @@ macro bool! Formatter.print_with_function(&self, any arg) self.width = old_width; self.prec = old_prec; } - arg.to_format(self)!; - return true; + return arg.to_format(self); } if (&arg.to_string) { @@ -137,15 +137,14 @@ macro bool! Formatter.print_with_function(&self, any arg) } @pool() { - self.out_substr(arg.to_string(mem::temp()))!; - return true; + return self.out_substr(arg.to_string(mem::temp())); }; } - return false; + return SearchResult.MISSING?; } -fn void! Formatter.out_str(&self, any arg) @private +fn usz! Formatter.out_str(&self, any arg) @private { switch (arg.type.kindof) { @@ -158,74 +157,82 @@ fn void! Formatter.out_str(&self, any arg) @private return self.out_substr((*(anyfault*)arg.ptr).nameof); case ANY: return self.out_str(*(any*)arg); + case OPTIONAL: + unreachable(); + case SIGNED_INT: + case UNSIGNED_INT: + return self.ntoa_any(arg, 10); + case FLOAT: + return self.ftoa(float_from_any(arg)!!); + case BOOL: + return self.out_substr(*(bool*)arg.ptr ? "true" : "false"); + default: + } + usz! n = self.print_with_function(arg); + if (catch err = n) + { + case SearchResult.MISSING: + break; + default: + return err?; + } else { + return n; + } + switch (arg.type.kindof) + { case ENUM: - if (self.print_with_function(arg)!) return; usz i = types::any_to_int(arg, usz)!!; assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i); return self.out_substr(arg.type.names[i]); case STRUCT: - if (self.print_with_function(arg)!) return; return self.out_substr(""); case UNION: - if (self.print_with_function(arg)!) return; return self.out_substr(""); case BITSTRUCT: - if (self.print_with_function(arg)!) return; return self.out_substr(""); case FUNC: - if (self.print_with_function(arg)!) return; return self.out_substr(""); - case OPTIONAL: - unreachable(); case DISTINCT: - if (self.print_with_function(arg)!) return; if (arg.type == DString.typeid) { return self.out_substr(((DString*)arg).as_str()); } return self.out_str(any { arg.ptr, arg.type.inner }); case POINTER: - if (self.print_with_function(arg)!) return; return self.ntoa_any(arg, 16); - case SIGNED_INT: - case UNSIGNED_INT: - return self.ntoa_any(arg, 10); - case FLOAT: - return self.ftoa(float_from_any(arg)!!); case ARRAY: - if (self.print_with_function(arg)!) return; // this is SomeType[*] so grab the "SomeType" typeid inner = arg.type.inner; usz size = inner.sizeof; - usz len = arg.type.len; + usz alen = arg.type.len; // Pretend this is a String void* ptr = (void*)arg.ptr; - self.out('[')!; - for (usz i = 0; i < len; i++) + usz len = self.out('[')!; + for (usz i = 0; i < alen; i++) { - if (i != 0) self.out_substr(", ")!; - self.out_str(any { ptr, inner })!; + if (i != 0) len += self.out_substr(", ")!; + len += self.out_str(any { ptr, inner })!; ptr += size; } - return self.out(']'); + len += self.out(']')!; + return len; case VECTOR: - if (self.print_with_function(arg)!) return; // this is SomeType[*] so grab the "SomeType" typeid inner = arg.type.inner; usz size = inner.sizeof; - usz len = arg.type.len; + usz vlen = arg.type.len; // Pretend this is a String void* ptr = (void*)arg.ptr; - self.out_substr("[<")!; - for (usz i = 0; i < len; i++) + usz len = self.out_substr("[<")!; + for (usz i = 0; i < vlen; i++) { - if (i != 0) self.out_substr(", ")!; - self.out_str(any { ptr, inner })!; + if (i != 0) len += self.out_substr(", ")!; + len += self.out_str(any { ptr, inner })!; ptr += size; } - return self.out_substr(">]"); + len += self.out_substr(">]")!; + return len; case SUBARRAY: - if (self.print_with_function(arg)!) return; // this is SomeType[] so grab the "SomeType" typeid inner = arg.type.inner; if (inner == char.typeid) @@ -237,21 +244,19 @@ fn void! Formatter.out_str(&self, any arg) @private // Pretend this is a String String* temp = (void*)arg.ptr; void* ptr = (void*)temp.ptr; - usz len = temp.len; - self.out('[')!; - for (usz i = 0; i < len; i++) + usz slen = temp.len; + usz len = self.out('[')!; + for (usz i = 0; i < slen; i++) { - if (i != 0) self.out_substr(", ")!; - self.out_str(any { ptr, inner })!; + if (i != 0) len += self.out_substr(", ")!; + len += self.out_str(any { ptr, inner })!; ptr += size; } - self.out(']')!; - case BOOL: - return self.out_substr(*(bool*)arg.ptr ? "true" : "false"); + len += self.out(']')!; + return len; default: - if (self.print_with_function(arg)!) return; - return self.out_substr("Invalid type"); } + return self.out_substr("Invalid type"); } @@ -400,6 +405,25 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys) self.out_char(current)!; continue; case 's': + PrintFlags flags = self.flags; + uint width = self.width; + defer { + self.flags = flags; + self.width = width; + } + self.flags = {}; + self.width = 0; + if (flags.left) + { + usz len = self.out_str(current)!; + self.pad(' ', width, len)!; + continue; + } + OutputFn out_fn = self.out_fn; + self.out_fn = (OutputFn)&out_null_fn; + usz len = self.out_str(current)!; + self.out_fn = out_fn; + self.pad(' ', width, len)!; self.out_str(current)!; continue; case 'p': @@ -437,10 +461,6 @@ fn usz! Formatter.print(&self, String str) // use null output function self.out_fn = &out_null_fn; } - usz len = str.len; - for (usz i = 0; i < len; i++) - { - self.out(str[i])!; - } + foreach (c : str) self.out(c)!; return self.idx; -} +} \ No newline at end of file diff --git a/lib/std/net/inetaddr.c3 b/lib/std/net/inetaddr.c3 index 76e980ead..107bce82c 100644 --- a/lib/std/net/inetaddr.c3 +++ b/lib/std/net/inetaddr.c3 @@ -45,16 +45,15 @@ struct InetAddress } -fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic +fn usz! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic { if (addr.is_ipv6) { - formatter.printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", + return formatter.printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d, addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!; - return; } - formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!; + return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!; } fn String InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap()) @dynamic diff --git a/lib/std/time/time.c3 b/lib/std/time/time.c3 index aa36001fc..95486c5f8 100644 --- a/lib/std/time/time.c3 +++ b/lib/std/time/time.c3 @@ -86,13 +86,12 @@ fn long NanoDuration.to_ms(nd) => (long)nd / 1_000_000; fn Duration NanoDuration.to_duration(nd) => (Duration)nd / 1_000; fn NanoDuration Duration.to_nano(td) => (NanoDuration)td * 1_000; -fn void! NanoDuration.to_format(&self, Formatter* formatter) @dynamic +fn usz! NanoDuration.to_format(&self, Formatter* formatter) @dynamic { NanoDuration nd = *self; if (nd == 0) { - formatter.printf("0s")!; - return; + return formatter.printf("0s")!; } bool neg = nd < 0; @@ -150,5 +149,5 @@ fn void! NanoDuration.to_format(&self, Formatter* formatter) @dynamic str.printf("%ds", sec); } } - formatter.printf(str.as_str())!; + return formatter.printf(str.as_str())!; } \ No newline at end of file diff --git a/test/unit/stdlib/io/printf.c3 b/test/unit/stdlib/io/printf.c3 index 66feb321f..2234a782c 100644 --- a/test/unit/stdlib/io/printf.c3 +++ b/test/unit/stdlib/io/printf.c3 @@ -17,4 +17,33 @@ fn void printf_a() assert(s == "hello world ", "got '%s'; want 'hello world '", s); s = string::printf("%20s", "hello world"); assert(s == " hello world", "got '%s'; want ' hello world'", s); + + String str = "hello!"; + s = string::printf("%-20s", str); + assert(s == "hello! ", "got '%s'; want 'hello! '", s); + s = string::printf("%20s", str); + assert(s == " hello!", "got '%s'; want ' hello!'", s); + + int[2] a = { 12, 23 }; + s = string::printf("%-20s", a); + assert(s == "[12, 23] ", "got '%s'; want '[12, 23] '", s); + s = string::printf("%20s", a); + assert(s == " [12, 23]", "got '%s'; want ' [12, 23]'", s); + + s = string::printf("%-20s", a[..]); + assert(s == "[12, 23] ", "got '%s'; want '[12, 23] '", s); + s = string::printf("%20s", a[..]); + assert(s == " [12, 23]", "got '%s'; want ' [12, 23]'", s); + + float[2] f = { 12.0, 23.0 }; + s = string::printf("%-24s", f); + assert(s == "[12.000000, 23.000000] ", "got '%s'; want '[12.000000, 23.000000] '", s); + s = string::printf("%24s", f); + assert(s == " [12.000000, 23.000000]", "got '%s'; want ' [12.000000, 23.000000]'", s); + + int[<2>] vec = { 12, 23 }; + s = string::printf("%-20s", vec); + assert(s == "[<12, 23>] ", "got '%s'; want '[<12, 23>] '", s); + s = string::printf("%20s", vec); + assert(s == " [<12, 23>]", "got '%s'; want ' [<12, 23>]'", s); } \ No newline at end of file