| ... | @@ -35,11 +35,11 @@ pub const FormatOptions = struct { | ... | @@ -35,11 +35,11 @@ pub const FormatOptions = struct { |
| 35 | /// | 35 | /// |
| 36 | /// The format string must be comptime known and may contain placeholders following | 36 | /// The format string must be comptime known and may contain placeholders following |
| 37 | /// this format: | 37 | /// this format: |
| 38 | /// `{[position][specifier]:[fill][alignment][width].[precision]}` | 38 | /// `{[argument][specifier]:[fill][alignment][width].[precision]}` |
| 39 | /// | 39 | /// |
| 40 | /// Each word between `[` and `]` is a parameter you have to replace with something: | 40 | /// Each word between `[` and `]` is a parameter you have to replace with something: |
| 41 | /// | 41 | /// |
| 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 |
| 43 | /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) | 43 | /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) |
| 44 | /// - *fill* is a single character which is used to pad the formatted text | 44 | /// - *fill* is a single character which is used to pad the formatted text |
| 45 | /// - *alignment* is one of the three characters `<`, `^` or `>`. they define if the text is *left*, *center*, or *right* aligned | 45 | /// - *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 { | ... | @@ -52,16 +52,10 @@ pub const FormatOptions = struct { |
| 52 | /// the digits after `:` is interpreted as *width*, not *fill*. | 52 | /// the digits after `:` is interpreted as *width*, not *fill*. |
| 53 | /// | 53 | /// |
| 54 | /// The *specifier* has several options for types: | 54 | /// The *specifier* has several options for types: |
| 55 | /// - `x` and `X`: | 55 | /// - `x` and `X`: output numeric value in hexadecimal notation |
| 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 | | |
| 58 | /// - `s`: | 56 | /// - `s`: |
| 59 | /// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination | 57 | /// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination |
| 60 | /// - for slices of u8, print the entire slice as a string without zero-termination | 58 | /// - 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 | | |
| 65 | /// - `e`: output floating point value in scientific notation | 59 | /// - `e`: output floating point value in scientific notation |
| 66 | /// - `d`: output numeric value in decimal notation | 60 | /// - `d`: output numeric value in decimal notation |
| 67 | /// - `b`: output integer value in binary notation | 61 | /// - `b`: output integer value in binary notation |
| ... | @@ -620,9 +614,9 @@ fn formatValue( | ... | @@ -620,9 +614,9 @@ fn formatValue( |
| 620 | writer: anytype, | 614 | writer: anytype, |
| 621 | ) !void { | 615 | ) !void { |
| 622 | if (comptime std.mem.eql(u8, fmt, "B")) { | 616 | 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"); |
| 624 | } else if (comptime std.mem.eql(u8, fmt, "Bi")) { | 618 | } 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"); |
| 626 | } | 620 | } |
| 627 | | 621 | |
| 628 | const T = @TypeOf(value); | 622 | const T = @TypeOf(value); |
| ... | @@ -790,6 +784,67 @@ pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscap | ... | @@ -790,6 +784,67 @@ pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscap |
| 790 | return .{ .data = bytes }; | 784 | return .{ .data = bytes }; |
| 791 | } | 785 | } |
| 792 | | 786 | |
| | 787 | fn 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 | |
| | 831 | const formatSizeDec = formatSizeImpl(1000).f; |
| | 832 | const 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, ...). |
| | 837 | pub 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, ...). |
| | 844 | pub fn fmtIntSizeBin(value: u64) std.fmt.Formatter(formatSizeBin) { |
| | 845 | return .{ .data = value }; |
| | 846 | } |
| | 847 | |
| 793 | pub fn formatText( | 848 | pub fn formatText( |
| 794 | bytes: []const u8, | 849 | bytes: []const u8, |
| 795 | comptime fmt: []const u8, | 850 | comptime fmt: []const u8, |
| ... | @@ -1111,47 +1166,6 @@ pub fn formatFloatDecimal( | ... | @@ -1111,47 +1166,6 @@ pub fn formatFloatDecimal( |
| 1111 | } | 1166 | } |
| 1112 | } | 1167 | } |
| 1113 | | 1168 | |
| 1114 | pub 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 | | | |
| 1155 | pub fn formatInt( | 1169 | pub fn formatInt( |
| 1156 | value: anytype, | 1170 | value: anytype, |
| 1157 | base: u8, | 1171 | base: u8, |
| ... | @@ -1806,8 +1820,12 @@ test "cstr" { | ... | @@ -1806,8 +1820,12 @@ test "cstr" { |
| 1806 | } | 1820 | } |
| 1807 | | 1821 | |
| 1808 | test "filesize" { | 1822 | test "filesize" { |
| 1809 | try expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); | 1823 | try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeDec(42)}); |
| 1810 | try expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); | 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)}); |
| 1811 | } | 1829 | } |
| 1812 | | 1830 | |
| 1813 | test "struct" { | 1831 | test "struct" { |
| ... | @@ -2213,8 +2231,6 @@ test "vector" { | ... | @@ -2213,8 +2231,6 @@ test "vector" { |
| 2213 | try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); | 2231 | try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); |
| 2214 | try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); | 2232 | try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); |
| 2215 | try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); | 2233 | 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}); | | |
| 2218 | } | 2234 | } |
| 2219 | | 2235 | |
| 2220 | test "enum-literal" { | 2236 | test "enum-literal" { |