const io = @import("./io.zig"); const math = @import("./math.zig"); const stack = @import("./stack.zig"); const testing = @import("./testing.zig"); /// /// [PrintError.WriteFailure] occurs when the underlying [io.Writer] implementation failed to write /// the entirety of a the requested print operation. /// pub const PrintError = io.AccessError || error { WriteFailure, }; /// /// Named identifiers for number formats used in printing functions. /// pub const Radix = enum { binary, tinary, quaternary, quinary, senary, septenary, octal, nonary, decimal, undecimal, duodecimal, tridecimal, tetradecimal, pentadecimal, hexadecimal, /// /// Returns the base number of `radix`. /// pub fn base(radix: Radix) u8 { return switch (radix) { .binary => 2, .tinary => 3, .quaternary => 4, .quinary => 5, .senary => 6, .septenary => 7, .octal => 8, .nonary => 9, .decimal => 10, .undecimal => 11, .duodecimal => 12, .tridecimal => 13, .tetradecimal => 14, .pentadecimal => 15, .hexadecimal => 16, }; } }; /// /// Writes `value` as a ASCII / UTF-8 encoded integer to `writer`, returning `true` if the full /// sequence was successfully written, otherwise `false`. /// /// The `radix` argument identifies which base system to format `value` as. /// pub fn printInt(writer: io.Writer, radix: Radix, value: anytype) PrintError!void { const Int = @TypeOf(value); switch (@typeInfo(Int)) { .Int => |info| { if (value == 0) { const zero = "0"; if ((try writer.write(zero)) != zero.len) return error.WriteFailure; } else { // Big enough to hold the hexadecimal representation of the integer type, which is // the largest number format accomodated for in [Radix]. var buffer = [_]u8{0} ** (@sizeOf(Int) * (@bitSizeOf(u8) / 4)); var buffer_count: usize = 0; var n1 = value; if (info.signedness == .signed and value < 0) { // Negative value. n1 = -value; buffer[0] = '-'; buffer_count += 1; } while (n1 != 0) { const base = radix.base(); buffer[buffer_count] = @intCast(u8, (n1 % base) + '0'); n1 = (n1 / base); buffer_count += 1; } for (buffer[0 .. (buffer_count / 2)]) |_, i| io.swap(u8, &buffer[i], &buffer[buffer_count - i - 1]); if ((try writer.write(buffer[0 .. buffer_count])) != buffer_count) return error.WriteFailure; } }, // Cast comptime int into known-size integer and try again. .ComptimeInt => return printInt(writer, radix, @intCast(math.IntFittingRange(value, value), value)), else => @compileError("`value` must be of type int or comptime_int"), } } test "printInt" { // Max digits to represent a decimal u8 is 3 (i.e. 127 / 255). var decimal_buffer = [_]u8{0} ** 3; var decimal_stack = stack.Fixed(u8){.buffer = &decimal_buffer}; var decimal_writer = stack.fixedWriter(&decimal_stack); try printInt(decimal_writer, .decimal, 365); try testing.expect(decimal_stack.isFull()); }