std: replace formatInteger with formatNumber

This commit is contained in:
Andrew Kelley 2025-07-09 14:54:54 -07:00
parent 5bc95a6fa2
commit 51a9a6aab6
3 changed files with 96 additions and 37 deletions

View file

@ -37,6 +37,45 @@ pub const Options = struct {
width: ?usize = null, width: ?usize = null,
alignment: Alignment = default_alignment, alignment: Alignment = default_alignment,
fill: u8 = default_fill_char, fill: u8 = default_fill_char,
pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number {
return .{
.mode = mode,
.case = case,
.precision = o.precision,
.width = o.width,
.alignment = o.alignment,
.fill = o.fill,
};
}
};
pub const Number = struct {
mode: Mode = .decimal,
/// Affects hex digits as well as floating point "inf"/"INF".
case: Case = .lower,
precision: ?usize = null,
width: ?usize = null,
alignment: Alignment = default_alignment,
fill: u8 = default_fill_char,
pub const Mode = enum {
decimal,
binary,
octal,
hex,
scientific,
pub fn base(mode: Mode) ?u8 {
return switch (mode) {
.decimal => 10,
.binary => 2,
.octal => 8,
.hex => 16,
.scientific => null,
};
}
};
}; };
/// Renders fmt string with args, calling `writer` with slices of bytes. /// Renders fmt string with args, calling `writer` with slices of bytes.

View file

@ -744,11 +744,8 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.pointer => |info| { .pointer => |info| {
try w.writeAll(@typeName(info.child) ++ "@"); try w.writeAll(@typeName(info.child) ++ "@");
if (info.size == .slice) const int = if (info.size == .slice) @intFromPtr(value.ptr) else @intFromPtr(value);
try w.printInt(@intFromPtr(value.ptr), 16, .lower, .{}) return w.printInt(int, 16, .lower, .{});
else
try w.printInt(@intFromPtr(value), 16, .lower, .{});
return;
}, },
.optional => |info| { .optional => |info| {
if (@typeInfo(info.child) == .pointer) { if (@typeInfo(info.child) == .pointer) {
@ -777,9 +774,9 @@ pub fn printValue(
'*' => return w.printAddress(value), '*' => return w.printAddress(value),
'f' => return value.format(w), 'f' => return value.format(w),
'd' => switch (@typeInfo(T)) { 'd' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloat(w, value, .decimal, options), .float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)),
.int, .comptime_int => return printInt(w, value, 10, .lower, options), .int, .comptime_int => return printInt(w, value, 10, .lower, options),
.@"struct" => return value.formatInteger(w, 10, .lower), .@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)),
.@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options), .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options),
.vector => return printVector(w, fmt, options, value, max_depth), .vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value), else => invalidFmtError(fmt, value),
@ -789,22 +786,22 @@ pub fn printValue(
'b' => switch (@typeInfo(T)) { 'b' => switch (@typeInfo(T)) {
.int, .comptime_int => return printInt(w, value, 2, .lower, options), .int, .comptime_int => return printInt(w, value, 2, .lower, options),
.@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options), .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options),
.@"struct" => return value.formatInteger(w, 2, .lower), .@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)),
.vector => return printVector(w, fmt, options, value, max_depth), .vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value), else => invalidFmtError(fmt, value),
}, },
'o' => switch (@typeInfo(T)) { 'o' => switch (@typeInfo(T)) {
.int, .comptime_int => return printInt(w, value, 8, .lower, options), .int, .comptime_int => return printInt(w, value, 8, .lower, options),
.@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options), .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options),
.@"struct" => return value.formatInteger(w, 8, .lower), .@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)),
.vector => return printVector(w, fmt, options, value, max_depth), .vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value), else => invalidFmtError(fmt, value),
}, },
'x' => switch (@typeInfo(T)) { 'x' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloatHexOptions(w, value, .lower, options), .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
.int, .comptime_int => return printInt(w, value, 16, .lower, options), .int, .comptime_int => return printInt(w, value, 16, .lower, options),
.@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options), .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options),
.@"struct" => return value.formatInteger(w, 16, .lower), .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)),
.pointer => |info| switch (info.size) { .pointer => |info| switch (info.size) {
.one, .slice => { .one, .slice => {
const slice: []const u8 = value; const slice: []const u8 = value;
@ -823,10 +820,10 @@ pub fn printValue(
else => invalidFmtError(fmt, value), else => invalidFmtError(fmt, value),
}, },
'X' => switch (@typeInfo(T)) { 'X' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloatHexOptions(w, value, .lower, options), .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
.int, .comptime_int => return printInt(w, value, 16, .upper, options), .int, .comptime_int => return printInt(w, value, 16, .upper, options),
.@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options), .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options),
.@"struct" => return value.formatInteger(w, 16, .upper), .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)),
.pointer => |info| switch (info.size) { .pointer => |info| switch (info.size) {
.one, .slice => { .one, .slice => {
const slice: []const u8 = value; const slice: []const u8 = value;
@ -872,8 +869,13 @@ pub fn printValue(
else => invalidFmtError(fmt, value), else => invalidFmtError(fmt, value),
}, },
'e' => switch (@typeInfo(T)) { 'e' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloat(w, value, .scientific, options), .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),
.@"struct" => return value.formatFloat(w, .scientific), .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),
else => invalidFmtError(fmt, value),
},
'E' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)),
.@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)),
else => invalidFmtError(fmt, value), else => invalidFmtError(fmt, value),
}, },
't' => switch (@typeInfo(T)) { 't' => switch (@typeInfo(T)) {
@ -923,7 +925,7 @@ pub fn printValue(
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.float, .comptime_float => { .float, .comptime_float => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
return printFloat(w, value, .decimal, options); return printFloat(w, value, options.toNumber(.decimal, .lower));
}, },
.int, .comptime_int => { .int, .comptime_int => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
@ -1262,12 +1264,13 @@ pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void {
return w.writeAll(buf[0..len]); return w.writeAll(buf[0..len]);
} }
pub fn printFloat( /// Uses a larger stack buffer; asserts mode is decimal or scientific.
w: *Writer, pub fn printFloat(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
value: anytype, const mode: std.fmt.float.Mode = switch (options.mode) {
mode: std.fmt.float.Mode, .decimal => .decimal,
options: std.fmt.Options, .scientific => .scientific,
) Error!void { .binary, .octal, .hex => unreachable,
};
var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined; var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
const s = std.fmt.float.render(&buf, value, .{ const s = std.fmt.float.render(&buf, value, .{
.mode = mode, .mode = mode,
@ -1275,20 +1278,36 @@ pub fn printFloat(
}) catch |err| switch (err) { }) catch |err| switch (err) {
error.BufferTooSmall => "(float)", error.BufferTooSmall => "(float)",
}; };
return w.alignBufferOptions(s, options); return w.alignBuffer(s, options.width orelse s.len, options.alignment, options.fill);
} }
pub fn printFloatHexOptions(w: *Writer, value: anytype, case: std.fmt.Case, options: std.fmt.Options) Error!void { /// Uses a smaller stack buffer; asserts mode is not decimal or scientific.
pub fn printFloatHexOptions(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
var buf: [50]u8 = undefined; // for aligning var buf: [50]u8 = undefined; // for aligning
var sub_writer: Writer = .fixed(&buf); var sub_writer: Writer = .fixed(&buf);
printFloatHex(&sub_writer, value, case, options.precision) catch unreachable; // buf is large enough switch (options.mode) {
return w.alignBufferOptions(sub_writer.buffered(), options); .decimal => unreachable,
.scientific => unreachable,
.binary => @panic("TODO"),
.octal => @panic("TODO"),
.hex => {},
}
printFloatHex(&sub_writer, value, options.case, options.precision) catch unreachable; // buf is large enough
const printed = sub_writer.buffered();
return w.alignBuffer(printed, options.width orelse printed.len, options.alignment, options.fill);
} }
pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void { pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void {
if (std.math.signbit(value)) try w.writeByte('-'); if (std.math.signbit(value)) try w.writeByte('-');
if (std.math.isNan(value)) return w.writeAll("nan"); if (std.math.isNan(value)) return w.writeAll(switch (case) {
if (std.math.isInf(value)) return w.writeAll("inf"); .lower => "nan",
.upper => "NAN",
});
if (std.math.isInf(value)) return w.writeAll(switch (case) {
.lower => "inf",
.upper => "INF",
});
const T = @TypeOf(value); const T = @TypeOf(value);
const TU = std.meta.Int(.unsigned, @bitSizeOf(T)); const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
@ -1822,7 +1841,7 @@ test printInt {
test "printFloat with comptime_float" { test "printFloat with comptime_float" {
var buf: [20]u8 = undefined; var buf: [20]u8 = undefined;
var w: Writer = .fixed(&buf); var w: Writer = .fixed(&buf);
try w.printFloat(@as(comptime_float, 1.0), .scientific, .{}); try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));
try std.testing.expectEqualStrings(w.buffered(), "1e0"); try std.testing.expectEqualStrings(w.buffered(), "1e0");
try std.testing.expectFmt("1", "{}", .{1.0}); try std.testing.expectFmt("1", "{}", .{1.0});
} }

View file

@ -2030,11 +2030,11 @@ pub const Mutable = struct {
} }
pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void { pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void {
return formatInteger(self, w, 10, .lower); return formatNumber(self, w, .{});
} }
pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { pub fn formatNumber(self: Const, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
return self.toConst().formatInteger(w, base, case); return self.toConst().formatNumber(w, n);
} }
}; };
@ -2329,7 +2329,7 @@ pub const Const = struct {
/// this function will fail to print the string, printing "(BigInt)" instead of a number. /// this function will fail to print the string, printing "(BigInt)" instead of a number.
/// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
/// See `toString` and `toStringAlloc` for a way to print big integers without failure. /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { pub fn formatNumber(self: Const, w: *std.io.Writer, number: std.fmt.Number) std.io.Writer.Error!void {
const available_len = 64; const available_len = 64;
if (self.limbs.len > available_len) if (self.limbs.len > available_len)
return w.writeAll("(BigInt)"); return w.writeAll("(BigInt)");
@ -2341,7 +2341,8 @@ pub const Const = struct {
.positive = false, .positive = false,
}; };
var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined; var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined;
const len = self.toString(&buf, base, case, &limbs); const base: u8 = number.mode.base() orelse @panic("TODO print big int in scientific form");
const len = self.toString(&buf, base, number.case, &limbs);
return w.writeAll(buf[0..len]); return w.writeAll(buf[0..len]);
} }
@ -2913,15 +2914,15 @@ pub const Managed = struct {
/// To allow `std.fmt.format` to work with `Managed`. /// To allow `std.fmt.format` to work with `Managed`.
pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void { pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void {
return formatInteger(self, w, 10, .lower); return formatNumber(self, w, .{});
} }
/// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`, /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,
/// this function will fail to print the string, printing "(BigInt)" instead of a number. /// this function will fail to print the string, printing "(BigInt)" instead of a number.
/// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
/// See `toString` and `toStringAlloc` for a way to print big integers without failure. /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
pub fn formatInteger(self: Managed, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { pub fn formatNumber(self: Managed, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
return self.toConst().formatInteger(w, base, case); return self.toConst().formatNumber(w, n);
} }
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==