Commit 72664df491

LemonBoy <thatlemon@gmail.com>
2021-03-06 09:57:21
std: Deprecate the B and Bi format specifiers
Following #8007 and #8137 let's get rid of the last weird format.
1 parent e47b754
Changed files (4)
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);
                         }
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" {
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);
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 <dir>] --out <dir> --abi <name>\n", .{arg0});
+    std.debug.warn("Usage: {s} [--search-path <dir>] --out <dir> --abi <name>\n", .{arg0});
     std.debug.warn("--search-path can be used any number of times.\n", .{});
     std.debug.warn("    subdirectories of search paths look like, e.g. x86_64-linux-gnu\n", .{});
     std.debug.warn("--out is a dir that will be created, and populated with the results\n", .{});