diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig index 28ce2a561062..cf028f104ea4 100644 --- a/lib/std/buffer.zig +++ b/lib/std/buffer.zig @@ -65,13 +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.fmt.count(format, args) catch |err| switch (err) { + error.Overflow => return error.OutOfMemory, + }; var self = try Buffer.initSize(allocator, size); assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); return self; @@ -155,7 +151,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/builtin.zig b/lib/std/builtin.zig index fa5b49db37d5..c8cd6c05cf7b 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -436,19 +436,17 @@ pub const Version = struct { 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 { + 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.fmt.format(out_stream, "{}", .{self.major}); } else { - return std.fmt.format(context, Error, output, "{}.{}", .{ self.major, self.minor }); + return std.fmt.format(out_stream, "{}.{}", .{ self.major, self.minor }); } } else { - return std.fmt.format(context, Error, output, "{}.{}.{}", .{ 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/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 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/http/headers.zig b/lib/std/http/headers.zig index a7a1464f990a..1e1e71e3eb21 100644 --- a/lib/std/http/headers.zig +++ b/lib/std/http/headers.zig @@ -350,15 +350,13 @@ pub const Headers = struct { 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 { + 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"); } } }; diff --git a/lib/std/io/out_stream.zig b/lib/std/io/out_stream.zig index 03901f6672be..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.fmt.format(self, Error, writeAll, 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 4e2440d4e9b5..f5a72d86da23 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.fmt.formatFloatScientific(value, std.fmt.FormatOptions{}, out_stream); }, .Int, .ComptimeInt => { - return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, context, Errors, output); + return std.fmt.formatIntValue(value, "", std.fmt.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.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 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.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); + try out_stream.writeAll("\\u"); + try std.fmt.formatIntValue(low, "x", std.fmt.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 }); } diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 8fda3f647a68..95d0764f6866 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -519,16 +519,14 @@ pub const Int = struct { self: Int, comptime fmt: []const u8, options: std.fmt.FormatOptions, - context: var, - comptime FmtError: type, - output: fn (@TypeOf(context), []const u8) FmtError!void, + 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..c2328b9bd0eb 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -269,15 +269,13 @@ pub const Address = extern union { self: Address, comptime fmt: []const u8, options: std.fmt.FormatOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + 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.fmt.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.fmt.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.fmt.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.fmt.format(out_stream, "]:{}", .{port}); }, os.AF_UNIX => { if (!has_unix_sockets) { unreachable; } - try std.fmt.format(context, Errors, output, "{}", .{&self.un.path}); + try std.fmt.format(out_stream, "{}", .{&self.un.path}); }, else => unreachable, } diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 81d13ac1c314..5c16483ecfe9 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.fmt.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.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/src-self-hosted/dep_tokenizer.zig b/src-self-hosted/dep_tokenizer.zig index 5c250cdb994b..c73719ebeaab 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; } @@ -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/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index f14ebe330611..89d6f94d7bbf 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -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.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); @@ -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.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") @@ -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.fmt.allocPrint(c.a(), "{}_{}", .{ name, c.getMangle() }) else name; if (scope.containsNow(mangled_name)) { continue; }