Commit 47f267d25f

Andrew Kelley <superjoe30@gmail.com>
2017-03-10 01:12:15
break off some of std.io into std.fmt, generalize printf
closes #250
1 parent c62db57
Changed files (6)
example/guess_number/main.zig
@@ -1,5 +1,6 @@
 const std = @import("std");
 const io = std.io;
+const fmt = std.fmt;
 const Rand = std.rand.Rand;
 const os = std.os;
 
@@ -23,7 +24,7 @@ pub fn main(args: [][]u8) -> %void {
             return err;
         };
 
-        const guess = io.parseUnsigned(u8, line_buf[0...line_len - 1], 10) %% {
+        const guess = fmt.parseUnsigned(u8, line_buf[0...line_len - 1], 10) %% {
             %%io.stdout.printf("Invalid number.\n");
             continue;
         };
std/fmt.zig
@@ -0,0 +1,334 @@
+const math = @import("math.zig");
+const debug = @import("debug.zig");
+const assert = debug.assert;
+const mem = @import("mem.zig");
+
+const max_f64_digits = 65;
+const max_int_digits = 65;
+
+const State = enum { // TODO put inside format function and make sure the name and debug info is correct
+    Start,
+    OpenBrace,
+    CloseBrace,
+    Integer,
+    IntegerWidth,
+    Character,
+};
+
+/// Renders fmt string with args, calling output with slices of bytes.
+/// Return false from output function and output will not be called again.
+/// Returns false if output ever returned false, true otherwise.
+pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
+    comptime fmt: []const u8, args: ...) -> bool
+{
+    comptime var start_index = 0;
+    comptime var state = State.Start;
+    comptime var next_arg = 0;
+    comptime var radix = 0;
+    comptime var uppercase = false;
+    comptime var width = 0;
+    comptime var width_start = 0;
+
+    inline for (fmt) |c, i| {
+        switch (state) {
+            State.Start => switch (c) {
+                '{' => {
+                    // TODO if you make this an if statement with && then it breaks
+                    if (start_index < i) {
+                        if (!output(context, fmt[start_index...i]))
+                            return false;
+                    }
+                    state = State.OpenBrace;
+                },
+                '}' => {
+                    if (start_index < i) {
+                        if (!output(context, fmt[start_index...i]))
+                            return false;
+                    }
+                    state = State.CloseBrace;
+                },
+                else => {},
+            },
+            State.OpenBrace => switch (c) {
+                '{' => {
+                    state = State.Start;
+                    start_index = i;
+                },
+                '}' => {
+                    if (!formatValue(args[next_arg], context, output))
+                        return false;
+                    next_arg += 1;
+                    state = State.Start;
+                    start_index = i + 1;
+                },
+                'd' => {
+                    radix = 10;
+                    uppercase = false;
+                    width = 0;
+                    state = State.Integer;
+                },
+                'x' => {
+                    radix = 16;
+                    uppercase = false;
+                    width = 0;
+                    state = State.Integer;
+                },
+                'X' => {
+                    radix = 16;
+                    uppercase = true;
+                    width = 0;
+                    state = State.Integer;
+                },
+                'c' => {
+                    state = State.Character;
+                },
+                else => @compileError("Unknown format character: " ++ []u8{c}),
+            },
+            State.CloseBrace => switch (c) {
+                '}' => {
+                    state = State.Start;
+                    start_index = i;
+                },
+                else => @compileError("Single '}' encountered in format string"),
+            },
+            State.Integer => switch (c) {
+                '}' => {
+                    if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
+                        return false;
+                    next_arg += 1;
+                    state = State.Start;
+                    start_index = i + 1;
+                },
+                '0' ... '9' => {
+                    width_start = i;
+                    state = State.IntegerWidth;
+                },
+                else => @compileError("Unexpected character in format string: " ++ []u8{c}),
+            },
+            State.IntegerWidth => switch (c) {
+                '}' => {
+                    width = comptime %%parseUnsigned(usize, fmt[width_start...i], 10);
+                    if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
+                        return false;
+                    next_arg += 1;
+                    state = State.Start;
+                    start_index = i + 1;
+                },
+                '0' ... '9' => {},
+                else => @compileError("Unexpected character in format string: " ++ []u8{c}),
+            },
+            State.Character => switch (c) {
+                '}' => {
+                    if (!formatAsciiChar(args[next_arg], context, output))
+                        return false;
+                    next_arg += 1;
+                    state = State.Start;
+                    start_index = i + 1;
+                },
+                else => @compileError("Unexpected character in format string: " ++ []u8{c}),
+            },
+        }
+    }
+    comptime {
+        if (args.len != next_arg) {
+            @compileError("Unused arguments");
+        }
+        if (state != State.Start) {
+            @compileError("Incomplete format string: " ++ fmt);
+        }
+    }
+    if (start_index < fmt.len) {
+        if (!output(context, fmt[start_index...]))
+            return false;
+    }
+
+    return true;
+}
+
+pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
+    const T = @typeOf(value);
+    if (@isInteger(T)) {
+        return formatInt(value, 10, false, 0, context, output);
+    } else if (@isFloat(T)) {
+        @compileError("TODO implement formatFloat");
+    } else if (@canImplicitCast([]const u8, value)) {
+        const casted_value = ([]const u8)(value);
+        return output(context, casted_value);
+    } else if (T == void) {
+        return output(context, "void");
+    } else {
+        @compileError("Unable to format type '" ++ @typeName(T) ++ "'");
+    }
+}
+
+pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
+    return output(context, (&c)[0...1]);
+}
+
+pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
+    context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
+{
+    if (@typeOf(value).is_signed) {
+        return formatIntSigned(value, base, uppercase, width, context, output);
+    } else {
+        return formatIntUnsigned(value, base, uppercase, width, context, output);
+    }
+}
+
+fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
+    context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
+{
+    const uint = @intType(false, @typeOf(value).bit_count);
+    if (value < 0) {
+        const minus_sign: u8 = '-';
+        if (!output(context, (&minus_sign)[0...1]))
+            return false;
+        const new_value = uint(-(value + 1)) + 1;
+        const new_width = if (width == 0) 0 else (width - 1);
+        return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
+    } else if (width == 0) {
+        return formatIntUnsigned(uint(value), base, uppercase, width, context, output);
+    } else {
+        const plus_sign: u8 = '+';
+        if (!output(context, (&plus_sign)[0...1]))
+            return false;
+        const new_value = uint(value);
+        const new_width = if (width == 0) 0 else (width - 1);
+        return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
+    }
+}
+
+fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
+    context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
+{
+    // max_int_digits accounts for the minus sign. when printing an unsigned
+    // number we don't need to do that.
+    var buf: [max_int_digits - 1]u8 = undefined;
+    var a = value;
+    var index: usize = buf.len;
+
+    while (true) {
+        const digit = a % base;
+        index -= 1;
+        buf[index] = digitToChar(u8(digit), uppercase);
+        a /= base;
+        if (a == 0)
+            break;
+    }
+
+    const digits_buf = buf[index...];
+    const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;
+
+    if (padding > index) {
+        const zero_byte: u8 = '0';
+        var leftover_padding = padding - index;
+        while (true) {
+            if (!output(context, (&zero_byte)[0...1]))
+                return false;
+            leftover_padding -= 1;
+            if (leftover_padding == 0)
+                break;
+        }
+        mem.set(u8, buf[0...index], '0');
+        return output(context, buf);
+    } else {
+        const padded_buf = buf[index - padding...];
+        mem.set(u8, padded_buf[0...padding], '0');
+        return output(context, padded_buf);
+    }
+}
+
+pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> usize {
+    var context = FormatIntBuf {
+        .out_buf = out_buf,
+        .index = 0,
+    };
+    _ = formatInt(value, base, uppercase, width, &context, formatIntCallback);
+    return context.index;
+}
+const FormatIntBuf = struct {
+    out_buf: []u8,
+    index: usize,
+};
+fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) -> bool {
+    mem.copy(u8, context.out_buf[context.index...], bytes);
+    context.index += bytes.len;
+    return true;
+}
+
+pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
+    var x: T = 0;
+
+    for (buf) |c| {
+        const digit = %return charToDigit(c, radix);
+        x = %return math.mulOverflow(T, x, radix);
+        x = %return math.addOverflow(T, x, digit);
+    }
+
+    return x;
+}
+
+error InvalidChar;
+fn charToDigit(c: u8, radix: u8) -> %u8 {
+    const value = switch (c) {
+        '0' ... '9' => c - '0',
+        'A' ... 'Z' => c - 'A' + 10,
+        'a' ... 'z' => c - 'a' + 10,
+        else => return error.InvalidChar,
+    };
+
+    if (value >= radix)
+        return error.InvalidChar;
+
+    return value;
+}
+
+fn digitToChar(digit: u8, uppercase: bool) -> u8 {
+    return switch (digit) {
+        0 ... 9 => digit + '0',
+        10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10),
+        else => @unreachable(),
+    };
+}
+
+fn testBufPrintInt() {
+    @setFnTest(this);
+
+    var buffer: [max_int_digits]u8 = undefined;
+    const buf = buffer[0...];
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678"));
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e"));
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E"));
+
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678"));
+
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666"));
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234"));
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234"));
+
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42"));
+    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42"));
+}
+
+fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 {
+    return buf[0...formatIntBuf(buf, value, base, uppercase, width)];
+}
+
+fn testParseU64DigitTooBig() {
+    @setFnTest(this);
+
+    parseUnsigned(u64, "123a", 10) %% |err| {
+        if (err == error.InvalidChar) return;
+        @unreachable();
+    };
+    @unreachable();
+}
+
+fn testParseUnsignedComptime() {
+    @setFnTest(this);
+
+    comptime {
+        assert(%%parseUnsigned(usize, "2", 10) == 2);
+    }
+}
std/index.zig
@@ -1,20 +1,21 @@
-pub const rand = @import("rand.zig");
-pub const io = @import("io.zig");
-pub const os = @import("os.zig");
-pub const math = @import("math.zig");
 pub const cstr = @import("cstr.zig");
-pub const sort = @import("sort.zig");
-pub const net = @import("net.zig");
-pub const list = @import("list.zig");
+pub const debug = @import("debug.zig");
+pub const fmt = @import("fmt.zig");
 pub const hash_map = @import("hash_map.zig");
+pub const io = @import("io.zig");
+pub const list = @import("list.zig");
+pub const math = @import("math.zig");
 pub const mem = @import("mem.zig");
-pub const debug = @import("debug.zig");
+pub const net = @import("net.zig");
+pub const os = @import("os.zig");
+pub const rand = @import("rand.zig");
+pub const sort = @import("sort.zig");
 pub const linux = switch(@compileVar("os")) {
     Os.linux => @import("linux.zig"),
-    else => null_import,
+    else => empty_import,
 };
 pub const darwin = switch(@compileVar("os")) {
     Os.darwin => @import("darwin.zig"),
-    else => null_import,
+    else => empty_import,
 };
-const null_import = @import("empty.zig");
+pub const empty_import = @import("empty.zig");
std/io.zig
@@ -11,6 +11,7 @@ const assert = debug.assert;
 const os = @import("os.zig");
 const mem = @import("mem.zig");
 const Buffer0 = @import("cstr.zig").Buffer0;
+const fmt = @import("fmt.zig");
 
 pub const stdin_fileno = 0;
 pub const stdout_fileno = 1;
@@ -61,8 +62,6 @@ error Unseekable;
 error Eof;
 
 const buffer_size = 4 * 1024;
-const max_f64_digits = 65;
-const max_int_digits = 65;
 
 pub const OpenRead     = 0b0001;
 pub const OpenWrite    = 0b0010;
@@ -81,173 +80,46 @@ pub const OutStream = struct {
     }
 
     pub fn write(self: &OutStream, bytes: []const u8) -> %void {
-        var src_bytes_left = bytes.len;
         var src_index: usize = 0;
-        const dest_space_left = self.buffer.len - self.index;
 
-        while (src_bytes_left > 0) {
-            const copy_amt = math.min(dest_space_left, src_bytes_left);
-            @memcpy(&self.buffer[self.index], &bytes[src_index], copy_amt);
+        while (src_index < bytes.len) {
+            const dest_space_left = self.buffer.len - self.index;
+            const copy_amt = math.min(dest_space_left, bytes.len - src_index);
+            mem.copy(u8, self.buffer[self.index...], bytes[src_index...src_index + copy_amt]);
             self.index += copy_amt;
+            assert(self.index <= self.buffer.len);
             if (self.index == self.buffer.len) {
                 %return self.flush();
             }
-            src_bytes_left -= copy_amt;
+            src_index += copy_amt;
         }
     }
 
-    const State = enum { // TODO put inside printf function and make sure the name and debug info is correct
-        Start,
-        OpenBrace,
-        CloseBrace,
-        Integer,
-        IntegerWidth,
-        Character,
-    };
-
     /// Calls print and then flushes the buffer.
     pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
-        comptime var start_index = 0;
-        comptime var state = State.Start;
-        comptime var next_arg = 0;
-        comptime var radix = 0;
-        comptime var uppercase = false;
-        comptime var width = 0;
-        comptime var width_start = 0;
-
-        inline for (format) |c, i| {
-            switch (state) {
-                State.Start => switch (c) {
-                    '{' => {
-                        if (start_index < i) %return self.write(format[start_index...i]);
-                        state = State.OpenBrace;
-                    },
-                    '}' => {
-                        if (start_index < i) %return self.write(format[start_index...i]);
-                        state = State.CloseBrace;
-                    },
-                    else => {},
-                },
-                State.OpenBrace => switch (c) {
-                    '{' => {
-                        state = State.Start;
-                        start_index = i;
-                    },
-                    '}' => {
-                        %return self.printValue(args[next_arg]);
-                        next_arg += 1;
-                        state = State.Start;
-                        start_index = i + 1;
-                    },
-                    'd' => {
-                        radix = 10;
-                        uppercase = false;
-                        width = 0;
-                        state = State.Integer;
-                    },
-                    'x' => {
-                        radix = 16;
-                        uppercase = false;
-                        width = 0;
-                        state = State.Integer;
-                    },
-                    'X' => {
-                        radix = 16;
-                        uppercase = true;
-                        width = 0;
-                        state = State.Integer;
-                    },
-                    'c' => {
-                        state = State.Character;
-                    },
-                    else => @compileError("Unknown format character: " ++ []u8{c}),
-                },
-                State.CloseBrace => switch (c) {
-                    '}' => {
-                        state = State.Start;
-                        start_index = i;
-                    },
-                    else => @compileError("Single '}' encountered in format string"),
-                },
-                State.Integer => switch (c) {
-                    '}' => {
-                        %return self.printInt(args[next_arg], radix, uppercase, width);
-                        next_arg += 1;
-                        state = State.Start;
-                        start_index = i + 1;
-                    },
-                    '0' ... '9' => {
-                        width_start = i;
-                        state = State.IntegerWidth;
-                    },
-                    else => @compileError("Unexpected character in format string: " ++ []u8{c}),
-                },
-                State.IntegerWidth => switch (c) {
-                    '}' => {
-                        width = comptime %%parseUnsigned(usize, format[width_start...i], 10);
-                        %return self.printInt(args[next_arg], radix, uppercase, width);
-                        next_arg += 1;
-                        state = State.Start;
-                        start_index = i + 1;
-                    },
-                    '0' ... '9' => {},
-                    else => @compileError("Unexpected character in format string: " ++ []u8{c}),
-                },
-                State.Character => switch (c) {
-                    '}' => {
-                        %return self.printAsciiChar(args[next_arg]);
-                        next_arg += 1;
-                        state = State.Start;
-                        start_index = i + 1;
-                    },
-                    else => @compileError("Unexpected character in format string: " ++ []u8{c}),
-                },
-            }
-        }
-        comptime {
-            if (args.len != next_arg) {
-                @compileError("Unused arguments");
-            }
-            if (state != State.Start) {
-                @compileError("Incomplete format string: " ++ format);
-            }
-        }
-        if (start_index < format.len) {
-            %return self.write(format[start_index...format.len]);
-        }
+        %return self.print(format, args);
         %return self.flush();
     }
 
-    pub fn printValue(self: &OutStream, value: var) -> %void {
-        const T = @typeOf(value);
-        if (@isInteger(T)) {
-            return self.printInt(value, 10, false, 0);
-        } else if (@isFloat(T)) {
-            return self.printFloat(T, value);
-        } else if (@canImplicitCast([]const u8, value)) {
-            const casted_value = ([]const u8)(value);
-            return self.write(casted_value);
-        } else if (T == void) {
-            return self.write("void");
-        } else {
-            @compileError("Unable to print type '" ++ @typeName(T) ++ "'");
-        }
+    /// Does not flush the buffer.
+    pub fn print(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
+        var context = PrintContext {
+            .self = self,
+            .result = {},
+        };
+        _ = fmt.format(&context, printOutput, format, args);
+        return context.result;
     }
-
-    pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool, width: usize) -> %void {
-        if (self.index + max_int_digits >= self.buffer.len) {
-            %return self.flush();
-        }
-        const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase, width);
-        self.index += amt_printed;
-    }
-
-    pub fn printAsciiChar(self: &OutStream, c: u8) -> %void {
-        if (self.index + 1 >= self.buffer.len) {
-            %return self.flush();
-        }
-        self.buffer[self.index] = c;
-        self.index += 1;
+    const PrintContext = struct {
+        self: &OutStream,
+        result: %void,
+    };
+    fn printOutput(context: &PrintContext, bytes: []const u8) -> bool {
+        context.self.write(bytes) %% |err| {
+            context.result = err;
+            return false;
+        };
+        return true;
     }
 
     pub fn flush(self: &OutStream) -> %void {
@@ -506,90 +378,6 @@ pub const InStream = struct {
     }
 };
 
-pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
-    var x: T = 0;
-
-    for (buf) |c| {
-        const digit = %return charToDigit(c, radix);
-        x = %return math.mulOverflow(T, x, radix);
-        x = %return math.addOverflow(T, x, digit);
-    }
-
-    return x;
-}
-
-error InvalidChar;
-fn charToDigit(c: u8, radix: u8) -> %u8 {
-    const value = switch (c) {
-        '0' ... '9' => c - '0',
-        'A' ... 'Z' => c - 'A' + 10,
-        'a' ... 'z' => c - 'a' + 10,
-        else => return error.InvalidChar,
-    };
-
-    if (value >= radix)
-        return error.InvalidChar;
-
-    return value;
-}
-
-fn digitToChar(digit: u8, uppercase: bool) -> u8 {
-    return switch (digit) {
-        0 ... 9 => digit + '0',
-        10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10),
-        else => @unreachable(),
-    };
-}
-
-/// Guaranteed to not use more than max_int_digits
-pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
-    if (@typeOf(x).is_signed)
-        bufPrintSigned(out_buf, x, base, uppercase, width)
-    else
-        bufPrintUnsigned(out_buf, x, base, uppercase, width)
-}
-
-fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
-    const uint = @intType(false, @typeOf(x).bit_count);
-    if (x < 0) {
-        out_buf[0] = '-';
-        const new_value = uint(-(x + 1)) + 1;
-        const new_width = if (width == 0) 0 else (width - 1);
-        return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width);
-    } else if (width == 0) {
-        return bufPrintUnsigned(out_buf, uint(x), base, uppercase, width);
-    } else {
-        out_buf[0] = '+';
-        const new_value = uint(x);
-        const new_width = if (width == 0) 0 else (width - 1);
-        return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width);
-    }
-}
-
-fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
-    // max_int_digits accounts for the minus sign. when printing an unsigned
-    // number we don't need to do that.
-    var buf: [max_int_digits - 1]u8 = undefined;
-    var a = x;
-    var index: usize = buf.len;
-
-    while (true) {
-        const digit = a % base;
-        index -= 1;
-        buf[index] = digitToChar(u8(digit), uppercase);
-        a /= base;
-        if (a == 0)
-            break;
-    }
-
-    const src_buf = buf[index...];
-    const padding = if (width > src_buf.len) (width - src_buf.len) else 0;
-
-    mem.set(u8, out_buf[0...padding], '0');
-    mem.copy(u8, out_buf[padding...], src_buf);
-    return src_buf.len + padding;
-}
-
 pub fn openSelfExe(stream: &InStream) -> %void {
     switch (@compileVar("os")) {
         Os.linux => {
@@ -602,45 +390,3 @@ pub fn openSelfExe(stream: &InStream) -> %void {
         else => @compileError("unsupported os"),
     }
 }
-
-fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 {
-    return buf[0...bufPrintInt(buf, value, base, uppercase, width)];
-}
-
-fn testParseU64DigitTooBig() {
-    @setFnTest(this);
-
-    parseUnsigned(u64, "123a", 10) %% |err| {
-        if (err == error.InvalidChar) return;
-        @unreachable();
-    };
-    @unreachable();
-}
-
-fn testParseUnsignedComptime() {
-    @setFnTest(this);
-
-    comptime {
-        assert(%%parseUnsigned(usize, "2", 10) == 2);
-    }
-}
-
-fn testBufPrintInt() {
-    @setFnTest(this);
-
-    var buffer: [max_int_digits]u8 = undefined;
-    const buf = buffer[0...];
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678"));
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e"));
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E"));
-
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678"));
-
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666"));
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234"));
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234"));
-
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42"));
-    assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42"));
-}
test/cases/enum_with_members.zig
@@ -1,6 +1,6 @@
 const assert = @import("std").debug.assert;
 const mem = @import("std").mem;
-const io = @import("std").io;
+const fmt = @import("std").fmt;
 
 const ET = enum {
     SINT: i32,
@@ -8,8 +8,8 @@ const ET = enum {
 
     pub fn print(a: &const ET, buf: []u8) -> %usize {
         return switch (*a) {
-            ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) },
-            ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) },
+            ET.SINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) },
+            ET.UINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) },
         }
     }
 };
CMakeLists.txt
@@ -210,6 +210,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/elf.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/empty.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/endian.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/errno.zig" DESTINATION "${ZIG_STD_DEST}")
+install(FILES "${CMAKE_SOURCE_DIR}/std/fmt.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")