diff --git a/ci/srht/update-download-page.zig b/ci/srht/update-download-page.zig index 0fb1f3913c..d9dadc9ff2 100644 --- a/ci/srht/update-download-page.zig +++ b/ci/srht/update-download-page.zig @@ -73,7 +73,8 @@ fn render( if (vars.get(var_name)) |value| { const trimmed = mem.trim(u8, value, " \r\n"); if (fmt == .html and mem.endsWith(u8, var_name, "BYTESIZE")) { - try writer.print("{Bi:.1}", .{try std.fmt.parseInt(u64, trimmed, 10)}); + const size = try std.fmt.parseInt(u64, trimmed, 10); + try writer.print("{:.1}", .{std.fmt.fmtIntSizeDec(size)}); } else { try writer.writeAll(trimmed); } diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 1f924bf00c..2d176e761a 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -35,11 +35,11 @@ pub const FormatOptions = struct { /// /// The format string must be comptime known and may contain placeholders following /// this format: -/// `{[position][specifier]:[fill][alignment][width].[precision]}` +/// `{[argument][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 +/// - *argument* is either the index or the name 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 @@ -52,16 +52,10 @@ pub const FormatOptions = struct { /// 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 +/// - `x` and `X`: output numeric value in hexadecimal notation /// - `s`: /// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination /// - for slices of u8, print the entire slice as a string without zero-termination -/// - `z`: escape the string with @"" syntax if it is not a valid Zig identifier. -/// - `Z`: print the string escaping non-printable characters using Zig escape sequences. -/// - `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` and `E`: if printing a string, escape non-printable characters /// - `e`: output floating point value in scientific notation /// - `d`: output numeric value in decimal notation /// - `b`: output integer value in binary notation @@ -620,9 +614,9 @@ fn formatValue( writer: anytype, ) !void { if (comptime std.mem.eql(u8, fmt, "B")) { - return formatBytes(value, options, 1000, writer); + @compileError("specifier 'B' has been deprecated, wrap your argument in std.fmt.fmtIntSizeDec instead"); } else if (comptime std.mem.eql(u8, fmt, "Bi")) { - return formatBytes(value, options, 1024, writer); + @compileError("specifier 'Bi' has been deprecated, wrap your argument in std.fmt.fmtIntSizeBin instead"); } const T = @TypeOf(value); @@ -790,6 +784,67 @@ pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscap return .{ .data = bytes }; } +fn formatSizeImpl(comptime radix: comptime_int) type { + return struct { + fn f( + value: u64, + comptime fmt: []const u8, + options: FormatOptions, + writer: anytype, + ) !void { + if (value == 0) { + return writer.writeAll("0B"); + } + + const mags_si = " kMGTPEZY"; + const mags_iec = " KMGTPEZY"; + + const log2 = math.log2(value); + const magnitude = switch (radix) { + 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1), + 1024 => math.min(log2 / 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, writer); + + if (suffix == ' ') { + return writer.writeAll("B"); + } + + const buf = switch (radix) { + 1000 => &[_]u8{ suffix, 'B' }, + 1024 => &[_]u8{ suffix, 'i', 'B' }, + else => unreachable, + }; + return writer.writeAll(buf); + } + }; +} + +const formatSizeDec = formatSizeImpl(1000).f; +const formatSizeBin = formatSizeImpl(1024).f; + +/// Return a Formatter for a u64 value representing a file size. +/// This formatter represents the number as multiple of 1000 and uses the SI +/// measurement units (kB, MB, GB, ...). +pub fn fmtIntSizeDec(value: u64) std.fmt.Formatter(formatSizeDec) { + return .{ .data = value }; +} + +/// Return a Formatter for a u64 value representing a file size. +/// This formatter represents the number as multiple of 1024 and uses the IEC +/// measurement units (KiB, MiB, GiB, ...). +pub fn fmtIntSizeBin(value: u64) std.fmt.Formatter(formatSizeBin) { + return .{ .data = value }; +} + pub fn formatText( bytes: []const u8, comptime fmt: []const u8, @@ -1111,47 +1166,6 @@ pub fn formatFloatDecimal( } } -pub fn formatBytes( - value: anytype, - options: FormatOptions, - comptime radix: usize, - writer: anytype, -) !void { - if (value == 0) { - return writer.writeAll("0B"); - } - - const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value)); - const mags_si = " kMGTPEZY"; - const mags_iec = " KMGTPEZY"; - - const log2 = if (is_float) @floatToInt(usize, math.log2(value)) else math.log2(value); - const magnitude = switch (radix) { - 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1), - 1024 => math.min(log2 / 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, writer); - - if (suffix == ' ') { - return writer.writeAll("B"); - } - - const buf = switch (radix) { - 1000 => &[_]u8{ suffix, 'B' }, - 1024 => &[_]u8{ suffix, 'i', 'B' }, - else => unreachable, - }; - return writer.writeAll(buf); -} - pub fn formatInt( value: anytype, base: u8, @@ -1806,8 +1820,12 @@ test "cstr" { } test "filesize" { - try expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); - try expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); + try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeDec(42)}); + try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeBin(42)}); + try expectFmt("file size: 63MB\n", "file size: {}\n", .{fmtIntSizeDec(63 * 1000 * 1000)}); + try expectFmt("file size: 63MiB\n", "file size: {}\n", .{fmtIntSizeBin(63 * 1024 * 1024)}); + try expectFmt("file size: 66.06MB\n", "file size: {:.2}\n", .{fmtIntSizeDec(63 * 1024 * 1024)}); + try expectFmt("file size: 60.08MiB\n", "file size: {:.2}\n", .{fmtIntSizeBin(63 * 1000 * 1000)}); } test "struct" { @@ -2213,8 +2231,6 @@ test "vector" { try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); - try expectFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); - try expectFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); } test "enum-literal" { diff --git a/src/link/Coff.zig b/src/link/Coff.zig index a73b8aaf9c..8b2b13eb71 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -701,7 +701,11 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { } } else { const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); + log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ + mem.spanZ(decl.name), + vaddr, + std.fmt.fmtIntSizeDec(code.len), + }); errdefer self.freeTextBlock(&decl.link.coff); self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); diff --git a/tools/process_headers.zig b/tools/process_headers.zig index c5279ba74f..9c0822a9f3 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -270,7 +270,7 @@ pub fn main() !void { if (std.mem.eql(u8, args[arg_i], "--help")) usageAndExit(args[0]); if (arg_i + 1 >= args.len) { - std.debug.warn("expected argument after '{}'\n", .{args[arg_i]}); + std.debug.warn("expected argument after '{s}'\n", .{args[arg_i]}); usageAndExit(args[0]); } @@ -283,7 +283,7 @@ pub fn main() !void { assert(opt_abi == null); opt_abi = args[arg_i + 1]; } else { - std.debug.warn("unrecognized argument: {}\n", .{args[arg_i]}); + std.debug.warn("unrecognized argument: {s}\n", .{args[arg_i]}); usageAndExit(args[0]); } @@ -297,10 +297,10 @@ pub fn main() !void { else if (std.mem.eql(u8, abi_name, "glibc")) LibCVendor.glibc else { - std.debug.warn("unrecognized C ABI: {}\n", .{abi_name}); + std.debug.warn("unrecognized C ABI: {s}\n", .{abi_name}); usageAndExit(args[0]); }; - const generic_name = try std.fmt.allocPrint(allocator, "generic-{}", .{abi_name}); + const generic_name = try std.fmt.allocPrint(allocator, "generic-{s}", .{abi_name}); // TODO compiler crashed when I wrote this the canonical way var libc_targets: []const LibCTarget = undefined; @@ -368,10 +368,10 @@ pub fn main() !void { if (gop.found_existing) { max_bytes_saved += raw_bytes.len; gop.entry.value.hit_count += 1; - std.debug.warn("duplicate: {} {} ({Bi:2})\n", .{ + std.debug.warn("duplicate: {s} {s} ({:2})\n", .{ libc_target.name, rel_path, - raw_bytes.len, + std.fmt.fmtIntSizeDec(raw_bytes.len), }); } else { gop.entry.value = Contents{ @@ -390,16 +390,19 @@ pub fn main() !void { }; try target_to_hash.putNoClobber(dest_target, hash); }, - else => std.debug.warn("warning: weird file: {}\n", .{full_path}), + else => std.debug.warn("warning: weird file: {s}\n", .{full_path}), } } } break; } else { - std.debug.warn("warning: libc target not found: {}\n", .{libc_target.name}); + std.debug.warn("warning: libc target not found: {s}\n", .{libc_target.name}); } } - std.debug.warn("summary: {Bi:2} could be reduced to {Bi:2}\n", .{ total_bytes, total_bytes - max_bytes_saved }); + std.debug.warn("summary: {:2} could be reduced to {:2}\n", .{ + std.fmt.fmtIntSizeDec(total_bytes), + std.fmt.fmtIntSizeDec(total_bytes - max_bytes_saved), + }); try std.fs.cwd().makePath(out_dir); var missed_opportunity_bytes: usize = 0; @@ -428,7 +431,10 @@ pub fn main() !void { if (contender.hit_count > 1) { const this_missed_bytes = contender.hit_count * contender.bytes.len; missed_opportunity_bytes += this_missed_bytes; - std.debug.warn("Missed opportunity ({Bi:2}): {}\n", .{ this_missed_bytes, path_kv.key }); + std.debug.warn("Missed opportunity ({:2}): {s}\n", .{ + std.fmt.fmtIntSizeDec(this_missed_bytes), + path_kv.key, + }); } else break; } } @@ -442,7 +448,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.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ arch_name, @tagName(dest_target.os), @tagName(dest_target.abi), @@ -455,7 +461,7 @@ pub fn main() !void { } fn usageAndExit(arg0: []const u8) noreturn { - std.debug.warn("Usage: {} [--search-path