From ae3fb6df007807a37fe448fdef2b8ee669b29a5a Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 11:56:21 -0600 Subject: [PATCH 01/19] Copy fmtstream --- lib/std/fmtstream.zig | 1754 +++++++++++++++++++++++++++++++++++++++++ lib/std/std.zig | 1 + 2 files changed, 1755 insertions(+) create mode 100644 lib/std/fmtstream.zig diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig new file mode 100644 index 000000000000..a7525ba79545 --- /dev/null +++ b/lib/std/fmtstream.zig @@ -0,0 +1,1754 @@ +const std = @import("std.zig"); +const math = std.math; +const assert = std.debug.assert; +const mem = std.mem; +const builtin = @import("builtin"); +const errol = @import("fmt/errol.zig"); +const lossyCast = std.math.lossyCast; + +pub const default_max_depth = 3; + +pub const Alignment = enum { + Left, + Center, + Right, +}; + +pub const FormatOptions = struct { + precision: ?usize = null, + width: ?usize = null, + alignment: ?Alignment = null, + fill: u8 = ' ', +}; + +fn peekIsAlign(comptime fmt: []const u8) bool { + // Should only be called during a state transition to the format segment. + comptime assert(fmt[0] == ':'); + + inline for (([_]u8{ 1, 2 })[0..]) |i| { + if (fmt.len > i and (fmt[i] == '<' or fmt[i] == '^' or fmt[i] == '>')) { + return true; + } + } + return false; +} + +/// Renders fmt string with args, calling output with slices of bytes. +/// If `output` returns an error, the error is returned from `format` and +/// `output` is not called again. +/// +/// The format string must be comptime known and may contain placeholders following +/// this format: +/// `{[position][specifier]:[fill][alignment][width].[precision]}` +/// +/// Each word between `[` and `]` is a parameter you have to replace with something: +/// +/// - *position* is the index of the argument that should be inserted +/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) +/// - *fill* is a single character which is used to pad the formatted text +/// - *alignment* is one of the three characters `<`, `^` or `>`. they define if the text is *left*, *center*, or *right* aligned +/// - *width* is the total width of the field in characters +/// - *precision* specifies how many decimals a formatted number should have +/// +/// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when +/// all parameters after the separator are omitted. +/// Only exception is the *fill* parameter. If *fill* is required, one has to specify *alignment* as well, as otherwise +/// the digits after `:` is interpreted as *width*, not *fill*. +/// +/// The *specifier* has several options for types: +/// - `x` and `X`: +/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case +/// - output numeric value in hexadecimal notation +/// - `s`: print a pointer-to-many as a c-string, use zero-termination +/// - `B` and `Bi`: output a memory size in either metric (1000) or power-of-two (1024) based notation. works for both float and integer values. +/// - `e`: output floating point value in scientific notation +/// - `d`: output numeric value in decimal notation +/// - `b`: output integer value in binary notation +/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. +/// - `*`: output the address of the value instead of the value itself. +/// +/// If a formatted user type contains a function of the type +/// ``` +/// fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, context: var, comptime Errors: type, comptime output: fn (@TypeOf(context), []const u8) Errors!void) Errors!void +/// ``` +/// with `?` being the type formatted, this function will be called instead of the default implementation. +/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type. +/// +/// A user type may be a `struct`, `vector`, `union` or `enum` type. +pub fn format( + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, + comptime fmt: []const u8, + args: var, +) Errors!void { + const ArgSetType = u32; + if (@typeInfo(@TypeOf(args)) != .Struct) { + @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); + } + if (args.len > ArgSetType.bit_count) { + @compileError("32 arguments max are supported per format call"); + } + + const State = enum { + Start, + Positional, + CloseBrace, + Specifier, + FormatFillAndAlign, + FormatWidth, + FormatPrecision, + }; + + comptime var start_index = 0; + comptime var state = State.Start; + comptime var maybe_pos_arg: ?comptime_int = null; + comptime var specifier_start = 0; + comptime var specifier_end = 0; + comptime var options = FormatOptions{}; + comptime var arg_state: struct { + next_arg: usize = 0, + used_args: ArgSetType = 0, + args_len: usize = args.len, + + fn hasUnusedArgs(comptime self: *@This()) bool { + return (@popCount(ArgSetType, self.used_args) != self.args_len); + } + + fn nextArg(comptime self: *@This(), comptime pos_arg: ?comptime_int) comptime_int { + const next_idx = pos_arg orelse blk: { + const arg = self.next_arg; + self.next_arg += 1; + break :blk arg; + }; + + if (next_idx >= self.args_len) { + @compileError("Too few arguments"); + } + + // Mark this argument as used + self.used_args |= 1 << next_idx; + + return next_idx; + } + } = .{}; + + inline for (fmt) |c, i| { + switch (state) { + .Start => switch (c) { + '{' => { + if (start_index < i) { + try output(context, fmt[start_index..i]); + } + + start_index = i; + specifier_start = i + 1; + specifier_end = i + 1; + maybe_pos_arg = null; + state = .Positional; + options = FormatOptions{}; + }, + '}' => { + if (start_index < i) { + try output(context, fmt[start_index..i]); + } + state = .CloseBrace; + }, + else => {}, + }, + .Positional => switch (c) { + '{' => { + state = .Start; + start_index = i; + }, + ':' => { + state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; + specifier_end = i; + }, + '0'...'9' => { + if (maybe_pos_arg == null) { + maybe_pos_arg = 0; + } + + maybe_pos_arg.? *= 10; + maybe_pos_arg.? += c - '0'; + specifier_start = i + 1; + + if (maybe_pos_arg.? >= args.len) { + @compileError("Positional value refers to non-existent argument"); + } + }, + '}' => { + const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); + + try formatType( + args[arg_to_print], + fmt[0..0], + options, + context, + Errors, + output, + default_max_depth, + ); + + state = .Start; + start_index = i + 1; + }, + else => { + state = .Specifier; + specifier_start = i; + }, + }, + .CloseBrace => switch (c) { + '}' => { + state = .Start; + start_index = i; + }, + else => @compileError("Single '}' encountered in format string"), + }, + .Specifier => switch (c) { + ':' => { + specifier_end = i; + state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; + }, + '}' => { + const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); + + try formatType( + args[arg_to_print], + fmt[specifier_start..i], + options, + context, + Errors, + output, + default_max_depth, + ); + state = .Start; + start_index = i + 1; + }, + else => {}, + }, + // Only entered if the format string contains a fill/align segment. + .FormatFillAndAlign => switch (c) { + '<' => { + options.alignment = Alignment.Left; + state = .FormatWidth; + }, + '^' => { + options.alignment = Alignment.Center; + state = .FormatWidth; + }, + '>' => { + options.alignment = Alignment.Right; + state = .FormatWidth; + }, + else => { + options.fill = c; + }, + }, + .FormatWidth => switch (c) { + '0'...'9' => { + if (options.width == null) { + options.width = 0; + } + + options.width.? *= 10; + options.width.? += c - '0'; + }, + '.' => { + state = .FormatPrecision; + }, + '}' => { + const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); + + try formatType( + args[arg_to_print], + fmt[specifier_start..specifier_end], + options, + context, + Errors, + output, + default_max_depth, + ); + state = .Start; + start_index = i + 1; + }, + else => { + @compileError("Unexpected character in width value: " ++ [_]u8{c}); + }, + }, + .FormatPrecision => switch (c) { + '0'...'9' => { + if (options.precision == null) { + options.precision = 0; + } + + options.precision.? *= 10; + options.precision.? += c - '0'; + }, + '}' => { + const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); + + try formatType( + args[arg_to_print], + fmt[specifier_start..specifier_end], + options, + context, + Errors, + output, + default_max_depth, + ); + state = .Start; + start_index = i + 1; + }, + else => { + @compileError("Unexpected character in precision value: " ++ [_]u8{c}); + }, + }, + } + } + comptime { + if (comptime arg_state.hasUnusedArgs()) { + @compileError("Unused arguments"); + } + if (state != State.Start) { + @compileError("Incomplete format string: " ++ fmt); + } + } + if (start_index < fmt.len) { + try output(context, fmt[start_index..]); + } +} + +pub fn formatType( + value: var, + comptime fmt: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, + max_depth: usize, +) Errors!void { + if (comptime std.mem.eql(u8, fmt, "*")) { + try output(context, @typeName(@TypeOf(value).Child)); + try output(context, "@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, context, Errors, output); + return; + } + + const T = @TypeOf(value); + switch (@typeInfo(T)) { + .ComptimeInt, .Int, .Float => { + return formatValue(value, fmt, options, context, Errors, output); + }, + .Void => { + return output(context, "void"); + }, + .Bool => { + return output(context, if (value) "true" else "false"); + }, + .Optional => { + if (value) |payload| { + return formatType(payload, fmt, options, context, Errors, output, max_depth); + } else { + return output(context, "null"); + } + }, + .ErrorUnion => { + if (value) |payload| { + return formatType(payload, fmt, options, context, Errors, output, max_depth); + } else |err| { + return formatType(err, fmt, options, context, Errors, output, max_depth); + } + }, + .ErrorSet => { + try output(context, "error."); + return output(context, @errorName(value)); + }, + .Enum => |enumInfo| { + if (comptime std.meta.trait.hasFn("format")(T)) { + return value.format(fmt, options, context, Errors, output); + } + + try output(context, @typeName(T)); + if (enumInfo.is_exhaustive) { + try output(context, "."); + try output(context, @tagName(value)); + } else { + // TODO: when @tagName works on exhaustive enums print known enum strings + try output(context, "("); + try formatType(@enumToInt(value), fmt, options, context, Errors, output, max_depth); + try output(context, ")"); + } + }, + .Union => { + if (comptime std.meta.trait.hasFn("format")(T)) { + return value.format(fmt, options, context, Errors, output); + } + + try output(context, @typeName(T)); + if (max_depth == 0) { + return output(context, "{ ... }"); + } + const info = @typeInfo(T).Union; + if (info.tag_type) |UnionTagType| { + try output(context, "{ ."); + try output(context, @tagName(@as(UnionTagType, value))); + try output(context, " = "); + inline for (info.fields) |u_field| { + if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) { + try formatType(@field(value, u_field.name), fmt, options, context, Errors, output, max_depth - 1); + } + } + try output(context, " }"); + } else { + try format(context, Errors, output, "@{x}", .{@ptrToInt(&value)}); + } + }, + .Struct => |StructT| { + if (comptime std.meta.trait.hasFn("format")(T)) { + return value.format(fmt, options, context, Errors, output); + } + + try output(context, @typeName(T)); + if (max_depth == 0) { + return output(context, "{ ... }"); + } + try output(context, "{"); + inline for (StructT.fields) |f, i| { + if (i == 0) { + try output(context, " ."); + } else { + try output(context, ", ."); + } + try output(context, f.name); + try output(context, " = "); + try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1); + } + try output(context, " }"); + }, + .Pointer => |ptr_info| switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { + .Array => |info| { + if (info.child == u8) { + return formatText(value, fmt, options, context, Errors, output); + } + return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); + }, + .Enum, .Union, .Struct => { + return formatType(value.*, fmt, options, context, Errors, output, max_depth); + }, + else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), + }, + .Many, .C => { + if (ptr_info.sentinel) |sentinel| { + return formatType(mem.span(value), fmt, options, context, Errors, output, max_depth); + } + if (ptr_info.child == u8) { + if (fmt.len > 0 and fmt[0] == 's') { + return formatText(mem.span(value), fmt, options, context, Errors, output); + } + } + return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); + }, + .Slice => { + if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { + return formatText(value, fmt, options, context, Errors, output); + } + if (ptr_info.child == u8) { + return formatText(value, fmt, options, context, Errors, output); + } + return format(context, Errors, output, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); + }, + }, + .Array => |info| { + const Slice = @Type(builtin.TypeInfo{ + .Pointer = .{ + .size = .Slice, + .is_const = true, + .is_volatile = false, + .is_allowzero = false, + .alignment = @alignOf(info.child), + .child = info.child, + .sentinel = null, + }, + }); + return formatType(@as(Slice, &value), fmt, options, context, Errors, output, max_depth); + }, + .Vector => { + const len = @typeInfo(T).Vector.len; + try output(context, "{ "); + var i: usize = 0; + while (i < len) : (i += 1) { + try formatValue(value[i], fmt, options, context, Errors, output); + if (i < len - 1) { + try output(context, ", "); + } + } + try output(context, " }"); + }, + .Fn => { + return format(context, Errors, output, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); + }, + .Type => return output(context, @typeName(T)), + .EnumLiteral => { + const buffer = [_]u8{'.'} ++ @tagName(value); + return formatType(buffer, fmt, options, context, Errors, output, max_depth); + }, + else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), + } +} + +fn formatValue( + value: var, + comptime fmt: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + if (comptime std.mem.eql(u8, fmt, "B")) { + return formatBytes(value, options, 1000, context, Errors, output); + } else if (comptime std.mem.eql(u8, fmt, "Bi")) { + return formatBytes(value, options, 1024, context, Errors, output); + } + + const T = @TypeOf(value); + switch (@typeInfo(T)) { + .Float => return formatFloatValue(value, fmt, options, context, Errors, output), + .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output), + .Bool => return output(context, if (value) "true" else "false"), + else => comptime unreachable, + } +} + +pub fn formatIntValue( + value: var, + comptime fmt: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + comptime var radix = 10; + comptime var uppercase = false; + + const int_value = if (@TypeOf(value) == comptime_int) blk: { + const Int = math.IntFittingRange(value, value); + break :blk @as(Int, value); + } else + value; + + if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) { + radix = 10; + uppercase = false; + } else if (comptime std.mem.eql(u8, fmt, "c")) { + if (@TypeOf(int_value).bit_count <= 8) { + return formatAsciiChar(@as(u8, int_value), options, context, Errors, output); + } else { + @compileError("Cannot print integer that is larger than 8 bits as a ascii"); + } + } else if (comptime std.mem.eql(u8, fmt, "b")) { + radix = 2; + uppercase = false; + } else if (comptime std.mem.eql(u8, fmt, "x")) { + radix = 16; + uppercase = false; + } else if (comptime std.mem.eql(u8, fmt, "X")) { + radix = 16; + uppercase = true; + } else { + @compileError("Unknown format string: '" ++ fmt ++ "'"); + } + + return formatInt(int_value, radix, uppercase, options, context, Errors, output); +} + +fn formatFloatValue( + value: var, + comptime fmt: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { + return formatFloatScientific(value, options, context, Errors, output); + } else if (comptime std.mem.eql(u8, fmt, "d")) { + return formatFloatDecimal(value, options, context, Errors, output); + } else { + @compileError("Unknown format string: '" ++ fmt ++ "'"); + } +} + +pub fn formatText( + bytes: []const u8, + comptime fmt: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + if (fmt.len == 0) { + return output(context, bytes); + } else if (comptime std.mem.eql(u8, fmt, "s")) { + return formatBuf(bytes, options, context, Errors, output); + } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { + for (bytes) |c| { + try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, context, Errors, output); + } + return; + } else { + @compileError("Unknown format string: '" ++ fmt ++ "'"); + } +} + +pub fn formatAsciiChar( + c: u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + return output(context, @as(*const [1]u8, &c)[0..]); +} + +pub fn formatBuf( + buf: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + try output(context, buf); + + const width = options.width orelse 0; + var leftover_padding = if (width > buf.len) (width - buf.len) else return; + const pad_byte: u8 = options.fill; + while (leftover_padding > 0) : (leftover_padding -= 1) { + try output(context, @as(*const [1]u8, &pad_byte)[0..1]); + } +} + +// Print a float in scientific notation to the specified precision. Null uses full precision. +// It should be the case that every full precision, printed value can be re-parsed back to the +// same type unambiguously. +pub fn formatFloatScientific( + value: var, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + var x = @floatCast(f64, value); + + // Errol doesn't handle these special cases. + if (math.signbit(x)) { + try output(context, "-"); + x = -x; + } + + if (math.isNan(x)) { + return output(context, "nan"); + } + if (math.isPositiveInf(x)) { + return output(context, "inf"); + } + if (x == 0.0) { + try output(context, "0"); + + if (options.precision) |precision| { + if (precision != 0) { + try output(context, "."); + var i: usize = 0; + while (i < precision) : (i += 1) { + try output(context, "0"); + } + } + } else { + try output(context, ".0"); + } + + try output(context, "e+00"); + return; + } + + var buffer: [32]u8 = undefined; + var float_decimal = errol.errol3(x, buffer[0..]); + + if (options.precision) |precision| { + errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); + + try output(context, float_decimal.digits[0..1]); + + // {e0} case prints no `.` + if (precision != 0) { + try output(context, "."); + + var printed: usize = 0; + if (float_decimal.digits.len > 1) { + const num_digits = math.min(float_decimal.digits.len, precision + 1); + try output(context, float_decimal.digits[1..num_digits]); + printed += num_digits - 1; + } + + while (printed < precision) : (printed += 1) { + try output(context, "0"); + } + } + } else { + try output(context, float_decimal.digits[0..1]); + try output(context, "."); + if (float_decimal.digits.len > 1) { + const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len; + + try output(context, float_decimal.digits[1..num_digits]); + } else { + try output(context, "0"); + } + } + + try output(context, "e"); + const exp = float_decimal.exp - 1; + + if (exp >= 0) { + try output(context, "+"); + if (exp > -10 and exp < 10) { + try output(context, "0"); + } + try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); + } else { + try output(context, "-"); + if (exp > -10 and exp < 10) { + try output(context, "0"); + } + try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); + } +} + +// Print a float of the format x.yyyyy where the number of y is specified by the precision argument. +// By default floats are printed at full precision (no rounding). +pub fn formatFloatDecimal( + value: var, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + var x = @as(f64, value); + + // Errol doesn't handle these special cases. + if (math.signbit(x)) { + try output(context, "-"); + x = -x; + } + + if (math.isNan(x)) { + return output(context, "nan"); + } + if (math.isPositiveInf(x)) { + return output(context, "inf"); + } + if (x == 0.0) { + try output(context, "0"); + + if (options.precision) |precision| { + if (precision != 0) { + try output(context, "."); + var i: usize = 0; + while (i < precision) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context, ".0"); + } + } + + return; + } + + // non-special case, use errol3 + var buffer: [32]u8 = undefined; + var float_decimal = errol.errol3(x, buffer[0..]); + + if (options.precision) |precision| { + errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); + + // exp < 0 means the leading is always 0 as errol result is normalized. + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; + + // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. + var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); + + if (num_digits_whole > 0) { + // We may have to zero pad, for instance 1e4 requires zero padding. + try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + + var i = num_digits_whole_no_pad; + while (i < num_digits_whole) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context, "0"); + } + + // {.0} special case doesn't want a trailing '.' + if (precision == 0) { + return; + } + + try output(context, "."); + + // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. + var printed: usize = 0; + + // Zero-fill until we reach significant digits or run out of precision. + if (float_decimal.exp <= 0) { + const zero_digit_count = @intCast(usize, -float_decimal.exp); + const zeros_to_print = math.min(zero_digit_count, precision); + + var i: usize = 0; + while (i < zeros_to_print) : (i += 1) { + try output(context, "0"); + printed += 1; + } + + if (printed >= precision) { + return; + } + } + + // Remaining fractional portion, zero-padding if insufficient. + assert(precision >= printed); + if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { + try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); + return; + } else { + try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + printed += float_decimal.digits.len - num_digits_whole_no_pad; + + while (printed < precision) : (printed += 1) { + try output(context, "0"); + } + } + } else { + // exp < 0 means the leading is always 0 as errol result is normalized. + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; + + // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. + var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); + + if (num_digits_whole > 0) { + // We may have to zero pad, for instance 1e4 requires zero padding. + try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + + var i = num_digits_whole_no_pad; + while (i < num_digits_whole) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context, "0"); + } + + // Omit `.` if no fractional portion + if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { + return; + } + + try output(context, "."); + + // Zero-fill until we reach significant digits or run out of precision. + if (float_decimal.exp < 0) { + const zero_digit_count = @intCast(usize, -float_decimal.exp); + + var i: usize = 0; + while (i < zero_digit_count) : (i += 1) { + try output(context, "0"); + } + } + + try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + } +} + +pub fn formatBytes( + value: var, + options: FormatOptions, + comptime radix: usize, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + if (value == 0) { + return output(context, "0B"); + } + + const mags_si = " kMGTPEZY"; + const mags_iec = " KMGTPEZY"; + const magnitude = switch (radix) { + 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1), + 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), + else => unreachable, + }; + const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); + const suffix = switch (radix) { + 1000 => mags_si[magnitude], + 1024 => mags_iec[magnitude], + else => unreachable, + }; + + try formatFloatDecimal(new_value, options, context, Errors, output); + + if (suffix == ' ') { + return output(context, "B"); + } + + const buf = switch (radix) { + 1000 => &[_]u8{ suffix, 'B' }, + 1024 => &[_]u8{ suffix, 'i', 'B' }, + else => unreachable, + }; + return output(context, buf); +} + +pub fn formatInt( + value: var, + base: u8, + uppercase: bool, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + const int_value = if (@TypeOf(value) == comptime_int) blk: { + const Int = math.IntFittingRange(value, value); + break :blk @as(Int, value); + } else + value; + + if (@TypeOf(int_value).is_signed) { + return formatIntSigned(int_value, base, uppercase, options, context, Errors, output); + } else { + return formatIntUnsigned(int_value, base, uppercase, options, context, Errors, output); + } +} + +fn formatIntSigned( + value: var, + base: u8, + uppercase: bool, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + const new_options = FormatOptions{ + .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null, + .precision = options.precision, + .fill = options.fill, + }; + const bit_count = @typeInfo(@TypeOf(value)).Int.bits; + const Uint = std.meta.IntType(false, bit_count); + if (value < 0) { + try output(context, "-"); + const new_value = math.absCast(value); + return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); + } else if (options.width == null or options.width.? == 0) { + return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, context, Errors, output); + } else { + try output(context, "+"); + const new_value = @intCast(Uint, value); + return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); + } +} + +fn formatIntUnsigned( + value: var, + base: u8, + uppercase: bool, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, +) Errors!void { + assert(base >= 2); + var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined; + const min_int_bits = comptime math.max(@TypeOf(value).bit_count, @TypeOf(base).bit_count); + const MinInt = std.meta.IntType(@TypeOf(value).is_signed, min_int_bits); + var a: MinInt = value; + var index: usize = buf.len; + + while (true) { + const digit = a % base; + index -= 1; + buf[index] = digitToChar(@intCast(u8, digit), uppercase); + a /= base; + if (a == 0) break; + } + + const digits_buf = buf[index..]; + const width = options.width orelse 0; + const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; + + if (padding > index) { + const zero_byte: u8 = options.fill; + var leftover_padding = padding - index; + while (true) { + try output(context, @as(*const [1]u8, &zero_byte)[0..]); + leftover_padding -= 1; + if (leftover_padding == 0) break; + } + mem.set(u8, buf[0..index], options.fill); + return output(context, &buf); + } else { + const padded_buf = buf[index - padding ..]; + mem.set(u8, padded_buf[0..padding], options.fill); + return output(context, padded_buf); + } +} + +pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { + var context = FormatIntBuf{ + .out_buf = out_buf, + .index = 0, + }; + formatInt(value, base, uppercase, options, &context, error{}, formatIntCallback) catch unreachable; + return context.index; +} +const FormatIntBuf = struct { + out_buf: []u8, + index: usize, +}; +fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { + mem.copy(u8, context.out_buf[context.index..], bytes); + context.index += bytes.len; +} + +pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { + if (!T.is_signed) return parseUnsigned(T, buf, radix); + if (buf.len == 0) return @as(T, 0); + if (buf[0] == '-') { + return math.negate(try parseUnsigned(T, buf[1..], radix)); + } else if (buf[0] == '+') { + return parseUnsigned(T, buf[1..], radix); + } else { + return parseUnsigned(T, buf, radix); + } +} + +test "parseInt" { + std.testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); + std.testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); + std.testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); + std.testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); + std.testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); + std.testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); + std.testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); +} + +pub const ParseUnsignedError = error{ + /// The result cannot fit in the type specified + Overflow, + + /// The input had a byte that was not a digit + InvalidCharacter, +}; + +pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { + var x: T = 0; + + for (buf) |c| { + const digit = try charToDigit(c, radix); + + if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); + x = try math.add(T, x, try math.cast(T, digit)); + } + + return x; +} + +test "parseUnsigned" { + std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); + std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); + std.testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); + + std.testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); + std.testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); + + std.testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); + + std.testing.expect((try parseUnsigned(u7, "1", 10)) == 1); + std.testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); + + std.testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); + std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); + + std.testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); + + // these numbers should fit even though the radix itself doesn't fit in the destination type + std.testing.expect((try parseUnsigned(u1, "0", 10)) == 0); + std.testing.expect((try parseUnsigned(u1, "1", 10)) == 1); + std.testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); + std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1); + std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3); + std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); +} + +pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; + +test "parseFloat" { + _ = @import("fmt/parse_float.zig"); +} + +pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { + const value = switch (c) { + '0'...'9' => c - '0', + 'A'...'Z' => c - 'A' + 10, + 'a'...'z' => c - 'a' + 10, + else => return error.InvalidCharacter, + }; + + if (value >= radix) return error.InvalidCharacter; + + return value; +} + +fn digitToChar(digit: u8, uppercase: bool) u8 { + return switch (digit) { + 0...9 => digit + '0', + 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10), + else => unreachable, + }; +} + +const BufPrintContext = struct { + remaining: []u8, +}; + +fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { + if (context.remaining.len < bytes.len) { + mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); + return error.BufferTooSmall; + } + mem.copy(u8, context.remaining, bytes); + context.remaining = context.remaining[bytes.len..]; +} + +pub const BufPrintError = error{ + /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. + BufferTooSmall, +}; +pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { + var context = BufPrintContext{ .remaining = buf }; + try format(&context, BufPrintError, bufPrintWrite, fmt, args); + return buf[0 .. buf.len - context.remaining.len]; +} + +pub const AllocPrintError = error{OutOfMemory}; + +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { + var size: usize = 0; + format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; + const buf = try allocator.alloc(u8, size); + return bufPrint(buf, fmt, args) catch |err| switch (err) { + error.BufferTooSmall => unreachable, // we just counted the size above + }; +} + +fn countSize(size: *usize, bytes: []const u8) (error{}!void) { + size.* += bytes.len; +} + +pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { + const result = try allocPrint(allocator, fmt ++ "\x00", args); + return result[0 .. result.len - 1 :0]; +} + +test "bufPrintInt" { + var buffer: [100]u8 = undefined; + const buf = buffer[0..]; + + std.testing.expectEqualSlices(u8, "-1", bufPrintIntToSlice(buf, @as(i1, -1), 10, false, FormatOptions{})); + + std.testing.expectEqualSlices(u8, "-101111000110000101001110", bufPrintIntToSlice(buf, @as(i32, -12345678), 2, false, FormatOptions{})); + std.testing.expectEqualSlices(u8, "-12345678", bufPrintIntToSlice(buf, @as(i32, -12345678), 10, false, FormatOptions{})); + std.testing.expectEqualSlices(u8, "-bc614e", bufPrintIntToSlice(buf, @as(i32, -12345678), 16, false, FormatOptions{})); + std.testing.expectEqualSlices(u8, "-BC614E", bufPrintIntToSlice(buf, @as(i32, -12345678), 16, true, FormatOptions{})); + + std.testing.expectEqualSlices(u8, "12345678", bufPrintIntToSlice(buf, @as(u32, 12345678), 10, true, FormatOptions{})); + + std.testing.expectEqualSlices(u8, " 666", bufPrintIntToSlice(buf, @as(u32, 666), 10, false, FormatOptions{ .width = 6 })); + std.testing.expectEqualSlices(u8, " 1234", bufPrintIntToSlice(buf, @as(u32, 0x1234), 16, false, FormatOptions{ .width = 6 })); + std.testing.expectEqualSlices(u8, "1234", bufPrintIntToSlice(buf, @as(u32, 0x1234), 16, false, FormatOptions{ .width = 1 })); + + std.testing.expectEqualSlices(u8, "+42", bufPrintIntToSlice(buf, @as(i32, 42), 10, false, FormatOptions{ .width = 3 })); + std.testing.expectEqualSlices(u8, "-42", bufPrintIntToSlice(buf, @as(i32, -42), 10, false, FormatOptions{ .width = 3 })); +} + +fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 { + return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; +} + +test "parse u64 digit too big" { + _ = parseUnsigned(u64, "123a", 10) catch |err| { + if (err == error.InvalidCharacter) return; + unreachable; + }; + unreachable; +} + +test "parse unsigned comptime" { + comptime { + std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); + } +} + +test "optional" { + { + const value: ?i32 = 1234; + try testFmt("optional: 1234\n", "optional: {}\n", .{value}); + } + { + const value: ?i32 = null; + try testFmt("optional: null\n", "optional: {}\n", .{value}); + } +} + +test "error" { + { + const value: anyerror!i32 = 1234; + try testFmt("error union: 1234\n", "error union: {}\n", .{value}); + } + { + const value: anyerror!i32 = error.InvalidChar; + try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); + } +} + +test "int.small" { + { + const value: u3 = 0b101; + try testFmt("u3: 5\n", "u3: {}\n", .{value}); + } +} + +test "int.specifier" { + { + const value: u8 = 'a'; + try testFmt("u8: a\n", "u8: {c}\n", .{value}); + } + { + const value: u8 = 0b1100; + try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); + } +} + +test "int.padded" { + try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); + try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); +} + +test "buffer" { + { + var buf1: [32]u8 = undefined; + var context = BufPrintContext{ .remaining = buf1[0..] }; + try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); + var res = buf1[0 .. buf1.len - context.remaining.len]; + std.testing.expect(mem.eql(u8, res, "1234")); + + context = BufPrintContext{ .remaining = buf1[0..] }; + try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); + res = buf1[0 .. buf1.len - context.remaining.len]; + std.testing.expect(mem.eql(u8, res, "a")); + + context = BufPrintContext{ .remaining = buf1[0..] }; + try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); + res = buf1[0 .. buf1.len - context.remaining.len]; + std.testing.expect(mem.eql(u8, res, "1100")); + } +} + +test "array" { + { + const value: [3]u8 = "abc".*; + try testFmt("array: abc\n", "array: {}\n", .{value}); + try testFmt("array: abc\n", "array: {}\n", .{&value}); + + var buf: [100]u8 = undefined; + try testFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), + "array: {*}\n", + .{&value}, + ); + } +} + +test "slice" { + { + const value: []const u8 = "abc"; + try testFmt("slice: abc\n", "slice: {}\n", .{value}); + } + { + const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; + try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); + } + + try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); + try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); +} + +test "pointer" { + { + const value = @intToPtr(*align(1) i32, 0xdeadbeef); + try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); + try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); + } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); + } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); + } +} + +test "cstr" { + try testFmt( + "cstr: Test C\n", + "cstr: {s}\n", + .{@ptrCast([*c]const u8, "Test C")}, + ); + try testFmt( + "cstr: Test C \n", + "cstr: {s:10}\n", + .{@ptrCast([*c]const u8, "Test C")}, + ); +} + +test "filesize" { + try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); + try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); +} + +test "struct" { + { + const Struct = struct { + field: u8, + }; + const value = Struct{ .field = 42 }; + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); + } + { + const Struct = struct { + a: u0, + b: u1, + }; + const value = Struct{ .a = 0, .b = 1 }; + try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); + } +} + +test "enum" { + const Enum = enum { + One, + Two, + }; + const value = Enum.Two; + try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); + try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); +} + +test "non-exhaustive enum" { + const Enum = enum(u16) { + One = 0x000f, + Two = 0xbeef, + _, + }; + try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); + try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); + try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); + try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); + try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); + try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); +} + +test "float.scientific" { + try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); + try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); + try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); + try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); +} + +test "float.scientific.precision" { + try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); + try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); + try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); + // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. + // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. + try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); +} + +test "float.special" { + try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); + // negative nan is not defined by IEE 754, + // and ARM thus normalizes it to positive nan + if (builtin.arch != builtin.Arch.arm) { + try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); + } + try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); + try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); +} + +test "float.decimal" { + try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); + try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); + try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); + try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); + // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). + // -11.12339... is rounded back up to -11.1234 + try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); + try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); + try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); + try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); + try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); + try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); + try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); +} + +test "float.libc.sanity" { + try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); + try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); + try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); + try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); + try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); + + // libc differences + // + // This is 0.015625 exactly according to gdb. We thus round down, + // however glibc rounds up for some reason. This occurs for all + // floats of the form x.yyyy25 on a precision point. + try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); + // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 + // also rounds to 630 so I'm inclined to believe libc is not + // optimal here. + try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); +} + +test "custom" { + const Vec2 = struct { + const SelfType = @This(); + x: f32, + y: f32, + + pub fn format( + self: SelfType, + comptime fmt: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, + ) Errors!void { + if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { + return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); + } else if (comptime std.mem.eql(u8, fmt, "d")) { + return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", .{ self.x, self.y }); + } else { + @compileError("Unknown format character: '" ++ fmt ++ "'"); + } + } + }; + + var buf1: [32]u8 = undefined; + var value = Vec2{ + .x = 10.2, + .y = 2.22, + }; + try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); + + // same thing but not passing a pointer + try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); +} + +test "struct" { + const S = struct { + a: u32, + b: anyerror, + }; + + const inst = S{ + .a = 456, + .b = error.Unused, + }; + + try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); +} + +test "union" { + const TU = union(enum) { + float: f32, + int: u32, + }; + + const UU = union { + float: f32, + int: u32, + }; + + const EU = extern union { + float: f32, + int: u32, + }; + + const tu_inst = TU{ .int = 123 }; + const uu_inst = UU{ .int = 456 }; + const eu_inst = EU{ .float = 321.123 }; + + try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); + + var buf: [100]u8 = undefined; + const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); + std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); + + const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); + std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); +} + +test "enum" { + const E = enum { + One, + Two, + Three, + }; + + const inst = E.Two; + + try testFmt("E.Two", "{}", .{inst}); +} + +test "struct.self-referential" { + const S = struct { + const SelfType = @This(); + a: ?*SelfType, + }; + + var inst = S{ + .a = null, + }; + inst.a = &inst; + + try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); +} + +test "struct.zero-size" { + const A = struct { + fn foo() void {} + }; + const B = struct { + a: A, + c: i32, + }; + + const a = A{}; + const b = B{ .a = a, .c = 0 }; + + try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); +} + +test "bytes.hex" { + const some_bytes = "\xCA\xFE\xBA\xBE"; + try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); + try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); + //Test Slices + try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); + try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); + const bytes_with_zeros = "\x00\x0E\xBA\xBE"; + try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); +} + +fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { + var buf: [100]u8 = undefined; + const result = try bufPrint(buf[0..], template, args); + if (mem.eql(u8, result, expected)) return; + + std.debug.warn("\n====== expected this output: =========\n", .{}); + std.debug.warn("{}", .{expected}); + std.debug.warn("\n======== instead found this: =========\n", .{}); + std.debug.warn("{}", .{result}); + std.debug.warn("\n======================================\n", .{}); + return error.TestFailed; +} + +pub fn trim(buf: []const u8) []const u8 { + var start: usize = 0; + while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} + + var end: usize = buf.len; + while (true) { + if (end > start) { + const new_end = end - 1; + if (isWhiteSpace(buf[new_end])) { + end = new_end; + continue; + } + } + break; + } + return buf[start..end]; +} + +test "trim" { + std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); + std.testing.expect(mem.eql(u8, "", trim(" "))); + std.testing.expect(mem.eql(u8, "", trim(""))); + std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); + std.testing.expect(mem.eql(u8, "abc", trim("abc "))); +} + +pub fn isWhiteSpace(byte: u8) bool { + return switch (byte) { + ' ', '\t', '\n', '\r' => true, + else => false, + }; +} + +pub fn hexToBytes(out: []u8, input: []const u8) !void { + if (out.len * 2 < input.len) + return error.InvalidLength; + + var in_i: usize = 0; + while (in_i != input.len) : (in_i += 2) { + const hi = try charToDigit(input[in_i], 16); + const lo = try charToDigit(input[in_i + 1], 16); + out[in_i / 2] = (hi << 4) | lo; + } +} + +test "hexToBytes" { + const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; + var pb: [32]u8 = undefined; + try hexToBytes(pb[0..], test_hex_str); + try testFmt(test_hex_str, "{X}", .{pb}); +} + +test "formatIntValue with comptime_int" { + const value: comptime_int = 123456789123456789; + + var buf = std.ArrayList(u8).init(std.testing.allocator); + defer buf.deinit(); + try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice); + std.testing.expect(mem.eql(u8, buf.toSliceConst(), "123456789123456789")); +} + +test "formatType max_depth" { + const Vec2 = struct { + const SelfType = @This(); + x: f32, + y: f32, + + pub fn format( + self: SelfType, + comptime fmt: []const u8, + options: FormatOptions, + context: var, + comptime Errors: type, + comptime output: fn (@TypeOf(context), []const u8) Errors!void, + ) Errors!void { + if (fmt.len == 0) { + return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); + } else { + @compileError("Unknown format string: '" ++ fmt ++ "'"); + } + } + }; + const E = enum { + One, + Two, + Three, + }; + const TU = union(enum) { + const SelfType = @This(); + float: f32, + int: u32, + ptr: ?*SelfType, + }; + const S = struct { + const SelfType = @This(); + a: ?*SelfType, + tu: TU, + e: E, + vec: Vec2, + }; + + var inst = S{ + .a = null, + .tu = TU{ .ptr = null }, + .e = E.Two, + .vec = Vec2{ .x = 10.2, .y = 2.22 }, + }; + inst.a = &inst; + inst.tu.ptr = &inst.tu; + + var buf0 = std.ArrayList(u8).init(std.testing.allocator); + defer buf0.deinit(); + try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 0); + std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); + + var buf1 = std.ArrayList(u8).init(std.testing.allocator); + defer buf1.deinit(); + try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 1); + std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); + + var buf2 = std.ArrayList(u8).init(std.testing.allocator); + defer buf2.deinit(); + try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 2); + std.testing.expect(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); + + var buf3 = std.ArrayList(u8).init(std.testing.allocator); + defer buf3.deinit(); + try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 3); + std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); +} + +test "positional" { + try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); + try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); + try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); +} + +test "positional with specifier" { + try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); +} + +test "positional/alignment/width/precision" { + try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); +} + +test "vector" { + // https://github.com/ziglang/zig/issues/3317 + if (builtin.arch == .mipsel) return error.SkipZigTest; + + const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; + const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; + const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; + + try testFmt("{ true, false, true, false }", "{}", .{vbool}); + try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); + try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); + try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); + try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); + try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); + try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); +} + +test "enum-literal" { + try testFmt(".hello_world", "{}", .{.hello_world}); +} diff --git a/lib/std/std.zig b/lib/std/std.zig index 9277370ca6ab..b7fe709c7c16 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -38,6 +38,7 @@ pub const elf = @import("elf.zig"); pub const event = @import("event.zig"); pub const fifo = @import("fifo.zig"); pub const fmt = @import("fmt.zig"); +pub const fmtstream = @import("fmtstream.zig"); pub const fs = @import("fs.zig"); pub const hash = @import("hash.zig"); pub const hash_map = @import("hash_map.zig"); From 278b9ec1aaabb8bcfbbf9c964012dc5bd2ad154a Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 11:59:37 -0600 Subject: [PATCH 02/19] Blind translation --- lib/std/fmtstream.zig | 376 +++++++++++++++++++----------------------- 1 file changed, 170 insertions(+), 206 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index a7525ba79545..bb8d52d742e3 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -69,19 +69,17 @@ fn peekIsAlign(comptime fmt: []const u8) bool { /// /// If a formatted user type contains a function of the type /// ``` -/// fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, context: var, comptime Errors: type, comptime output: fn (@TypeOf(context), []const u8) Errors!void) Errors!void +/// fn format(value: ?, comptime fmt: []const u8, options: std.fmtstream.FormatOptions, out_stream: var) !void /// ``` /// with `?` being the type formatted, this function will be called instead of the default implementation. /// This allows user types to be formatted in a logical manner instead of dumping all fields of the type. /// /// A user type may be a `struct`, `vector`, `union` or `enum` type. pub fn format( - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + out_stream: var, comptime fmt: []const u8, args: var, -) Errors!void { +) !void { const ArgSetType = u32; if (@typeInfo(@TypeOf(args)) != .Struct) { @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); @@ -138,7 +136,7 @@ pub fn format( .Start => switch (c) { '{' => { if (start_index < i) { - try output(context, fmt[start_index..i]); + try out_stream.writeAll(fmt[start_index..i]); } start_index = i; @@ -150,7 +148,7 @@ pub fn format( }, '}' => { if (start_index < i) { - try output(context, fmt[start_index..i]); + try out_stream.writeAll(fmt[start_index..i]); } state = .CloseBrace; }, @@ -185,9 +183,7 @@ pub fn format( args[arg_to_print], fmt[0..0], options, - context, - Errors, - output, + out_stream, default_max_depth, ); @@ -218,9 +214,7 @@ pub fn format( args[arg_to_print], fmt[specifier_start..i], options, - context, - Errors, - output, + out_stream, default_max_depth, ); state = .Start; @@ -265,9 +259,7 @@ pub fn format( args[arg_to_print], fmt[specifier_start..specifier_end], options, - context, - Errors, - output, + out_stream, default_max_depth, ); state = .Start; @@ -293,9 +285,7 @@ pub fn format( args[arg_to_print], fmt[specifier_start..specifier_end], options, - context, - Errors, - output, + out_stream, default_max_depth, ); state = .Start; @@ -316,7 +306,7 @@ pub fn format( } } if (start_index < fmt.len) { - try output(context, fmt[start_index..]); + try out_stream.writeAll(fmt[start_index..]); } } @@ -324,121 +314,119 @@ pub fn formatType( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + out_stream: var, max_depth: usize, -) Errors!void { +) !void { if (comptime std.mem.eql(u8, fmt, "*")) { - try output(context, @typeName(@TypeOf(value).Child)); - try output(context, "@"); - try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, context, Errors, output); + try out_stream.writeAll(@typeName(@TypeOf(value).Child)); + try out_stream.writeAll("@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, out_stream); return; } const T = @TypeOf(value); switch (@typeInfo(T)) { .ComptimeInt, .Int, .Float => { - return formatValue(value, fmt, options, context, Errors, output); + return formatValue(value, fmt, options, out_stream); }, .Void => { - return output(context, "void"); + return out_stream.writeAll("void"); }, .Bool => { - return output(context, if (value) "true" else "false"); + return out_stream.writeAll(if (value) "true" else "false"); }, .Optional => { if (value) |payload| { - return formatType(payload, fmt, options, context, Errors, output, max_depth); + return formatType(payload, fmt, options, out_stream, max_depth); } else { - return output(context, "null"); + return out_stream.writeAll("null"); } }, .ErrorUnion => { if (value) |payload| { - return formatType(payload, fmt, options, context, Errors, output, max_depth); + return formatType(payload, fmt, options, out_stream, max_depth); } else |err| { - return formatType(err, fmt, options, context, Errors, output, max_depth); + return formatType(err, fmt, options, out_stream, max_depth); } }, .ErrorSet => { - try output(context, "error."); - return output(context, @errorName(value)); + try out_stream.writeAll("error."); + return out_stream.writeAll(@errorName(value)); }, .Enum => |enumInfo| { if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, context, Errors, output); + return value.format(fmt, options, out_stream); } - try output(context, @typeName(T)); + try out_stream.writeAll(@typeName(T)); if (enumInfo.is_exhaustive) { - try output(context, "."); - try output(context, @tagName(value)); + try out_stream.writeAll("."); + try out_stream.writeAll(@tagName(value)); } else { // TODO: when @tagName works on exhaustive enums print known enum strings - try output(context, "("); - try formatType(@enumToInt(value), fmt, options, context, Errors, output, max_depth); - try output(context, ")"); + try out_stream.writeAll("("); + try formatType(@enumToInt(value), fmt, options, out_stream, max_depth); + try out_stream.writeAll(")"); } }, .Union => { if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, context, Errors, output); + return value.format(fmt, options, out_stream); } - try output(context, @typeName(T)); + try out_stream.writeAll(@typeName(T)); if (max_depth == 0) { - return output(context, "{ ... }"); + return out_stream.writeAll("{ ... }"); } const info = @typeInfo(T).Union; if (info.tag_type) |UnionTagType| { - try output(context, "{ ."); - try output(context, @tagName(@as(UnionTagType, value))); - try output(context, " = "); + try out_stream.writeAll("{ ."); + try out_stream.writeAll(@tagName(@as(UnionTagType, value))); + try out_stream.writeAll(" = "); inline for (info.fields) |u_field| { if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) { - try formatType(@field(value, u_field.name), fmt, options, context, Errors, output, max_depth - 1); + try formatType(@field(value, u_field.name), fmt, options, out_stream, max_depth - 1); } } - try output(context, " }"); + try out_stream.writeAll(" }"); } else { - try format(context, Errors, output, "@{x}", .{@ptrToInt(&value)}); + try format(out_stream, "@{x}", .{@ptrToInt(&value)}); } }, .Struct => |StructT| { if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, context, Errors, output); + return value.format(fmt, options, out_stream); } - try output(context, @typeName(T)); + try out_stream.writeAll(@typeName(T)); if (max_depth == 0) { - return output(context, "{ ... }"); + return out_stream.writeAll("{ ... }"); } - try output(context, "{"); + try out_stream.writeAll("{"); inline for (StructT.fields) |f, i| { if (i == 0) { - try output(context, " ."); + try out_stream.writeAll(" ."); } else { - try output(context, ", ."); + try out_stream.writeAll(", ."); } - try output(context, f.name); - try output(context, " = "); - try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1); + try out_stream.writeAll(f.name); + try out_stream.writeAll(" = "); + try formatType(@field(value, f.name), fmt, options, out_stream, max_depth - 1); } - try output(context, " }"); + try out_stream.writeAll(" }"); }, .Pointer => |ptr_info| switch (ptr_info.size) { .One => switch (@typeInfo(ptr_info.child)) { .Array => |info| { if (info.child == u8) { - return formatText(value, fmt, options, context, Errors, output); + return formatText(value, fmt, options, out_stream); } - return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); + return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); }, .Enum, .Union, .Struct => { - return formatType(value.*, fmt, options, context, Errors, output, max_depth); + return formatType(value.*, fmt, options, out_stream, max_depth); }, - else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), + else => return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), }, .Many, .C => { if (ptr_info.sentinel) |sentinel| { @@ -446,19 +434,19 @@ pub fn formatType( } if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { - return formatText(mem.span(value), fmt, options, context, Errors, output); + return formatText(mem.span(value), fmt, options, out_stream); } } - return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); + return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); }, .Slice => { if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { - return formatText(value, fmt, options, context, Errors, output); + return formatText(value, fmt, options, out_stream); } if (ptr_info.child == u8) { - return formatText(value, fmt, options, context, Errors, output); + return formatText(value, fmt, options, out_stream); } - return format(context, Errors, output, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); + return format(out_stream, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); }, }, .Array => |info| { @@ -473,24 +461,24 @@ pub fn formatType( .sentinel = null, }, }); - return formatType(@as(Slice, &value), fmt, options, context, Errors, output, max_depth); + return formatType(@as(Slice, &value), fmt, options, out_stream, max_depth); }, .Vector => { const len = @typeInfo(T).Vector.len; - try output(context, "{ "); + try out_stream.writeAll("{ "); var i: usize = 0; while (i < len) : (i += 1) { - try formatValue(value[i], fmt, options, context, Errors, output); + try formatValue(value[i], fmt, options, out_stream); if (i < len - 1) { - try output(context, ", "); + try out_stream.writeAll(", "); } } - try output(context, " }"); + try out_stream.writeAll(" }"); }, .Fn => { - return format(context, Errors, output, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); + return format(out_stream, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); }, - .Type => return output(context, @typeName(T)), + .Type => return out_stream.writeAll(@typeName(T)), .EnumLiteral => { const buffer = [_]u8{'.'} ++ @tagName(value); return formatType(buffer, fmt, options, context, Errors, output, max_depth); @@ -503,21 +491,19 @@ fn formatValue( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (comptime std.mem.eql(u8, fmt, "B")) { - return formatBytes(value, options, 1000, context, Errors, output); + return formatBytes(value, options, 1000, out_stream); } else if (comptime std.mem.eql(u8, fmt, "Bi")) { - return formatBytes(value, options, 1024, context, Errors, output); + return formatBytes(value, options, 1024, out_stream); } const T = @TypeOf(value); switch (@typeInfo(T)) { - .Float => return formatFloatValue(value, fmt, options, context, Errors, output), - .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output), - .Bool => return output(context, if (value) "true" else "false"), + .Float => return formatFloatValue(value, fmt, options, out_stream), + .Int, .ComptimeInt => return formatIntValue(value, fmt, options, out_stream), + .Bool => return out_stream.writeAll(if (value) "true" else "false"), else => comptime unreachable, } } @@ -526,10 +512,8 @@ pub fn formatIntValue( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { comptime var radix = 10; comptime var uppercase = false; @@ -544,7 +528,7 @@ pub fn formatIntValue( uppercase = false; } else if (comptime std.mem.eql(u8, fmt, "c")) { if (@TypeOf(int_value).bit_count <= 8) { - return formatAsciiChar(@as(u8, int_value), options, context, Errors, output); + return formatAsciiChar(@as(u8, int_value), options, out_stream); } else { @compileError("Cannot print integer that is larger than 8 bits as a ascii"); } @@ -561,21 +545,19 @@ pub fn formatIntValue( @compileError("Unknown format string: '" ++ fmt ++ "'"); } - return formatInt(int_value, radix, uppercase, options, context, Errors, output); + return formatInt(int_value, radix, uppercase, options, out_stream); } fn formatFloatValue( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { - return formatFloatScientific(value, options, context, Errors, output); + return formatFloatScientific(value, options, out_stream); } else if (comptime std.mem.eql(u8, fmt, "d")) { - return formatFloatDecimal(value, options, context, Errors, output); + return formatFloatDecimal(value, options, out_stream); } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); } @@ -585,17 +567,15 @@ pub fn formatText( bytes: []const u8, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (fmt.len == 0) { - return output(context, bytes); + return out_stream.writeAll(bytes); } else if (comptime std.mem.eql(u8, fmt, "s")) { - return formatBuf(bytes, options, context, Errors, output); + return formatBuf(bytes, options, out_stream); } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { for (bytes) |c| { - try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, context, Errors, output); + try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, out_stream); } return; } else { @@ -606,27 +586,23 @@ pub fn formatText( pub fn formatAsciiChar( c: u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { - return output(context, @as(*const [1]u8, &c)[0..]); + out_stream: var, +) !void { + return out_stream.writeAll(@as(*const [1]u8, &c)[0..]); } pub fn formatBuf( buf: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { - try output(context, buf); + out_stream: var, +) !void { + try out_stream.writeAll(buf); const width = options.width orelse 0; var leftover_padding = if (width > buf.len) (width - buf.len) else return; const pad_byte: u8 = options.fill; while (leftover_padding > 0) : (leftover_padding -= 1) { - try output(context, @as(*const [1]u8, &pad_byte)[0..1]); + try out_stream.writeAll(@as(*const [1]u8, &pad_byte)[0..1]); } } @@ -636,40 +612,38 @@ pub fn formatBuf( pub fn formatFloatScientific( value: var, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { var x = @floatCast(f64, value); // Errol doesn't handle these special cases. if (math.signbit(x)) { - try output(context, "-"); + try out_stream.writeAll("-"); x = -x; } if (math.isNan(x)) { - return output(context, "nan"); + return out_stream.writeAll("nan"); } if (math.isPositiveInf(x)) { - return output(context, "inf"); + return out_stream.writeAll("inf"); } if (x == 0.0) { - try output(context, "0"); + try out_stream.writeAll("0"); if (options.precision) |precision| { if (precision != 0) { - try output(context, "."); + try out_stream.writeAll("."); var i: usize = 0; while (i < precision) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } } else { - try output(context, ".0"); + try out_stream.writeAll(".0"); } - try output(context, "e+00"); + try out_stream.writeAll("e+00"); return; } @@ -679,50 +653,50 @@ pub fn formatFloatScientific( if (options.precision) |precision| { errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); - try output(context, float_decimal.digits[0..1]); + try out_stream.writeAll(float_decimal.digits[0..1]); // {e0} case prints no `.` if (precision != 0) { - try output(context, "."); + try out_stream.writeAll("."); var printed: usize = 0; if (float_decimal.digits.len > 1) { const num_digits = math.min(float_decimal.digits.len, precision + 1); - try output(context, float_decimal.digits[1..num_digits]); + try out_stream.writeAll(float_decimal.digits[1..num_digits]); printed += num_digits - 1; } while (printed < precision) : (printed += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } } else { - try output(context, float_decimal.digits[0..1]); - try output(context, "."); + try out_stream.writeAll(float_decimal.digits[0..1]); + try out_stream.writeAll("."); if (float_decimal.digits.len > 1) { const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len; - try output(context, float_decimal.digits[1..num_digits]); + try out_stream.writeAll(float_decimal.digits[1..num_digits]); } else { - try output(context, "0"); + try out_stream.writeAll("0"); } } - try output(context, "e"); + try out_stream.writeAll("e"); const exp = float_decimal.exp - 1; if (exp >= 0) { - try output(context, "+"); + try out_stream.writeAll("+"); if (exp > -10 and exp < 10) { - try output(context, "0"); + try out_stream.writeAll("0"); } - try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); + try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, out_stream); } else { - try output(context, "-"); + try out_stream.writeAll("-"); if (exp > -10 and exp < 10) { - try output(context, "0"); + try out_stream.writeAll("0"); } - try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); + try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, out_stream); } } @@ -731,36 +705,34 @@ pub fn formatFloatScientific( pub fn formatFloatDecimal( value: var, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { var x = @as(f64, value); // Errol doesn't handle these special cases. if (math.signbit(x)) { - try output(context, "-"); + try out_stream.writeAll("-"); x = -x; } if (math.isNan(x)) { - return output(context, "nan"); + return out_stream.writeAll("nan"); } if (math.isPositiveInf(x)) { - return output(context, "inf"); + return out_stream.writeAll("inf"); } if (x == 0.0) { - try output(context, "0"); + try out_stream.writeAll("0"); if (options.precision) |precision| { if (precision != 0) { - try output(context, "."); + try out_stream.writeAll("."); var i: usize = 0; while (i < precision) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } else { - try output(context, ".0"); + try out_stream.writeAll(".0"); } } @@ -782,14 +754,14 @@ pub fn formatFloatDecimal( if (num_digits_whole > 0) { // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]); var i = num_digits_whole_no_pad; while (i < num_digits_whole) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } else { - try output(context, "0"); + try out_stream.writeAll("0"); } // {.0} special case doesn't want a trailing '.' @@ -797,7 +769,7 @@ pub fn formatFloatDecimal( return; } - try output(context, "."); + try out_stream.writeAll("."); // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. var printed: usize = 0; @@ -809,7 +781,7 @@ pub fn formatFloatDecimal( var i: usize = 0; while (i < zeros_to_print) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); printed += 1; } @@ -821,14 +793,14 @@ pub fn formatFloatDecimal( // Remaining fractional portion, zero-padding if insufficient. assert(precision >= printed); if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { - try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); + try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); return; } else { - try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]); printed += float_decimal.digits.len - num_digits_whole_no_pad; while (printed < precision) : (printed += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } } else { @@ -840,14 +812,14 @@ pub fn formatFloatDecimal( if (num_digits_whole > 0) { // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]); var i = num_digits_whole_no_pad; while (i < num_digits_whole) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } else { - try output(context, "0"); + try out_stream.writeAll("0"); } // Omit `.` if no fractional portion @@ -855,7 +827,7 @@ pub fn formatFloatDecimal( return; } - try output(context, "."); + try out_stream.writeAll("."); // Zero-fill until we reach significant digits or run out of precision. if (float_decimal.exp < 0) { @@ -863,11 +835,11 @@ pub fn formatFloatDecimal( var i: usize = 0; while (i < zero_digit_count) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } - try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]); } } @@ -875,12 +847,10 @@ pub fn formatBytes( value: var, options: FormatOptions, comptime radix: usize, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (value == 0) { - return output(context, "0B"); + return out_stream.writeAll("0B"); } const mags_si = " kMGTPEZY"; @@ -897,10 +867,10 @@ pub fn formatBytes( else => unreachable, }; - try formatFloatDecimal(new_value, options, context, Errors, output); + try formatFloatDecimal(new_value, options, out_stream); if (suffix == ' ') { - return output(context, "B"); + return out_stream.writeAll("B"); } const buf = switch (radix) { @@ -908,7 +878,7 @@ pub fn formatBytes( 1024 => &[_]u8{ suffix, 'i', 'B' }, else => unreachable, }; - return output(context, buf); + return out_stream.writeAll(buf); } pub fn formatInt( @@ -916,10 +886,8 @@ pub fn formatInt( base: u8, uppercase: bool, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { const int_value = if (@TypeOf(value) == comptime_int) blk: { const Int = math.IntFittingRange(value, value); break :blk @as(Int, value); @@ -927,9 +895,9 @@ pub fn formatInt( value; if (@TypeOf(int_value).is_signed) { - return formatIntSigned(int_value, base, uppercase, options, context, Errors, output); + return formatIntSigned(int_value, base, uppercase, options, out_stream); } else { - return formatIntUnsigned(int_value, base, uppercase, options, context, Errors, output); + return formatIntUnsigned(int_value, base, uppercase, options, out_stream); } } @@ -938,10 +906,8 @@ fn formatIntSigned( base: u8, uppercase: bool, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { const new_options = FormatOptions{ .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null, .precision = options.precision, @@ -950,15 +916,15 @@ fn formatIntSigned( const bit_count = @typeInfo(@TypeOf(value)).Int.bits; const Uint = std.meta.IntType(false, bit_count); if (value < 0) { - try output(context, "-"); + try out_stream.writeAll("-"); const new_value = math.absCast(value); - return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); + return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream); } else if (options.width == null or options.width.? == 0) { - return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, context, Errors, output); + return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, out_stream); } else { - try output(context, "+"); + try out_stream.writeAll("+"); const new_value = @intCast(Uint, value); - return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); + return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream); } } @@ -967,10 +933,8 @@ fn formatIntUnsigned( base: u8, uppercase: bool, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { assert(base >= 2); var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined; const min_int_bits = comptime math.max(@TypeOf(value).bit_count, @TypeOf(base).bit_count); @@ -994,16 +958,16 @@ fn formatIntUnsigned( const zero_byte: u8 = options.fill; var leftover_padding = padding - index; while (true) { - try output(context, @as(*const [1]u8, &zero_byte)[0..]); + try out_stream.writeAll(@as(*const [1]u8, &zero_byte)[0..]); leftover_padding -= 1; if (leftover_padding == 0) break; } mem.set(u8, buf[0..index], options.fill); - return output(context, &buf); + return out_stream.writeAll(&buf); } else { const padded_buf = buf[index - padding ..]; mem.set(u8, padded_buf[0..padding], options.fill); - return output(context, padded_buf); + return out_stream.writeAll(padded_buf); } } @@ -1451,12 +1415,12 @@ test "custom" { options: FormatOptions, context: var, comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, - ) Errors!void { + comptime output: fn (@TypeOf(context), []const u8) !void, + ) !void { if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { - return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); + return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); } else if (comptime std.mem.eql(u8, fmt, "d")) { - return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", .{ self.x, self.y }); + return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); } else { @compileError("Unknown format character: '" ++ fmt ++ "'"); } @@ -1658,10 +1622,10 @@ test "formatType max_depth" { options: FormatOptions, context: var, comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, - ) Errors!void { + comptime output: fn (@TypeOf(context), []const u8) !void, + ) !void { if (fmt.len == 0) { - return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); + return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); } From e1e9ff9546d0898764ff9c9383e57d0fa4e9bd0e Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 12:02:33 -0600 Subject: [PATCH 03/19] Get formatIntBuf working --- lib/std/fmtstream.zig | 1227 ++++++++++++++++++++--------------------- 1 file changed, 608 insertions(+), 619 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index bb8d52d742e3..d64c6cdf7f51 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -972,20 +972,9 @@ fn formatIntUnsigned( } pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { - var context = FormatIntBuf{ - .out_buf = out_buf, - .index = 0, - }; - formatInt(value, base, uppercase, options, &context, error{}, formatIntCallback) catch unreachable; - return context.index; -} -const FormatIntBuf = struct { - out_buf: []u8, - index: usize, -}; -fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { - mem.copy(u8, context.out_buf[context.index..], bytes); - context.index += bytes.len; + var fbs = std.io.fixedBufferStream(out_buf); + formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable; + return fbs.pos; } pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { @@ -1085,48 +1074,48 @@ fn digitToChar(digit: u8, uppercase: bool) u8 { }; } -const BufPrintContext = struct { - remaining: []u8, -}; - -fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { - if (context.remaining.len < bytes.len) { - mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); - return error.BufferTooSmall; - } - mem.copy(u8, context.remaining, bytes); - context.remaining = context.remaining[bytes.len..]; -} - -pub const BufPrintError = error{ - /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. - BufferTooSmall, -}; -pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { - var context = BufPrintContext{ .remaining = buf }; - try format(&context, BufPrintError, bufPrintWrite, fmt, args); - return buf[0 .. buf.len - context.remaining.len]; -} - -pub const AllocPrintError = error{OutOfMemory}; - -pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { - var size: usize = 0; - format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; - const buf = try allocator.alloc(u8, size); - return bufPrint(buf, fmt, args) catch |err| switch (err) { - error.BufferTooSmall => unreachable, // we just counted the size above - }; -} - -fn countSize(size: *usize, bytes: []const u8) (error{}!void) { - size.* += bytes.len; -} - -pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { - const result = try allocPrint(allocator, fmt ++ "\x00", args); - return result[0 .. result.len - 1 :0]; -} +// const BufPrintContext = struct { +// remaining: []u8, +// }; + +// fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { +// if (context.remaining.len < bytes.len) { +// mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); +// return error.BufferTooSmall; +// } +// mem.copy(u8, context.remaining, bytes); +// context.remaining = context.remaining[bytes.len..]; +// } + +// pub const BufPrintError = error{ +// /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. +// BufferTooSmall, +// }; +// pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { +// var context = BufPrintContext{ .remaining = buf }; +// try format(&context, BufPrintError, bufPrintWrite, fmt, args); +// return buf[0 .. buf.len - context.remaining.len]; +// } + +// pub const AllocPrintError = error{OutOfMemory}; + +// pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { +// var size: usize = 0; +// format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; +// const buf = try allocator.alloc(u8, size); +// return bufPrint(buf, fmt, args) catch |err| switch (err) { +// error.BufferTooSmall => unreachable, // we just counted the size above +// }; +// } + +// fn countSize(size: *usize, bytes: []const u8) (error{}!void) { +// size.* += bytes.len; +// } + +// pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { +// const result = try allocPrint(allocator, fmt ++ "\x00", args); +// return result[0 .. result.len - 1 :0]; +// } test "bufPrintInt" { var buffer: [100]u8 = undefined; @@ -1153,566 +1142,566 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; } -test "parse u64 digit too big" { - _ = parseUnsigned(u64, "123a", 10) catch |err| { - if (err == error.InvalidCharacter) return; - unreachable; - }; - unreachable; -} - -test "parse unsigned comptime" { - comptime { - std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); - } -} - -test "optional" { - { - const value: ?i32 = 1234; - try testFmt("optional: 1234\n", "optional: {}\n", .{value}); - } - { - const value: ?i32 = null; - try testFmt("optional: null\n", "optional: {}\n", .{value}); - } -} - -test "error" { - { - const value: anyerror!i32 = 1234; - try testFmt("error union: 1234\n", "error union: {}\n", .{value}); - } - { - const value: anyerror!i32 = error.InvalidChar; - try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); - } -} - -test "int.small" { - { - const value: u3 = 0b101; - try testFmt("u3: 5\n", "u3: {}\n", .{value}); - } -} - -test "int.specifier" { - { - const value: u8 = 'a'; - try testFmt("u8: a\n", "u8: {c}\n", .{value}); - } - { - const value: u8 = 0b1100; - try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); - } -} - -test "int.padded" { - try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); - try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); -} - -test "buffer" { - { - var buf1: [32]u8 = undefined; - var context = BufPrintContext{ .remaining = buf1[0..] }; - try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); - var res = buf1[0 .. buf1.len - context.remaining.len]; - std.testing.expect(mem.eql(u8, res, "1234")); - - context = BufPrintContext{ .remaining = buf1[0..] }; - try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); - res = buf1[0 .. buf1.len - context.remaining.len]; - std.testing.expect(mem.eql(u8, res, "a")); - - context = BufPrintContext{ .remaining = buf1[0..] }; - try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); - res = buf1[0 .. buf1.len - context.remaining.len]; - std.testing.expect(mem.eql(u8, res, "1100")); - } -} - -test "array" { - { - const value: [3]u8 = "abc".*; - try testFmt("array: abc\n", "array: {}\n", .{value}); - try testFmt("array: abc\n", "array: {}\n", .{&value}); - - var buf: [100]u8 = undefined; - try testFmt( - try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), - "array: {*}\n", - .{&value}, - ); - } -} - -test "slice" { - { - const value: []const u8 = "abc"; - try testFmt("slice: abc\n", "slice: {}\n", .{value}); - } - { - const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; - try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); - } - - try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); - try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); -} - -test "pointer" { - { - const value = @intToPtr(*align(1) i32, 0xdeadbeef); - try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); - try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); - } - { - const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); - } - { - const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); - } -} - -test "cstr" { - try testFmt( - "cstr: Test C\n", - "cstr: {s}\n", - .{@ptrCast([*c]const u8, "Test C")}, - ); - try testFmt( - "cstr: Test C \n", - "cstr: {s:10}\n", - .{@ptrCast([*c]const u8, "Test C")}, - ); -} - -test "filesize" { - try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); - try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); -} - -test "struct" { - { - const Struct = struct { - field: u8, - }; - const value = Struct{ .field = 42 }; - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); - } - { - const Struct = struct { - a: u0, - b: u1, - }; - const value = Struct{ .a = 0, .b = 1 }; - try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); - } -} - -test "enum" { - const Enum = enum { - One, - Two, - }; - const value = Enum.Two; - try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); - try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); -} - -test "non-exhaustive enum" { - const Enum = enum(u16) { - One = 0x000f, - Two = 0xbeef, - _, - }; - try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); - try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); - try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); - try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); - try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); - try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); -} - -test "float.scientific" { - try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); - try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); - try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); - try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); -} - -test "float.scientific.precision" { - try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); - try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); - try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); - // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. - // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. - try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); -} - -test "float.special" { - try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); - // negative nan is not defined by IEE 754, - // and ARM thus normalizes it to positive nan - if (builtin.arch != builtin.Arch.arm) { - try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); - } - try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); - try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); -} - -test "float.decimal" { - try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); - try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); - try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); - try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); - // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). - // -11.12339... is rounded back up to -11.1234 - try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); - try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); - try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); - try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); - try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); - try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); - try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); -} - -test "float.libc.sanity" { - try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); - try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); - try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); - try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); - try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); - - // libc differences - // - // This is 0.015625 exactly according to gdb. We thus round down, - // however glibc rounds up for some reason. This occurs for all - // floats of the form x.yyyy25 on a precision point. - try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); - // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 - // also rounds to 630 so I'm inclined to believe libc is not - // optimal here. - try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); -} - -test "custom" { - const Vec2 = struct { - const SelfType = @This(); - x: f32, - y: f32, - - pub fn format( - self: SelfType, - comptime fmt: []const u8, - options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) !void, - ) !void { - if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { - return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); - } else if (comptime std.mem.eql(u8, fmt, "d")) { - return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); - } else { - @compileError("Unknown format character: '" ++ fmt ++ "'"); - } - } - }; - - var buf1: [32]u8 = undefined; - var value = Vec2{ - .x = 10.2, - .y = 2.22, - }; - try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); - - // same thing but not passing a pointer - try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); -} - -test "struct" { - const S = struct { - a: u32, - b: anyerror, - }; - - const inst = S{ - .a = 456, - .b = error.Unused, - }; - - try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); -} - -test "union" { - const TU = union(enum) { - float: f32, - int: u32, - }; - - const UU = union { - float: f32, - int: u32, - }; - - const EU = extern union { - float: f32, - int: u32, - }; - - const tu_inst = TU{ .int = 123 }; - const uu_inst = UU{ .int = 456 }; - const eu_inst = EU{ .float = 321.123 }; - - try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); - - var buf: [100]u8 = undefined; - const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); - std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); - - const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); - std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); -} - -test "enum" { - const E = enum { - One, - Two, - Three, - }; - - const inst = E.Two; - - try testFmt("E.Two", "{}", .{inst}); -} - -test "struct.self-referential" { - const S = struct { - const SelfType = @This(); - a: ?*SelfType, - }; - - var inst = S{ - .a = null, - }; - inst.a = &inst; - - try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); -} - -test "struct.zero-size" { - const A = struct { - fn foo() void {} - }; - const B = struct { - a: A, - c: i32, - }; - - const a = A{}; - const b = B{ .a = a, .c = 0 }; - - try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); -} - -test "bytes.hex" { - const some_bytes = "\xCA\xFE\xBA\xBE"; - try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); - try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); - //Test Slices - try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); - try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); - const bytes_with_zeros = "\x00\x0E\xBA\xBE"; - try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); -} - -fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { - var buf: [100]u8 = undefined; - const result = try bufPrint(buf[0..], template, args); - if (mem.eql(u8, result, expected)) return; - - std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{}", .{expected}); - std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{}", .{result}); - std.debug.warn("\n======================================\n", .{}); - return error.TestFailed; -} - -pub fn trim(buf: []const u8) []const u8 { - var start: usize = 0; - while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} - - var end: usize = buf.len; - while (true) { - if (end > start) { - const new_end = end - 1; - if (isWhiteSpace(buf[new_end])) { - end = new_end; - continue; - } - } - break; - } - return buf[start..end]; -} - -test "trim" { - std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); - std.testing.expect(mem.eql(u8, "", trim(" "))); - std.testing.expect(mem.eql(u8, "", trim(""))); - std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); - std.testing.expect(mem.eql(u8, "abc", trim("abc "))); -} - -pub fn isWhiteSpace(byte: u8) bool { - return switch (byte) { - ' ', '\t', '\n', '\r' => true, - else => false, - }; -} - -pub fn hexToBytes(out: []u8, input: []const u8) !void { - if (out.len * 2 < input.len) - return error.InvalidLength; - - var in_i: usize = 0; - while (in_i != input.len) : (in_i += 2) { - const hi = try charToDigit(input[in_i], 16); - const lo = try charToDigit(input[in_i + 1], 16); - out[in_i / 2] = (hi << 4) | lo; - } -} - -test "hexToBytes" { - const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; - var pb: [32]u8 = undefined; - try hexToBytes(pb[0..], test_hex_str); - try testFmt(test_hex_str, "{X}", .{pb}); -} - -test "formatIntValue with comptime_int" { - const value: comptime_int = 123456789123456789; - - var buf = std.ArrayList(u8).init(std.testing.allocator); - defer buf.deinit(); - try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice); - std.testing.expect(mem.eql(u8, buf.toSliceConst(), "123456789123456789")); -} - -test "formatType max_depth" { - const Vec2 = struct { - const SelfType = @This(); - x: f32, - y: f32, - - pub fn format( - self: SelfType, - comptime fmt: []const u8, - options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) !void, - ) !void { - if (fmt.len == 0) { - return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); - } else { - @compileError("Unknown format string: '" ++ fmt ++ "'"); - } - } - }; - const E = enum { - One, - Two, - Three, - }; - const TU = union(enum) { - const SelfType = @This(); - float: f32, - int: u32, - ptr: ?*SelfType, - }; - const S = struct { - const SelfType = @This(); - a: ?*SelfType, - tu: TU, - e: E, - vec: Vec2, - }; - - var inst = S{ - .a = null, - .tu = TU{ .ptr = null }, - .e = E.Two, - .vec = Vec2{ .x = 10.2, .y = 2.22 }, - }; - inst.a = &inst; - inst.tu.ptr = &inst.tu; - - var buf0 = std.ArrayList(u8).init(std.testing.allocator); - defer buf0.deinit(); - try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 0); - std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); - - var buf1 = std.ArrayList(u8).init(std.testing.allocator); - defer buf1.deinit(); - try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 1); - std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); - - var buf2 = std.ArrayList(u8).init(std.testing.allocator); - defer buf2.deinit(); - try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 2); - std.testing.expect(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); - - var buf3 = std.ArrayList(u8).init(std.testing.allocator); - defer buf3.deinit(); - try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 3); - std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); -} - -test "positional" { - try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); - try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); - try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); - try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); - try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); -} - -test "positional with specifier" { - try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); -} - -test "positional/alignment/width/precision" { - try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); -} - -test "vector" { - // https://github.com/ziglang/zig/issues/3317 - if (builtin.arch == .mipsel) return error.SkipZigTest; - - const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; - const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; - const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; - - try testFmt("{ true, false, true, false }", "{}", .{vbool}); - try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); - try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); - try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); - try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); - try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); - try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); -} - -test "enum-literal" { - try testFmt(".hello_world", "{}", .{.hello_world}); -} +// test "parse u64 digit too big" { +// _ = parseUnsigned(u64, "123a", 10) catch |err| { +// if (err == error.InvalidCharacter) return; +// unreachable; +// }; +// unreachable; +// } + +// test "parse unsigned comptime" { +// comptime { +// std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); +// } +// } + +// test "optional" { +// { +// const value: ?i32 = 1234; +// try testFmt("optional: 1234\n", "optional: {}\n", .{value}); +// } +// { +// const value: ?i32 = null; +// try testFmt("optional: null\n", "optional: {}\n", .{value}); +// } +// } + +// test "error" { +// { +// const value: anyerror!i32 = 1234; +// try testFmt("error union: 1234\n", "error union: {}\n", .{value}); +// } +// { +// const value: anyerror!i32 = error.InvalidChar; +// try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); +// } +// } + +// test "int.small" { +// { +// const value: u3 = 0b101; +// try testFmt("u3: 5\n", "u3: {}\n", .{value}); +// } +// } + +// test "int.specifier" { +// { +// const value: u8 = 'a'; +// try testFmt("u8: a\n", "u8: {c}\n", .{value}); +// } +// { +// const value: u8 = 0b1100; +// try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); +// } +// } + +// test "int.padded" { +// try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); +// try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); +// } + +// test "buffer" { +// { +// var buf1: [32]u8 = undefined; +// var context = BufPrintContext{ .remaining = buf1[0..] }; +// try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); +// var res = buf1[0 .. buf1.len - context.remaining.len]; +// std.testing.expect(mem.eql(u8, res, "1234")); + +// context = BufPrintContext{ .remaining = buf1[0..] }; +// try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); +// res = buf1[0 .. buf1.len - context.remaining.len]; +// std.testing.expect(mem.eql(u8, res, "a")); + +// context = BufPrintContext{ .remaining = buf1[0..] }; +// try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); +// res = buf1[0 .. buf1.len - context.remaining.len]; +// std.testing.expect(mem.eql(u8, res, "1100")); +// } +// } + +// test "array" { +// { +// const value: [3]u8 = "abc".*; +// try testFmt("array: abc\n", "array: {}\n", .{value}); +// try testFmt("array: abc\n", "array: {}\n", .{&value}); + +// var buf: [100]u8 = undefined; +// try testFmt( +// try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), +// "array: {*}\n", +// .{&value}, +// ); +// } +// } + +// test "slice" { +// { +// const value: []const u8 = "abc"; +// try testFmt("slice: abc\n", "slice: {}\n", .{value}); +// } +// { +// const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; +// try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); +// } + +// try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); +// try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); +// } + +// test "pointer" { +// { +// const value = @intToPtr(*align(1) i32, 0xdeadbeef); +// try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); +// try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); +// } +// { +// const value = @intToPtr(fn () void, 0xdeadbeef); +// try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); +// } +// { +// const value = @intToPtr(fn () void, 0xdeadbeef); +// try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); +// } +// } + +// test "cstr" { +// try testFmt( +// "cstr: Test C\n", +// "cstr: {s}\n", +// .{@ptrCast([*c]const u8, "Test C")}, +// ); +// try testFmt( +// "cstr: Test C \n", +// "cstr: {s:10}\n", +// .{@ptrCast([*c]const u8, "Test C")}, +// ); +// } + +// test "filesize" { +// try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); +// try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); +// } + +// test "struct" { +// { +// const Struct = struct { +// field: u8, +// }; +// const value = Struct{ .field = 42 }; +// try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); +// try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); +// } +// { +// const Struct = struct { +// a: u0, +// b: u1, +// }; +// const value = Struct{ .a = 0, .b = 1 }; +// try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); +// } +// } + +// test "enum" { +// const Enum = enum { +// One, +// Two, +// }; +// const value = Enum.Two; +// try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); +// try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); +// } + +// test "non-exhaustive enum" { +// const Enum = enum(u16) { +// One = 0x000f, +// Two = 0xbeef, +// _, +// }; +// try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); +// try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); +// try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); +// try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); +// try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); +// try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); +// } + +// test "float.scientific" { +// try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); +// try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); +// try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); +// try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); +// } + +// test "float.scientific.precision" { +// try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); +// try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); +// try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); +// // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. +// // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. +// try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); +// } + +// test "float.special" { +// try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); +// // negative nan is not defined by IEE 754, +// // and ARM thus normalizes it to positive nan +// if (builtin.arch != builtin.Arch.arm) { +// try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); +// } +// try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); +// try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); +// } + +// test "float.decimal" { +// try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); +// try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); +// try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); +// try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); +// // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). +// // -11.12339... is rounded back up to -11.1234 +// try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); +// try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); +// try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); +// try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); +// try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); +// try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); +// try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); +// try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); +// try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); +// try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); +// } + +// test "float.libc.sanity" { +// try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); +// try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); +// try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); +// try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); +// try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); + +// // libc differences +// // +// // This is 0.015625 exactly according to gdb. We thus round down, +// // however glibc rounds up for some reason. This occurs for all +// // floats of the form x.yyyy25 on a precision point. +// try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); +// // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 +// // also rounds to 630 so I'm inclined to believe libc is not +// // optimal here. +// try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); +// } + +// test "custom" { +// const Vec2 = struct { +// const SelfType = @This(); +// x: f32, +// y: f32, + +// pub fn format( +// self: SelfType, +// comptime fmt: []const u8, +// options: FormatOptions, +// context: var, +// comptime Errors: type, +// comptime output: fn (@TypeOf(context), []const u8) !void, +// ) !void { +// if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { +// return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); +// } else if (comptime std.mem.eql(u8, fmt, "d")) { +// return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); +// } else { +// @compileError("Unknown format character: '" ++ fmt ++ "'"); +// } +// } +// }; + +// var buf1: [32]u8 = undefined; +// var value = Vec2{ +// .x = 10.2, +// .y = 2.22, +// }; +// try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); +// try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); + +// // same thing but not passing a pointer +// try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); +// try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); +// } + +// test "struct" { +// const S = struct { +// a: u32, +// b: anyerror, +// }; + +// const inst = S{ +// .a = 456, +// .b = error.Unused, +// }; + +// try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); +// } + +// test "union" { +// const TU = union(enum) { +// float: f32, +// int: u32, +// }; + +// const UU = union { +// float: f32, +// int: u32, +// }; + +// const EU = extern union { +// float: f32, +// int: u32, +// }; + +// const tu_inst = TU{ .int = 123 }; +// const uu_inst = UU{ .int = 456 }; +// const eu_inst = EU{ .float = 321.123 }; + +// try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); + +// var buf: [100]u8 = undefined; +// const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); +// std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); + +// const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); +// std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); +// } + +// test "enum" { +// const E = enum { +// One, +// Two, +// Three, +// }; + +// const inst = E.Two; + +// try testFmt("E.Two", "{}", .{inst}); +// } + +// test "struct.self-referential" { +// const S = struct { +// const SelfType = @This(); +// a: ?*SelfType, +// }; + +// var inst = S{ +// .a = null, +// }; +// inst.a = &inst; + +// try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); +// } + +// test "struct.zero-size" { +// const A = struct { +// fn foo() void {} +// }; +// const B = struct { +// a: A, +// c: i32, +// }; + +// const a = A{}; +// const b = B{ .a = a, .c = 0 }; + +// try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); +// } + +// test "bytes.hex" { +// const some_bytes = "\xCA\xFE\xBA\xBE"; +// try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); +// try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); +// //Test Slices +// try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); +// try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); +// const bytes_with_zeros = "\x00\x0E\xBA\xBE"; +// try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); +// } + +// fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { +// var buf: [100]u8 = undefined; +// const result = try bufPrint(buf[0..], template, args); +// if (mem.eql(u8, result, expected)) return; + +// std.debug.warn("\n====== expected this output: =========\n", .{}); +// std.debug.warn("{}", .{expected}); +// std.debug.warn("\n======== instead found this: =========\n", .{}); +// std.debug.warn("{}", .{result}); +// std.debug.warn("\n======================================\n", .{}); +// return error.TestFailed; +// } + +// pub fn trim(buf: []const u8) []const u8 { +// var start: usize = 0; +// while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} + +// var end: usize = buf.len; +// while (true) { +// if (end > start) { +// const new_end = end - 1; +// if (isWhiteSpace(buf[new_end])) { +// end = new_end; +// continue; +// } +// } +// break; +// } +// return buf[start..end]; +// } + +// test "trim" { +// std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); +// std.testing.expect(mem.eql(u8, "", trim(" "))); +// std.testing.expect(mem.eql(u8, "", trim(""))); +// std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); +// std.testing.expect(mem.eql(u8, "abc", trim("abc "))); +// } + +// pub fn isWhiteSpace(byte: u8) bool { +// return switch (byte) { +// ' ', '\t', '\n', '\r' => true, +// else => false, +// }; +// } + +// pub fn hexToBytes(out: []u8, input: []const u8) !void { +// if (out.len * 2 < input.len) +// return error.InvalidLength; + +// var in_i: usize = 0; +// while (in_i != input.len) : (in_i += 2) { +// const hi = try charToDigit(input[in_i], 16); +// const lo = try charToDigit(input[in_i + 1], 16); +// out[in_i / 2] = (hi << 4) | lo; +// } +// } + +// test "hexToBytes" { +// const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; +// var pb: [32]u8 = undefined; +// try hexToBytes(pb[0..], test_hex_str); +// try testFmt(test_hex_str, "{X}", .{pb}); +// } + +// test "formatIntValue with comptime_int" { +// const value: comptime_int = 123456789123456789; + +// var buf = std.ArrayList(u8).init(std.testing.allocator); +// defer buf.deinit(); +// try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice); +// std.testing.expect(mem.eql(u8, buf.toSliceConst(), "123456789123456789")); +// } + +// test "formatType max_depth" { +// const Vec2 = struct { +// const SelfType = @This(); +// x: f32, +// y: f32, + +// pub fn format( +// self: SelfType, +// comptime fmt: []const u8, +// options: FormatOptions, +// context: var, +// comptime Errors: type, +// comptime output: fn (@TypeOf(context), []const u8) !void, +// ) !void { +// if (fmt.len == 0) { +// return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); +// } else { +// @compileError("Unknown format string: '" ++ fmt ++ "'"); +// } +// } +// }; +// const E = enum { +// One, +// Two, +// Three, +// }; +// const TU = union(enum) { +// const SelfType = @This(); +// float: f32, +// int: u32, +// ptr: ?*SelfType, +// }; +// const S = struct { +// const SelfType = @This(); +// a: ?*SelfType, +// tu: TU, +// e: E, +// vec: Vec2, +// }; + +// var inst = S{ +// .a = null, +// .tu = TU{ .ptr = null }, +// .e = E.Two, +// .vec = Vec2{ .x = 10.2, .y = 2.22 }, +// }; +// inst.a = &inst; +// inst.tu.ptr = &inst.tu; + +// var buf0 = std.ArrayList(u8).init(std.testing.allocator); +// defer buf0.deinit(); +// try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 0); +// std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); + +// var buf1 = std.ArrayList(u8).init(std.testing.allocator); +// defer buf1.deinit(); +// try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 1); +// std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); + +// var buf2 = std.ArrayList(u8).init(std.testing.allocator); +// defer buf2.deinit(); +// try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 2); +// std.testing.expect(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); + +// var buf3 = std.ArrayList(u8).init(std.testing.allocator); +// defer buf3.deinit(); +// try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 3); +// std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); +// } + +// test "positional" { +// try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); +// try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); +// try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); +// try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); +// try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); +// } + +// test "positional with specifier" { +// try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); +// } + +// test "positional/alignment/width/precision" { +// try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); +// } + +// test "vector" { +// // https://github.com/ziglang/zig/issues/3317 +// if (builtin.arch == .mipsel) return error.SkipZigTest; + +// const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; +// const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; +// const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; + +// try testFmt("{ true, false, true, false }", "{}", .{vbool}); +// try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); +// try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); +// try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); +// try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); +// try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); +// try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); +// } + +// test "enum-literal" { +// try testFmt(".hello_world", "{}", .{.hello_world}); +// } From 8241b96f78fa8ee33f9a73d4d296509d49b6f351 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 12:25:12 -0600 Subject: [PATCH 04/19] Re-enable testFmt --- lib/std/fmtstream.zig | 77 +++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index d64c6cdf7f51..4a7d8041c029 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -1074,28 +1074,17 @@ fn digitToChar(digit: u8, uppercase: bool) u8 { }; } -// const BufPrintContext = struct { -// remaining: []u8, -// }; - -// fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { -// if (context.remaining.len < bytes.len) { -// mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); -// return error.BufferTooSmall; -// } -// mem.copy(u8, context.remaining, bytes); -// context.remaining = context.remaining[bytes.len..]; -// } - -// pub const BufPrintError = error{ -// /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. -// BufferTooSmall, -// }; -// pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { -// var context = BufPrintContext{ .remaining = buf }; -// try format(&context, BufPrintError, bufPrintWrite, fmt, args); -// return buf[0 .. buf.len - context.remaining.len]; -// } +pub const BufPrintError = error{ + /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. + BufferTooSmall, +}; +pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { + var os = std.io.SliceOutStream.init(buf); + format(&os.stream, fmt, args) catch |err| switch (err) { + error.OutOfMemory => return error.BufferTooSmall, + }; + return buf[0..os.pos]; +} // pub const AllocPrintError = error{OutOfMemory}; @@ -1514,29 +1503,29 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: // try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); // } -// test "bytes.hex" { -// const some_bytes = "\xCA\xFE\xBA\xBE"; -// try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); -// try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); -// //Test Slices -// try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); -// try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); -// const bytes_with_zeros = "\x00\x0E\xBA\xBE"; -// try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); -// } +test "bytes.hex" { + const some_bytes = "\xCA\xFE\xBA\xBE"; + try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); + try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); + //Test Slices + try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); + try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); + const bytes_with_zeros = "\x00\x0E\xBA\xBE"; + try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); +} -// fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { -// var buf: [100]u8 = undefined; -// const result = try bufPrint(buf[0..], template, args); -// if (mem.eql(u8, result, expected)) return; - -// std.debug.warn("\n====== expected this output: =========\n", .{}); -// std.debug.warn("{}", .{expected}); -// std.debug.warn("\n======== instead found this: =========\n", .{}); -// std.debug.warn("{}", .{result}); -// std.debug.warn("\n======================================\n", .{}); -// return error.TestFailed; -// } +fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { + var buf: [100]u8 = undefined; + const result = try bufPrint(buf[0..], template, args); + if (mem.eql(u8, result, expected)) return; + + std.debug.warn("\n====== expected this output: =========\n", .{}); + std.debug.warn("{}", .{expected}); + std.debug.warn("\n======== instead found this: =========\n", .{}); + std.debug.warn("{}", .{result}); + std.debug.warn("\n======================================\n", .{}); + return error.TestFailed; +} // pub fn trim(buf: []const u8) []const u8 { // var start: usize = 0; From 78d12762a9c78f9563a3cfd29541d0a04f78ab58 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 13:22:27 -0600 Subject: [PATCH 05/19] Re-enable a bunch of tests --- lib/std/fmtstream.zig | 812 +++++++++++++++++++++--------------------- 1 file changed, 405 insertions(+), 407 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index 4a7d8041c029..3a2767c85ab3 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -430,7 +430,7 @@ pub fn formatType( }, .Many, .C => { if (ptr_info.sentinel) |sentinel| { - return formatType(mem.span(value), fmt, options, context, Errors, output, max_depth); + return formatType(mem.span(value), fmt, options, out_stream, max_depth); } if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { @@ -481,7 +481,7 @@ pub fn formatType( .Type => return out_stream.writeAll(@typeName(T)), .EnumLiteral => { const buffer = [_]u8{'.'} ++ @tagName(value); - return formatType(buffer, fmt, options, context, Errors, output, max_depth); + return formatType(buffer, fmt, options, out_stream, max_depth); }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } @@ -1079,11 +1079,11 @@ pub const BufPrintError = error{ BufferTooSmall, }; pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { - var os = std.io.SliceOutStream.init(buf); - format(&os.stream, fmt, args) catch |err| switch (err) { - error.OutOfMemory => return error.BufferTooSmall, + var fbs = std.io.fixedBufferStream(buf); + format(fbs.outStream(), fmt, args) catch |err| switch (err) { + error.NoSpaceLeft => return error.BufferTooSmall, }; - return buf[0..os.pos]; + return buf[0..fbs.pos]; } // pub const AllocPrintError = error{OutOfMemory}; @@ -1131,348 +1131,346 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; } -// test "parse u64 digit too big" { -// _ = parseUnsigned(u64, "123a", 10) catch |err| { -// if (err == error.InvalidCharacter) return; -// unreachable; -// }; -// unreachable; -// } +test "parse u64 digit too big" { + _ = parseUnsigned(u64, "123a", 10) catch |err| { + if (err == error.InvalidCharacter) return; + unreachable; + }; + unreachable; +} -// test "parse unsigned comptime" { -// comptime { -// std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); -// } -// } +test "parse unsigned comptime" { + comptime { + std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); + } +} -// test "optional" { -// { -// const value: ?i32 = 1234; -// try testFmt("optional: 1234\n", "optional: {}\n", .{value}); -// } -// { -// const value: ?i32 = null; -// try testFmt("optional: null\n", "optional: {}\n", .{value}); -// } -// } +test "optional" { + { + const value: ?i32 = 1234; + try testFmt("optional: 1234\n", "optional: {}\n", .{value}); + } + { + const value: ?i32 = null; + try testFmt("optional: null\n", "optional: {}\n", .{value}); + } +} -// test "error" { -// { -// const value: anyerror!i32 = 1234; -// try testFmt("error union: 1234\n", "error union: {}\n", .{value}); -// } -// { -// const value: anyerror!i32 = error.InvalidChar; -// try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); -// } -// } +test "error" { + { + const value: anyerror!i32 = 1234; + try testFmt("error union: 1234\n", "error union: {}\n", .{value}); + } + { + const value: anyerror!i32 = error.InvalidChar; + try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); + } +} -// test "int.small" { -// { -// const value: u3 = 0b101; -// try testFmt("u3: 5\n", "u3: {}\n", .{value}); -// } -// } +test "int.small" { + { + const value: u3 = 0b101; + try testFmt("u3: 5\n", "u3: {}\n", .{value}); + } +} -// test "int.specifier" { -// { -// const value: u8 = 'a'; -// try testFmt("u8: a\n", "u8: {c}\n", .{value}); -// } -// { -// const value: u8 = 0b1100; -// try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); -// } -// } +test "int.specifier" { + { + const value: u8 = 'a'; + try testFmt("u8: a\n", "u8: {c}\n", .{value}); + } + { + const value: u8 = 0b1100; + try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); + } +} -// test "int.padded" { -// try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); -// try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); -// } +test "int.padded" { + try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); + try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); +} -// test "buffer" { -// { -// var buf1: [32]u8 = undefined; -// var context = BufPrintContext{ .remaining = buf1[0..] }; -// try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); -// var res = buf1[0 .. buf1.len - context.remaining.len]; -// std.testing.expect(mem.eql(u8, res, "1234")); - -// context = BufPrintContext{ .remaining = buf1[0..] }; -// try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); -// res = buf1[0 .. buf1.len - context.remaining.len]; -// std.testing.expect(mem.eql(u8, res, "a")); - -// context = BufPrintContext{ .remaining = buf1[0..] }; -// try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); -// res = buf1[0 .. buf1.len - context.remaining.len]; -// std.testing.expect(mem.eql(u8, res, "1100")); -// } -// } +test "buffer" { + { + var buf1: [32]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf1); + try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth); + var res = buf1[0..fbs.pos]; + std.testing.expect(mem.eql(u8, res, "1234")); + + try fbs.seekTo(0); + try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth); + res = buf1[0..fbs.pos]; + std.testing.expect(mem.eql(u8, res, "a")); + + try fbs.seekTo(0); + try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth); + res = buf1[0..fbs.pos]; + std.testing.expect(mem.eql(u8, res, "1100")); + } +} -// test "array" { -// { -// const value: [3]u8 = "abc".*; -// try testFmt("array: abc\n", "array: {}\n", .{value}); -// try testFmt("array: abc\n", "array: {}\n", .{&value}); - -// var buf: [100]u8 = undefined; -// try testFmt( -// try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), -// "array: {*}\n", -// .{&value}, -// ); -// } -// } +test "array" { + { + const value: [3]u8 = "abc".*; + try testFmt("array: abc\n", "array: {}\n", .{value}); + try testFmt("array: abc\n", "array: {}\n", .{&value}); + + var buf: [100]u8 = undefined; + try testFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), + "array: {*}\n", + .{&value}, + ); + } +} -// test "slice" { -// { -// const value: []const u8 = "abc"; -// try testFmt("slice: abc\n", "slice: {}\n", .{value}); -// } -// { -// const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; -// try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); -// } - -// try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); -// try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); -// } +test "slice" { + { + const value: []const u8 = "abc"; + try testFmt("slice: abc\n", "slice: {}\n", .{value}); + } + { + const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; + try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); + } -// test "pointer" { -// { -// const value = @intToPtr(*align(1) i32, 0xdeadbeef); -// try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); -// try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); -// } -// { -// const value = @intToPtr(fn () void, 0xdeadbeef); -// try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); -// } -// { -// const value = @intToPtr(fn () void, 0xdeadbeef); -// try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); -// } -// } + try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); + try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); +} -// test "cstr" { -// try testFmt( -// "cstr: Test C\n", -// "cstr: {s}\n", -// .{@ptrCast([*c]const u8, "Test C")}, -// ); -// try testFmt( -// "cstr: Test C \n", -// "cstr: {s:10}\n", -// .{@ptrCast([*c]const u8, "Test C")}, -// ); -// } +test "pointer" { + { + const value = @intToPtr(*align(1) i32, 0xdeadbeef); + try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); + try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); + } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); + } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); + } +} -// test "filesize" { -// try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); -// try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); -// } +test "cstr" { + try testFmt( + "cstr: Test C\n", + "cstr: {s}\n", + .{@ptrCast([*c]const u8, "Test C")}, + ); + try testFmt( + "cstr: Test C \n", + "cstr: {s:10}\n", + .{@ptrCast([*c]const u8, "Test C")}, + ); +} -// test "struct" { -// { -// const Struct = struct { -// field: u8, -// }; -// const value = Struct{ .field = 42 }; -// try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); -// try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); -// } -// { -// const Struct = struct { -// a: u0, -// b: u1, -// }; -// const value = Struct{ .a = 0, .b = 1 }; -// try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); -// } -// } +test "filesize" { + try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); + try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); +} -// test "enum" { -// const Enum = enum { -// One, -// Two, -// }; -// const value = Enum.Two; -// try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); -// try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); -// } +test "struct" { + { + const Struct = struct { + field: u8, + }; + const value = Struct{ .field = 42 }; + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); + } + { + const Struct = struct { + a: u0, + b: u1, + }; + const value = Struct{ .a = 0, .b = 1 }; + try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); + } +} -// test "non-exhaustive enum" { -// const Enum = enum(u16) { -// One = 0x000f, -// Two = 0xbeef, -// _, -// }; -// try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); -// try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); -// try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); -// try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); -// try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); -// try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); -// } +test "enum" { + const Enum = enum { + One, + Two, + }; + const value = Enum.Two; + try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); + try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); +} -// test "float.scientific" { -// try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); -// try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); -// try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); -// try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); -// } +test "non-exhaustive enum" { + const Enum = enum(u16) { + One = 0x000f, + Two = 0xbeef, + _, + }; + try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); + try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); + try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); + try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); + try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); + try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); +} -// test "float.scientific.precision" { -// try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); -// try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); -// try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); -// // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. -// // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. -// try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); -// } +test "float.scientific" { + try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); + try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); + try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); + try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); +} -// test "float.special" { -// try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); -// // negative nan is not defined by IEE 754, -// // and ARM thus normalizes it to positive nan -// if (builtin.arch != builtin.Arch.arm) { -// try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); -// } -// try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); -// try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); -// } +test "float.scientific.precision" { + try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); + try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); + try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); + // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. + // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. + try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); +} -// test "float.decimal" { -// try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); -// try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); -// try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); -// try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); -// // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). -// // -11.12339... is rounded back up to -11.1234 -// try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); -// try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); -// try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); -// try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); -// try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); -// try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); -// try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); -// try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); -// try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); -// try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); -// } +test "float.special" { + try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); + // negative nan is not defined by IEE 754, + // and ARM thus normalizes it to positive nan + if (builtin.arch != builtin.Arch.arm) { + try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); + } + try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); + try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); +} -// test "float.libc.sanity" { -// try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); -// try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); -// try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); -// try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); -// try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); - -// // libc differences -// // -// // This is 0.015625 exactly according to gdb. We thus round down, -// // however glibc rounds up for some reason. This occurs for all -// // floats of the form x.yyyy25 on a precision point. -// try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); -// // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 -// // also rounds to 630 so I'm inclined to believe libc is not -// // optimal here. -// try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); -// } +test "float.decimal" { + try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); + try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); + try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); + try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); + // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). + // -11.12339... is rounded back up to -11.1234 + try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); + try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); + try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); + try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); + try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); + try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); + try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); + try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); +} -// test "custom" { -// const Vec2 = struct { -// const SelfType = @This(); -// x: f32, -// y: f32, +test "float.libc.sanity" { + try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); + try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); + try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); + try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); + try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); + + // libc differences + // + // This is 0.015625 exactly according to gdb. We thus round down, + // however glibc rounds up for some reason. This occurs for all + // floats of the form x.yyyy25 on a precision point. + try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); + // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 + // also rounds to 630 so I'm inclined to believe libc is not + // optimal here. + try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); +} -// pub fn format( -// self: SelfType, -// comptime fmt: []const u8, -// options: FormatOptions, -// context: var, -// comptime Errors: type, -// comptime output: fn (@TypeOf(context), []const u8) !void, -// ) !void { -// if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { -// return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); -// } else if (comptime std.mem.eql(u8, fmt, "d")) { -// return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); -// } else { -// @compileError("Unknown format character: '" ++ fmt ++ "'"); -// } -// } -// }; +test "custom" { + const Vec2 = struct { + const SelfType = @This(); + x: f32, + y: f32, + + pub fn format( + self: SelfType, + comptime fmt: []const u8, + options: FormatOptions, + out_stream: var, + ) !void { + if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { + return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); + } else if (comptime std.mem.eql(u8, fmt, "d")) { + return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); + } else { + @compileError("Unknown format character: '" ++ fmt ++ "'"); + } + } + }; -// var buf1: [32]u8 = undefined; -// var value = Vec2{ -// .x = 10.2, -// .y = 2.22, -// }; -// try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); -// try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); + var buf1: [32]u8 = undefined; + var value = Vec2{ + .x = 10.2, + .y = 2.22, + }; + try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); -// // same thing but not passing a pointer -// try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); -// try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); -// } + // same thing but not passing a pointer + try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); +} -// test "struct" { -// const S = struct { -// a: u32, -// b: anyerror, -// }; +test "struct" { + const S = struct { + a: u32, + b: anyerror, + }; -// const inst = S{ -// .a = 456, -// .b = error.Unused, -// }; + const inst = S{ + .a = 456, + .b = error.Unused, + }; -// try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); -// } + try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); +} -// test "union" { -// const TU = union(enum) { -// float: f32, -// int: u32, -// }; +test "union" { + const TU = union(enum) { + float: f32, + int: u32, + }; -// const UU = union { -// float: f32, -// int: u32, -// }; + const UU = union { + float: f32, + int: u32, + }; -// const EU = extern union { -// float: f32, -// int: u32, -// }; + const EU = extern union { + float: f32, + int: u32, + }; -// const tu_inst = TU{ .int = 123 }; -// const uu_inst = UU{ .int = 456 }; -// const eu_inst = EU{ .float = 321.123 }; + const tu_inst = TU{ .int = 123 }; + const uu_inst = UU{ .int = 456 }; + const eu_inst = EU{ .float = 321.123 }; -// try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); + try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); -// var buf: [100]u8 = undefined; -// const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); -// std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); + var buf: [100]u8 = undefined; + const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); + std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); -// const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); -// std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); -// } + const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); + std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); +} -// test "enum" { -// const E = enum { -// One, -// Two, -// Three, -// }; +test "enum" { + const E = enum { + One, + Two, + Three, + }; -// const inst = E.Two; + const inst = E.Two; -// try testFmt("E.Two", "{}", .{inst}); -// } + try testFmt("E.Two", "{}", .{inst}); +} // test "struct.self-referential" { // const S = struct { @@ -1488,20 +1486,20 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: // try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); // } -// test "struct.zero-size" { -// const A = struct { -// fn foo() void {} -// }; -// const B = struct { -// a: A, -// c: i32, -// }; +test "struct.zero-size" { + const A = struct { + fn foo() void {} + }; + const B = struct { + a: A, + c: i32, + }; -// const a = A{}; -// const b = B{ .a = a, .c = 0 }; + const a = A{}; + const b = B{ .a = a, .c = 0 }; -// try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); -// } + try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); +} test "bytes.hex" { const some_bytes = "\xCA\xFE\xBA\xBE"; @@ -1527,66 +1525,66 @@ fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void return error.TestFailed; } -// pub fn trim(buf: []const u8) []const u8 { -// var start: usize = 0; -// while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} +pub fn trim(buf: []const u8) []const u8 { + var start: usize = 0; + while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} -// var end: usize = buf.len; -// while (true) { -// if (end > start) { -// const new_end = end - 1; -// if (isWhiteSpace(buf[new_end])) { -// end = new_end; -// continue; -// } -// } -// break; -// } -// return buf[start..end]; -// } + var end: usize = buf.len; + while (true) { + if (end > start) { + const new_end = end - 1; + if (isWhiteSpace(buf[new_end])) { + end = new_end; + continue; + } + } + break; + } + return buf[start..end]; +} -// test "trim" { -// std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); -// std.testing.expect(mem.eql(u8, "", trim(" "))); -// std.testing.expect(mem.eql(u8, "", trim(""))); -// std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); -// std.testing.expect(mem.eql(u8, "abc", trim("abc "))); -// } +test "trim" { + std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); + std.testing.expect(mem.eql(u8, "", trim(" "))); + std.testing.expect(mem.eql(u8, "", trim(""))); + std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); + std.testing.expect(mem.eql(u8, "abc", trim("abc "))); +} -// pub fn isWhiteSpace(byte: u8) bool { -// return switch (byte) { -// ' ', '\t', '\n', '\r' => true, -// else => false, -// }; -// } +pub fn isWhiteSpace(byte: u8) bool { + return switch (byte) { + ' ', '\t', '\n', '\r' => true, + else => false, + }; +} -// pub fn hexToBytes(out: []u8, input: []const u8) !void { -// if (out.len * 2 < input.len) -// return error.InvalidLength; +pub fn hexToBytes(out: []u8, input: []const u8) !void { + if (out.len * 2 < input.len) + return error.InvalidLength; -// var in_i: usize = 0; -// while (in_i != input.len) : (in_i += 2) { -// const hi = try charToDigit(input[in_i], 16); -// const lo = try charToDigit(input[in_i + 1], 16); -// out[in_i / 2] = (hi << 4) | lo; -// } -// } + var in_i: usize = 0; + while (in_i != input.len) : (in_i += 2) { + const hi = try charToDigit(input[in_i], 16); + const lo = try charToDigit(input[in_i + 1], 16); + out[in_i / 2] = (hi << 4) | lo; + } +} -// test "hexToBytes" { -// const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; -// var pb: [32]u8 = undefined; -// try hexToBytes(pb[0..], test_hex_str); -// try testFmt(test_hex_str, "{X}", .{pb}); -// } +test "hexToBytes" { + const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; + var pb: [32]u8 = undefined; + try hexToBytes(pb[0..], test_hex_str); + try testFmt(test_hex_str, "{X}", .{pb}); +} -// test "formatIntValue with comptime_int" { -// const value: comptime_int = 123456789123456789; +test "formatIntValue with comptime_int" { + const value: comptime_int = 123456789123456789; -// var buf = std.ArrayList(u8).init(std.testing.allocator); -// defer buf.deinit(); -// try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice); -// std.testing.expect(mem.eql(u8, buf.toSliceConst(), "123456789123456789")); -// } + var buf: [20]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + try formatIntValue(value, "", FormatOptions{}, fbs.outStream()); + std.testing.expect(mem.eql(u8, buf[0..fbs.pos], "123456789123456789")); +} // test "formatType max_depth" { // const Vec2 = struct { @@ -1658,39 +1656,39 @@ fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void // std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); // } -// test "positional" { -// try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); -// try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); -// try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); -// try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); -// try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); -// } +test "positional" { + try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); + try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); + try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); +} -// test "positional with specifier" { -// try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); -// } +test "positional with specifier" { + try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); +} -// test "positional/alignment/width/precision" { -// try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); -// } +test "positional/alignment/width/precision" { + try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); +} -// test "vector" { -// // https://github.com/ziglang/zig/issues/3317 -// if (builtin.arch == .mipsel) return error.SkipZigTest; - -// const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; -// const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; -// const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; - -// try testFmt("{ true, false, true, false }", "{}", .{vbool}); -// try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); -// try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); -// try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); -// try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); -// try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); -// try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); -// } +test "vector" { + // https://github.com/ziglang/zig/issues/3317 + if (builtin.arch == .mipsel) return error.SkipZigTest; + + const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; + const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; + const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; + + try testFmt("{ true, false, true, false }", "{}", .{vbool}); + try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); + try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); + try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); + try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); + try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); + try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); +} -// test "enum-literal" { -// try testFmt(".hello_world", "{}", .{.hello_world}); -// } +test "enum-literal" { + try testFmt(".hello_world", "{}", .{.hello_world}); +} From f51c8f26c195819bb49a641c82ee65bb2f8e3044 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 15:02:29 -0600 Subject: [PATCH 06/19] Apply explicit error type --- lib/std/fmtstream.zig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index 3a2767c85ab3..ff7c492a1933 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -316,7 +316,7 @@ pub fn formatType( options: FormatOptions, out_stream: var, max_depth: usize, -) !void { +) @TypeOf(out_stream).Error!void { if (comptime std.mem.eql(u8, fmt, "*")) { try out_stream.writeAll(@typeName(@TypeOf(value).Child)); try out_stream.writeAll("@"); @@ -1472,19 +1472,19 @@ test "enum" { try testFmt("E.Two", "{}", .{inst}); } -// test "struct.self-referential" { -// const S = struct { -// const SelfType = @This(); -// a: ?*SelfType, -// }; +test "struct.self-referential" { + const S = struct { + const SelfType = @This(); + a: ?*SelfType, + }; -// var inst = S{ -// .a = null, -// }; -// inst.a = &inst; + var inst = S{ + .a = null, + }; + inst.a = &inst; -// try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); -// } + try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); +} test "struct.zero-size" { const A = struct { From 1c18ab01a44bf3cbef356ff215e97dfe2788265b Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 15:10:27 -0600 Subject: [PATCH 07/19] Add back max_depth test --- lib/std/fmtstream.zig | 152 ++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 79 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index ff7c492a1933..c062bbaa1b9b 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -1083,6 +1083,8 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![] format(fbs.outStream(), fmt, args) catch |err| switch (err) { error.NoSpaceLeft => return error.BufferTooSmall, }; + //TODO: should we change one of these return signatures? + //return fbs.getWritten(); return buf[0..fbs.pos]; } @@ -1195,18 +1197,15 @@ test "buffer" { var buf1: [32]u8 = undefined; var fbs = std.io.fixedBufferStream(&buf1); try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth); - var res = buf1[0..fbs.pos]; - std.testing.expect(mem.eql(u8, res, "1234")); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234")); - try fbs.seekTo(0); + fbs.reset(); try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth); - res = buf1[0..fbs.pos]; - std.testing.expect(mem.eql(u8, res, "a")); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "a")); - try fbs.seekTo(0); + fbs.reset(); try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth); - res = buf1[0..fbs.pos]; - std.testing.expect(mem.eql(u8, res, "1100")); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100")); } } @@ -1583,78 +1582,73 @@ test "formatIntValue with comptime_int" { var buf: [20]u8 = undefined; var fbs = std.io.fixedBufferStream(&buf); try formatIntValue(value, "", FormatOptions{}, fbs.outStream()); - std.testing.expect(mem.eql(u8, buf[0..fbs.pos], "123456789123456789")); -} - -// test "formatType max_depth" { -// const Vec2 = struct { -// const SelfType = @This(); -// x: f32, -// y: f32, - -// pub fn format( -// self: SelfType, -// comptime fmt: []const u8, -// options: FormatOptions, -// context: var, -// comptime Errors: type, -// comptime output: fn (@TypeOf(context), []const u8) !void, -// ) !void { -// if (fmt.len == 0) { -// return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); -// } else { -// @compileError("Unknown format string: '" ++ fmt ++ "'"); -// } -// } -// }; -// const E = enum { -// One, -// Two, -// Three, -// }; -// const TU = union(enum) { -// const SelfType = @This(); -// float: f32, -// int: u32, -// ptr: ?*SelfType, -// }; -// const S = struct { -// const SelfType = @This(); -// a: ?*SelfType, -// tu: TU, -// e: E, -// vec: Vec2, -// }; + std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789")); +} -// var inst = S{ -// .a = null, -// .tu = TU{ .ptr = null }, -// .e = E.Two, -// .vec = Vec2{ .x = 10.2, .y = 2.22 }, -// }; -// inst.a = &inst; -// inst.tu.ptr = &inst.tu; - -// var buf0 = std.ArrayList(u8).init(std.testing.allocator); -// defer buf0.deinit(); -// try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 0); -// std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); - -// var buf1 = std.ArrayList(u8).init(std.testing.allocator); -// defer buf1.deinit(); -// try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 1); -// std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); - -// var buf2 = std.ArrayList(u8).init(std.testing.allocator); -// defer buf2.deinit(); -// try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 2); -// std.testing.expect(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); - -// var buf3 = std.ArrayList(u8).init(std.testing.allocator); -// defer buf3.deinit(); -// try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 3); -// std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); -// } +test "formatType max_depth" { + const Vec2 = struct { + const SelfType = @This(); + x: f32, + y: f32, + + pub fn format( + self: SelfType, + comptime fmt: []const u8, + options: FormatOptions, + out_stream: var, + ) !void { + if (fmt.len == 0) { + return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); + } else { + @compileError("Unknown format string: '" ++ fmt ++ "'"); + } + } + }; + const E = enum { + One, + Two, + Three, + }; + const TU = union(enum) { + const SelfType = @This(); + float: f32, + int: u32, + ptr: ?*SelfType, + }; + const S = struct { + const SelfType = @This(); + a: ?*SelfType, + tu: TU, + e: E, + vec: Vec2, + }; + + var inst = S{ + .a = null, + .tu = TU{ .ptr = null }, + .e = E.Two, + .vec = Vec2{ .x = 10.2, .y = 2.22 }, + }; + inst.a = &inst; + inst.tu.ptr = &inst.tu; + + var buf: [1000]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 0); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }")); + + fbs.reset(); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 1); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); + + fbs.reset(); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 2); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); + + fbs.reset(); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 3); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); +} test "positional" { try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); From d2e4aafd64184017dc1f152a4b46eeafca94bbd8 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 15:16:04 -0600 Subject: [PATCH 08/19] Fixup allocPrint --- lib/std/fmtstream.zig | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index c062bbaa1b9b..806ee3d04de0 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -1088,25 +1088,23 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![] return buf[0..fbs.pos]; } -// pub const AllocPrintError = error{OutOfMemory}; - -// pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { -// var size: usize = 0; -// format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; -// const buf = try allocator.alloc(u8, size); -// return bufPrint(buf, fmt, args) catch |err| switch (err) { -// error.BufferTooSmall => unreachable, // we just counted the size above -// }; -// } - -// fn countSize(size: *usize, bytes: []const u8) (error{}!void) { -// size.* += bytes.len; -// } - -// pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { -// const result = try allocPrint(allocator, fmt ++ "\x00", args); -// return result[0 .. result.len - 1 :0]; -// } +pub const AllocPrintError = error{OutOfMemory}; + +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { + // Count the characters we need to preallocate + var counting_stream = std.io.countingOutStream(std.io.null_out_stream); + format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {}; + + const buf = try allocator.alloc(u8, counting_stream.bytes_written); + return bufPrint(buf, fmt, args) catch |err| switch (err) { + error.BufferTooSmall => unreachable, // we just counted the size above + }; +} + +pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { + const result = try allocPrint(allocator, fmt ++ "\x00", args); + return result[0 .. result.len - 1 :0]; +} test "bufPrintInt" { var buffer: [100]u8 = undefined; From 7364e965f473147a86cc62ccf47feac4878a15de Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 15:24:03 -0600 Subject: [PATCH 09/19] Force error coercion of custom formatters --- lib/std/fmtstream.zig | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index 806ee3d04de0..8db0052a761c 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -325,6 +325,10 @@ pub fn formatType( } const T = @TypeOf(value); + if (comptime std.meta.trait.hasFn("format")(T)) { + return try value.format(fmt, options, out_stream); + } + switch (@typeInfo(T)) { .ComptimeInt, .Int, .Float => { return formatValue(value, fmt, options, out_stream); @@ -354,10 +358,6 @@ pub fn formatType( return out_stream.writeAll(@errorName(value)); }, .Enum => |enumInfo| { - if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, out_stream); - } - try out_stream.writeAll(@typeName(T)); if (enumInfo.is_exhaustive) { try out_stream.writeAll("."); @@ -370,10 +370,6 @@ pub fn formatType( } }, .Union => { - if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, out_stream); - } - try out_stream.writeAll(@typeName(T)); if (max_depth == 0) { return out_stream.writeAll("{ ... }"); @@ -394,10 +390,6 @@ pub fn formatType( } }, .Struct => |StructT| { - if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, out_stream); - } - try out_stream.writeAll(@typeName(T)); if (max_depth == 0) { return out_stream.writeAll("{ ... }"); From c11d1055b868add74cc58f3bf745e93110ae6071 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 15:35:18 -0600 Subject: [PATCH 10/19] Integrated outstreams with new formatter --- lib/std/http/headers.zig | 18 ++++++++---------- lib/std/io/out_stream.zig | 2 +- lib/std/math/big/int.zig | 8 +++----- lib/std/net.zig | 22 ++++++++++------------ lib/std/net/test.zig | 4 ++-- lib/std/os/uefi.zig | 10 +++------- 6 files changed, 27 insertions(+), 37 deletions(-) diff --git a/lib/std/http/headers.zig b/lib/std/http/headers.zig index a7a1464f990a..cb232a3e3e3e 100644 --- a/lib/std/http/headers.zig +++ b/lib/std/http/headers.zig @@ -349,16 +349,14 @@ pub const Headers = struct { pub fn format( self: Self, comptime fmt: []const u8, - options: std.fmt.FormatOptions, - context: var, - comptime Errors: type, - output: fn (@TypeOf(context), []const u8) Errors!void, - ) Errors!void { + options: std.fmtstream.FormatOptions, + out_stream: var, + ) !void { for (self.toSlice()) |entry| { - try output(context, entry.name); - try output(context, ": "); - try output(context, entry.value); - try output(context, "\n"); + try out_stream.writeAll(entry.name); + try out_stream.writeAll(": "); + try out_stream.writeAll(entry.value); + try out_stream.writeAll("\n"); } } }; @@ -593,5 +591,5 @@ test "Headers.format" { \\foo: bar \\cookie: somevalue \\ - , try std.fmt.bufPrint(buf[0..], "{}", .{h})); + , try std.fmtstream.bufPrint(buf[0..], "{}", .{h})); } diff --git a/lib/std/io/out_stream.zig b/lib/std/io/out_stream.zig index 03901f6672be..876a3b470132 100644 --- a/lib/std/io/out_stream.zig +++ b/lib/std/io/out_stream.zig @@ -25,7 +25,7 @@ pub fn OutStream( } pub fn print(self: Self, comptime format: []const u8, args: var) Error!void { - return std.fmt.format(self, Error, writeAll, format, args); + return std.fmtstream.format(self, format, args); } pub fn writeByte(self: Self, byte: u8) Error!void { diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 8fda3f647a68..cef2bae98c93 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -518,17 +518,15 @@ pub const Int = struct { pub fn format( self: Int, comptime fmt: []const u8, - options: std.fmt.FormatOptions, - context: var, - comptime FmtError: type, - output: fn (@TypeOf(context), []const u8) FmtError!void, + options: std.fmtstream.FormatOptions, + out_stream: var, ) FmtError!void { self.assertWritable(); // TODO look at fmt and support other bases // TODO support read-only fixed integers const str = self.toString(self.allocator.?, 10) catch @panic("TODO make this non allocating"); defer self.allocator.?.free(str); - return output(context, str); + return out_stream.print(str); } /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. diff --git a/lib/std/net.zig b/lib/std/net.zig index de10a17640c2..9395d34f4883 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -268,16 +268,14 @@ pub const Address = extern union { pub fn format( self: Address, comptime fmt: []const u8, - options: std.fmt.FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + options: std.fmtstream.FormatOptions, + out_stream: var, ) !void { switch (self.any.family) { os.AF_INET => { const port = mem.bigToNative(u16, self.in.port); const bytes = @ptrCast(*const [4]u8, &self.in.addr); - try std.fmt.format(context, Errors, output, "{}.{}.{}.{}:{}", .{ + try std.fmtstream.format(out_stream, "{}.{}.{}.{}:{}", .{ bytes[0], bytes[1], bytes[2], @@ -288,7 +286,7 @@ pub const Address = extern union { os.AF_INET6 => { const port = mem.bigToNative(u16, self.in6.port); if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { - try std.fmt.format(context, Errors, output, "[::ffff:{}.{}.{}.{}]:{}", .{ + try std.fmtstream.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{ self.in6.addr[12], self.in6.addr[13], self.in6.addr[14], @@ -308,30 +306,30 @@ pub const Address = extern union { break :blk buf; }, }; - try output(context, "["); + try out_stream.writeAll("["); var i: usize = 0; var abbrv = false; while (i < native_endian_parts.len) : (i += 1) { if (native_endian_parts[i] == 0) { if (!abbrv) { - try output(context, if (i == 0) "::" else ":"); + try out_stream.writeAll(if (i == 0) "::" else ":"); abbrv = true; } continue; } - try std.fmt.format(context, Errors, output, "{x}", .{native_endian_parts[i]}); + try std.fmtstream.format(out_stream, "{x}", .{native_endian_parts[i]}); if (i != native_endian_parts.len - 1) { - try output(context, ":"); + try out_stream.writeAll(":"); } } - try std.fmt.format(context, Errors, output, "]:{}", .{port}); + try std.fmtstream.format(out_stream, "]:{}", .{port}); }, os.AF_UNIX => { if (!has_unix_sockets) { unreachable; } - try std.fmt.format(context, Errors, output, "{}", .{&self.un.path}); + try std.fmtstream.format(out_stream, "{}", .{&self.un.path}); }, else => unreachable, } diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 087f965c4ee3..0dc3b3020dd6 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -29,7 +29,7 @@ test "parse and render IPv6 addresses" { }; for (ips) |ip, i| { var addr = net.Address.parseIp6(ip, 0) catch unreachable; - var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + var newIp = std.fmtstream.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); } @@ -51,7 +51,7 @@ test "parse and render IPv4 addresses" { "127.0.0.1", }) |ip| { var addr = net.Address.parseIp4(ip, 0) catch unreachable; - var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + var newIp = std.fmtstream.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); } diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 81d13ac1c314..5d9a678ce8b8 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -5,8 +5,6 @@ pub const protocols = @import("uefi/protocols.zig"); pub const status = @import("uefi/status.zig"); pub const tables = @import("uefi/tables.zig"); -const fmt = @import("std").fmt; - /// The EFI image's handle that is passed to its entry point. pub var handle: Handle = undefined; @@ -29,13 +27,11 @@ pub const Guid = extern struct { pub fn format( self: @This(), comptime f: []const u8, - options: fmt.FormatOptions, - context: var, - comptime Errors: type, - output: fn (@TypeOf(context), []const u8) Errors!void, + options: std.fmtstream.FormatOptions, + out_stream: var, ) Errors!void { if (f.len == 0) { - return fmt.format(context, Errors, output, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ + return std.fmtstream.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ self.time_low, self.time_mid, self.time_high_and_version, From 0fbccec000efcfe23188027e474530641daf67cd Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 6 Mar 2020 08:59:21 -0600 Subject: [PATCH 11/19] Convert builtin to fmtstream --- lib/std/builtin.zig | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index fa5b49db37d5..5cac8c962276 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -426,29 +426,27 @@ pub const Version = struct { pub fn parse(text: []const u8) !Version { var it = std.mem.separate(text, "."); return Version{ - .major = try std.fmt.parseInt(u32, it.next() orelse return error.InvalidVersion, 10), - .minor = try std.fmt.parseInt(u32, it.next() orelse "0", 10), - .patch = try std.fmt.parseInt(u32, it.next() orelse "0", 10), + .major = try std.fmtstream.parseInt(u32, it.next() orelse return error.InvalidVersion, 10), + .minor = try std.fmtstream.parseInt(u32, it.next() orelse "0", 10), + .patch = try std.fmtstream.parseInt(u32, it.next() orelse "0", 10), }; } pub fn format( self: Version, comptime fmt: []const u8, - options: std.fmt.FormatOptions, - context: var, - comptime Error: type, - comptime output: fn (@TypeOf(context), []const u8) Error!void, - ) Error!void { + options: std.fmtstream.FormatOptions, + out_stream: var, + ) !void { if (fmt.len == 0) { if (self.patch == 0) { if (self.minor == 0) { - return std.fmt.format(context, Error, output, "{}", .{self.major}); + return std.fmtstream.format(out_stream, "{}", .{self.major}); } else { - return std.fmt.format(context, Error, output, "{}.{}", .{ self.major, self.minor }); + return std.fmtstream.format(out_stream, "{}.{}", .{ self.major, self.minor }); } } else { - return std.fmt.format(context, Error, output, "{}.{}.{}", .{ self.major, self.minor, self.patch }); + return std.fmtstream.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch }); } } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); From 2429fdd73bc2405034805893f169ae58c1c34c56 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 18:59:16 -0600 Subject: [PATCH 12/19] Convert JSON to fmtstream --- lib/std/json.zig | 134 +++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 62 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 4e2440d4e9b5..f3a081107800 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2252,45 +2252,43 @@ pub const StringifyOptions = struct { pub fn stringify( value: var, options: StringifyOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { const T = @TypeOf(value); switch (@typeInfo(T)) { .Float, .ComptimeFloat => { - return std.fmt.formatFloatScientific(value, std.fmt.FormatOptions{}, context, Errors, output); + return std.fmtstream.formatFloatScientific(value, std.fmtstream.FormatOptions{}, out_stream); }, .Int, .ComptimeInt => { - return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, context, Errors, output); + return std.fmtstream.formatIntValue(value, "", std.fmtstream.FormatOptions{}, out_stream); }, .Bool => { - return output(context, if (value) "true" else "false"); + return out_stream.writeAll(if (value) "true" else "false"); }, .Optional => { if (value) |payload| { - return try stringify(payload, options, context, Errors, output); + return try stringify(payload, options, out_stream); } else { - return output(context, "null"); + return out_stream.writeAll("null"); } }, .Enum => { if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, context, Errors, output); + return value.jsonStringify(options, out_stream); } @compileError("Unable to stringify enum '" ++ @typeName(T) ++ "'"); }, .Union => { if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, context, Errors, output); + return value.jsonStringify(options, out_stream); } const info = @typeInfo(T).Union; if (info.tag_type) |UnionTagType| { inline for (info.fields) |u_field| { if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) { - return try stringify(@field(value, u_field.name), options, context, Errors, output); + return try stringify(@field(value, u_field.name), options, out_stream); } } } else { @@ -2299,10 +2297,10 @@ pub fn stringify( }, .Struct => |S| { if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, context, Errors, output); + return value.jsonStringify(options, out_stream); } - try output(context, "{"); + try out_stream.writeAll("{"); comptime var field_output = false; inline for (S.fields) |Field, field_i| { // don't include void fields @@ -2311,39 +2309,39 @@ pub fn stringify( if (!field_output) { field_output = true; } else { - try output(context, ","); + try out_stream.writeAll(","); } - try stringify(Field.name, options, context, Errors, output); - try output(context, ":"); - try stringify(@field(value, Field.name), options, context, Errors, output); + try stringify(Field.name, options, out_stream); + try out_stream.writeAll(":"); + try stringify(@field(value, Field.name), options, out_stream); } - try output(context, "}"); + try out_stream.writeAll("}"); return; }, .Pointer => |ptr_info| switch (ptr_info.size) { .One => { // TODO: avoid loops? - return try stringify(value.*, options, context, Errors, output); + return try stringify(value.*, options, out_stream); }, // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) .Slice => { if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) { - try output(context, "\""); + try out_stream.writeAll("\""); var i: usize = 0; while (i < value.len) : (i += 1) { switch (value[i]) { // normal ascii characters - 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => try output(context, value[i .. i + 1]), + 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => try out_stream.writeAll(value[i .. i + 1]), // control characters with short escapes - '\\' => try output(context, "\\\\"), - '\"' => try output(context, "\\\""), - '/' => try output(context, "\\/"), - 0x8 => try output(context, "\\b"), - 0xC => try output(context, "\\f"), - '\n' => try output(context, "\\n"), - '\r' => try output(context, "\\r"), - '\t' => try output(context, "\\t"), + '\\' => try out_stream.writeAll("\\\\"), + '\"' => try out_stream.writeAll("\\\""), + '/' => try out_stream.writeAll("\\/"), + 0x8 => try out_stream.writeAll("\\b"), + 0xC => try out_stream.writeAll("\\f"), + '\n' => try out_stream.writeAll("\\n"), + '\r' => try out_stream.writeAll("\\r"), + '\t' => try out_stream.writeAll("\\t"), else => { const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; @@ -2351,40 +2349,40 @@ pub fn stringify( // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF), // then it may be represented as a six-character sequence: a reverse solidus, followed // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. - try output(context, "\\u"); - try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output); + try out_stream.writeAll("\\u"); + try std.fmtstream.formatIntValue(codepoint, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream); } else { // To escape an extended character that is not in the Basic Multilingual Plane, // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair. const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800; const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00; - try output(context, "\\u"); - try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output); - try output(context, "\\u"); - try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output); + try out_stream.writeAll("\\u"); + try std.fmtstream.formatIntValue(high, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream); + try out_stream.writeAll("\\u"); + try std.fmtstream.formatIntValue(low, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream); } i += ulen - 1; }, } } - try output(context, "\""); + try out_stream.writeAll("\""); return; } - try output(context, "["); + try out_stream.writeAll("["); for (value) |x, i| { if (i != 0) { - try output(context, ","); + try out_stream.writeAll(","); } - try stringify(x, options, context, Errors, output); + try stringify(x, options, out_stream); } - try output(context, "]"); + try out_stream.writeAll("]"); return; }, else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), }, .Array => |info| { - return try stringify(value[0..], options, context, Errors, output); + return try stringify(value[0..], options, out_stream); }, else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), } @@ -2392,10 +2390,26 @@ pub fn stringify( } fn teststringify(expected: []const u8, value: var) !void { - const TestStringifyContext = struct { + const ValidationOutStream = struct { + const Self = @This(); + pub const OutStream = std.io.OutStream(*Self, Error, write); + pub const Error = error{ + TooMuchData, + DifferentData, + }; + expected_remaining: []const u8, - fn testStringifyWrite(context: *@This(), bytes: []const u8) !void { - if (context.expected_remaining.len < bytes.len) { + + fn init(exp: []const u8) Self { + return .{ .expected_remaining = exp }; + } + + pub fn outStream(self: *Self) OutStream { + return .{ .context = self }; + } + + fn write(self: *Self, bytes: []const u8) Error!usize { + if (self.expected_remaining.len < bytes.len) { std.debug.warn( \\====== expected this output: ========= \\{} @@ -2403,12 +2417,12 @@ fn teststringify(expected: []const u8, value: var) !void { \\{} \\====================================== , .{ - context.expected_remaining, + self.expected_remaining, bytes, }); return error.TooMuchData; } - if (!mem.eql(u8, context.expected_remaining[0..bytes.len], bytes)) { + if (!mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) { std.debug.warn( \\====== expected this output: ========= \\{} @@ -2416,21 +2430,19 @@ fn teststringify(expected: []const u8, value: var) !void { \\{} \\====================================== , .{ - context.expected_remaining[0..bytes.len], + self.expected_remaining[0..bytes.len], bytes, }); return error.DifferentData; } - context.expected_remaining = context.expected_remaining[bytes.len..]; + self.expected_remaining = self.expected_remaining[bytes.len..]; + return bytes.len; } }; - var buf: [100]u8 = undefined; - var context = TestStringifyContext{ .expected_remaining = expected }; - try stringify(value, StringifyOptions{}, &context, error{ - TooMuchData, - DifferentData, - }, TestStringifyContext.testStringifyWrite); - if (context.expected_remaining.len > 0) return error.NotEnoughData; + + var vos = ValidationOutStream.init(expected); + try stringify(value, StringifyOptions{}, vos.outStream()); + if (vos.expected_remaining.len > 0) return error.NotEnoughData; } test "stringify basic types" { @@ -2498,13 +2510,11 @@ test "stringify struct with custom stringifier" { pub fn jsonStringify( value: Self, options: StringifyOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + out_stream: var, ) !void { - try output(context, "[\"something special\","); - try stringify(42, options, context, Errors, output); - try output(context, "]"); + try out_stream.writeAll("[\"something special\","); + try stringify(42, options, out_stream); + try out_stream.writeAll("]"); } }{ .foo = 42 }); } From 8dd078b99a31f49194c07af93b0f555f2c7260dc Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Sat, 29 Feb 2020 21:43:54 -0600 Subject: [PATCH 13/19] Convert Buffer to use fmtstream --- lib/std/buffer.zig | 12 +++--------- lib/std/fmtstream.zig | 13 ++++++++----- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig index 28ce2a561062..93281c35ec49 100644 --- a/lib/std/buffer.zig +++ b/lib/std/buffer.zig @@ -65,15 +65,9 @@ pub const Buffer = struct { } pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Buffer { - const countSize = struct { - fn countSize(size: *usize, bytes: []const u8) (error{}!void) { - size.* += bytes.len; - } - }.countSize; - var size: usize = 0; - std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {}; + const size = std.fmtstream.count(format, args); var self = try Buffer.initSize(allocator, size); - assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); + assert((std.fmtstream.bufPrint(self.list.items, format, args) catch unreachable).len == size); return self; } @@ -155,7 +149,7 @@ pub const Buffer = struct { } pub fn print(self: *Buffer, comptime fmt: []const u8, args: var) !void { - return std.fmt.format(self, error{OutOfMemory}, Buffer.append, fmt, args); + return self.outStream().print(fmt, args); } pub fn outStream(self: *Buffer) std.io.OutStream(*Buffer, error{OutOfMemory}, appendWrite) { diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index 8db0052a761c..237d0014501b 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -1080,14 +1080,17 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![] return buf[0..fbs.pos]; } -pub const AllocPrintError = error{OutOfMemory}; - -pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { - // Count the characters we need to preallocate +// Count the characters needed for format. Useful for preallocating memory +pub fn count(comptime fmt: []const u8, args: var) usize { var counting_stream = std.io.countingOutStream(std.io.null_out_stream); format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {}; + return counting_stream.bytes_written; +} - const buf = try allocator.alloc(u8, counting_stream.bytes_written); +pub const AllocPrintError = error{OutOfMemory}; + +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { + const buf = try allocator.alloc(u8, count(fmt, args)); return bufPrint(buf, fmt, args) catch |err| switch (err) { error.BufferTooSmall => unreachable, // we just counted the size above }; From 4023ed56d4fd7b627aed73428b5fa4be4b7c05ad Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 6 Mar 2020 09:33:12 -0600 Subject: [PATCH 14/19] Convert translate-c to fmtstream --- src-self-hosted/translate_c.zig | 51 +++++++++++++++------------------ 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index f14ebe330611..7b05a76cb7cc 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -89,7 +89,7 @@ const Scope = struct { var proposed_name = name; while (scope.contains(proposed_name)) { scope.mangle_count += 1; - proposed_name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, scope.mangle_count }); + proposed_name = try std.fmtstream.allocPrint(c.a(), "{}_{}", .{ name, scope.mangle_count }); } try scope.variables.push(.{ .name = name, .alias = proposed_name }); return proposed_name; @@ -246,7 +246,7 @@ pub const Context = struct { const line = ZigClangSourceManager_getSpellingLineNumber(c.source_manager, spelling_loc); const column = ZigClangSourceManager_getSpellingColumnNumber(c.source_manager, spelling_loc); - return std.fmt.allocPrint(c.a(), "{}:{}:{}", .{ filename, line, column }); + return std.fmtstream.allocPrint(c.a(), "{}:{}:{}", .{ filename, line, column }); } }; @@ -516,7 +516,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { const arg_name = blk: { const param_prefix = if (is_const) "" else "arg_"; - const bare_arg_name = try std.fmt.allocPrint(c.a(), "{}{}", .{ param_prefix, mangled_param_name }); + const bare_arg_name = try std.fmtstream.allocPrint(c.a(), "{}{}", .{ param_prefix, mangled_param_name }); break :blk try block_scope.makeMangledName(c, bare_arg_name); }; @@ -560,7 +560,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.a(), "{}_{}", .{var_name, c.getMangle()}) else var_name; + const checked_name = if (isZigPrimitiveType(var_name)) try std.fmtstream.allocPrint(c.a(), "{}_{}", .{var_name, c.getMangle()}) else var_name; const var_decl_loc = ZigClangVarDecl_getLocation(var_decl); const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); @@ -620,7 +620,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmt.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmtstream.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -677,7 +677,7 @@ fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_l // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.a(), "{}_{}", .{typedef_name, c.getMangle()}) else typedef_name; + const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmtstream.allocPrint(c.a(), "{}_{}", .{typedef_name, c.getMangle()}) else typedef_name; if (mem.eql(u8, checked_name, "uint8_t")) return transTypeDefAsBuiltin(c, typedef_decl, "u8") @@ -738,7 +738,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* // Record declarations such as `struct {...} x` have no name but they're not // anonymous hence here isAnonymousStructOrUnion is not needed if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmtstream.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); is_unnamed = true; } @@ -755,7 +755,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* return null; } - const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); + const name = try std.fmtstream.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name); const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); @@ -812,7 +812,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* var is_anon = false; var raw_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl))); if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { - raw_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + raw_name = try std.fmtstream.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); is_anon = true; } const field_name = try appendIdentifier(c, raw_name); @@ -882,11 +882,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_decl))); var is_unnamed = false; if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmtstream.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); is_unnamed = true; } - const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name}); + const name = try std.fmtstream.allocPrint(c.a(), "enum_{}", .{bare_name}); _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name); const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); node.eq_token = try appendToken(c, .Equal, "="); @@ -1754,9 +1754,9 @@ fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 { // Handle the remaining escapes Zig doesn't support by turning them // into their respective hex representation if (std.ascii.isCntrl(c)) - return std.fmt.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable + return std.fmtstream.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable else - return std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable; + return std.fmtstream.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable; }, }; } @@ -2436,7 +2436,7 @@ fn transCase( ) TransError!*ast.Node { const block_scope = scope.findBlockScope(rp.c) catch unreachable; const switch_scope = scope.getSwitch(); - const label = try std.fmt.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)}); + const label = try std.fmtstream.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)}); _ = try appendToken(rp.c, .Semicolon, ";"); const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: { @@ -4607,7 +4607,7 @@ fn finishTransFnProto( _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmt.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmtstream.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -4752,15 +4752,10 @@ fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenInd } fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: var) !ast.TokenIndex { - const S = struct { - fn callback(context: *Context, bytes: []const u8) error{OutOfMemory}!void { - return context.source_buffer.append(bytes); - } - }; const start_index = c.source_buffer.len(); errdefer c.source_buffer.shrink(start_index); - try std.fmt.format(c, error{OutOfMemory}, S.callback, format, args); + try c.source_buffer.print(format, args); const end_index = c.source_buffer.len(); const token_index = c.tree.tokens.len; const new_token = try c.tree.tokens.addOne(); @@ -4871,7 +4866,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { const name = try c.str(raw_name); // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.a(), "{}_{}", .{name, c.getMangle()}) else name; + const mangled_name = if (isZigPrimitiveType(name)) try std.fmtstream.allocPrint(c.a(), "{}_{}", .{name, c.getMangle()}) else name; if (scope.containsNow(mangled_name)) { continue; } @@ -5156,11 +5151,11 @@ fn parseCNumLit(c: *Context, tok: *CToken, source: []const u8, source_loc: ZigCl switch (lit_bytes[1]) { '0'...'7' => { // Octal - lit_bytes = try std.fmt.allocPrint(c.a(), "0o{}", .{lit_bytes}); + lit_bytes = try std.fmtstream.allocPrint(c.a(), "0o{}", .{lit_bytes}); }, 'X' => { // Hexadecimal with capital X, valid in C but not in Zig - lit_bytes = try std.fmt.allocPrint(c.a(), "0x{}", .{lit_bytes[2..]}); + lit_bytes = try std.fmtstream.allocPrint(c.a(), "0x{}", .{lit_bytes[2..]}); }, else => {}, } @@ -5191,7 +5186,7 @@ fn parseCNumLit(c: *Context, tok: *CToken, source: []const u8, source_loc: ZigCl return &cast_node.base; } else if (tok.id == .FloatLiteral) { if (lit_bytes[0] == '.') - lit_bytes = try std.fmt.allocPrint(c.a(), "0{}", .{lit_bytes}); + lit_bytes = try std.fmtstream.allocPrint(c.a(), "0{}", .{lit_bytes}); if (tok.id.FloatLiteral == .None) { return transCreateNodeFloat(c, lit_bytes); } @@ -5324,7 +5319,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const num += c - 'A' + 10; }, else => { - i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmtstream.formatIntBuf(bytes[i..], num, 16, false, std.fmtstream.FormatOptions{ .fill = '0', .width = 2 }); num = 0; if (c == '\\') state = .Escape @@ -5350,7 +5345,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const }; num += c - '0'; } else { - i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmtstream.formatIntBuf(bytes[i..], num, 16, false, std.fmtstream.FormatOptions{ .fill = '0', .width = 2 }); num = 0; count = 0; if (c == '\\') @@ -5364,7 +5359,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const } } if (state == .Hex or state == .Octal) - i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmtstream.formatIntBuf(bytes[i..], num, 16, false, std.fmtstream.FormatOptions{ .fill = '0', .width = 2 }); return bytes[0..i]; } From 0059d9ee3e7298df03723d97adbea72ff142cacd Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 6 Mar 2020 09:47:42 -0600 Subject: [PATCH 15/19] Convert fmt.bufPrint / fmt.allocPrint --- lib/std/atomic/queue.zig | 4 ++-- lib/std/net.zig | 2 +- lib/std/os.zig | 2 +- lib/std/progress.zig | 6 +++--- lib/std/special/build_runner.zig | 6 +++--- lib/std/target.zig | 4 ++-- lib/std/zig/cross_target.zig | 2 +- lib/std/zig/system.zig | 6 +++--- src-self-hosted/compilation.zig | 6 +++--- src-self-hosted/dep_tokenizer.zig | 2 +- src-self-hosted/libc_installation.zig | 2 +- src-self-hosted/link.zig | 20 ++++++++++---------- src-self-hosted/print_targets.zig | 2 +- src-self-hosted/test.zig | 6 +++--- src-self-hosted/type.zig | 8 ++++---- test/src/compare_output.zig | 8 ++++---- test/src/run_translated_c.zig | 4 ++-- test/src/translate_c.zig | 4 ++-- test/tests.zig | 10 +++++----- tools/process_headers.zig | 4 ++-- tools/update_glibc.zig | 4 ++-- 21 files changed, 56 insertions(+), 56 deletions(-) diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig index 1a0f39587e96..c308fa681f8d 100644 --- a/lib/std/atomic/queue.zig +++ b/lib/std/atomic/queue.zig @@ -348,7 +348,7 @@ test "std.atomic.Queue dump" { fbs.reset(); try queue.dumpToStream(fbs.outStream()); - var expected = try std.fmt.bufPrint(expected_buffer[0..], + var expected = try std.fmtstream.bufPrint(expected_buffer[0..], \\head: 0x{x}=1 \\ (null) \\tail: 0x{x}=1 @@ -368,7 +368,7 @@ test "std.atomic.Queue dump" { fbs.reset(); try queue.dumpToStream(fbs.outStream()); - expected = try std.fmt.bufPrint(expected_buffer[0..], + expected = try std.fmtstream.bufPrint(expected_buffer[0..], \\head: 0x{x}=1 \\ 0x{x}=2 \\ (null) diff --git a/lib/std/net.zig b/lib/std/net.zig index 9395d34f4883..a1225654f014 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -438,7 +438,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* const name_c = try std.cstr.addNullByte(allocator, name); defer allocator.free(name_c); - const port_c = try std.fmt.allocPrint(allocator, "{}\x00", .{port}); + const port_c = try std.fmtstream.allocPrint(allocator, "{}\x00", .{port}); defer allocator.free(port_c); const hints = os.addrinfo{ diff --git a/lib/std/os.zig b/lib/std/os.zig index 76a5dc2be55c..baea4cecc593 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3049,7 +3049,7 @@ pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP defer close(fd); var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; - const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; + const proc_path = std.fmtstream.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; return readlinkC(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer); } diff --git a/lib/std/progress.zig b/lib/std/progress.zig index 0264d99c91a8..ebb983cdc622 100644 --- a/lib/std/progress.zig +++ b/lib/std/progress.zig @@ -130,11 +130,11 @@ pub const Progress = struct { var end: usize = 0; if (self.columns_written > 0) { // restore cursor position - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{}D", .{self.columns_written}) catch unreachable).len; + end += (std.fmtstream.bufPrint(self.output_buffer[end..], "\x1b[{}D", .{self.columns_written}) catch unreachable).len; self.columns_written = 0; // clear rest of line - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; + end += (std.fmtstream.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; } if (!self.done) { @@ -185,7 +185,7 @@ pub const Progress = struct { } fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: var) void { - if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { + if (std.fmtstream.bufPrint(self.output_buffer[end.*..], format, args)) |written| { const amt = written.len; end.* += amt; self.columns_written += amt; diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index 974247e2a1b7..c5a96df667fd 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -2,7 +2,7 @@ const root = @import("@build"); const std = @import("std"); const builtin = @import("builtin"); const io = std.io; -const fmt = std.fmt; +const fmtstream = std.fmtstream; const Builder = std.build.Builder; const mem = std.mem; const process = std.process; @@ -153,7 +153,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { const allocator = builder.allocator; for (builder.top_level_steps.toSliceConst()) |top_level_step| { const name = if (&top_level_step.step == builder.default_step) - try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) + try fmtstream.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) else top_level_step.step.name; try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description }); @@ -175,7 +175,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { try out_stream.print(" (none)\n", .{}); } else { for (builder.available_options_list.toSliceConst()) |option| { - const name = try fmt.allocPrint(allocator, " -D{}=[{}]", .{ + const name = try fmtstream.allocPrint(allocator, " -D{}=[{}]", .{ option.name, Builder.typeIdName(option.type_id), }); diff --git a/lib/std/target.zig b/lib/std/target.zig index 8beb7c3011db..c117f60cc04f 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -972,7 +972,7 @@ pub const Target = struct { } pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![:0]u8 { - return std.fmt.allocPrint0(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); + return std.fmtstream.allocPrint0(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); } pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { @@ -1158,7 +1158,7 @@ pub const Target = struct { var result: DynamicLinker = .{}; const S = struct { fn print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker { - r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1); + r.max_byte = @intCast(u8, (std.fmtstream.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1); return r.*; } fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker { diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index cec3ea05e5ce..a60833f93cf3 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -573,7 +573,7 @@ pub const CrossTarget = struct { .Dynamic => "", }; - return std.fmt.allocPrint0(allocator, "{}-{}{}", .{ arch, os, static_suffix }); + return std.fmtstream.allocPrint0(allocator, "{}-{}{}", .{ arch, os, static_suffix }); } pub const Executor = union(enum) { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 558b50b5b385..875ecd424c10 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -130,7 +130,7 @@ pub const NativePaths = struct { } pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void { - const item = try std.fmt.allocPrint0(self.include_dirs.allocator, fmt, args); + const item = try std.fmtstream.allocPrint0(self.include_dirs.allocator, fmt, args); errdefer self.include_dirs.allocator.free(item); try self.include_dirs.append(item); } @@ -140,7 +140,7 @@ pub const NativePaths = struct { } pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void { - const item = try std.fmt.allocPrint0(self.lib_dirs.allocator, fmt, args); + const item = try std.fmtstream.allocPrint0(self.lib_dirs.allocator, fmt, args); errdefer self.lib_dirs.allocator.free(item); try self.lib_dirs.append(item); } @@ -150,7 +150,7 @@ pub const NativePaths = struct { } pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void { - const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args); + const item = try std.fmtstream.allocPrint0(self.warnings.allocator, fmt, args); errdefer self.warnings.allocator.free(item); try self.warnings.append(item); } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 7a45bb3c37f1..fdaee7d12ff9 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -1051,7 +1051,7 @@ pub const Compilation = struct { } fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: var) !void { - const text = try std.fmt.allocPrint(self.gpa(), fmt, args); + const text = try std.fmtstream.allocPrint(self.gpa(), fmt, args); errdefer self.gpa().free(text); const msg = try Msg.createFromScope(self, tree_scope, span, text); @@ -1061,7 +1061,7 @@ pub const Compilation = struct { } fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void { - const text = try std.fmt.allocPrint(self.gpa(), fmt, args); + const text = try std.fmtstream.allocPrint(self.gpa(), fmt, args); errdefer self.gpa().free(text); const msg = try Msg.createFromCli(self, realpath, text); @@ -1154,7 +1154,7 @@ pub const Compilation = struct { const tmp_dir = try self.getTmpDir(); const file_prefix = self.getRandomFileName(); - const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix }); + const file_name = try std.fmtstream.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix }); defer self.gpa().free(file_name); const full_path = try fs.path.join(self.gpa(), &[_][]const u8{ tmp_dir, file_name[0..] }); diff --git a/src-self-hosted/dep_tokenizer.zig b/src-self-hosted/dep_tokenizer.zig index 5c250cdb994b..11724a3d2cb6 100644 --- a/src-self-hosted/dep_tokenizer.zig +++ b/src-self-hosted/dep_tokenizer.zig @@ -894,7 +894,7 @@ fn printSection(out: var, label: []const u8, bytes: []const u8) !void { fn printLabel(out: var, label: []const u8, bytes: []const u8) !void { var buf: [80]u8 = undefined; - var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len }); + var text = try std.fmtstream.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len }); try out.write(text); var i: usize = text.len; const end = 79; diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 0f9736456a0e..3b65fb172bdb 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -543,7 +543,7 @@ fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 { const allocator = args.allocator; const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe; - const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename}); + const arg1 = try std.fmtstream.allocPrint(allocator, "-print-file-name={}", .{args.search_basename}); defer allocator.free(arg1); const argv = [_][]const u8{ cc_exe, arg1 }; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 1efa15574a78..b63e6c24f472 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -296,13 +296,13 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { const is_library = ctx.comp.kind == .Lib; - const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", .{ctx.out_file_path.toSliceConst()}); + const out_arg = try std.fmtstream.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", .{ctx.out_file_path.toSliceConst()}); try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr)); if (ctx.comp.haveLibC()) { - try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr)); - try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr)); - try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmtstream.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmtstream.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmtstream.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr)); } if (ctx.link_in_crt) { @@ -310,20 +310,20 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { const d_str = if (ctx.comp.build_mode == .Debug) "d" else ""; if (ctx.comp.is_static) { - const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str}); + const cmt_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str}); try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr)); } else { - const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str}); + const msvcrt_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str}); try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr)); } - const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{ + const vcruntime_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{ lib_str, d_str, }); try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr)); - const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str }); + const crt_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str }); try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr)); // Visual C++ 2015 Conformance Changes @@ -383,7 +383,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { .IPhoneOS => try ctx.args.append("-iphoneos_version_min"), .IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"), } - const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{ + const ver_str = try std.fmtstream.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{ platform.major, platform.minor, platform.micro, @@ -445,7 +445,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { try ctx.args.append("-lSystem"); } else { if (mem.indexOfScalar(u8, lib.name, '/') == null) { - const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name}); + const arg = try std.fmtstream.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name}); try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); } else { const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig index ad506425d2af..408c39fe7b93 100644 --- a/src-self-hosted/print_targets.zig +++ b/src-self-hosted/print_targets.zig @@ -138,7 +138,7 @@ pub fn cmdTargets( for (available_glibcs) |glibc| { try jws.arrayElem(); - const tmp = try std.fmt.allocPrint(allocator, "{}", .{glibc}); + const tmp = try std.fmtstream.allocPrint(allocator, "{}", .{glibc}); defer allocator.free(tmp); try jws.emitString(tmp); } diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index e87164c9fb47..98e9fb06dfc9 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -81,7 +81,7 @@ pub const TestContext = struct { msg: []const u8, ) !void { var file_index_buf: [20]u8 = undefined; - const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); + const file_index = try std.fmtstream.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); if (std.fs.path.dirname(file1_path)) |dirname| { @@ -114,10 +114,10 @@ pub const TestContext = struct { expected_output: []const u8, ) !void { var file_index_buf: [20]u8 = undefined; - const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); + const file_index = try std.fmtstream.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); - const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); + const output_file = try std.fmtstream.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); if (std.fs.path.dirname(file1_path)) |dirname| { try std.fs.cwd().makePath(dirname); } diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 70ed754cea75..c2b406823ddc 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -581,7 +581,7 @@ pub const Type = struct { errdefer comp.gpa().destroy(self); const u_or_i = "ui"[@boolToInt(key.is_signed)]; - const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count }); + const name = try std.fmtstream.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count }); errdefer comp.gpa().free(name); self.base.init(comp, .Int, name); @@ -764,13 +764,13 @@ pub const Type = struct { .Non => "", }; const name = switch (self.key.alignment) { - .Abi => try std.fmt.allocPrint(comp.gpa(), "{}{}{}{}", .{ + .Abi => try std.fmtstream.allocPrint(comp.gpa(), "{}{}{}{}", .{ size_str, mut_str, vol_str, self.key.child_type.name, }), - .Override => |alignment| try std.fmt.allocPrint(comp.gpa(), "{}align<{}> {}{}{}", .{ + .Override => |alignment| try std.fmtstream.allocPrint(comp.gpa(), "{}align<{}> {}{}{}", .{ size_str, alignment, mut_str, @@ -845,7 +845,7 @@ pub const Type = struct { }; errdefer comp.gpa().destroy(self); - const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", .{ key.len, key.elem_type.name }); + const name = try std.fmtstream.allocPrint(comp.gpa(), "[{}]{}", .{ key.len, key.elem_type.name }); errdefer comp.gpa().free(name); self.base.init(comp, .Array, name); diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig index ae994a0697d5..c68eb1eebf61 100644 --- a/test/src/compare_output.zig +++ b/test/src/compare_output.zig @@ -4,7 +4,7 @@ const std = @import("std"); const builtin = std.builtin; const build = std.build; const ArrayList = std.ArrayList; -const fmt = std.fmt; +const fmtstream = std.fmtstream; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; @@ -97,7 +97,7 @@ pub const CompareOutputContext = struct { switch (case.special) { Special.Asm => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "assemble-and-link {}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -116,7 +116,7 @@ pub const CompareOutputContext = struct { }, Special.None => { for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "{} {} ({})", .{ "compare-output", case.name, @tagName(mode), @@ -141,7 +141,7 @@ pub const CompareOutputContext = struct { } }, Special.RuntimeSafety => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig index 14b0ce593c59..6304137e4a6c 100644 --- a/test/src/run_translated_c.zig +++ b/test/src/run_translated_c.zig @@ -3,7 +3,7 @@ const std = @import("std"); const build = std.build; const ArrayList = std.ArrayList; -const fmt = std.fmt; +const fmtstream = std.fmtstream; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; @@ -76,7 +76,7 @@ pub const RunTranslatedCContext = struct { pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 9a6bd0d32344..250ec6175ee9 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -3,7 +3,7 @@ const std = @import("std"); const build = std.build; const ArrayList = std.ArrayList; -const fmt = std.fmt; +const fmtstream = std.fmtstream; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; @@ -99,7 +99,7 @@ pub const TranslateCContext = struct { const b = self.b; const translate_c_cmd = "translate-c"; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/tests.zig b/test/tests.zig index 22dad10e3e46..61f694bedb6f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -8,7 +8,7 @@ const Buffer = std.Buffer; const io = std.io; const fs = std.fs; const mem = std.mem; -const fmt = std.fmt; +const fmtstream = std.fmtstream; const ArrayList = std.ArrayList; const Mode = builtin.Mode; const LibExeObjStep = build.LibExeObjStep; @@ -484,7 +484,7 @@ pub const StackTracesContext = struct { const expect_for_mode = expect[@enumToInt(mode)]; if (expect_for_mode.len == 0) continue; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "{} {} ({})", .{ "stack-trace", name, @tagName(mode), @@ -943,7 +943,7 @@ pub const CompileErrorContext = struct { pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {}", .{ + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "compile-error {}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -1009,7 +1009,7 @@ pub const StandaloneContext = struct { const b = self.b; for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", .{ + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "build {} ({})", .{ root_src, @tagName(mode), }) catch unreachable; @@ -1152,7 +1152,7 @@ pub const GenHContext = struct { const b = self.b; const mode = builtin.Mode.Debug; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; + const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/tools/process_headers.zig b/tools/process_headers.zig index abdc9fabf944..43950750e9e4 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -299,7 +299,7 @@ pub fn main() !void { std.debug.warn("unrecognized C ABI: {}\n", .{abi_name}); usageAndExit(args[0]); }; - const generic_name = try std.fmt.allocPrint(allocator, "generic-{}", .{abi_name}); + const generic_name = try std.fmtstream.allocPrint(allocator, "generic-{}", .{abi_name}); // TODO compiler crashed when I wrote this the canonical way var libc_targets: []const LibCTarget = undefined; @@ -440,7 +440,7 @@ pub fn main() !void { .specific => |a| @tagName(a), else => @tagName(dest_target.arch), }; - const out_subpath = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + const out_subpath = try std.fmtstream.allocPrint(allocator, "{}-{}-{}", .{ arch_name, @tagName(dest_target.os), @tagName(dest_target.abi), diff --git a/tools/update_glibc.zig b/tools/update_glibc.zig index 84522aabe450..35351e4d2a3f 100644 --- a/tools/update_glibc.zig +++ b/tools/update_glibc.zig @@ -1,6 +1,6 @@ const std = @import("std"); const fs = std.fs; -const fmt = std.fmt; +const fmtstream = std.fmtstream; const assert = std.debug.assert; // Example abilist path: @@ -154,7 +154,7 @@ pub fn main() !void { const fn_set = &target_funcs_gop.kv.value.list; for (lib_names) |lib_name, lib_name_index| { - const basename = try fmt.allocPrint(allocator, "lib{}.abilist", .{lib_name}); + const basename = try fmtstream.allocPrint(allocator, "lib{}.abilist", .{lib_name}); const abi_list_filename = blk: { if (abi_list.targets[0].abi == .gnuabi64 and std.mem.eql(u8, lib_name, "c")) { break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "n64", basename }); From 6a53fe7c93ddc6216e7cd41514bebe701531c9c3 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 6 Mar 2020 12:03:15 -0600 Subject: [PATCH 16/19] Handle potential downcast when translating stream size --- lib/std/buffer.zig | 4 +++- lib/std/fmtstream.zig | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig index 93281c35ec49..6d361bdb4ae1 100644 --- a/lib/std/buffer.zig +++ b/lib/std/buffer.zig @@ -65,7 +65,9 @@ pub const Buffer = struct { } pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Buffer { - const size = std.fmtstream.count(format, args); + const size = std.fmtstream.count(format, args) catch |err| switch (err) { + error.Overflow => return error.OutOfMemory, + }; var self = try Buffer.initSize(allocator, size); assert((std.fmtstream.bufPrint(self.list.items, format, args) catch unreachable).len == size); return self; diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig index 237d0014501b..93734d42aa33 100644 --- a/lib/std/fmtstream.zig +++ b/lib/std/fmtstream.zig @@ -1081,16 +1081,20 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![] } // Count the characters needed for format. Useful for preallocating memory -pub fn count(comptime fmt: []const u8, args: var) usize { +pub fn count(comptime fmt: []const u8, args: var) !usize { var counting_stream = std.io.countingOutStream(std.io.null_out_stream); format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {}; - return counting_stream.bytes_written; + return std.math.cast(usize, counting_stream.bytes_written); } pub const AllocPrintError = error{OutOfMemory}; pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { - const buf = try allocator.alloc(u8, count(fmt, args)); + const size = count(fmt, args) catch |err| switch (err) { + // Output too long. Can't possibly allocate enough memory to display it. + error.Overflow => return error.OutOfMemory, + }; + const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args) catch |err| switch (err) { error.BufferTooSmall => unreachable, // we just counted the size above }; From 786216ca5a7cc992d1be895702d37798bc85bf14 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 6 Mar 2020 16:52:46 -0600 Subject: [PATCH 17/19] Slap in workaround for Fifo --- lib/std/fifo.zig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index 85a0e4c9d265..65ddc65d3c09 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -294,7 +294,19 @@ pub fn LinearFifo( pub usingnamespace if (T == u8) struct { pub fn print(self: *Self, comptime format: []const u8, args: var) !void { - return std.fmt.format(self, error{OutOfMemory}, Self.write, format, args); + // TODO: maybe expose this stream as a method? + const FifoStream = struct { + const OutStream = std.io.OutStream(*Self, Error, write); + const Error = error{OutOfMemory}; + + fn write(fifo: *Self, bytes: []const u8) Error!usize { + try fifo.write(bytes); + return bytes.len; + } + }; + + var out_stream = FifoStream.OutStream{ .context = self }; + try out_stream.print(format, args); } } else From ed7f30e1cd0c00c82c511ad826fe8d8b60b2f57f Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 6 Mar 2020 16:45:42 -0600 Subject: [PATCH 18/19] Migrate last vestiges of fmt --- src-self-hosted/dep_tokenizer.zig | 26 ++++++++++------------ test/stage1/behavior/enum_with_members.zig | 6 ++--- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src-self-hosted/dep_tokenizer.zig b/src-self-hosted/dep_tokenizer.zig index 11724a3d2cb6..233b6781719f 100644 --- a/src-self-hosted/dep_tokenizer.zig +++ b/src-self-hosted/dep_tokenizer.zig @@ -306,12 +306,12 @@ pub const Tokenizer = struct { fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: var) Error { var buffer = try std.Buffer.initSize(&self.arena.allocator, 0); - std.fmt.format(&buffer, anyerror, std.Buffer.append, fmt, args) catch {}; + try buffer.print(fmt, args); try buffer.append(" '"); var out = makeOutput(std.Buffer.append, &buffer); try printCharValues(&out, bytes); try buffer.append("'"); - std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", .{position - (bytes.len - 1)}) catch {}; + try buffer.print(" at position {}", .{position - (bytes.len - 1)}); self.error_text = buffer.toSlice(); return Error.InvalidInput; } @@ -319,10 +319,9 @@ pub const Tokenizer = struct { fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: var) Error { var buffer = try std.Buffer.initSize(&self.arena.allocator, 0); try buffer.append("illegal char "); - var out = makeOutput(std.Buffer.append, &buffer); - try printUnderstandableChar(&out, char); - std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", .{position}) catch {}; - if (fmt.len != 0) std.fmt.format(&buffer, anyerror, std.Buffer.append, ": " ++ fmt, args) catch {}; + try printUnderstandableChar(&buffer, char); + try buffer.print(" at position {}", .{position}); + if (fmt.len != 0) try buffer.print(": " ++ fmt, args); self.error_text = buffer.toSlice(); return Error.InvalidInput; } @@ -980,13 +979,13 @@ fn hexDump16(out: var, offset: usize, bytes: []const u8) !void { fn printDecValue(out: var, value: u64, width: u8) !void { var buffer: [20]u8 = undefined; - const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width); + const len = std.fmtstream.formatIntBuf(buffer[0..], value, 10, false, width); try out.write(buffer[0..len]); } fn printHexValue(out: var, value: u64, width: u8) !void { var buffer: [16]u8 = undefined; - const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width); + const len = std.fmtstream.formatIntBuf(buffer[0..], value, 16, false, width); try out.write(buffer[0..len]); } @@ -996,14 +995,13 @@ fn printCharValues(out: var, bytes: []const u8) !void { } } -fn printUnderstandableChar(out: var, char: u8) !void { +fn printUnderstandableChar(buffer: *std.Buffer, char: u8) !void { if (!std.ascii.isPrint(char) or char == ' ') { - const output = @typeInfo(@TypeOf(out)).Pointer.child.output; - std.fmt.format(out.context, anyerror, output, "\\x{X:2}", .{char}) catch {}; + try buffer.print("\\x{X:2}", .{char}); } else { - try out.write("'"); - try out.write(&[_]u8{printable_char_tab[char]}); - try out.write("'"); + try buffer.append("'"); + try buffer.appendByte(printable_char_tab[char]); + try buffer.append("'"); } } diff --git a/test/stage1/behavior/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig index 08b195494b54..b299301ced01 100644 --- a/test/stage1/behavior/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -1,6 +1,6 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; -const fmt = @import("std").fmt; +const fmtstream = @import("std").fmtstream; const ET = union(enum) { SINT: i32, @@ -8,8 +8,8 @@ const ET = union(enum) { pub fn print(a: *const ET, buf: []u8) anyerror!usize { return switch (a.*) { - ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}), - ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}), + ET.SINT => |x| fmtstream.formatIntBuf(buf, x, 10, false, fmtstream.FormatOptions{}), + ET.UINT => |x| fmtstream.formatIntBuf(buf, x, 10, false, fmtstream.FormatOptions{}), }; } }; From 4aae55b4ccf44fa3c2c2a81a6a34f3c898dece30 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Fri, 6 Mar 2020 16:59:21 -0600 Subject: [PATCH 19/19] Replace fmt with new fmtstream --- lib/std/atomic/queue.zig | 4 +- lib/std/buffer.zig | 4 +- lib/std/builtin.zig | 14 +- lib/std/fmt.zig | 519 +++--- lib/std/fmtstream.zig | 1685 -------------------- lib/std/http/headers.zig | 4 +- lib/std/io/out_stream.zig | 2 +- lib/std/json.zig | 10 +- lib/std/math/big/int.zig | 2 +- lib/std/net.zig | 14 +- lib/std/net/test.zig | 4 +- lib/std/os.zig | 2 +- lib/std/os/uefi.zig | 4 +- lib/std/progress.zig | 6 +- lib/std/special/build_runner.zig | 6 +- lib/std/std.zig | 1 - lib/std/target.zig | 4 +- lib/std/zig/cross_target.zig | 2 +- lib/std/zig/system.zig | 6 +- src-self-hosted/compilation.zig | 6 +- src-self-hosted/dep_tokenizer.zig | 6 +- src-self-hosted/libc_installation.zig | 2 +- src-self-hosted/link.zig | 20 +- src-self-hosted/print_targets.zig | 2 +- src-self-hosted/test.zig | 6 +- src-self-hosted/translate_c.zig | 44 +- src-self-hosted/type.zig | 8 +- test/src/compare_output.zig | 8 +- test/src/run_translated_c.zig | 4 +- test/src/translate_c.zig | 4 +- test/stage1/behavior/enum_with_members.zig | 6 +- test/tests.zig | 10 +- tools/process_headers.zig | 4 +- tools/update_glibc.zig | 4 +- 34 files changed, 336 insertions(+), 2091 deletions(-) delete mode 100644 lib/std/fmtstream.zig diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig index c308fa681f8d..1a0f39587e96 100644 --- a/lib/std/atomic/queue.zig +++ b/lib/std/atomic/queue.zig @@ -348,7 +348,7 @@ test "std.atomic.Queue dump" { fbs.reset(); try queue.dumpToStream(fbs.outStream()); - var expected = try std.fmtstream.bufPrint(expected_buffer[0..], + var expected = try std.fmt.bufPrint(expected_buffer[0..], \\head: 0x{x}=1 \\ (null) \\tail: 0x{x}=1 @@ -368,7 +368,7 @@ test "std.atomic.Queue dump" { fbs.reset(); try queue.dumpToStream(fbs.outStream()); - expected = try std.fmtstream.bufPrint(expected_buffer[0..], + expected = try std.fmt.bufPrint(expected_buffer[0..], \\head: 0x{x}=1 \\ 0x{x}=2 \\ (null) diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig index 6d361bdb4ae1..cf028f104ea4 100644 --- a/lib/std/buffer.zig +++ b/lib/std/buffer.zig @@ -65,11 +65,11 @@ pub const Buffer = struct { } pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Buffer { - const size = std.fmtstream.count(format, args) catch |err| switch (err) { + const size = std.fmt.count(format, args) catch |err| switch (err) { error.Overflow => return error.OutOfMemory, }; var self = try Buffer.initSize(allocator, size); - assert((std.fmtstream.bufPrint(self.list.items, format, args) catch unreachable).len == size); + assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); return self; } diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 5cac8c962276..c8cd6c05cf7b 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -426,27 +426,27 @@ pub const Version = struct { pub fn parse(text: []const u8) !Version { var it = std.mem.separate(text, "."); return Version{ - .major = try std.fmtstream.parseInt(u32, it.next() orelse return error.InvalidVersion, 10), - .minor = try std.fmtstream.parseInt(u32, it.next() orelse "0", 10), - .patch = try std.fmtstream.parseInt(u32, it.next() orelse "0", 10), + .major = try std.fmt.parseInt(u32, it.next() orelse return error.InvalidVersion, 10), + .minor = try std.fmt.parseInt(u32, it.next() orelse "0", 10), + .patch = try std.fmt.parseInt(u32, it.next() orelse "0", 10), }; } pub fn format( self: Version, comptime fmt: []const u8, - options: std.fmtstream.FormatOptions, + options: std.fmt.FormatOptions, out_stream: var, ) !void { if (fmt.len == 0) { if (self.patch == 0) { if (self.minor == 0) { - return std.fmtstream.format(out_stream, "{}", .{self.major}); + return std.fmt.format(out_stream, "{}", .{self.major}); } else { - return std.fmtstream.format(out_stream, "{}.{}", .{ self.major, self.minor }); + return std.fmt.format(out_stream, "{}.{}", .{ self.major, self.minor }); } } else { - return std.fmtstream.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch }); + return std.fmt.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch }); } } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index a7525ba79545..5173015b38d0 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -69,19 +69,17 @@ fn peekIsAlign(comptime fmt: []const u8) bool { /// /// If a formatted user type contains a function of the type /// ``` -/// fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, context: var, comptime Errors: type, comptime output: fn (@TypeOf(context), []const u8) Errors!void) Errors!void +/// fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void /// ``` /// with `?` being the type formatted, this function will be called instead of the default implementation. /// This allows user types to be formatted in a logical manner instead of dumping all fields of the type. /// /// A user type may be a `struct`, `vector`, `union` or `enum` type. pub fn format( - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + out_stream: var, comptime fmt: []const u8, args: var, -) Errors!void { +) !void { const ArgSetType = u32; if (@typeInfo(@TypeOf(args)) != .Struct) { @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); @@ -138,7 +136,7 @@ pub fn format( .Start => switch (c) { '{' => { if (start_index < i) { - try output(context, fmt[start_index..i]); + try out_stream.writeAll(fmt[start_index..i]); } start_index = i; @@ -150,7 +148,7 @@ pub fn format( }, '}' => { if (start_index < i) { - try output(context, fmt[start_index..i]); + try out_stream.writeAll(fmt[start_index..i]); } state = .CloseBrace; }, @@ -185,9 +183,7 @@ pub fn format( args[arg_to_print], fmt[0..0], options, - context, - Errors, - output, + out_stream, default_max_depth, ); @@ -218,9 +214,7 @@ pub fn format( args[arg_to_print], fmt[specifier_start..i], options, - context, - Errors, - output, + out_stream, default_max_depth, ); state = .Start; @@ -265,9 +259,7 @@ pub fn format( args[arg_to_print], fmt[specifier_start..specifier_end], options, - context, - Errors, - output, + out_stream, default_max_depth, ); state = .Start; @@ -293,9 +285,7 @@ pub fn format( args[arg_to_print], fmt[specifier_start..specifier_end], options, - context, - Errors, - output, + out_stream, default_max_depth, ); state = .Start; @@ -316,7 +306,7 @@ pub fn format( } } if (start_index < fmt.len) { - try output(context, fmt[start_index..]); + try out_stream.writeAll(fmt[start_index..]); } } @@ -324,141 +314,131 @@ pub fn formatType( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + out_stream: var, max_depth: usize, -) Errors!void { +) @TypeOf(out_stream).Error!void { if (comptime std.mem.eql(u8, fmt, "*")) { - try output(context, @typeName(@TypeOf(value).Child)); - try output(context, "@"); - try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, context, Errors, output); + try out_stream.writeAll(@typeName(@TypeOf(value).Child)); + try out_stream.writeAll("@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, out_stream); return; } const T = @TypeOf(value); + if (comptime std.meta.trait.hasFn("format")(T)) { + return try value.format(fmt, options, out_stream); + } + switch (@typeInfo(T)) { .ComptimeInt, .Int, .Float => { - return formatValue(value, fmt, options, context, Errors, output); + return formatValue(value, fmt, options, out_stream); }, .Void => { - return output(context, "void"); + return out_stream.writeAll("void"); }, .Bool => { - return output(context, if (value) "true" else "false"); + return out_stream.writeAll(if (value) "true" else "false"); }, .Optional => { if (value) |payload| { - return formatType(payload, fmt, options, context, Errors, output, max_depth); + return formatType(payload, fmt, options, out_stream, max_depth); } else { - return output(context, "null"); + return out_stream.writeAll("null"); } }, .ErrorUnion => { if (value) |payload| { - return formatType(payload, fmt, options, context, Errors, output, max_depth); + return formatType(payload, fmt, options, out_stream, max_depth); } else |err| { - return formatType(err, fmt, options, context, Errors, output, max_depth); + return formatType(err, fmt, options, out_stream, max_depth); } }, .ErrorSet => { - try output(context, "error."); - return output(context, @errorName(value)); + try out_stream.writeAll("error."); + return out_stream.writeAll(@errorName(value)); }, .Enum => |enumInfo| { - if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, context, Errors, output); - } - - try output(context, @typeName(T)); + try out_stream.writeAll(@typeName(T)); if (enumInfo.is_exhaustive) { - try output(context, "."); - try output(context, @tagName(value)); + try out_stream.writeAll("."); + try out_stream.writeAll(@tagName(value)); } else { // TODO: when @tagName works on exhaustive enums print known enum strings - try output(context, "("); - try formatType(@enumToInt(value), fmt, options, context, Errors, output, max_depth); - try output(context, ")"); + try out_stream.writeAll("("); + try formatType(@enumToInt(value), fmt, options, out_stream, max_depth); + try out_stream.writeAll(")"); } }, .Union => { - if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, context, Errors, output); - } - - try output(context, @typeName(T)); + try out_stream.writeAll(@typeName(T)); if (max_depth == 0) { - return output(context, "{ ... }"); + return out_stream.writeAll("{ ... }"); } const info = @typeInfo(T).Union; if (info.tag_type) |UnionTagType| { - try output(context, "{ ."); - try output(context, @tagName(@as(UnionTagType, value))); - try output(context, " = "); + try out_stream.writeAll("{ ."); + try out_stream.writeAll(@tagName(@as(UnionTagType, value))); + try out_stream.writeAll(" = "); inline for (info.fields) |u_field| { if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) { - try formatType(@field(value, u_field.name), fmt, options, context, Errors, output, max_depth - 1); + try formatType(@field(value, u_field.name), fmt, options, out_stream, max_depth - 1); } } - try output(context, " }"); + try out_stream.writeAll(" }"); } else { - try format(context, Errors, output, "@{x}", .{@ptrToInt(&value)}); + try format(out_stream, "@{x}", .{@ptrToInt(&value)}); } }, .Struct => |StructT| { - if (comptime std.meta.trait.hasFn("format")(T)) { - return value.format(fmt, options, context, Errors, output); - } - - try output(context, @typeName(T)); + try out_stream.writeAll(@typeName(T)); if (max_depth == 0) { - return output(context, "{ ... }"); + return out_stream.writeAll("{ ... }"); } - try output(context, "{"); + try out_stream.writeAll("{"); inline for (StructT.fields) |f, i| { if (i == 0) { - try output(context, " ."); + try out_stream.writeAll(" ."); } else { - try output(context, ", ."); + try out_stream.writeAll(", ."); } - try output(context, f.name); - try output(context, " = "); - try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1); + try out_stream.writeAll(f.name); + try out_stream.writeAll(" = "); + try formatType(@field(value, f.name), fmt, options, out_stream, max_depth - 1); } - try output(context, " }"); + try out_stream.writeAll(" }"); }, .Pointer => |ptr_info| switch (ptr_info.size) { .One => switch (@typeInfo(ptr_info.child)) { .Array => |info| { if (info.child == u8) { - return formatText(value, fmt, options, context, Errors, output); + return formatText(value, fmt, options, out_stream); } - return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); + return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); }, .Enum, .Union, .Struct => { - return formatType(value.*, fmt, options, context, Errors, output, max_depth); + return formatType(value.*, fmt, options, out_stream, max_depth); }, - else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), + else => return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), }, .Many, .C => { if (ptr_info.sentinel) |sentinel| { - return formatType(mem.span(value), fmt, options, context, Errors, output, max_depth); + return formatType(mem.span(value), fmt, options, out_stream, max_depth); } if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { - return formatText(mem.span(value), fmt, options, context, Errors, output); + return formatText(mem.span(value), fmt, options, out_stream); } } - return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); + return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); }, .Slice => { if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { - return formatText(value, fmt, options, context, Errors, output); + return formatText(value, fmt, options, out_stream); } if (ptr_info.child == u8) { - return formatText(value, fmt, options, context, Errors, output); + return formatText(value, fmt, options, out_stream); } - return format(context, Errors, output, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); + return format(out_stream, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); }, }, .Array => |info| { @@ -473,27 +453,27 @@ pub fn formatType( .sentinel = null, }, }); - return formatType(@as(Slice, &value), fmt, options, context, Errors, output, max_depth); + return formatType(@as(Slice, &value), fmt, options, out_stream, max_depth); }, .Vector => { const len = @typeInfo(T).Vector.len; - try output(context, "{ "); + try out_stream.writeAll("{ "); var i: usize = 0; while (i < len) : (i += 1) { - try formatValue(value[i], fmt, options, context, Errors, output); + try formatValue(value[i], fmt, options, out_stream); if (i < len - 1) { - try output(context, ", "); + try out_stream.writeAll(", "); } } - try output(context, " }"); + try out_stream.writeAll(" }"); }, .Fn => { - return format(context, Errors, output, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); + return format(out_stream, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); }, - .Type => return output(context, @typeName(T)), + .Type => return out_stream.writeAll(@typeName(T)), .EnumLiteral => { const buffer = [_]u8{'.'} ++ @tagName(value); - return formatType(buffer, fmt, options, context, Errors, output, max_depth); + return formatType(buffer, fmt, options, out_stream, max_depth); }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } @@ -503,21 +483,19 @@ fn formatValue( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (comptime std.mem.eql(u8, fmt, "B")) { - return formatBytes(value, options, 1000, context, Errors, output); + return formatBytes(value, options, 1000, out_stream); } else if (comptime std.mem.eql(u8, fmt, "Bi")) { - return formatBytes(value, options, 1024, context, Errors, output); + return formatBytes(value, options, 1024, out_stream); } const T = @TypeOf(value); switch (@typeInfo(T)) { - .Float => return formatFloatValue(value, fmt, options, context, Errors, output), - .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output), - .Bool => return output(context, if (value) "true" else "false"), + .Float => return formatFloatValue(value, fmt, options, out_stream), + .Int, .ComptimeInt => return formatIntValue(value, fmt, options, out_stream), + .Bool => return out_stream.writeAll(if (value) "true" else "false"), else => comptime unreachable, } } @@ -526,10 +504,8 @@ pub fn formatIntValue( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { comptime var radix = 10; comptime var uppercase = false; @@ -544,7 +520,7 @@ pub fn formatIntValue( uppercase = false; } else if (comptime std.mem.eql(u8, fmt, "c")) { if (@TypeOf(int_value).bit_count <= 8) { - return formatAsciiChar(@as(u8, int_value), options, context, Errors, output); + return formatAsciiChar(@as(u8, int_value), options, out_stream); } else { @compileError("Cannot print integer that is larger than 8 bits as a ascii"); } @@ -561,21 +537,19 @@ pub fn formatIntValue( @compileError("Unknown format string: '" ++ fmt ++ "'"); } - return formatInt(int_value, radix, uppercase, options, context, Errors, output); + return formatInt(int_value, radix, uppercase, options, out_stream); } fn formatFloatValue( value: var, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { - return formatFloatScientific(value, options, context, Errors, output); + return formatFloatScientific(value, options, out_stream); } else if (comptime std.mem.eql(u8, fmt, "d")) { - return formatFloatDecimal(value, options, context, Errors, output); + return formatFloatDecimal(value, options, out_stream); } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); } @@ -585,17 +559,15 @@ pub fn formatText( bytes: []const u8, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (fmt.len == 0) { - return output(context, bytes); + return out_stream.writeAll(bytes); } else if (comptime std.mem.eql(u8, fmt, "s")) { - return formatBuf(bytes, options, context, Errors, output); + return formatBuf(bytes, options, out_stream); } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { for (bytes) |c| { - try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, context, Errors, output); + try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, out_stream); } return; } else { @@ -606,27 +578,23 @@ pub fn formatText( pub fn formatAsciiChar( c: u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { - return output(context, @as(*const [1]u8, &c)[0..]); + out_stream: var, +) !void { + return out_stream.writeAll(@as(*const [1]u8, &c)[0..]); } pub fn formatBuf( buf: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { - try output(context, buf); + out_stream: var, +) !void { + try out_stream.writeAll(buf); const width = options.width orelse 0; var leftover_padding = if (width > buf.len) (width - buf.len) else return; const pad_byte: u8 = options.fill; while (leftover_padding > 0) : (leftover_padding -= 1) { - try output(context, @as(*const [1]u8, &pad_byte)[0..1]); + try out_stream.writeAll(@as(*const [1]u8, &pad_byte)[0..1]); } } @@ -636,40 +604,38 @@ pub fn formatBuf( pub fn formatFloatScientific( value: var, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { var x = @floatCast(f64, value); // Errol doesn't handle these special cases. if (math.signbit(x)) { - try output(context, "-"); + try out_stream.writeAll("-"); x = -x; } if (math.isNan(x)) { - return output(context, "nan"); + return out_stream.writeAll("nan"); } if (math.isPositiveInf(x)) { - return output(context, "inf"); + return out_stream.writeAll("inf"); } if (x == 0.0) { - try output(context, "0"); + try out_stream.writeAll("0"); if (options.precision) |precision| { if (precision != 0) { - try output(context, "."); + try out_stream.writeAll("."); var i: usize = 0; while (i < precision) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } } else { - try output(context, ".0"); + try out_stream.writeAll(".0"); } - try output(context, "e+00"); + try out_stream.writeAll("e+00"); return; } @@ -679,50 +645,50 @@ pub fn formatFloatScientific( if (options.precision) |precision| { errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); - try output(context, float_decimal.digits[0..1]); + try out_stream.writeAll(float_decimal.digits[0..1]); // {e0} case prints no `.` if (precision != 0) { - try output(context, "."); + try out_stream.writeAll("."); var printed: usize = 0; if (float_decimal.digits.len > 1) { const num_digits = math.min(float_decimal.digits.len, precision + 1); - try output(context, float_decimal.digits[1..num_digits]); + try out_stream.writeAll(float_decimal.digits[1..num_digits]); printed += num_digits - 1; } while (printed < precision) : (printed += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } } else { - try output(context, float_decimal.digits[0..1]); - try output(context, "."); + try out_stream.writeAll(float_decimal.digits[0..1]); + try out_stream.writeAll("."); if (float_decimal.digits.len > 1) { const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len; - try output(context, float_decimal.digits[1..num_digits]); + try out_stream.writeAll(float_decimal.digits[1..num_digits]); } else { - try output(context, "0"); + try out_stream.writeAll("0"); } } - try output(context, "e"); + try out_stream.writeAll("e"); const exp = float_decimal.exp - 1; if (exp >= 0) { - try output(context, "+"); + try out_stream.writeAll("+"); if (exp > -10 and exp < 10) { - try output(context, "0"); + try out_stream.writeAll("0"); } - try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); + try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, out_stream); } else { - try output(context, "-"); + try out_stream.writeAll("-"); if (exp > -10 and exp < 10) { - try output(context, "0"); + try out_stream.writeAll("0"); } - try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output); + try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, out_stream); } } @@ -731,36 +697,34 @@ pub fn formatFloatScientific( pub fn formatFloatDecimal( value: var, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { var x = @as(f64, value); // Errol doesn't handle these special cases. if (math.signbit(x)) { - try output(context, "-"); + try out_stream.writeAll("-"); x = -x; } if (math.isNan(x)) { - return output(context, "nan"); + return out_stream.writeAll("nan"); } if (math.isPositiveInf(x)) { - return output(context, "inf"); + return out_stream.writeAll("inf"); } if (x == 0.0) { - try output(context, "0"); + try out_stream.writeAll("0"); if (options.precision) |precision| { if (precision != 0) { - try output(context, "."); + try out_stream.writeAll("."); var i: usize = 0; while (i < precision) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } else { - try output(context, ".0"); + try out_stream.writeAll(".0"); } } @@ -782,14 +746,14 @@ pub fn formatFloatDecimal( if (num_digits_whole > 0) { // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]); var i = num_digits_whole_no_pad; while (i < num_digits_whole) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } else { - try output(context, "0"); + try out_stream.writeAll("0"); } // {.0} special case doesn't want a trailing '.' @@ -797,7 +761,7 @@ pub fn formatFloatDecimal( return; } - try output(context, "."); + try out_stream.writeAll("."); // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. var printed: usize = 0; @@ -809,7 +773,7 @@ pub fn formatFloatDecimal( var i: usize = 0; while (i < zeros_to_print) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); printed += 1; } @@ -821,14 +785,14 @@ pub fn formatFloatDecimal( // Remaining fractional portion, zero-padding if insufficient. assert(precision >= printed); if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { - try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); + try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); return; } else { - try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]); printed += float_decimal.digits.len - num_digits_whole_no_pad; while (printed < precision) : (printed += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } } else { @@ -840,14 +804,14 @@ pub fn formatFloatDecimal( if (num_digits_whole > 0) { // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]); var i = num_digits_whole_no_pad; while (i < num_digits_whole) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } else { - try output(context, "0"); + try out_stream.writeAll("0"); } // Omit `.` if no fractional portion @@ -855,7 +819,7 @@ pub fn formatFloatDecimal( return; } - try output(context, "."); + try out_stream.writeAll("."); // Zero-fill until we reach significant digits or run out of precision. if (float_decimal.exp < 0) { @@ -863,11 +827,11 @@ pub fn formatFloatDecimal( var i: usize = 0; while (i < zero_digit_count) : (i += 1) { - try output(context, "0"); + try out_stream.writeAll("0"); } } - try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]); } } @@ -875,12 +839,10 @@ pub fn formatBytes( value: var, options: FormatOptions, comptime radix: usize, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { if (value == 0) { - return output(context, "0B"); + return out_stream.writeAll("0B"); } const mags_si = " kMGTPEZY"; @@ -897,10 +859,10 @@ pub fn formatBytes( else => unreachable, }; - try formatFloatDecimal(new_value, options, context, Errors, output); + try formatFloatDecimal(new_value, options, out_stream); if (suffix == ' ') { - return output(context, "B"); + return out_stream.writeAll("B"); } const buf = switch (radix) { @@ -908,7 +870,7 @@ pub fn formatBytes( 1024 => &[_]u8{ suffix, 'i', 'B' }, else => unreachable, }; - return output(context, buf); + return out_stream.writeAll(buf); } pub fn formatInt( @@ -916,10 +878,8 @@ pub fn formatInt( base: u8, uppercase: bool, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { const int_value = if (@TypeOf(value) == comptime_int) blk: { const Int = math.IntFittingRange(value, value); break :blk @as(Int, value); @@ -927,9 +887,9 @@ pub fn formatInt( value; if (@TypeOf(int_value).is_signed) { - return formatIntSigned(int_value, base, uppercase, options, context, Errors, output); + return formatIntSigned(int_value, base, uppercase, options, out_stream); } else { - return formatIntUnsigned(int_value, base, uppercase, options, context, Errors, output); + return formatIntUnsigned(int_value, base, uppercase, options, out_stream); } } @@ -938,10 +898,8 @@ fn formatIntSigned( base: u8, uppercase: bool, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { const new_options = FormatOptions{ .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null, .precision = options.precision, @@ -950,15 +908,15 @@ fn formatIntSigned( const bit_count = @typeInfo(@TypeOf(value)).Int.bits; const Uint = std.meta.IntType(false, bit_count); if (value < 0) { - try output(context, "-"); + try out_stream.writeAll("-"); const new_value = math.absCast(value); - return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); + return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream); } else if (options.width == null or options.width.? == 0) { - return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, context, Errors, output); + return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, out_stream); } else { - try output(context, "+"); + try out_stream.writeAll("+"); const new_value = @intCast(Uint, value); - return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); + return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream); } } @@ -967,10 +925,8 @@ fn formatIntUnsigned( base: u8, uppercase: bool, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { assert(base >= 2); var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined; const min_int_bits = comptime math.max(@TypeOf(value).bit_count, @TypeOf(base).bit_count); @@ -994,34 +950,23 @@ fn formatIntUnsigned( const zero_byte: u8 = options.fill; var leftover_padding = padding - index; while (true) { - try output(context, @as(*const [1]u8, &zero_byte)[0..]); + try out_stream.writeAll(@as(*const [1]u8, &zero_byte)[0..]); leftover_padding -= 1; if (leftover_padding == 0) break; } mem.set(u8, buf[0..index], options.fill); - return output(context, &buf); + return out_stream.writeAll(&buf); } else { const padded_buf = buf[index - padding ..]; mem.set(u8, padded_buf[0..padding], options.fill); - return output(context, padded_buf); + return out_stream.writeAll(padded_buf); } } pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { - var context = FormatIntBuf{ - .out_buf = out_buf, - .index = 0, - }; - formatInt(value, base, uppercase, options, &context, error{}, formatIntCallback) catch unreachable; - return context.index; -} -const FormatIntBuf = struct { - out_buf: []u8, - index: usize, -}; -fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { - mem.copy(u8, context.out_buf[context.index..], bytes); - context.index += bytes.len; + var fbs = std.io.fixedBufferStream(out_buf); + formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable; + return fbs.pos; } pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { @@ -1121,44 +1066,40 @@ fn digitToChar(digit: u8, uppercase: bool) u8 { }; } -const BufPrintContext = struct { - remaining: []u8, -}; - -fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { - if (context.remaining.len < bytes.len) { - mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); - return error.BufferTooSmall; - } - mem.copy(u8, context.remaining, bytes); - context.remaining = context.remaining[bytes.len..]; -} - pub const BufPrintError = error{ /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. BufferTooSmall, }; pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { - var context = BufPrintContext{ .remaining = buf }; - try format(&context, BufPrintError, bufPrintWrite, fmt, args); - return buf[0 .. buf.len - context.remaining.len]; + var fbs = std.io.fixedBufferStream(buf); + format(fbs.outStream(), fmt, args) catch |err| switch (err) { + error.NoSpaceLeft => return error.BufferTooSmall, + }; + //TODO: should we change one of these return signatures? + //return fbs.getWritten(); + return buf[0..fbs.pos]; +} + +// Count the characters needed for format. Useful for preallocating memory +pub fn count(comptime fmt: []const u8, args: var) !usize { + var counting_stream = std.io.countingOutStream(std.io.null_out_stream); + format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {}; + return std.math.cast(usize, counting_stream.bytes_written); } pub const AllocPrintError = error{OutOfMemory}; pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { - var size: usize = 0; - format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; + const size = count(fmt, args) catch |err| switch (err) { + // Output too long. Can't possibly allocate enough memory to display it. + error.Overflow => return error.OutOfMemory, + }; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args) catch |err| switch (err) { error.BufferTooSmall => unreachable, // we just counted the size above }; } -fn countSize(size: *usize, bytes: []const u8) (error{}!void) { - size.* += bytes.len; -} - pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { const result = try allocPrint(allocator, fmt ++ "\x00", args); return result[0 .. result.len - 1 :0]; @@ -1251,20 +1192,17 @@ test "int.padded" { test "buffer" { { var buf1: [32]u8 = undefined; - var context = BufPrintContext{ .remaining = buf1[0..] }; - try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); - var res = buf1[0 .. buf1.len - context.remaining.len]; - std.testing.expect(mem.eql(u8, res, "1234")); - - context = BufPrintContext{ .remaining = buf1[0..] }; - try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); - res = buf1[0 .. buf1.len - context.remaining.len]; - std.testing.expect(mem.eql(u8, res, "a")); - - context = BufPrintContext{ .remaining = buf1[0..] }; - try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); - res = buf1[0 .. buf1.len - context.remaining.len]; - std.testing.expect(mem.eql(u8, res, "1100")); + var fbs = std.io.fixedBufferStream(&buf1); + try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234")); + + fbs.reset(); + try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "a")); + + fbs.reset(); + try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100")); } } @@ -1449,14 +1387,12 @@ test "custom" { self: SelfType, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, - ) Errors!void { + out_stream: var, + ) !void { if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { - return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); + return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); } else if (comptime std.mem.eql(u8, fmt, "d")) { - return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", .{ self.x, self.y }); + return std.fmt.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); } else { @compileError("Unknown format character: '" ++ fmt ++ "'"); } @@ -1640,10 +1576,10 @@ test "hexToBytes" { test "formatIntValue with comptime_int" { const value: comptime_int = 123456789123456789; - var buf = std.ArrayList(u8).init(std.testing.allocator); - defer buf.deinit(); - try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice); - std.testing.expect(mem.eql(u8, buf.toSliceConst(), "123456789123456789")); + var buf: [20]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + try formatIntValue(value, "", FormatOptions{}, fbs.outStream()); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789")); } test "formatType max_depth" { @@ -1656,12 +1592,10 @@ test "formatType max_depth" { self: SelfType, comptime fmt: []const u8, options: FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, - ) Errors!void { + out_stream: var, + ) !void { if (fmt.len == 0) { - return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", .{ self.x, self.y }); + return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); } @@ -1695,25 +1629,22 @@ test "formatType max_depth" { inst.a = &inst; inst.tu.ptr = &inst.tu; - var buf0 = std.ArrayList(u8).init(std.testing.allocator); - defer buf0.deinit(); - try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 0); - std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); - - var buf1 = std.ArrayList(u8).init(std.testing.allocator); - defer buf1.deinit(); - try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 1); - std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); - - var buf2 = std.ArrayList(u8).init(std.testing.allocator); - defer buf2.deinit(); - try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 2); - std.testing.expect(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); - - var buf3 = std.ArrayList(u8).init(std.testing.allocator); - defer buf3.deinit(); - try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 3); - std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); + var buf: [1000]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 0); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }")); + + fbs.reset(); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 1); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); + + fbs.reset(); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 2); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); + + fbs.reset(); + try formatType(inst, "", FormatOptions{}, fbs.outStream(), 3); + std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); } test "positional" { diff --git a/lib/std/fmtstream.zig b/lib/std/fmtstream.zig deleted file mode 100644 index 93734d42aa33..000000000000 --- a/lib/std/fmtstream.zig +++ /dev/null @@ -1,1685 +0,0 @@ -const std = @import("std.zig"); -const math = std.math; -const assert = std.debug.assert; -const mem = std.mem; -const builtin = @import("builtin"); -const errol = @import("fmt/errol.zig"); -const lossyCast = std.math.lossyCast; - -pub const default_max_depth = 3; - -pub const Alignment = enum { - Left, - Center, - Right, -}; - -pub const FormatOptions = struct { - precision: ?usize = null, - width: ?usize = null, - alignment: ?Alignment = null, - fill: u8 = ' ', -}; - -fn peekIsAlign(comptime fmt: []const u8) bool { - // Should only be called during a state transition to the format segment. - comptime assert(fmt[0] == ':'); - - inline for (([_]u8{ 1, 2 })[0..]) |i| { - if (fmt.len > i and (fmt[i] == '<' or fmt[i] == '^' or fmt[i] == '>')) { - return true; - } - } - return false; -} - -/// Renders fmt string with args, calling output with slices of bytes. -/// If `output` returns an error, the error is returned from `format` and -/// `output` is not called again. -/// -/// The format string must be comptime known and may contain placeholders following -/// this format: -/// `{[position][specifier]:[fill][alignment][width].[precision]}` -/// -/// Each word between `[` and `]` is a parameter you have to replace with something: -/// -/// - *position* is the index of the argument that should be inserted -/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) -/// - *fill* is a single character which is used to pad the formatted text -/// - *alignment* is one of the three characters `<`, `^` or `>`. they define if the text is *left*, *center*, or *right* aligned -/// - *width* is the total width of the field in characters -/// - *precision* specifies how many decimals a formatted number should have -/// -/// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when -/// all parameters after the separator are omitted. -/// Only exception is the *fill* parameter. If *fill* is required, one has to specify *alignment* as well, as otherwise -/// the digits after `:` is interpreted as *width*, not *fill*. -/// -/// The *specifier* has several options for types: -/// - `x` and `X`: -/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case -/// - output numeric value in hexadecimal notation -/// - `s`: print a pointer-to-many as a c-string, use zero-termination -/// - `B` and `Bi`: output a memory size in either metric (1000) or power-of-two (1024) based notation. works for both float and integer values. -/// - `e`: output floating point value in scientific notation -/// - `d`: output numeric value in decimal notation -/// - `b`: output integer value in binary notation -/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. -/// - `*`: output the address of the value instead of the value itself. -/// -/// If a formatted user type contains a function of the type -/// ``` -/// fn format(value: ?, comptime fmt: []const u8, options: std.fmtstream.FormatOptions, out_stream: var) !void -/// ``` -/// with `?` being the type formatted, this function will be called instead of the default implementation. -/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type. -/// -/// A user type may be a `struct`, `vector`, `union` or `enum` type. -pub fn format( - out_stream: var, - comptime fmt: []const u8, - args: var, -) !void { - const ArgSetType = u32; - if (@typeInfo(@TypeOf(args)) != .Struct) { - @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); - } - if (args.len > ArgSetType.bit_count) { - @compileError("32 arguments max are supported per format call"); - } - - const State = enum { - Start, - Positional, - CloseBrace, - Specifier, - FormatFillAndAlign, - FormatWidth, - FormatPrecision, - }; - - comptime var start_index = 0; - comptime var state = State.Start; - comptime var maybe_pos_arg: ?comptime_int = null; - comptime var specifier_start = 0; - comptime var specifier_end = 0; - comptime var options = FormatOptions{}; - comptime var arg_state: struct { - next_arg: usize = 0, - used_args: ArgSetType = 0, - args_len: usize = args.len, - - fn hasUnusedArgs(comptime self: *@This()) bool { - return (@popCount(ArgSetType, self.used_args) != self.args_len); - } - - fn nextArg(comptime self: *@This(), comptime pos_arg: ?comptime_int) comptime_int { - const next_idx = pos_arg orelse blk: { - const arg = self.next_arg; - self.next_arg += 1; - break :blk arg; - }; - - if (next_idx >= self.args_len) { - @compileError("Too few arguments"); - } - - // Mark this argument as used - self.used_args |= 1 << next_idx; - - return next_idx; - } - } = .{}; - - inline for (fmt) |c, i| { - switch (state) { - .Start => switch (c) { - '{' => { - if (start_index < i) { - try out_stream.writeAll(fmt[start_index..i]); - } - - start_index = i; - specifier_start = i + 1; - specifier_end = i + 1; - maybe_pos_arg = null; - state = .Positional; - options = FormatOptions{}; - }, - '}' => { - if (start_index < i) { - try out_stream.writeAll(fmt[start_index..i]); - } - state = .CloseBrace; - }, - else => {}, - }, - .Positional => switch (c) { - '{' => { - state = .Start; - start_index = i; - }, - ':' => { - state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; - specifier_end = i; - }, - '0'...'9' => { - if (maybe_pos_arg == null) { - maybe_pos_arg = 0; - } - - maybe_pos_arg.? *= 10; - maybe_pos_arg.? += c - '0'; - specifier_start = i + 1; - - if (maybe_pos_arg.? >= args.len) { - @compileError("Positional value refers to non-existent argument"); - } - }, - '}' => { - const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); - - try formatType( - args[arg_to_print], - fmt[0..0], - options, - out_stream, - default_max_depth, - ); - - state = .Start; - start_index = i + 1; - }, - else => { - state = .Specifier; - specifier_start = i; - }, - }, - .CloseBrace => switch (c) { - '}' => { - state = .Start; - start_index = i; - }, - else => @compileError("Single '}' encountered in format string"), - }, - .Specifier => switch (c) { - ':' => { - specifier_end = i; - state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; - }, - '}' => { - const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); - - try formatType( - args[arg_to_print], - fmt[specifier_start..i], - options, - out_stream, - default_max_depth, - ); - state = .Start; - start_index = i + 1; - }, - else => {}, - }, - // Only entered if the format string contains a fill/align segment. - .FormatFillAndAlign => switch (c) { - '<' => { - options.alignment = Alignment.Left; - state = .FormatWidth; - }, - '^' => { - options.alignment = Alignment.Center; - state = .FormatWidth; - }, - '>' => { - options.alignment = Alignment.Right; - state = .FormatWidth; - }, - else => { - options.fill = c; - }, - }, - .FormatWidth => switch (c) { - '0'...'9' => { - if (options.width == null) { - options.width = 0; - } - - options.width.? *= 10; - options.width.? += c - '0'; - }, - '.' => { - state = .FormatPrecision; - }, - '}' => { - const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); - - try formatType( - args[arg_to_print], - fmt[specifier_start..specifier_end], - options, - out_stream, - default_max_depth, - ); - state = .Start; - start_index = i + 1; - }, - else => { - @compileError("Unexpected character in width value: " ++ [_]u8{c}); - }, - }, - .FormatPrecision => switch (c) { - '0'...'9' => { - if (options.precision == null) { - options.precision = 0; - } - - options.precision.? *= 10; - options.precision.? += c - '0'; - }, - '}' => { - const arg_to_print = comptime arg_state.nextArg(maybe_pos_arg); - - try formatType( - args[arg_to_print], - fmt[specifier_start..specifier_end], - options, - out_stream, - default_max_depth, - ); - state = .Start; - start_index = i + 1; - }, - else => { - @compileError("Unexpected character in precision value: " ++ [_]u8{c}); - }, - }, - } - } - comptime { - if (comptime arg_state.hasUnusedArgs()) { - @compileError("Unused arguments"); - } - if (state != State.Start) { - @compileError("Incomplete format string: " ++ fmt); - } - } - if (start_index < fmt.len) { - try out_stream.writeAll(fmt[start_index..]); - } -} - -pub fn formatType( - value: var, - comptime fmt: []const u8, - options: FormatOptions, - out_stream: var, - max_depth: usize, -) @TypeOf(out_stream).Error!void { - if (comptime std.mem.eql(u8, fmt, "*")) { - try out_stream.writeAll(@typeName(@TypeOf(value).Child)); - try out_stream.writeAll("@"); - try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, out_stream); - return; - } - - const T = @TypeOf(value); - if (comptime std.meta.trait.hasFn("format")(T)) { - return try value.format(fmt, options, out_stream); - } - - switch (@typeInfo(T)) { - .ComptimeInt, .Int, .Float => { - return formatValue(value, fmt, options, out_stream); - }, - .Void => { - return out_stream.writeAll("void"); - }, - .Bool => { - return out_stream.writeAll(if (value) "true" else "false"); - }, - .Optional => { - if (value) |payload| { - return formatType(payload, fmt, options, out_stream, max_depth); - } else { - return out_stream.writeAll("null"); - } - }, - .ErrorUnion => { - if (value) |payload| { - return formatType(payload, fmt, options, out_stream, max_depth); - } else |err| { - return formatType(err, fmt, options, out_stream, max_depth); - } - }, - .ErrorSet => { - try out_stream.writeAll("error."); - return out_stream.writeAll(@errorName(value)); - }, - .Enum => |enumInfo| { - try out_stream.writeAll(@typeName(T)); - if (enumInfo.is_exhaustive) { - try out_stream.writeAll("."); - try out_stream.writeAll(@tagName(value)); - } else { - // TODO: when @tagName works on exhaustive enums print known enum strings - try out_stream.writeAll("("); - try formatType(@enumToInt(value), fmt, options, out_stream, max_depth); - try out_stream.writeAll(")"); - } - }, - .Union => { - try out_stream.writeAll(@typeName(T)); - if (max_depth == 0) { - return out_stream.writeAll("{ ... }"); - } - const info = @typeInfo(T).Union; - if (info.tag_type) |UnionTagType| { - try out_stream.writeAll("{ ."); - try out_stream.writeAll(@tagName(@as(UnionTagType, value))); - try out_stream.writeAll(" = "); - inline for (info.fields) |u_field| { - if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) { - try formatType(@field(value, u_field.name), fmt, options, out_stream, max_depth - 1); - } - } - try out_stream.writeAll(" }"); - } else { - try format(out_stream, "@{x}", .{@ptrToInt(&value)}); - } - }, - .Struct => |StructT| { - try out_stream.writeAll(@typeName(T)); - if (max_depth == 0) { - return out_stream.writeAll("{ ... }"); - } - try out_stream.writeAll("{"); - inline for (StructT.fields) |f, i| { - if (i == 0) { - try out_stream.writeAll(" ."); - } else { - try out_stream.writeAll(", ."); - } - try out_stream.writeAll(f.name); - try out_stream.writeAll(" = "); - try formatType(@field(value, f.name), fmt, options, out_stream, max_depth - 1); - } - try out_stream.writeAll(" }"); - }, - .Pointer => |ptr_info| switch (ptr_info.size) { - .One => switch (@typeInfo(ptr_info.child)) { - .Array => |info| { - if (info.child == u8) { - return formatText(value, fmt, options, out_stream); - } - return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); - }, - .Enum, .Union, .Struct => { - return formatType(value.*, fmt, options, out_stream, max_depth); - }, - else => return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), - }, - .Many, .C => { - if (ptr_info.sentinel) |sentinel| { - return formatType(mem.span(value), fmt, options, out_stream, max_depth); - } - if (ptr_info.child == u8) { - if (fmt.len > 0 and fmt[0] == 's') { - return formatText(mem.span(value), fmt, options, out_stream); - } - } - return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }); - }, - .Slice => { - if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { - return formatText(value, fmt, options, out_stream); - } - if (ptr_info.child == u8) { - return formatText(value, fmt, options, out_stream); - } - return format(out_stream, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); - }, - }, - .Array => |info| { - const Slice = @Type(builtin.TypeInfo{ - .Pointer = .{ - .size = .Slice, - .is_const = true, - .is_volatile = false, - .is_allowzero = false, - .alignment = @alignOf(info.child), - .child = info.child, - .sentinel = null, - }, - }); - return formatType(@as(Slice, &value), fmt, options, out_stream, max_depth); - }, - .Vector => { - const len = @typeInfo(T).Vector.len; - try out_stream.writeAll("{ "); - var i: usize = 0; - while (i < len) : (i += 1) { - try formatValue(value[i], fmt, options, out_stream); - if (i < len - 1) { - try out_stream.writeAll(", "); - } - } - try out_stream.writeAll(" }"); - }, - .Fn => { - return format(out_stream, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); - }, - .Type => return out_stream.writeAll(@typeName(T)), - .EnumLiteral => { - const buffer = [_]u8{'.'} ++ @tagName(value); - return formatType(buffer, fmt, options, out_stream, max_depth); - }, - else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), - } -} - -fn formatValue( - value: var, - comptime fmt: []const u8, - options: FormatOptions, - out_stream: var, -) !void { - if (comptime std.mem.eql(u8, fmt, "B")) { - return formatBytes(value, options, 1000, out_stream); - } else if (comptime std.mem.eql(u8, fmt, "Bi")) { - return formatBytes(value, options, 1024, out_stream); - } - - const T = @TypeOf(value); - switch (@typeInfo(T)) { - .Float => return formatFloatValue(value, fmt, options, out_stream), - .Int, .ComptimeInt => return formatIntValue(value, fmt, options, out_stream), - .Bool => return out_stream.writeAll(if (value) "true" else "false"), - else => comptime unreachable, - } -} - -pub fn formatIntValue( - value: var, - comptime fmt: []const u8, - options: FormatOptions, - out_stream: var, -) !void { - comptime var radix = 10; - comptime var uppercase = false; - - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else - value; - - if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) { - radix = 10; - uppercase = false; - } else if (comptime std.mem.eql(u8, fmt, "c")) { - if (@TypeOf(int_value).bit_count <= 8) { - return formatAsciiChar(@as(u8, int_value), options, out_stream); - } else { - @compileError("Cannot print integer that is larger than 8 bits as a ascii"); - } - } else if (comptime std.mem.eql(u8, fmt, "b")) { - radix = 2; - uppercase = false; - } else if (comptime std.mem.eql(u8, fmt, "x")) { - radix = 16; - uppercase = false; - } else if (comptime std.mem.eql(u8, fmt, "X")) { - radix = 16; - uppercase = true; - } else { - @compileError("Unknown format string: '" ++ fmt ++ "'"); - } - - return formatInt(int_value, radix, uppercase, options, out_stream); -} - -fn formatFloatValue( - value: var, - comptime fmt: []const u8, - options: FormatOptions, - out_stream: var, -) !void { - if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { - return formatFloatScientific(value, options, out_stream); - } else if (comptime std.mem.eql(u8, fmt, "d")) { - return formatFloatDecimal(value, options, out_stream); - } else { - @compileError("Unknown format string: '" ++ fmt ++ "'"); - } -} - -pub fn formatText( - bytes: []const u8, - comptime fmt: []const u8, - options: FormatOptions, - out_stream: var, -) !void { - if (fmt.len == 0) { - return out_stream.writeAll(bytes); - } else if (comptime std.mem.eql(u8, fmt, "s")) { - return formatBuf(bytes, options, out_stream); - } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { - for (bytes) |c| { - try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, out_stream); - } - return; - } else { - @compileError("Unknown format string: '" ++ fmt ++ "'"); - } -} - -pub fn formatAsciiChar( - c: u8, - options: FormatOptions, - out_stream: var, -) !void { - return out_stream.writeAll(@as(*const [1]u8, &c)[0..]); -} - -pub fn formatBuf( - buf: []const u8, - options: FormatOptions, - out_stream: var, -) !void { - try out_stream.writeAll(buf); - - const width = options.width orelse 0; - var leftover_padding = if (width > buf.len) (width - buf.len) else return; - const pad_byte: u8 = options.fill; - while (leftover_padding > 0) : (leftover_padding -= 1) { - try out_stream.writeAll(@as(*const [1]u8, &pad_byte)[0..1]); - } -} - -// Print a float in scientific notation to the specified precision. Null uses full precision. -// It should be the case that every full precision, printed value can be re-parsed back to the -// same type unambiguously. -pub fn formatFloatScientific( - value: var, - options: FormatOptions, - out_stream: var, -) !void { - var x = @floatCast(f64, value); - - // Errol doesn't handle these special cases. - if (math.signbit(x)) { - try out_stream.writeAll("-"); - x = -x; - } - - if (math.isNan(x)) { - return out_stream.writeAll("nan"); - } - if (math.isPositiveInf(x)) { - return out_stream.writeAll("inf"); - } - if (x == 0.0) { - try out_stream.writeAll("0"); - - if (options.precision) |precision| { - if (precision != 0) { - try out_stream.writeAll("."); - var i: usize = 0; - while (i < precision) : (i += 1) { - try out_stream.writeAll("0"); - } - } - } else { - try out_stream.writeAll(".0"); - } - - try out_stream.writeAll("e+00"); - return; - } - - var buffer: [32]u8 = undefined; - var float_decimal = errol.errol3(x, buffer[0..]); - - if (options.precision) |precision| { - errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); - - try out_stream.writeAll(float_decimal.digits[0..1]); - - // {e0} case prints no `.` - if (precision != 0) { - try out_stream.writeAll("."); - - var printed: usize = 0; - if (float_decimal.digits.len > 1) { - const num_digits = math.min(float_decimal.digits.len, precision + 1); - try out_stream.writeAll(float_decimal.digits[1..num_digits]); - printed += num_digits - 1; - } - - while (printed < precision) : (printed += 1) { - try out_stream.writeAll("0"); - } - } - } else { - try out_stream.writeAll(float_decimal.digits[0..1]); - try out_stream.writeAll("."); - if (float_decimal.digits.len > 1) { - const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len; - - try out_stream.writeAll(float_decimal.digits[1..num_digits]); - } else { - try out_stream.writeAll("0"); - } - } - - try out_stream.writeAll("e"); - const exp = float_decimal.exp - 1; - - if (exp >= 0) { - try out_stream.writeAll("+"); - if (exp > -10 and exp < 10) { - try out_stream.writeAll("0"); - } - try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, out_stream); - } else { - try out_stream.writeAll("-"); - if (exp > -10 and exp < 10) { - try out_stream.writeAll("0"); - } - try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, out_stream); - } -} - -// Print a float of the format x.yyyyy where the number of y is specified by the precision argument. -// By default floats are printed at full precision (no rounding). -pub fn formatFloatDecimal( - value: var, - options: FormatOptions, - out_stream: var, -) !void { - var x = @as(f64, value); - - // Errol doesn't handle these special cases. - if (math.signbit(x)) { - try out_stream.writeAll("-"); - x = -x; - } - - if (math.isNan(x)) { - return out_stream.writeAll("nan"); - } - if (math.isPositiveInf(x)) { - return out_stream.writeAll("inf"); - } - if (x == 0.0) { - try out_stream.writeAll("0"); - - if (options.precision) |precision| { - if (precision != 0) { - try out_stream.writeAll("."); - var i: usize = 0; - while (i < precision) : (i += 1) { - try out_stream.writeAll("0"); - } - } else { - try out_stream.writeAll(".0"); - } - } - - return; - } - - // non-special case, use errol3 - var buffer: [32]u8 = undefined; - var float_decimal = errol.errol3(x, buffer[0..]); - - if (options.precision) |precision| { - errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); - - // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; - - // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. - var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); - - if (num_digits_whole > 0) { - // We may have to zero pad, for instance 1e4 requires zero padding. - try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]); - - var i = num_digits_whole_no_pad; - while (i < num_digits_whole) : (i += 1) { - try out_stream.writeAll("0"); - } - } else { - try out_stream.writeAll("0"); - } - - // {.0} special case doesn't want a trailing '.' - if (precision == 0) { - return; - } - - try out_stream.writeAll("."); - - // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. - var printed: usize = 0; - - // Zero-fill until we reach significant digits or run out of precision. - if (float_decimal.exp <= 0) { - const zero_digit_count = @intCast(usize, -float_decimal.exp); - const zeros_to_print = math.min(zero_digit_count, precision); - - var i: usize = 0; - while (i < zeros_to_print) : (i += 1) { - try out_stream.writeAll("0"); - printed += 1; - } - - if (printed >= precision) { - return; - } - } - - // Remaining fractional portion, zero-padding if insufficient. - assert(precision >= printed); - if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { - try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); - return; - } else { - try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]); - printed += float_decimal.digits.len - num_digits_whole_no_pad; - - while (printed < precision) : (printed += 1) { - try out_stream.writeAll("0"); - } - } - } else { - // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; - - // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. - var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); - - if (num_digits_whole > 0) { - // We may have to zero pad, for instance 1e4 requires zero padding. - try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]); - - var i = num_digits_whole_no_pad; - while (i < num_digits_whole) : (i += 1) { - try out_stream.writeAll("0"); - } - } else { - try out_stream.writeAll("0"); - } - - // Omit `.` if no fractional portion - if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { - return; - } - - try out_stream.writeAll("."); - - // Zero-fill until we reach significant digits or run out of precision. - if (float_decimal.exp < 0) { - const zero_digit_count = @intCast(usize, -float_decimal.exp); - - var i: usize = 0; - while (i < zero_digit_count) : (i += 1) { - try out_stream.writeAll("0"); - } - } - - try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]); - } -} - -pub fn formatBytes( - value: var, - options: FormatOptions, - comptime radix: usize, - out_stream: var, -) !void { - if (value == 0) { - return out_stream.writeAll("0B"); - } - - const mags_si = " kMGTPEZY"; - const mags_iec = " KMGTPEZY"; - const magnitude = switch (radix) { - 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1), - 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), - else => unreachable, - }; - const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); - const suffix = switch (radix) { - 1000 => mags_si[magnitude], - 1024 => mags_iec[magnitude], - else => unreachable, - }; - - try formatFloatDecimal(new_value, options, out_stream); - - if (suffix == ' ') { - return out_stream.writeAll("B"); - } - - const buf = switch (radix) { - 1000 => &[_]u8{ suffix, 'B' }, - 1024 => &[_]u8{ suffix, 'i', 'B' }, - else => unreachable, - }; - return out_stream.writeAll(buf); -} - -pub fn formatInt( - value: var, - base: u8, - uppercase: bool, - options: FormatOptions, - out_stream: var, -) !void { - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else - value; - - if (@TypeOf(int_value).is_signed) { - return formatIntSigned(int_value, base, uppercase, options, out_stream); - } else { - return formatIntUnsigned(int_value, base, uppercase, options, out_stream); - } -} - -fn formatIntSigned( - value: var, - base: u8, - uppercase: bool, - options: FormatOptions, - out_stream: var, -) !void { - const new_options = FormatOptions{ - .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null, - .precision = options.precision, - .fill = options.fill, - }; - const bit_count = @typeInfo(@TypeOf(value)).Int.bits; - const Uint = std.meta.IntType(false, bit_count); - if (value < 0) { - try out_stream.writeAll("-"); - const new_value = math.absCast(value); - return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream); - } else if (options.width == null or options.width.? == 0) { - return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, out_stream); - } else { - try out_stream.writeAll("+"); - const new_value = @intCast(Uint, value); - return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream); - } -} - -fn formatIntUnsigned( - value: var, - base: u8, - uppercase: bool, - options: FormatOptions, - out_stream: var, -) !void { - assert(base >= 2); - var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined; - const min_int_bits = comptime math.max(@TypeOf(value).bit_count, @TypeOf(base).bit_count); - const MinInt = std.meta.IntType(@TypeOf(value).is_signed, min_int_bits); - var a: MinInt = value; - var index: usize = buf.len; - - while (true) { - const digit = a % base; - index -= 1; - buf[index] = digitToChar(@intCast(u8, digit), uppercase); - a /= base; - if (a == 0) break; - } - - const digits_buf = buf[index..]; - const width = options.width orelse 0; - const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; - - if (padding > index) { - const zero_byte: u8 = options.fill; - var leftover_padding = padding - index; - while (true) { - try out_stream.writeAll(@as(*const [1]u8, &zero_byte)[0..]); - leftover_padding -= 1; - if (leftover_padding == 0) break; - } - mem.set(u8, buf[0..index], options.fill); - return out_stream.writeAll(&buf); - } else { - const padded_buf = buf[index - padding ..]; - mem.set(u8, padded_buf[0..padding], options.fill); - return out_stream.writeAll(padded_buf); - } -} - -pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { - var fbs = std.io.fixedBufferStream(out_buf); - formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable; - return fbs.pos; -} - -pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { - if (!T.is_signed) return parseUnsigned(T, buf, radix); - if (buf.len == 0) return @as(T, 0); - if (buf[0] == '-') { - return math.negate(try parseUnsigned(T, buf[1..], radix)); - } else if (buf[0] == '+') { - return parseUnsigned(T, buf[1..], radix); - } else { - return parseUnsigned(T, buf, radix); - } -} - -test "parseInt" { - std.testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); - std.testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); - std.testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); - std.testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); - std.testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); - std.testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); - std.testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); -} - -pub const ParseUnsignedError = error{ - /// The result cannot fit in the type specified - Overflow, - - /// The input had a byte that was not a digit - InvalidCharacter, -}; - -pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { - var x: T = 0; - - for (buf) |c| { - const digit = try charToDigit(c, radix); - - if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); - x = try math.add(T, x, try math.cast(T, digit)); - } - - return x; -} - -test "parseUnsigned" { - std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); - std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); - std.testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); - - std.testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); - std.testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); - - std.testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); - - std.testing.expect((try parseUnsigned(u7, "1", 10)) == 1); - std.testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); - - std.testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); - std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); - - std.testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); - - // these numbers should fit even though the radix itself doesn't fit in the destination type - std.testing.expect((try parseUnsigned(u1, "0", 10)) == 0); - std.testing.expect((try parseUnsigned(u1, "1", 10)) == 1); - std.testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); - std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1); - std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3); - std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); -} - -pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; - -test "parseFloat" { - _ = @import("fmt/parse_float.zig"); -} - -pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { - const value = switch (c) { - '0'...'9' => c - '0', - 'A'...'Z' => c - 'A' + 10, - 'a'...'z' => c - 'a' + 10, - else => return error.InvalidCharacter, - }; - - if (value >= radix) return error.InvalidCharacter; - - return value; -} - -fn digitToChar(digit: u8, uppercase: bool) u8 { - return switch (digit) { - 0...9 => digit + '0', - 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10), - else => unreachable, - }; -} - -pub const BufPrintError = error{ - /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. - BufferTooSmall, -}; -pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { - var fbs = std.io.fixedBufferStream(buf); - format(fbs.outStream(), fmt, args) catch |err| switch (err) { - error.NoSpaceLeft => return error.BufferTooSmall, - }; - //TODO: should we change one of these return signatures? - //return fbs.getWritten(); - return buf[0..fbs.pos]; -} - -// Count the characters needed for format. Useful for preallocating memory -pub fn count(comptime fmt: []const u8, args: var) !usize { - var counting_stream = std.io.countingOutStream(std.io.null_out_stream); - format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {}; - return std.math.cast(usize, counting_stream.bytes_written); -} - -pub const AllocPrintError = error{OutOfMemory}; - -pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { - const size = count(fmt, args) catch |err| switch (err) { - // Output too long. Can't possibly allocate enough memory to display it. - error.Overflow => return error.OutOfMemory, - }; - const buf = try allocator.alloc(u8, size); - return bufPrint(buf, fmt, args) catch |err| switch (err) { - error.BufferTooSmall => unreachable, // we just counted the size above - }; -} - -pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { - const result = try allocPrint(allocator, fmt ++ "\x00", args); - return result[0 .. result.len - 1 :0]; -} - -test "bufPrintInt" { - var buffer: [100]u8 = undefined; - const buf = buffer[0..]; - - std.testing.expectEqualSlices(u8, "-1", bufPrintIntToSlice(buf, @as(i1, -1), 10, false, FormatOptions{})); - - std.testing.expectEqualSlices(u8, "-101111000110000101001110", bufPrintIntToSlice(buf, @as(i32, -12345678), 2, false, FormatOptions{})); - std.testing.expectEqualSlices(u8, "-12345678", bufPrintIntToSlice(buf, @as(i32, -12345678), 10, false, FormatOptions{})); - std.testing.expectEqualSlices(u8, "-bc614e", bufPrintIntToSlice(buf, @as(i32, -12345678), 16, false, FormatOptions{})); - std.testing.expectEqualSlices(u8, "-BC614E", bufPrintIntToSlice(buf, @as(i32, -12345678), 16, true, FormatOptions{})); - - std.testing.expectEqualSlices(u8, "12345678", bufPrintIntToSlice(buf, @as(u32, 12345678), 10, true, FormatOptions{})); - - std.testing.expectEqualSlices(u8, " 666", bufPrintIntToSlice(buf, @as(u32, 666), 10, false, FormatOptions{ .width = 6 })); - std.testing.expectEqualSlices(u8, " 1234", bufPrintIntToSlice(buf, @as(u32, 0x1234), 16, false, FormatOptions{ .width = 6 })); - std.testing.expectEqualSlices(u8, "1234", bufPrintIntToSlice(buf, @as(u32, 0x1234), 16, false, FormatOptions{ .width = 1 })); - - std.testing.expectEqualSlices(u8, "+42", bufPrintIntToSlice(buf, @as(i32, 42), 10, false, FormatOptions{ .width = 3 })); - std.testing.expectEqualSlices(u8, "-42", bufPrintIntToSlice(buf, @as(i32, -42), 10, false, FormatOptions{ .width = 3 })); -} - -fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 { - return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; -} - -test "parse u64 digit too big" { - _ = parseUnsigned(u64, "123a", 10) catch |err| { - if (err == error.InvalidCharacter) return; - unreachable; - }; - unreachable; -} - -test "parse unsigned comptime" { - comptime { - std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); - } -} - -test "optional" { - { - const value: ?i32 = 1234; - try testFmt("optional: 1234\n", "optional: {}\n", .{value}); - } - { - const value: ?i32 = null; - try testFmt("optional: null\n", "optional: {}\n", .{value}); - } -} - -test "error" { - { - const value: anyerror!i32 = 1234; - try testFmt("error union: 1234\n", "error union: {}\n", .{value}); - } - { - const value: anyerror!i32 = error.InvalidChar; - try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); - } -} - -test "int.small" { - { - const value: u3 = 0b101; - try testFmt("u3: 5\n", "u3: {}\n", .{value}); - } -} - -test "int.specifier" { - { - const value: u8 = 'a'; - try testFmt("u8: a\n", "u8: {c}\n", .{value}); - } - { - const value: u8 = 0b1100; - try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); - } -} - -test "int.padded" { - try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); - try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); -} - -test "buffer" { - { - var buf1: [32]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf1); - try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234")); - - fbs.reset(); - try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "a")); - - fbs.reset(); - try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100")); - } -} - -test "array" { - { - const value: [3]u8 = "abc".*; - try testFmt("array: abc\n", "array: {}\n", .{value}); - try testFmt("array: abc\n", "array: {}\n", .{&value}); - - var buf: [100]u8 = undefined; - try testFmt( - try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), - "array: {*}\n", - .{&value}, - ); - } -} - -test "slice" { - { - const value: []const u8 = "abc"; - try testFmt("slice: abc\n", "slice: {}\n", .{value}); - } - { - const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; - try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); - } - - try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); - try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); -} - -test "pointer" { - { - const value = @intToPtr(*align(1) i32, 0xdeadbeef); - try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); - try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); - } - { - const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); - } - { - const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); - } -} - -test "cstr" { - try testFmt( - "cstr: Test C\n", - "cstr: {s}\n", - .{@ptrCast([*c]const u8, "Test C")}, - ); - try testFmt( - "cstr: Test C \n", - "cstr: {s:10}\n", - .{@ptrCast([*c]const u8, "Test C")}, - ); -} - -test "filesize" { - try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); - try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); -} - -test "struct" { - { - const Struct = struct { - field: u8, - }; - const value = Struct{ .field = 42 }; - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); - } - { - const Struct = struct { - a: u0, - b: u1, - }; - const value = Struct{ .a = 0, .b = 1 }; - try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); - } -} - -test "enum" { - const Enum = enum { - One, - Two, - }; - const value = Enum.Two; - try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); - try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); -} - -test "non-exhaustive enum" { - const Enum = enum(u16) { - One = 0x000f, - Two = 0xbeef, - _, - }; - try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); - try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); - try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); - try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); - try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); - try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); -} - -test "float.scientific" { - try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); - try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); - try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); - try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); -} - -test "float.scientific.precision" { - try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); - try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); - try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); - // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. - // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. - try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); -} - -test "float.special" { - try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); - // negative nan is not defined by IEE 754, - // and ARM thus normalizes it to positive nan - if (builtin.arch != builtin.Arch.arm) { - try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); - } - try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); - try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); -} - -test "float.decimal" { - try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); - try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); - try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); - try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); - // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). - // -11.12339... is rounded back up to -11.1234 - try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); - try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); - try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); - try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); - try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); - try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); - try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); -} - -test "float.libc.sanity" { - try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); - try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); - try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); - try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); - try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); - - // libc differences - // - // This is 0.015625 exactly according to gdb. We thus round down, - // however glibc rounds up for some reason. This occurs for all - // floats of the form x.yyyy25 on a precision point. - try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); - // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 - // also rounds to 630 so I'm inclined to believe libc is not - // optimal here. - try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); -} - -test "custom" { - const Vec2 = struct { - const SelfType = @This(); - x: f32, - y: f32, - - pub fn format( - self: SelfType, - comptime fmt: []const u8, - options: FormatOptions, - out_stream: var, - ) !void { - if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { - return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); - } else if (comptime std.mem.eql(u8, fmt, "d")) { - return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); - } else { - @compileError("Unknown format character: '" ++ fmt ++ "'"); - } - } - }; - - var buf1: [32]u8 = undefined; - var value = Vec2{ - .x = 10.2, - .y = 2.22, - }; - try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); - - // same thing but not passing a pointer - try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); -} - -test "struct" { - const S = struct { - a: u32, - b: anyerror, - }; - - const inst = S{ - .a = 456, - .b = error.Unused, - }; - - try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); -} - -test "union" { - const TU = union(enum) { - float: f32, - int: u32, - }; - - const UU = union { - float: f32, - int: u32, - }; - - const EU = extern union { - float: f32, - int: u32, - }; - - const tu_inst = TU{ .int = 123 }; - const uu_inst = UU{ .int = 456 }; - const eu_inst = EU{ .float = 321.123 }; - - try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); - - var buf: [100]u8 = undefined; - const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); - std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); - - const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); - std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); -} - -test "enum" { - const E = enum { - One, - Two, - Three, - }; - - const inst = E.Two; - - try testFmt("E.Two", "{}", .{inst}); -} - -test "struct.self-referential" { - const S = struct { - const SelfType = @This(); - a: ?*SelfType, - }; - - var inst = S{ - .a = null, - }; - inst.a = &inst; - - try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); -} - -test "struct.zero-size" { - const A = struct { - fn foo() void {} - }; - const B = struct { - a: A, - c: i32, - }; - - const a = A{}; - const b = B{ .a = a, .c = 0 }; - - try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); -} - -test "bytes.hex" { - const some_bytes = "\xCA\xFE\xBA\xBE"; - try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); - try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); - //Test Slices - try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); - try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); - const bytes_with_zeros = "\x00\x0E\xBA\xBE"; - try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); -} - -fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { - var buf: [100]u8 = undefined; - const result = try bufPrint(buf[0..], template, args); - if (mem.eql(u8, result, expected)) return; - - std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{}", .{expected}); - std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{}", .{result}); - std.debug.warn("\n======================================\n", .{}); - return error.TestFailed; -} - -pub fn trim(buf: []const u8) []const u8 { - var start: usize = 0; - while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} - - var end: usize = buf.len; - while (true) { - if (end > start) { - const new_end = end - 1; - if (isWhiteSpace(buf[new_end])) { - end = new_end; - continue; - } - } - break; - } - return buf[start..end]; -} - -test "trim" { - std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); - std.testing.expect(mem.eql(u8, "", trim(" "))); - std.testing.expect(mem.eql(u8, "", trim(""))); - std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); - std.testing.expect(mem.eql(u8, "abc", trim("abc "))); -} - -pub fn isWhiteSpace(byte: u8) bool { - return switch (byte) { - ' ', '\t', '\n', '\r' => true, - else => false, - }; -} - -pub fn hexToBytes(out: []u8, input: []const u8) !void { - if (out.len * 2 < input.len) - return error.InvalidLength; - - var in_i: usize = 0; - while (in_i != input.len) : (in_i += 2) { - const hi = try charToDigit(input[in_i], 16); - const lo = try charToDigit(input[in_i + 1], 16); - out[in_i / 2] = (hi << 4) | lo; - } -} - -test "hexToBytes" { - const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; - var pb: [32]u8 = undefined; - try hexToBytes(pb[0..], test_hex_str); - try testFmt(test_hex_str, "{X}", .{pb}); -} - -test "formatIntValue with comptime_int" { - const value: comptime_int = 123456789123456789; - - var buf: [20]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - try formatIntValue(value, "", FormatOptions{}, fbs.outStream()); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789")); -} - -test "formatType max_depth" { - const Vec2 = struct { - const SelfType = @This(); - x: f32, - y: f32, - - pub fn format( - self: SelfType, - comptime fmt: []const u8, - options: FormatOptions, - out_stream: var, - ) !void { - if (fmt.len == 0) { - return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); - } else { - @compileError("Unknown format string: '" ++ fmt ++ "'"); - } - } - }; - const E = enum { - One, - Two, - Three, - }; - const TU = union(enum) { - const SelfType = @This(); - float: f32, - int: u32, - ptr: ?*SelfType, - }; - const S = struct { - const SelfType = @This(); - a: ?*SelfType, - tu: TU, - e: E, - vec: Vec2, - }; - - var inst = S{ - .a = null, - .tu = TU{ .ptr = null }, - .e = E.Two, - .vec = Vec2{ .x = 10.2, .y = 2.22 }, - }; - inst.a = &inst; - inst.tu.ptr = &inst.tu; - - var buf: [1000]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - try formatType(inst, "", FormatOptions{}, fbs.outStream(), 0); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }")); - - fbs.reset(); - try formatType(inst, "", FormatOptions{}, fbs.outStream(), 1); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); - - fbs.reset(); - try formatType(inst, "", FormatOptions{}, fbs.outStream(), 2); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); - - fbs.reset(); - try formatType(inst, "", FormatOptions{}, fbs.outStream(), 3); - std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); -} - -test "positional" { - try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); - try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); - try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); - try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); - try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); -} - -test "positional with specifier" { - try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); -} - -test "positional/alignment/width/precision" { - try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); -} - -test "vector" { - // https://github.com/ziglang/zig/issues/3317 - if (builtin.arch == .mipsel) return error.SkipZigTest; - - const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; - const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; - const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; - - try testFmt("{ true, false, true, false }", "{}", .{vbool}); - try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); - try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); - try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); - try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); - try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); - try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); -} - -test "enum-literal" { - try testFmt(".hello_world", "{}", .{.hello_world}); -} diff --git a/lib/std/http/headers.zig b/lib/std/http/headers.zig index cb232a3e3e3e..1e1e71e3eb21 100644 --- a/lib/std/http/headers.zig +++ b/lib/std/http/headers.zig @@ -349,7 +349,7 @@ pub const Headers = struct { pub fn format( self: Self, comptime fmt: []const u8, - options: std.fmtstream.FormatOptions, + options: std.fmt.FormatOptions, out_stream: var, ) !void { for (self.toSlice()) |entry| { @@ -591,5 +591,5 @@ test "Headers.format" { \\foo: bar \\cookie: somevalue \\ - , try std.fmtstream.bufPrint(buf[0..], "{}", .{h})); + , try std.fmt.bufPrint(buf[0..], "{}", .{h})); } diff --git a/lib/std/io/out_stream.zig b/lib/std/io/out_stream.zig index 876a3b470132..bf3ae791a17e 100644 --- a/lib/std/io/out_stream.zig +++ b/lib/std/io/out_stream.zig @@ -25,7 +25,7 @@ pub fn OutStream( } pub fn print(self: Self, comptime format: []const u8, args: var) Error!void { - return std.fmtstream.format(self, format, args); + return std.fmt.format(self, format, args); } pub fn writeByte(self: Self, byte: u8) Error!void { diff --git a/lib/std/json.zig b/lib/std/json.zig index f3a081107800..f5a72d86da23 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2257,10 +2257,10 @@ pub fn stringify( const T = @TypeOf(value); switch (@typeInfo(T)) { .Float, .ComptimeFloat => { - return std.fmtstream.formatFloatScientific(value, std.fmtstream.FormatOptions{}, out_stream); + return std.fmt.formatFloatScientific(value, std.fmt.FormatOptions{}, out_stream); }, .Int, .ComptimeInt => { - return std.fmtstream.formatIntValue(value, "", std.fmtstream.FormatOptions{}, out_stream); + return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, out_stream); }, .Bool => { return out_stream.writeAll(if (value) "true" else "false"); @@ -2350,16 +2350,16 @@ pub fn stringify( // then it may be represented as a six-character sequence: a reverse solidus, followed // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. try out_stream.writeAll("\\u"); - try std.fmtstream.formatIntValue(codepoint, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream); + try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); } else { // To escape an extended character that is not in the Basic Multilingual Plane, // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair. const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800; const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00; try out_stream.writeAll("\\u"); - try std.fmtstream.formatIntValue(high, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream); + try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); try out_stream.writeAll("\\u"); - try std.fmtstream.formatIntValue(low, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream); + try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); } i += ulen - 1; }, diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index cef2bae98c93..95d0764f6866 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -518,7 +518,7 @@ pub const Int = struct { pub fn format( self: Int, comptime fmt: []const u8, - options: std.fmtstream.FormatOptions, + options: std.fmt.FormatOptions, out_stream: var, ) FmtError!void { self.assertWritable(); diff --git a/lib/std/net.zig b/lib/std/net.zig index a1225654f014..c2328b9bd0eb 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -268,14 +268,14 @@ pub const Address = extern union { pub fn format( self: Address, comptime fmt: []const u8, - options: std.fmtstream.FormatOptions, + options: std.fmt.FormatOptions, out_stream: var, ) !void { switch (self.any.family) { os.AF_INET => { const port = mem.bigToNative(u16, self.in.port); const bytes = @ptrCast(*const [4]u8, &self.in.addr); - try std.fmtstream.format(out_stream, "{}.{}.{}.{}:{}", .{ + try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{ bytes[0], bytes[1], bytes[2], @@ -286,7 +286,7 @@ pub const Address = extern union { os.AF_INET6 => { const port = mem.bigToNative(u16, self.in6.port); if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { - try std.fmtstream.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{ + try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{ self.in6.addr[12], self.in6.addr[13], self.in6.addr[14], @@ -317,19 +317,19 @@ pub const Address = extern union { } continue; } - try std.fmtstream.format(out_stream, "{x}", .{native_endian_parts[i]}); + try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]}); if (i != native_endian_parts.len - 1) { try out_stream.writeAll(":"); } } - try std.fmtstream.format(out_stream, "]:{}", .{port}); + try std.fmt.format(out_stream, "]:{}", .{port}); }, os.AF_UNIX => { if (!has_unix_sockets) { unreachable; } - try std.fmtstream.format(out_stream, "{}", .{&self.un.path}); + try std.fmt.format(out_stream, "{}", .{&self.un.path}); }, else => unreachable, } @@ -438,7 +438,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* const name_c = try std.cstr.addNullByte(allocator, name); defer allocator.free(name_c); - const port_c = try std.fmtstream.allocPrint(allocator, "{}\x00", .{port}); + const port_c = try std.fmt.allocPrint(allocator, "{}\x00", .{port}); defer allocator.free(port_c); const hints = os.addrinfo{ diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 0dc3b3020dd6..087f965c4ee3 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -29,7 +29,7 @@ test "parse and render IPv6 addresses" { }; for (ips) |ip, i| { var addr = net.Address.parseIp6(ip, 0) catch unreachable; - var newIp = std.fmtstream.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); } @@ -51,7 +51,7 @@ test "parse and render IPv4 addresses" { "127.0.0.1", }) |ip| { var addr = net.Address.parseIp4(ip, 0) catch unreachable; - var newIp = std.fmtstream.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); } diff --git a/lib/std/os.zig b/lib/std/os.zig index baea4cecc593..76a5dc2be55c 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3049,7 +3049,7 @@ pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP defer close(fd); var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; - const proc_path = std.fmtstream.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; + const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; return readlinkC(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer); } diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 5d9a678ce8b8..5c16483ecfe9 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -27,11 +27,11 @@ pub const Guid = extern struct { pub fn format( self: @This(), comptime f: []const u8, - options: std.fmtstream.FormatOptions, + options: std.fmt.FormatOptions, out_stream: var, ) Errors!void { if (f.len == 0) { - return std.fmtstream.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ + return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ self.time_low, self.time_mid, self.time_high_and_version, diff --git a/lib/std/progress.zig b/lib/std/progress.zig index ebb983cdc622..0264d99c91a8 100644 --- a/lib/std/progress.zig +++ b/lib/std/progress.zig @@ -130,11 +130,11 @@ pub const Progress = struct { var end: usize = 0; if (self.columns_written > 0) { // restore cursor position - end += (std.fmtstream.bufPrint(self.output_buffer[end..], "\x1b[{}D", .{self.columns_written}) catch unreachable).len; + end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{}D", .{self.columns_written}) catch unreachable).len; self.columns_written = 0; // clear rest of line - end += (std.fmtstream.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; + end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; } if (!self.done) { @@ -185,7 +185,7 @@ pub const Progress = struct { } fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: var) void { - if (std.fmtstream.bufPrint(self.output_buffer[end.*..], format, args)) |written| { + if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { const amt = written.len; end.* += amt; self.columns_written += amt; diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index c5a96df667fd..974247e2a1b7 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -2,7 +2,7 @@ const root = @import("@build"); const std = @import("std"); const builtin = @import("builtin"); const io = std.io; -const fmtstream = std.fmtstream; +const fmt = std.fmt; const Builder = std.build.Builder; const mem = std.mem; const process = std.process; @@ -153,7 +153,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { const allocator = builder.allocator; for (builder.top_level_steps.toSliceConst()) |top_level_step| { const name = if (&top_level_step.step == builder.default_step) - try fmtstream.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) + try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) else top_level_step.step.name; try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description }); @@ -175,7 +175,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { try out_stream.print(" (none)\n", .{}); } else { for (builder.available_options_list.toSliceConst()) |option| { - const name = try fmtstream.allocPrint(allocator, " -D{}=[{}]", .{ + const name = try fmt.allocPrint(allocator, " -D{}=[{}]", .{ option.name, Builder.typeIdName(option.type_id), }); diff --git a/lib/std/std.zig b/lib/std/std.zig index b7fe709c7c16..9277370ca6ab 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -38,7 +38,6 @@ pub const elf = @import("elf.zig"); pub const event = @import("event.zig"); pub const fifo = @import("fifo.zig"); pub const fmt = @import("fmt.zig"); -pub const fmtstream = @import("fmtstream.zig"); pub const fs = @import("fs.zig"); pub const hash = @import("hash.zig"); pub const hash_map = @import("hash_map.zig"); diff --git a/lib/std/target.zig b/lib/std/target.zig index c117f60cc04f..8beb7c3011db 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -972,7 +972,7 @@ pub const Target = struct { } pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![:0]u8 { - return std.fmtstream.allocPrint0(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); + return std.fmt.allocPrint0(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); } pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![:0]u8 { @@ -1158,7 +1158,7 @@ pub const Target = struct { var result: DynamicLinker = .{}; const S = struct { fn print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker { - r.max_byte = @intCast(u8, (std.fmtstream.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1); + r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1); return r.*; } fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker { diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index a60833f93cf3..cec3ea05e5ce 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -573,7 +573,7 @@ pub const CrossTarget = struct { .Dynamic => "", }; - return std.fmtstream.allocPrint0(allocator, "{}-{}{}", .{ arch, os, static_suffix }); + return std.fmt.allocPrint0(allocator, "{}-{}{}", .{ arch, os, static_suffix }); } pub const Executor = union(enum) { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 875ecd424c10..558b50b5b385 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -130,7 +130,7 @@ pub const NativePaths = struct { } pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void { - const item = try std.fmtstream.allocPrint0(self.include_dirs.allocator, fmt, args); + const item = try std.fmt.allocPrint0(self.include_dirs.allocator, fmt, args); errdefer self.include_dirs.allocator.free(item); try self.include_dirs.append(item); } @@ -140,7 +140,7 @@ pub const NativePaths = struct { } pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void { - const item = try std.fmtstream.allocPrint0(self.lib_dirs.allocator, fmt, args); + const item = try std.fmt.allocPrint0(self.lib_dirs.allocator, fmt, args); errdefer self.lib_dirs.allocator.free(item); try self.lib_dirs.append(item); } @@ -150,7 +150,7 @@ pub const NativePaths = struct { } pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void { - const item = try std.fmtstream.allocPrint0(self.warnings.allocator, fmt, args); + const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args); errdefer self.warnings.allocator.free(item); try self.warnings.append(item); } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index fdaee7d12ff9..7a45bb3c37f1 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -1051,7 +1051,7 @@ pub const Compilation = struct { } fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: var) !void { - const text = try std.fmtstream.allocPrint(self.gpa(), fmt, args); + const text = try std.fmt.allocPrint(self.gpa(), fmt, args); errdefer self.gpa().free(text); const msg = try Msg.createFromScope(self, tree_scope, span, text); @@ -1061,7 +1061,7 @@ pub const Compilation = struct { } fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void { - const text = try std.fmtstream.allocPrint(self.gpa(), fmt, args); + const text = try std.fmt.allocPrint(self.gpa(), fmt, args); errdefer self.gpa().free(text); const msg = try Msg.createFromCli(self, realpath, text); @@ -1154,7 +1154,7 @@ pub const Compilation = struct { const tmp_dir = try self.getTmpDir(); const file_prefix = self.getRandomFileName(); - const file_name = try std.fmtstream.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix }); + const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", .{ file_prefix[0..], suffix }); defer self.gpa().free(file_name); const full_path = try fs.path.join(self.gpa(), &[_][]const u8{ tmp_dir, file_name[0..] }); diff --git a/src-self-hosted/dep_tokenizer.zig b/src-self-hosted/dep_tokenizer.zig index 233b6781719f..c73719ebeaab 100644 --- a/src-self-hosted/dep_tokenizer.zig +++ b/src-self-hosted/dep_tokenizer.zig @@ -893,7 +893,7 @@ fn printSection(out: var, label: []const u8, bytes: []const u8) !void { fn printLabel(out: var, label: []const u8, bytes: []const u8) !void { var buf: [80]u8 = undefined; - var text = try std.fmtstream.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len }); + var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len }); try out.write(text); var i: usize = text.len; const end = 79; @@ -979,13 +979,13 @@ fn hexDump16(out: var, offset: usize, bytes: []const u8) !void { fn printDecValue(out: var, value: u64, width: u8) !void { var buffer: [20]u8 = undefined; - const len = std.fmtstream.formatIntBuf(buffer[0..], value, 10, false, width); + const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width); try out.write(buffer[0..len]); } fn printHexValue(out: var, value: u64, width: u8) !void { var buffer: [16]u8 = undefined; - const len = std.fmtstream.formatIntBuf(buffer[0..], value, 16, false, width); + const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width); try out.write(buffer[0..len]); } diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 3b65fb172bdb..0f9736456a0e 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -543,7 +543,7 @@ fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 { const allocator = args.allocator; const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe; - const arg1 = try std.fmtstream.allocPrint(allocator, "-print-file-name={}", .{args.search_basename}); + const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename}); defer allocator.free(arg1); const argv = [_][]const u8{ cc_exe, arg1 }; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index b63e6c24f472..1efa15574a78 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -296,13 +296,13 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { const is_library = ctx.comp.kind == .Lib; - const out_arg = try std.fmtstream.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", .{ctx.out_file_path.toSliceConst()}); + const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", .{ctx.out_file_path.toSliceConst()}); try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr)); if (ctx.comp.haveLibC()) { - try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmtstream.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr)); - try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmtstream.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr)); - try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmtstream.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr)); } if (ctx.link_in_crt) { @@ -310,20 +310,20 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { const d_str = if (ctx.comp.build_mode == .Debug) "d" else ""; if (ctx.comp.is_static) { - const cmt_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str}); + const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str}); try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr)); } else { - const msvcrt_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str}); + const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str}); try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr)); } - const vcruntime_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{ + const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{ lib_str, d_str, }); try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr)); - const crt_lib_name = try std.fmtstream.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str }); + const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str }); try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr)); // Visual C++ 2015 Conformance Changes @@ -383,7 +383,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { .IPhoneOS => try ctx.args.append("-iphoneos_version_min"), .IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"), } - const ver_str = try std.fmtstream.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{ + const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{ platform.major, platform.minor, platform.micro, @@ -445,7 +445,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { try ctx.args.append("-lSystem"); } else { if (mem.indexOfScalar(u8, lib.name, '/') == null) { - const arg = try std.fmtstream.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name}); + const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name}); try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); } else { const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig index 408c39fe7b93..ad506425d2af 100644 --- a/src-self-hosted/print_targets.zig +++ b/src-self-hosted/print_targets.zig @@ -138,7 +138,7 @@ pub fn cmdTargets( for (available_glibcs) |glibc| { try jws.arrayElem(); - const tmp = try std.fmtstream.allocPrint(allocator, "{}", .{glibc}); + const tmp = try std.fmt.allocPrint(allocator, "{}", .{glibc}); defer allocator.free(tmp); try jws.emitString(tmp); } diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 98e9fb06dfc9..e87164c9fb47 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -81,7 +81,7 @@ pub const TestContext = struct { msg: []const u8, ) !void { var file_index_buf: [20]u8 = undefined; - const file_index = try std.fmtstream.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); if (std.fs.path.dirname(file1_path)) |dirname| { @@ -114,10 +114,10 @@ pub const TestContext = struct { expected_output: []const u8, ) !void { var file_index_buf: [20]u8 = undefined; - const file_index = try std.fmtstream.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); - const output_file = try std.fmtstream.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); + const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); if (std.fs.path.dirname(file1_path)) |dirname| { try std.fs.cwd().makePath(dirname); } diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 7b05a76cb7cc..89d6f94d7bbf 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -89,7 +89,7 @@ const Scope = struct { var proposed_name = name; while (scope.contains(proposed_name)) { scope.mangle_count += 1; - proposed_name = try std.fmtstream.allocPrint(c.a(), "{}_{}", .{ name, scope.mangle_count }); + proposed_name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, scope.mangle_count }); } try scope.variables.push(.{ .name = name, .alias = proposed_name }); return proposed_name; @@ -246,7 +246,7 @@ pub const Context = struct { const line = ZigClangSourceManager_getSpellingLineNumber(c.source_manager, spelling_loc); const column = ZigClangSourceManager_getSpellingColumnNumber(c.source_manager, spelling_loc); - return std.fmtstream.allocPrint(c.a(), "{}:{}:{}", .{ filename, line, column }); + return std.fmt.allocPrint(c.a(), "{}:{}:{}", .{ filename, line, column }); } }; @@ -516,7 +516,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { const arg_name = blk: { const param_prefix = if (is_const) "" else "arg_"; - const bare_arg_name = try std.fmtstream.allocPrint(c.a(), "{}{}", .{ param_prefix, mangled_param_name }); + const bare_arg_name = try std.fmt.allocPrint(c.a(), "{}{}", .{ param_prefix, mangled_param_name }); break :blk try block_scope.makeMangledName(c, bare_arg_name); }; @@ -560,7 +560,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(var_name)) try std.fmtstream.allocPrint(c.a(), "{}_{}", .{var_name, c.getMangle()}) else var_name; + const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.a(), "{}_{}", .{ var_name, c.getMangle() }) else var_name; const var_decl_loc = ZigClangVarDecl_getLocation(var_decl); const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); @@ -620,7 +620,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmtstream.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmt.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -677,7 +677,7 @@ fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_l // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmtstream.allocPrint(c.a(), "{}_{}", .{typedef_name, c.getMangle()}) else typedef_name; + const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.a(), "{}_{}", .{ typedef_name, c.getMangle() }) else typedef_name; if (mem.eql(u8, checked_name, "uint8_t")) return transTypeDefAsBuiltin(c, typedef_decl, "u8") @@ -738,7 +738,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* // Record declarations such as `struct {...} x` have no name but they're not // anonymous hence here isAnonymousStructOrUnion is not needed if (bare_name.len == 0) { - bare_name = try std.fmtstream.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); is_unnamed = true; } @@ -755,7 +755,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* return null; } - const name = try std.fmtstream.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); + const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name); const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); @@ -812,7 +812,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* var is_anon = false; var raw_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl))); if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { - raw_name = try std.fmtstream.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + raw_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); is_anon = true; } const field_name = try appendIdentifier(c, raw_name); @@ -882,11 +882,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_decl))); var is_unnamed = false; if (bare_name.len == 0) { - bare_name = try std.fmtstream.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); is_unnamed = true; } - const name = try std.fmtstream.allocPrint(c.a(), "enum_{}", .{bare_name}); + const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name}); _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name); const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); node.eq_token = try appendToken(c, .Equal, "="); @@ -1754,9 +1754,9 @@ fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 { // Handle the remaining escapes Zig doesn't support by turning them // into their respective hex representation if (std.ascii.isCntrl(c)) - return std.fmtstream.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable + return std.fmt.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable else - return std.fmtstream.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable; + return std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable; }, }; } @@ -2436,7 +2436,7 @@ fn transCase( ) TransError!*ast.Node { const block_scope = scope.findBlockScope(rp.c) catch unreachable; const switch_scope = scope.getSwitch(); - const label = try std.fmtstream.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)}); + const label = try std.fmt.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)}); _ = try appendToken(rp.c, .Semicolon, ";"); const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: { @@ -4607,7 +4607,7 @@ fn finishTransFnProto( _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmtstream.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmt.allocPrint(rp.c.a(), "\"{}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -4866,7 +4866,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { const name = try c.str(raw_name); // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const mangled_name = if (isZigPrimitiveType(name)) try std.fmtstream.allocPrint(c.a(), "{}_{}", .{name, c.getMangle()}) else name; + const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, c.getMangle() }) else name; if (scope.containsNow(mangled_name)) { continue; } @@ -5151,11 +5151,11 @@ fn parseCNumLit(c: *Context, tok: *CToken, source: []const u8, source_loc: ZigCl switch (lit_bytes[1]) { '0'...'7' => { // Octal - lit_bytes = try std.fmtstream.allocPrint(c.a(), "0o{}", .{lit_bytes}); + lit_bytes = try std.fmt.allocPrint(c.a(), "0o{}", .{lit_bytes}); }, 'X' => { // Hexadecimal with capital X, valid in C but not in Zig - lit_bytes = try std.fmtstream.allocPrint(c.a(), "0x{}", .{lit_bytes[2..]}); + lit_bytes = try std.fmt.allocPrint(c.a(), "0x{}", .{lit_bytes[2..]}); }, else => {}, } @@ -5186,7 +5186,7 @@ fn parseCNumLit(c: *Context, tok: *CToken, source: []const u8, source_loc: ZigCl return &cast_node.base; } else if (tok.id == .FloatLiteral) { if (lit_bytes[0] == '.') - lit_bytes = try std.fmtstream.allocPrint(c.a(), "0{}", .{lit_bytes}); + lit_bytes = try std.fmt.allocPrint(c.a(), "0{}", .{lit_bytes}); if (tok.id.FloatLiteral == .None) { return transCreateNodeFloat(c, lit_bytes); } @@ -5319,7 +5319,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const num += c - 'A' + 10; }, else => { - i += std.fmtstream.formatIntBuf(bytes[i..], num, 16, false, std.fmtstream.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); num = 0; if (c == '\\') state = .Escape @@ -5345,7 +5345,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const }; num += c - '0'; } else { - i += std.fmtstream.formatIntBuf(bytes[i..], num, 16, false, std.fmtstream.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); num = 0; count = 0; if (c == '\\') @@ -5359,7 +5359,7 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const } } if (state == .Hex or state == .Octal) - i += std.fmtstream.formatIntBuf(bytes[i..], num, 16, false, std.fmtstream.FormatOptions{ .fill = '0', .width = 2 }); + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); return bytes[0..i]; } diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index c2b406823ddc..70ed754cea75 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -581,7 +581,7 @@ pub const Type = struct { errdefer comp.gpa().destroy(self); const u_or_i = "ui"[@boolToInt(key.is_signed)]; - const name = try std.fmtstream.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count }); + const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count }); errdefer comp.gpa().free(name); self.base.init(comp, .Int, name); @@ -764,13 +764,13 @@ pub const Type = struct { .Non => "", }; const name = switch (self.key.alignment) { - .Abi => try std.fmtstream.allocPrint(comp.gpa(), "{}{}{}{}", .{ + .Abi => try std.fmt.allocPrint(comp.gpa(), "{}{}{}{}", .{ size_str, mut_str, vol_str, self.key.child_type.name, }), - .Override => |alignment| try std.fmtstream.allocPrint(comp.gpa(), "{}align<{}> {}{}{}", .{ + .Override => |alignment| try std.fmt.allocPrint(comp.gpa(), "{}align<{}> {}{}{}", .{ size_str, alignment, mut_str, @@ -845,7 +845,7 @@ pub const Type = struct { }; errdefer comp.gpa().destroy(self); - const name = try std.fmtstream.allocPrint(comp.gpa(), "[{}]{}", .{ key.len, key.elem_type.name }); + const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", .{ key.len, key.elem_type.name }); errdefer comp.gpa().free(name); self.base.init(comp, .Array, name); diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig index c68eb1eebf61..ae994a0697d5 100644 --- a/test/src/compare_output.zig +++ b/test/src/compare_output.zig @@ -4,7 +4,7 @@ const std = @import("std"); const builtin = std.builtin; const build = std.build; const ArrayList = std.ArrayList; -const fmtstream = std.fmtstream; +const fmt = std.fmt; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; @@ -97,7 +97,7 @@ pub const CompareOutputContext = struct { switch (case.special) { Special.Asm => { - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "assemble-and-link {}", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -116,7 +116,7 @@ pub const CompareOutputContext = struct { }, Special.None => { for (self.modes) |mode| { - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ "compare-output", case.name, @tagName(mode), @@ -141,7 +141,7 @@ pub const CompareOutputContext = struct { } }, Special.RuntimeSafety => { - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig index 6304137e4a6c..14b0ce593c59 100644 --- a/test/src/run_translated_c.zig +++ b/test/src/run_translated_c.zig @@ -3,7 +3,7 @@ const std = @import("std"); const build = std.build; const ArrayList = std.ArrayList; -const fmtstream = std.fmtstream; +const fmt = std.fmt; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; @@ -76,7 +76,7 @@ pub const RunTranslatedCContext = struct { pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 250ec6175ee9..9a6bd0d32344 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -3,7 +3,7 @@ const std = @import("std"); const build = std.build; const ArrayList = std.ArrayList; -const fmtstream = std.fmtstream; +const fmt = std.fmt; const mem = std.mem; const fs = std.fs; const warn = std.debug.warn; @@ -99,7 +99,7 @@ pub const TranslateCContext = struct { const b = self.b; const translate_c_cmd = "translate-c"; - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/stage1/behavior/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig index b299301ced01..08b195494b54 100644 --- a/test/stage1/behavior/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -1,6 +1,6 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; -const fmtstream = @import("std").fmtstream; +const fmt = @import("std").fmt; const ET = union(enum) { SINT: i32, @@ -8,8 +8,8 @@ const ET = union(enum) { pub fn print(a: *const ET, buf: []u8) anyerror!usize { return switch (a.*) { - ET.SINT => |x| fmtstream.formatIntBuf(buf, x, 10, false, fmtstream.FormatOptions{}), - ET.UINT => |x| fmtstream.formatIntBuf(buf, x, 10, false, fmtstream.FormatOptions{}), + ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}), + ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}), }; } }; diff --git a/test/tests.zig b/test/tests.zig index 61f694bedb6f..22dad10e3e46 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -8,7 +8,7 @@ const Buffer = std.Buffer; const io = std.io; const fs = std.fs; const mem = std.mem; -const fmtstream = std.fmtstream; +const fmt = std.fmt; const ArrayList = std.ArrayList; const Mode = builtin.Mode; const LibExeObjStep = build.LibExeObjStep; @@ -484,7 +484,7 @@ pub const StackTracesContext = struct { const expect_for_mode = expect[@enumToInt(mode)]; if (expect_for_mode.len == 0) continue; - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ "stack-trace", name, @tagName(mode), @@ -943,7 +943,7 @@ pub const CompileErrorContext = struct { pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "compile-error {}", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -1009,7 +1009,7 @@ pub const StandaloneContext = struct { const b = self.b; for (self.modes) |mode| { - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "build {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", .{ root_src, @tagName(mode), }) catch unreachable; @@ -1152,7 +1152,7 @@ pub const GenHContext = struct { const b = self.b; const mode = builtin.Mode.Debug; - const annotated_case_name = fmtstream.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/tools/process_headers.zig b/tools/process_headers.zig index 43950750e9e4..abdc9fabf944 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -299,7 +299,7 @@ pub fn main() !void { std.debug.warn("unrecognized C ABI: {}\n", .{abi_name}); usageAndExit(args[0]); }; - const generic_name = try std.fmtstream.allocPrint(allocator, "generic-{}", .{abi_name}); + const generic_name = try std.fmt.allocPrint(allocator, "generic-{}", .{abi_name}); // TODO compiler crashed when I wrote this the canonical way var libc_targets: []const LibCTarget = undefined; @@ -440,7 +440,7 @@ pub fn main() !void { .specific => |a| @tagName(a), else => @tagName(dest_target.arch), }; - const out_subpath = try std.fmtstream.allocPrint(allocator, "{}-{}-{}", .{ + const out_subpath = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ arch_name, @tagName(dest_target.os), @tagName(dest_target.abi), diff --git a/tools/update_glibc.zig b/tools/update_glibc.zig index 35351e4d2a3f..84522aabe450 100644 --- a/tools/update_glibc.zig +++ b/tools/update_glibc.zig @@ -1,6 +1,6 @@ const std = @import("std"); const fs = std.fs; -const fmtstream = std.fmtstream; +const fmt = std.fmt; const assert = std.debug.assert; // Example abilist path: @@ -154,7 +154,7 @@ pub fn main() !void { const fn_set = &target_funcs_gop.kv.value.list; for (lib_names) |lib_name, lib_name_index| { - const basename = try fmtstream.allocPrint(allocator, "lib{}.abilist", .{lib_name}); + const basename = try fmt.allocPrint(allocator, "lib{}.abilist", .{lib_name}); const abi_list_filename = blk: { if (abi_list.targets[0].abi == .gnuabi64 and std.mem.eql(u8, lib_name, "c")) { break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "n64", basename });