Skip to content

Commit

Permalink
Merge pull request #6360 from LemonBoy/some-fmt-fixes
Browse files Browse the repository at this point in the history
Two std.fmt fixes
  • Loading branch information
andrewrk authored Sep 17, 2020
2 parents 2962be8 + b7f9f77 commit 3672a18
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 84 deletions.
139 changes: 56 additions & 83 deletions lib/std/fmt.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub const Alignment = enum {
pub const FormatOptions = struct {
precision: ?usize = null,
width: ?usize = null,
alignment: Alignment = .Left,
alignment: Alignment = .Right,
fill: u8 = ' ',
};

Expand Down Expand Up @@ -327,7 +327,7 @@ pub fn formatType(
max_depth: usize,
) @TypeOf(writer).Error!void {
if (comptime std.mem.eql(u8, fmt, "*")) {
try writer.writeAll(@typeName(@typeInfo(@TypeOf(value)).Pointer.child));
try writer.writeAll(@typeName(std.meta.Child(@TypeOf(value))));
try writer.writeAll("@");
try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
return;
Expand Down Expand Up @@ -631,26 +631,22 @@ pub fn formatBuf(
writer: anytype,
) !void {
const width = options.width orelse buf.len;
var padding = if (width > buf.len) (width - buf.len) else 0;
const pad_byte = [1]u8{options.fill};
const padding = if (width > buf.len) (width - buf.len) else 0;

switch (options.alignment) {
.Left => {
try writer.writeAll(buf);
while (padding > 0) : (padding -= 1) {
try writer.writeAll(&pad_byte);
}
try writer.writeByteNTimes(options.fill, padding);
},
.Center => {
const padl = padding / 2;
var i: usize = 0;
while (i < padl) : (i += 1) try writer.writeAll(&pad_byte);
const left_padding = padding / 2;
const right_padding = (padding + 1) / 2;
try writer.writeByteNTimes(options.fill, left_padding);
try writer.writeAll(buf);
while (i < padding) : (i += 1) try writer.writeAll(&pad_byte);
try writer.writeByteNTimes(options.fill, right_padding);
},
.Right => {
while (padding > 0) : (padding -= 1) {
try writer.writeAll(&pad_byte);
}
try writer.writeByteNTimes(options.fill, padding);
try writer.writeAll(buf);
},
}
Expand Down Expand Up @@ -941,61 +937,27 @@ pub fn formatInt(
options: FormatOptions,
writer: anytype,
) !void {
assert(base >= 2);

const int_value = if (@TypeOf(value) == comptime_int) blk: {
const Int = math.IntFittingRange(value, value);
break :blk @as(Int, value);
} else
value;

if (@typeInfo(@TypeOf(int_value)).Int.is_signed) {
return formatIntSigned(int_value, base, uppercase, options, writer);
} else {
return formatIntUnsigned(int_value, base, uppercase, options, writer);
}
}
const value_info = @typeInfo(@TypeOf(int_value)).Int;

fn formatIntSigned(
value: anytype,
base: u8,
uppercase: bool,
options: FormatOptions,
writer: anytype,
) !void {
const new_options = FormatOptions{
.width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
.precision = options.precision,
.fill = options.fill,
};
const bit_count = @typeInfo(@TypeOf(value)).Int.bits;
const Uint = std.meta.Int(false, bit_count);
if (value < 0) {
try writer.writeAll("-");
const new_value = math.absCast(value);
return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
} else if (options.width == null or options.width.? == 0) {
return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, writer);
} else {
try writer.writeAll("+");
const new_value = @intCast(Uint, value);
return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
}
}
// The type must have the same size as `base` or be wider in order for the
// division to work
const min_int_bits = comptime math.max(value_info.bits, 8);
const MinInt = std.meta.Int(false, min_int_bits);

fn formatIntUnsigned(
value: anytype,
base: u8,
uppercase: bool,
options: FormatOptions,
writer: anytype,
) !void {
assert(base >= 2);
const value_info = @typeInfo(@TypeOf(value)).Int;
var buf: [math.max(value_info.bits, 1)]u8 = undefined;
const min_int_bits = comptime math.max(value_info.bits, @typeInfo(@TypeOf(base)).Int.bits);
const MinInt = std.meta.Int(value_info.is_signed, min_int_bits);
var a: MinInt = value;
var index: usize = buf.len;
const abs_value = math.absCast(int_value);
// The worst case in terms of space needed is base 2, plus 1 for the sign
var buf: [1 + math.max(value_info.bits, 1)]u8 = undefined;

var a: MinInt = abs_value;
var index: usize = buf.len;
while (true) {
const digit = a % base;
index -= 1;
Expand All @@ -1004,25 +966,21 @@ fn formatIntUnsigned(
if (a == 0) break;
}

const digits_buf = buf[index..];
const width = options.width orelse 0;
const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;

if (padding > index) {
const zero_byte: u8 = options.fill;
var leftover_padding = padding - index;
while (true) {
try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
leftover_padding -= 1;
if (leftover_padding == 0) break;
if (value_info.is_signed) {
if (value < 0) {
// Negative integer
index -= 1;
buf[index] = '-';
} else if (options.width == null or options.width.? == 0) {
// Positive integer, omit the plus sign
} else {
// Positive integer
index -= 1;
buf[index] = '+';
}
mem.set(u8, buf[0..index], options.fill);
return writer.writeAll(&buf);
} else {
const padded_buf = buf[index - padding ..];
mem.set(u8, padded_buf[0..padding], options.fill);
return writer.writeAll(padded_buf);
}

return formatBuf(buf[index..], options, writer);
}

pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize {
Expand Down Expand Up @@ -1246,6 +1204,10 @@ test "optional" {
const value: ?i32 = null;
try testFmt("optional: null\n", "optional: {}\n", .{value});
}
{
const value = @intToPtr(?*i32, 0xf000d000);
try testFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value});
}
}

test "error" {
Expand Down Expand Up @@ -1283,7 +1245,17 @@ test "int.specifier" {

test "int.padded" {
try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)});
try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)});
try testFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)});
try testFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)});
try testFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)});
try testFmt("i8: '-1 '", "i8: '{:<4}'", .{@as(i8, -1)});
try testFmt("i8: ' -1'", "i8: '{:>4}'", .{@as(i8, -1)});
try testFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)});
try testFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)});
try testFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)});
try testFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});
try testFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});
try testFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)});
}

test "buffer" {
Expand Down Expand Up @@ -1329,7 +1301,7 @@ test "slice" {
try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value});
}

try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"});
try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"});
try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
}

Expand Down Expand Up @@ -1362,7 +1334,7 @@ test "cstr" {
.{@ptrCast([*c]const u8, "Test C")},
);
try testFmt(
"cstr: Test C \n",
"cstr: Test C\n",
"cstr: {s:10}\n",
.{@ptrCast([*c]const u8, "Test C")},
);
Expand Down Expand Up @@ -1805,7 +1777,7 @@ test "vector" {

try testFmt("{ true, false, true, false }", "{}", .{vbool});
try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});
try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64});
try testFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64});
try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});
try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64});
Expand All @@ -1818,15 +1790,16 @@ test "enum-literal" {

test "padding" {
try testFmt("Simple", "{}", .{"Simple"});
try testFmt("true ", "{:10}", .{true});
try testFmt(" true", "{:10}", .{true});
try testFmt(" true", "{:>10}", .{true});
try testFmt("======true", "{:=>10}", .{true});
try testFmt("true======", "{:=<10}", .{true});
try testFmt(" true ", "{:^10}", .{true});
try testFmt("===true===", "{:=^10}", .{true});
try testFmt("Minimum width", "{:18} width", .{"Minimum"});
try testFmt(" Minimum width", "{:18} width", .{"Minimum"});
try testFmt("==================Filled", "{:=>24}", .{"Filled"});
try testFmt(" Centered ", "{:^24}", .{"Centered"});
try testFmt("-", "{:-^1}", .{""});
}

test "decimal float padding" {
Expand Down
2 changes: 1 addition & 1 deletion src-self-hosted/translate_c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2032,7 +2032,7 @@ fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
// Handle the remaining escapes Zig doesn't support by turning them
// into their respective hex representation
else => if (std.ascii.isCntrl(c))
std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable
std.fmt.bufPrint(char_buf, "\\x{x:0>2}", .{c}) catch unreachable
else
std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,
};
Expand Down

0 comments on commit 3672a18

Please sign in to comment.