| ... | @@ -28,6 +28,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), | ... | @@ -28,6 +28,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 28 | Buf, | 28 | Buf, |
| 29 | BufWidth, | 29 | BufWidth, |
| 30 | Bytes, | 30 | Bytes, |
| | 31 | BytesBase, |
| 31 | BytesWidth, | 32 | BytesWidth, |
| 32 | }; | 33 | }; |
| 33 | | 34 | |
| ... | @@ -99,6 +100,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), | ... | @@ -99,6 +100,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 99 | }, | 100 | }, |
| 100 | 'B' => { | 101 | 'B' => { |
| 101 | width = 0; | 102 | width = 0; |
| | 103 | radix = 1000; |
| 102 | state = State.Bytes; | 104 | state = State.Bytes; |
| 103 | }, | 105 | }, |
| 104 | else => @compileError("Unknown format character: " ++ []u8{c}), | 106 | else => @compileError("Unknown format character: " ++ []u8{c}), |
| ... | @@ -214,7 +216,24 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), | ... | @@ -214,7 +216,24 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 214 | }, | 216 | }, |
| 215 | State.Bytes => switch (c) { | 217 | State.Bytes => switch (c) { |
| 216 | '}' => { | 218 | '}' => { |
| 217 | try formatBytes(args[next_arg], 0, context, Errors, output); | 219 | try formatBytes(args[next_arg], 0, radix, context, Errors, output); |
| | 220 | next_arg += 1; |
| | 221 | state = State.Start; |
| | 222 | start_index = i + 1; |
| | 223 | }, |
| | 224 | 'i' => { |
| | 225 | radix = 1024; |
| | 226 | state = State.BytesBase; |
| | 227 | }, |
| | 228 | '0' ... '9' => { |
| | 229 | width_start = i; |
| | 230 | state = State.BytesWidth; |
| | 231 | }, |
| | 232 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| | 233 | }, |
| | 234 | State.BytesBase => switch (c) { |
| | 235 | '}' => { |
| | 236 | try formatBytes(args[next_arg], 0, radix, context, Errors, output); |
| 218 | next_arg += 1; | 237 | next_arg += 1; |
| 219 | state = State.Start; | 238 | state = State.Start; |
| 220 | start_index = i + 1; | 239 | start_index = i + 1; |
| ... | @@ -228,7 +247,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), | ... | @@ -228,7 +247,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 228 | State.BytesWidth => switch (c) { | 247 | State.BytesWidth => switch (c) { |
| 229 | '}' => { | 248 | '}' => { |
| 230 | width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); | 249 | width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); |
| 231 | try formatBytes(args[next_arg], width, context, Errors, output); | 250 | try formatBytes(args[next_arg], width, radix, context, Errors, output); |
| 232 | next_arg += 1; | 251 | next_arg += 1; |
| 233 | state = State.Start; | 252 | state = State.Start; |
| 234 | start_index = i + 1; | 253 | start_index = i + 1; |
| ... | @@ -550,7 +569,7 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com | ... | @@ -550,7 +569,7 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com |
| 550 | } | 569 | } |
| 551 | } | 570 | } |
| 552 | | 571 | |
| 553 | pub fn formatBytes(value: var, width: ?usize, | 572 | pub fn formatBytes(value: var, width: ?usize, comptime radix: usize, |
| 554 | context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void | 573 | context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void |
| 555 | { | 574 | { |
| 556 | if (value == 0) { | 575 | if (value == 0) { |
| ... | @@ -558,16 +577,26 @@ pub fn formatBytes(value: var, width: ?usize, | ... | @@ -558,16 +577,26 @@ pub fn formatBytes(value: var, width: ?usize, |
| 558 | } | 577 | } |
| 559 | | 578 | |
| 560 | const mags = " KMGTPEZY"; | 579 | const mags = " KMGTPEZY"; |
| 561 | const magnitude = math.min(math.log2(value) / 10, mags.len - 1); | 580 | const magnitude = switch (radix) { |
| 562 | const new_value = f64(value) / math.pow(f64, 1024, f64(magnitude)); | 581 | 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags.len - 1), |
| | 582 | 1024 => math.min(math.log2(value) / 10, mags.len - 1), |
| | 583 | else => unreachable, |
| | 584 | }; |
| | 585 | const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude)); |
| 563 | const suffix = mags[magnitude]; | 586 | const suffix = mags[magnitude]; |
| 564 | | 587 | |
| 565 | try formatFloatDecimal(new_value, width, context, Errors, output); | 588 | try formatFloatDecimal(new_value, width, context, Errors, output); |
| 566 | | 589 | |
| 567 | if (suffix != ' ') { | 590 | if (suffix == ' ') { |
| 568 | try output(context, (&suffix)[0..1]); | 591 | return output(context, "B"); |
| 569 | } | 592 | } |
| 570 | return output(context, "B"); | 593 | |
| | 594 | const buf = switch (radix) { |
| | 595 | 1000 => []u8 { suffix, 'B' }, |
| | 596 | 1024 => []u8 { suffix, 'i', 'B' }, |
| | 597 | else => unreachable, |
| | 598 | }; |
| | 599 | return output(context, buf); |
| 571 | } | 600 | } |
| 572 | | 601 | |
| 573 | pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, | 602 | pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, |
| ... | @@ -787,41 +816,27 @@ test "parse unsigned comptime" { | ... | @@ -787,41 +816,27 @@ test "parse unsigned comptime" { |
| 787 | | 816 | |
| 788 | test "fmt.format" { | 817 | test "fmt.format" { |
| 789 | { | 818 | { |
| 790 | var buf1: [32]u8 = undefined; | | |
| 791 | const value: ?i32 = 1234; | 819 | const value: ?i32 = 1234; |
| 792 | const result = try bufPrint(buf1[0..], "nullable: {}\n", value); | 820 | try testFmt("nullable: 1234\n", "nullable: {}\n", value); |
| 793 | assert(mem.eql(u8, result, "nullable: 1234\n")); | | |
| 794 | } | 821 | } |
| 795 | { | 822 | { |
| 796 | var buf1: [32]u8 = undefined; | | |
| 797 | const value: ?i32 = null; | 823 | const value: ?i32 = null; |
| 798 | const result = try bufPrint(buf1[0..], "nullable: {}\n", value); | 824 | try testFmt("nullable: null\n", "nullable: {}\n", value); |
| 799 | assert(mem.eql(u8, result, "nullable: null\n")); | | |
| 800 | } | 825 | } |
| 801 | { | 826 | { |
| 802 | var buf1: [32]u8 = undefined; | | |
| 803 | const value: error!i32 = 1234; | 827 | const value: error!i32 = 1234; |
| 804 | const result = try bufPrint(buf1[0..], "error union: {}\n", value); | 828 | try testFmt("error union: 1234\n", "error union: {}\n", value); |
| 805 | assert(mem.eql(u8, result, "error union: 1234\n")); | | |
| 806 | } | 829 | } |
| 807 | { | 830 | { |
| 808 | var buf1: [32]u8 = undefined; | | |
| 809 | const value: error!i32 = error.InvalidChar; | 831 | const value: error!i32 = error.InvalidChar; |
| 810 | const result = try bufPrint(buf1[0..], "error union: {}\n", value); | 832 | try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); |
| 811 | assert(mem.eql(u8, result, "error union: error.InvalidChar\n")); | | |
| 812 | } | 833 | } |
| 813 | { | 834 | { |
| 814 | var buf1: [32]u8 = undefined; | | |
| 815 | const value: u3 = 0b101; | 835 | const value: u3 = 0b101; |
| 816 | const result = try bufPrint(buf1[0..], "u3: {}\n", value); | 836 | try testFmt("u3: 5\n", "u3: {}\n", value); |
| 817 | assert(mem.eql(u8, result, "u3: 5\n")); | | |
| 818 | } | | |
| 819 | { | | |
| 820 | var buf1: [32]u8 = undefined; | | |
| 821 | const value: usize = 63 * 1024 * 1024; | | |
| 822 | const result = try bufPrint(buf1[0..], "file size: {B}\n", value); | | |
| 823 | assert(mem.eql(u8, result, "file size: 63MB\n")); | | |
| 824 | } | 837 | } |
| | 838 | try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); |
| | 839 | try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); |
| 825 | { | 840 | { |
| 826 | // Dummy field because of https://github.com/zig-lang/zig/issues/557. | 841 | // Dummy field because of https://github.com/zig-lang/zig/issues/557. |
| 827 | const Struct = struct { | 842 | const Struct = struct { |
| ... | @@ -1041,6 +1056,20 @@ test "fmt.format" { | ... | @@ -1041,6 +1056,20 @@ test "fmt.format" { |
| 1041 | } | 1056 | } |
| 1042 | } | 1057 | } |
| 1043 | | 1058 | |
| | 1059 | fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { |
| | 1060 | var buf: [100]u8 = undefined; |
| | 1061 | const result = try bufPrint(buf[0..], template, args); |
| | 1062 | if (mem.eql(u8, result, expected)) |
| | 1063 | return; |
| | 1064 | |
| | 1065 | std.debug.warn("\n====== expected this output: =========\n"); |
| | 1066 | std.debug.warn("{}", expected); |
| | 1067 | std.debug.warn("\n======== instead found this: =========\n"); |
| | 1068 | std.debug.warn("{}", result); |
| | 1069 | std.debug.warn("\n======================================\n"); |
| | 1070 | return error.TestFailed; |
| | 1071 | } |
| | 1072 | |
| 1044 | pub fn trim(buf: []const u8) []const u8 { | 1073 | pub fn trim(buf: []const u8) []const u8 { |
| 1045 | var start: usize = 0; | 1074 | var start: usize = 0; |
| 1046 | while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) { } | 1075 | while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) { } |