120 lines
3.5 KiB
Zig
120 lines
3.5 KiB
Zig
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());
|
|
}
|