Commit 72664df491
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", .{});