authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-03-06 09:57:21+01:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-03-07 14:58:45+02:00
log72664df4911f5d5bddead48acde6275f1d5f2a5e
tree3b4d64102af11acfa5bcf146f266c46b5cd150c5
parente47b754b28d658f05b72811207b9f953c1ab83e9

std: Deprecate the B and Bi format specifiers

Following #8007 and #8137 let's get rid of the last weird format.

4 files changed, 97 insertions(+), 70 deletions(-)

ci/srht/update-download-page.zig+2-1
......@@ -73,7 +73,8 @@ fn render(
7373 if (vars.get(var_name)) |value| {
7474 const trimmed = mem.trim(u8, value, " \r\n");
7575 if (fmt == .html and mem.endsWith(u8, var_name, "BYTESIZE")) {
76 try writer.print("{Bi:.1}", .{try std.fmt.parseInt(u64, trimmed, 10)});
76 const size = try std.fmt.parseInt(u64, trimmed, 10);
77 try writer.print("{:.1}", .{std.fmt.fmtIntSizeDec(size)});
7778 } else {
7879 try writer.writeAll(trimmed);
7980 }
lib/std/fmt.zig+72-56
......@@ -35,11 +35,11 @@ pub const FormatOptions = struct {
3535///
3636/// The format string must be comptime known and may contain placeholders following
3737/// this format:
38/// `{[position][specifier]:[fill][alignment][width].[precision]}`
38/// `{[argument][specifier]:[fill][alignment][width].[precision]}`
3939///
4040/// Each word between `[` and `]` is a parameter you have to replace with something:
4141///
42/// - *position* is the index of the argument that should be inserted
42/// - *argument* is either the index or the name of the argument that should be inserted
4343/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below)
4444/// - *fill* is a single character which is used to pad the formatted text
4545/// - *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 {
5252/// the digits after `:` is interpreted as *width*, not *fill*.
5353///
5454/// The *specifier* has several options for types:
55/// - `x` and `X`:
56/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case
57/// - output numeric value in hexadecimal notation
55/// - `x` and `X`: output numeric value in hexadecimal notation
5856/// - `s`:
5957/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination
6058/// - for slices of u8, print the entire slice as a string without zero-termination
61/// - `z`: escape the string with @"" syntax if it is not a valid Zig identifier.
62/// - `Z`: print the string escaping non-printable characters using Zig escape sequences.
63/// - `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.
64/// - `e` and `E`: if printing a string, escape non-printable characters
6559/// - `e`: output floating point value in scientific notation
6660/// - `d`: output numeric value in decimal notation
6761/// - `b`: output integer value in binary notation
......@@ -620,9 +614,9 @@ fn formatValue(
620614 writer: anytype,
621615) !void {
622616 if (comptime std.mem.eql(u8, fmt, "B")) {
623 return formatBytes(value, options, 1000, writer);
617 @compileError("specifier 'B' has been deprecated, wrap your argument in std.fmt.fmtIntSizeDec instead");
624618 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {
625 return formatBytes(value, options, 1024, writer);
619 @compileError("specifier 'Bi' has been deprecated, wrap your argument in std.fmt.fmtIntSizeBin instead");
626620 }
627621
628622 const T = @TypeOf(value);
......@@ -790,6 +784,67 @@ pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscap
790784 return .{ .data = bytes };
791785}
792786
787fn formatSizeImpl(comptime radix: comptime_int) type {
788 return struct {
789 fn f(
790 value: u64,
791 comptime fmt: []const u8,
792 options: FormatOptions,
793 writer: anytype,
794 ) !void {
795 if (value == 0) {
796 return writer.writeAll("0B");
797 }
798
799 const mags_si = " kMGTPEZY";
800 const mags_iec = " KMGTPEZY";
801
802 const log2 = math.log2(value);
803 const magnitude = switch (radix) {
804 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1),
805 1024 => math.min(log2 / 10, mags_iec.len - 1),
806 else => unreachable,
807 };
808 const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude));
809 const suffix = switch (radix) {
810 1000 => mags_si[magnitude],
811 1024 => mags_iec[magnitude],
812 else => unreachable,
813 };
814
815 try formatFloatDecimal(new_value, options, writer);
816
817 if (suffix == ' ') {
818 return writer.writeAll("B");
819 }
820
821 const buf = switch (radix) {
822 1000 => &[_]u8{ suffix, 'B' },
823 1024 => &[_]u8{ suffix, 'i', 'B' },
824 else => unreachable,
825 };
826 return writer.writeAll(buf);
827 }
828 };
829}
830
831const formatSizeDec = formatSizeImpl(1000).f;
832const formatSizeBin = formatSizeImpl(1024).f;
833
834/// Return a Formatter for a u64 value representing a file size.
835/// This formatter represents the number as multiple of 1000 and uses the SI
836/// measurement units (kB, MB, GB, ...).
837pub fn fmtIntSizeDec(value: u64) std.fmt.Formatter(formatSizeDec) {
838 return .{ .data = value };
839}
840
841/// Return a Formatter for a u64 value representing a file size.
842/// This formatter represents the number as multiple of 1024 and uses the IEC
843/// measurement units (KiB, MiB, GiB, ...).
844pub fn fmtIntSizeBin(value: u64) std.fmt.Formatter(formatSizeBin) {
845 return .{ .data = value };
846}
847
793848pub fn formatText(
794849 bytes: []const u8,
795850 comptime fmt: []const u8,
......@@ -1111,47 +1166,6 @@ pub fn formatFloatDecimal(
11111166 }
11121167}
11131168
1114pub fn formatBytes(
1115 value: anytype,
1116 options: FormatOptions,
1117 comptime radix: usize,
1118 writer: anytype,
1119) !void {
1120 if (value == 0) {
1121 return writer.writeAll("0B");
1122 }
1123
1124 const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value));
1125 const mags_si = " kMGTPEZY";
1126 const mags_iec = " KMGTPEZY";
1127
1128 const log2 = if (is_float) @floatToInt(usize, math.log2(value)) else math.log2(value);
1129 const magnitude = switch (radix) {
1130 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1),
1131 1024 => math.min(log2 / 10, mags_iec.len - 1),
1132 else => unreachable,
1133 };
1134 const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude));
1135 const suffix = switch (radix) {
1136 1000 => mags_si[magnitude],
1137 1024 => mags_iec[magnitude],
1138 else => unreachable,
1139 };
1140
1141 try formatFloatDecimal(new_value, options, writer);
1142
1143 if (suffix == ' ') {
1144 return writer.writeAll("B");
1145 }
1146
1147 const buf = switch (radix) {
1148 1000 => &[_]u8{ suffix, 'B' },
1149 1024 => &[_]u8{ suffix, 'i', 'B' },
1150 else => unreachable,
1151 };
1152 return writer.writeAll(buf);
1153}
1154
11551169pub fn formatInt(
11561170 value: anytype,
11571171 base: u8,
......@@ -1806,8 +1820,12 @@ test "cstr" {
18061820}
18071821
18081822test "filesize" {
1809 try expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)});
1810 try expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)});
1823 try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeDec(42)});
1824 try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeBin(42)});
1825 try expectFmt("file size: 63MB\n", "file size: {}\n", .{fmtIntSizeDec(63 * 1000 * 1000)});
1826 try expectFmt("file size: 63MiB\n", "file size: {}\n", .{fmtIntSizeBin(63 * 1024 * 1024)});
1827 try expectFmt("file size: 66.06MB\n", "file size: {:.2}\n", .{fmtIntSizeDec(63 * 1024 * 1024)});
1828 try expectFmt("file size: 60.08MiB\n", "file size: {:.2}\n", .{fmtIntSizeBin(63 * 1000 * 1000)});
18111829}
18121830
18131831test "struct" {
......@@ -2213,8 +2231,6 @@ test "vector" {
22132231 try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64});
22142232 try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
22152233 try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});
2216 try expectFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64});
2217 try expectFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64});
22182234}
22192235
22202236test "enum-literal" {
src/link/Coff.zig+5-1
......@@ -701,7 +701,11 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
701701 }
702702 } else {
703703 const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
704 log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len });
704 log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{
705 mem.spanZ(decl.name),
706 vaddr,
707 std.fmt.fmtIntSizeDec(code.len),
708 });
705709 errdefer self.freeTextBlock(&decl.link.coff);
706710 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
707711 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
tools/process_headers.zig+18-12
......@@ -270,7 +270,7 @@ pub fn main() !void {
270270 if (std.mem.eql(u8, args[arg_i], "--help"))
271271 usageAndExit(args[0]);
272272 if (arg_i + 1 >= args.len) {
273 std.debug.warn("expected argument after '{}'\n", .{args[arg_i]});
273 std.debug.warn("expected argument after '{s}'\n", .{args[arg_i]});
274274 usageAndExit(args[0]);
275275 }
276276
......@@ -283,7 +283,7 @@ pub fn main() !void {
283283 assert(opt_abi == null);
284284 opt_abi = args[arg_i + 1];
285285 } else {
286 std.debug.warn("unrecognized argument: {}\n", .{args[arg_i]});
286 std.debug.warn("unrecognized argument: {s}\n", .{args[arg_i]});
287287 usageAndExit(args[0]);
288288 }
289289
......@@ -297,10 +297,10 @@ pub fn main() !void {
297297 else if (std.mem.eql(u8, abi_name, "glibc"))
298298 LibCVendor.glibc
299299 else {
300 std.debug.warn("unrecognized C ABI: {}\n", .{abi_name});
300 std.debug.warn("unrecognized C ABI: {s}\n", .{abi_name});
301301 usageAndExit(args[0]);
302302 };
303 const generic_name = try std.fmt.allocPrint(allocator, "generic-{}", .{abi_name});
303 const generic_name = try std.fmt.allocPrint(allocator, "generic-{s}", .{abi_name});
304304
305305 // TODO compiler crashed when I wrote this the canonical way
306306 var libc_targets: []const LibCTarget = undefined;
......@@ -368,10 +368,10 @@ pub fn main() !void {
368368 if (gop.found_existing) {
369369 max_bytes_saved += raw_bytes.len;
370370 gop.entry.value.hit_count += 1;
371 std.debug.warn("duplicate: {} {} ({Bi:2})\n", .{
371 std.debug.warn("duplicate: {s} {s} ({:2})\n", .{
372372 libc_target.name,
373373 rel_path,
374 raw_bytes.len,
374 std.fmt.fmtIntSizeDec(raw_bytes.len),
375375 });
376376 } else {
377377 gop.entry.value = Contents{
......@@ -390,16 +390,19 @@ pub fn main() !void {
390390 };
391391 try target_to_hash.putNoClobber(dest_target, hash);
392392 },
393 else => std.debug.warn("warning: weird file: {}\n", .{full_path}),
393 else => std.debug.warn("warning: weird file: {s}\n", .{full_path}),
394394 }
395395 }
396396 }
397397 break;
398398 } else {
399 std.debug.warn("warning: libc target not found: {}\n", .{libc_target.name});
399 std.debug.warn("warning: libc target not found: {s}\n", .{libc_target.name});
400400 }
401401 }
402 std.debug.warn("summary: {Bi:2} could be reduced to {Bi:2}\n", .{ total_bytes, total_bytes - max_bytes_saved });
402 std.debug.warn("summary: {:2} could be reduced to {:2}\n", .{
403 std.fmt.fmtIntSizeDec(total_bytes),
404 std.fmt.fmtIntSizeDec(total_bytes - max_bytes_saved),
405 });
403406 try std.fs.cwd().makePath(out_dir);
404407
405408 var missed_opportunity_bytes: usize = 0;
......@@ -428,7 +431,10 @@ pub fn main() !void {
428431 if (contender.hit_count > 1) {
429432 const this_missed_bytes = contender.hit_count * contender.bytes.len;
430433 missed_opportunity_bytes += this_missed_bytes;
431 std.debug.warn("Missed opportunity ({Bi:2}): {}\n", .{ this_missed_bytes, path_kv.key });
434 std.debug.warn("Missed opportunity ({:2}): {s}\n", .{
435 std.fmt.fmtIntSizeDec(this_missed_bytes),
436 path_kv.key,
437 });
432438 } else break;
433439 }
434440 }
......@@ -442,7 +448,7 @@ pub fn main() !void {
442448 .specific => |a| @tagName(a),
443449 else => @tagName(dest_target.arch),
444450 };
445 const out_subpath = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{
451 const out_subpath = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{
446452 arch_name,
447453 @tagName(dest_target.os),
448454 @tagName(dest_target.abi),
......@@ -455,7 +461,7 @@ pub fn main() !void {
455461}
456462
457463fn usageAndExit(arg0: []const u8) noreturn {
458 std.debug.warn("Usage: {} [--search-path <dir>] --out <dir> --abi <name>\n", .{arg0});
464 std.debug.warn("Usage: {s} [--search-path <dir>] --out <dir> --abi <name>\n", .{arg0});
459465 std.debug.warn("--search-path can be used any number of times.\n", .{});
460466 std.debug.warn(" subdirectories of search paths look like, e.g. x86_64-linux-gnu\n", .{});
461467 std.debug.warn("--out is a dir that will be created, and populated with the results\n", .{});