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