| ... | @@ -22,7 +22,7 @@ pub const Alignment = enum { | ... | @@ -22,7 +22,7 @@ pub const Alignment = enum { |
| 22 | pub const FormatOptions = struct { | 22 | pub const FormatOptions = struct { |
| 23 | precision: ?usize = null, | 23 | precision: ?usize = null, |
| 24 | width: ?usize = null, | 24 | width: ?usize = null, |
| 25 | alignment: Alignment = .Left, | 25 | alignment: Alignment = .Right, |
| 26 | fill: u8 = ' ', | 26 | fill: u8 = ' ', |
| 27 | }; | 27 | }; |
| 28 | | 28 | |
| ... | @@ -631,26 +631,22 @@ pub fn formatBuf( | ... | @@ -631,26 +631,22 @@ pub fn formatBuf( |
| 631 | writer: anytype, | 631 | writer: anytype, |
| 632 | ) !void { | 632 | ) !void { |
| 633 | const width = options.width orelse buf.len; | 633 | const width = options.width orelse buf.len; |
| 634 | var padding = if (width > buf.len) (width - buf.len) else 0; | 634 | const padding = if (width > buf.len) (width - buf.len) else 0; |
| 635 | const pad_byte = [1]u8{options.fill}; | 635 | |
| 636 | switch (options.alignment) { | 636 | switch (options.alignment) { |
| 637 | .Left => { | 637 | .Left => { |
| 638 | try writer.writeAll(buf); | 638 | try writer.writeAll(buf); |
| 639 | while (padding > 0) : (padding -= 1) { | 639 | try writer.writeByteNTimes(options.fill, padding); |
| 640 | try writer.writeAll(&pad_byte); | | |
| 641 | } | | |
| 642 | }, | 640 | }, |
| 643 | .Center => { | 641 | .Center => { |
| 644 | const padl = padding / 2; | 642 | const left_padding = padding / 2; |
| 645 | var i: usize = 0; | 643 | const right_padding = (padding + 1) / 2; |
| 646 | while (i < padl) : (i += 1) try writer.writeAll(&pad_byte); | 644 | try writer.writeByteNTimes(options.fill, left_padding); |
| 647 | try writer.writeAll(buf); | 645 | try writer.writeAll(buf); |
| 648 | while (i < padding) : (i += 1) try writer.writeAll(&pad_byte); | 646 | try writer.writeByteNTimes(options.fill, right_padding); |
| 649 | }, | 647 | }, |
| 650 | .Right => { | 648 | .Right => { |
| 651 | while (padding > 0) : (padding -= 1) { | 649 | try writer.writeByteNTimes(options.fill, padding); |
| 652 | try writer.writeAll(&pad_byte); | | |
| 653 | } | | |
| 654 | try writer.writeAll(buf); | 650 | try writer.writeAll(buf); |
| 655 | }, | 651 | }, |
| 656 | } | 652 | } |
| ... | @@ -941,61 +937,27 @@ pub fn formatInt( | ... | @@ -941,61 +937,27 @@ pub fn formatInt( |
| 941 | options: FormatOptions, | 937 | options: FormatOptions, |
| 942 | writer: anytype, | 938 | writer: anytype, |
| 943 | ) !void { | 939 | ) !void { |
| | 940 | assert(base >= 2); |
| | 941 | |
| 944 | const int_value = if (@TypeOf(value) == comptime_int) blk: { | 942 | const int_value = if (@TypeOf(value) == comptime_int) blk: { |
| 945 | const Int = math.IntFittingRange(value, value); | 943 | const Int = math.IntFittingRange(value, value); |
| 946 | break :blk @as(Int, value); | 944 | break :blk @as(Int, value); |
| 947 | } else | 945 | } else |
| 948 | value; | 946 | value; |
| 949 | | 947 | |
| 950 | if (@typeInfo(@TypeOf(int_value)).Int.is_signed) { | 948 | const value_info = @typeInfo(@TypeOf(int_value)).Int; |
| 951 | return formatIntSigned(int_value, base, uppercase, options, writer); | | |
| 952 | } else { | | |
| 953 | return formatIntUnsigned(int_value, base, uppercase, options, writer); | | |
| 954 | } | | |
| 955 | } | | |
| 956 | | 949 | |
| 957 | fn formatIntSigned( | 950 | // The type must have the same size as `base` or be wider in order for the |
| 958 | value: anytype, | 951 | // division to work |
| 959 | base: u8, | 952 | const min_int_bits = comptime math.max(value_info.bits, 8); |
| 960 | uppercase: bool, | 953 | const MinInt = std.meta.Int(false, min_int_bits); |
| 961 | options: FormatOptions, | | |
| 962 | writer: anytype, | | |
| 963 | ) !void { | | |
| 964 | const new_options = FormatOptions{ | | |
| 965 | .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null, | | |
| 966 | .precision = options.precision, | | |
| 967 | .fill = options.fill, | | |
| 968 | }; | | |
| 969 | const bit_count = @typeInfo(@TypeOf(value)).Int.bits; | | |
| 970 | const Uint = std.meta.Int(false, bit_count); | | |
| 971 | if (value < 0) { | | |
| 972 | try writer.writeAll("-"); | | |
| 973 | const new_value = math.absCast(value); | | |
| 974 | return formatIntUnsigned(new_value, base, uppercase, new_options, writer); | | |
| 975 | } else if (options.width == null or options.width.? == 0) { | | |
| 976 | return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, writer); | | |
| 977 | } else { | | |
| 978 | try writer.writeAll("+"); | | |
| 979 | const new_value = @intCast(Uint, value); | | |
| 980 | return formatIntUnsigned(new_value, base, uppercase, new_options, writer); | | |
| 981 | } | | |
| 982 | } | | |
| 983 | | 954 | |
| 984 | fn formatIntUnsigned( | 955 | const abs_value = math.absCast(int_value); |
| 985 | value: anytype, | 956 | // The worst case in terms of space needed is base 2, plus 1 for the sign |
| 986 | base: u8, | 957 | var buf: [1 + math.max(value_info.bits, 1)]u8 = undefined; |
| 987 | uppercase: bool, | | |
| 988 | options: FormatOptions, | | |
| 989 | writer: anytype, | | |
| 990 | ) !void { | | |
| 991 | assert(base >= 2); | | |
| 992 | const value_info = @typeInfo(@TypeOf(value)).Int; | | |
| 993 | var buf: [math.max(value_info.bits, 1)]u8 = undefined; | | |
| 994 | const min_int_bits = comptime math.max(value_info.bits, @typeInfo(@TypeOf(base)).Int.bits); | | |
| 995 | const MinInt = std.meta.Int(value_info.is_signed, min_int_bits); | | |
| 996 | var a: MinInt = value; | | |
| 997 | var index: usize = buf.len; | | |
| 998 | | 958 | |
| | 959 | var a: MinInt = abs_value; |
| | 960 | var index: usize = buf.len; |
| 999 | while (true) { | 961 | while (true) { |
| 1000 | const digit = a % base; | 962 | const digit = a % base; |
| 1001 | index -= 1; | 963 | index -= 1; |
| ... | @@ -1004,25 +966,21 @@ fn formatIntUnsigned( | ... | @@ -1004,25 +966,21 @@ fn formatIntUnsigned( |
| 1004 | if (a == 0) break; | 966 | if (a == 0) break; |
| 1005 | } | 967 | } |
| 1006 | | 968 | |
| 1007 | const digits_buf = buf[index..]; | 969 | if (value_info.is_signed) { |
| 1008 | const width = options.width orelse 0; | 970 | if (value < 0) { |
| 1009 | const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; | 971 | // Negative integer |
| 1010 | | 972 | index -= 1; |
| 1011 | if (padding > index) { | 973 | buf[index] = '-'; |
| 1012 | const zero_byte: u8 = options.fill; | 974 | } else if (options.width == null or options.width.? == 0) { |
| 1013 | var leftover_padding = padding - index; | 975 | // Positive integer, omit the plus sign |
| 1014 | while (true) { | 976 | } else { |
| 1015 | try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]); | 977 | // Positive integer |
| 1016 | leftover_padding -= 1; | 978 | index -= 1; |
| 1017 | if (leftover_padding == 0) break; | 979 | buf[index] = '+'; |
| 1018 | } | 980 | } |
| 1019 | mem.set(u8, buf[0..index], options.fill); | | |
| 1020 | return writer.writeAll(&buf); | | |
| 1021 | } else { | | |
| 1022 | const padded_buf = buf[index - padding ..]; | | |
| 1023 | mem.set(u8, padded_buf[0..padding], options.fill); | | |
| 1024 | return writer.writeAll(padded_buf); | | |
| 1025 | } | 981 | } |
| | 982 | |
| | 983 | return formatBuf(buf[index..], options, writer); |
| 1026 | } | 984 | } |
| 1027 | | 985 | |
| 1028 | pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize { | 986 | pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize { |
| ... | @@ -1287,7 +1245,17 @@ test "int.specifier" { | ... | @@ -1287,7 +1245,17 @@ test "int.specifier" { |
| 1287 | | 1245 | |
| 1288 | test "int.padded" { | 1246 | test "int.padded" { |
| 1289 | try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); | 1247 | try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); |
| 1290 | try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); | 1248 | try testFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)}); |
| | 1249 | try testFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)}); |
| | 1250 | try testFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)}); |
| | 1251 | try testFmt("i8: '-1 '", "i8: '{:<4}'", .{@as(i8, -1)}); |
| | 1252 | try testFmt("i8: ' -1'", "i8: '{:>4}'", .{@as(i8, -1)}); |
| | 1253 | try testFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)}); |
| | 1254 | try testFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)}); |
| | 1255 | try testFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)}); |
| | 1256 | try testFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)}); |
| | 1257 | try testFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)}); |
| | 1258 | try testFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)}); |
| 1291 | } | 1259 | } |
| 1292 | | 1260 | |
| 1293 | test "buffer" { | 1261 | test "buffer" { |
| ... | @@ -1333,7 +1301,7 @@ test "slice" { | ... | @@ -1333,7 +1301,7 @@ test "slice" { |
| 1333 | try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); | 1301 | try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); |
| 1334 | } | 1302 | } |
| 1335 | | 1303 | |
| 1336 | try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); | 1304 | try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); |
| 1337 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); | 1305 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); |
| 1338 | } | 1306 | } |
| 1339 | | 1307 | |
| ... | @@ -1366,7 +1334,7 @@ test "cstr" { | ... | @@ -1366,7 +1334,7 @@ test "cstr" { |
| 1366 | .{@ptrCast([*c]const u8, "Test C")}, | 1334 | .{@ptrCast([*c]const u8, "Test C")}, |
| 1367 | ); | 1335 | ); |
| 1368 | try testFmt( | 1336 | try testFmt( |
| 1369 | "cstr: Test C \n", | 1337 | "cstr: Test C\n", |
| 1370 | "cstr: {s:10}\n", | 1338 | "cstr: {s:10}\n", |
| 1371 | .{@ptrCast([*c]const u8, "Test C")}, | 1339 | .{@ptrCast([*c]const u8, "Test C")}, |
| 1372 | ); | 1340 | ); |
| ... | @@ -1809,7 +1777,7 @@ test "vector" { | ... | @@ -1809,7 +1777,7 @@ test "vector" { |
| 1809 | | 1777 | |
| 1810 | try testFmt("{ true, false, true, false }", "{}", .{vbool}); | 1778 | try testFmt("{ true, false, true, false }", "{}", .{vbool}); |
| 1811 | try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); | 1779 | try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); |
| 1812 | try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); | 1780 | try testFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); |
| 1813 | try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); | 1781 | try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); |
| 1814 | try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); | 1782 | try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); |
| 1815 | try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); | 1783 | try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); |
| ... | @@ -1822,15 +1790,16 @@ test "enum-literal" { | ... | @@ -1822,15 +1790,16 @@ test "enum-literal" { |
| 1822 | | 1790 | |
| 1823 | test "padding" { | 1791 | test "padding" { |
| 1824 | try testFmt("Simple", "{}", .{"Simple"}); | 1792 | try testFmt("Simple", "{}", .{"Simple"}); |
| 1825 | try testFmt("true ", "{:10}", .{true}); | 1793 | try testFmt(" true", "{:10}", .{true}); |
| 1826 | try testFmt(" true", "{:>10}", .{true}); | 1794 | try testFmt(" true", "{:>10}", .{true}); |
| 1827 | try testFmt("======true", "{:=>10}", .{true}); | 1795 | try testFmt("======true", "{:=>10}", .{true}); |
| 1828 | try testFmt("true======", "{:=<10}", .{true}); | 1796 | try testFmt("true======", "{:=<10}", .{true}); |
| 1829 | try testFmt(" true ", "{:^10}", .{true}); | 1797 | try testFmt(" true ", "{:^10}", .{true}); |
| 1830 | try testFmt("===true===", "{:=^10}", .{true}); | 1798 | try testFmt("===true===", "{:=^10}", .{true}); |
| 1831 | try testFmt("Minimum width", "{:18} width", .{"Minimum"}); | 1799 | try testFmt(" Minimum width", "{:18} width", .{"Minimum"}); |
| 1832 | try testFmt("==================Filled", "{:=>24}", .{"Filled"}); | 1800 | try testFmt("==================Filled", "{:=>24}", .{"Filled"}); |
| 1833 | try testFmt(" Centered ", "{:^24}", .{"Centered"}); | 1801 | try testFmt(" Centered ", "{:^24}", .{"Centered"}); |
| | 1802 | try testFmt("-", "{:-^1}", .{""}); |
| 1834 | } | 1803 | } |
| 1835 | | 1804 | |
| 1836 | test "decimal float padding" { | 1805 | test "decimal float padding" { |