ona/src/core/unicode.zig

120 lines
3.5 KiB
Zig
Raw Normal View History

2022-10-30 22:07:36 +00:00
const io = @import("./io.zig");
const math = @import("./math.zig");
const stack = @import("./stack.zig");
const testing = @import("./testing.zig");
2022-10-30 22:07:36 +00:00
///
/// [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 {
2022-10-30 22:07:36 +00:00
WriteFailure,
};
///
/// Named identifiers for number formats used in printing functions.
2022-10-30 22:07:36 +00:00
///
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,
};
}
2022-10-30 22:07:36 +00:00
};
///
/// 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";
2022-10-30 22:07:36 +00:00
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;
2022-10-30 22:07:36 +00:00
if (info.signedness == .signed and value < 0) {
// Negative value.
n1 = -value;
buffer[0] = '-';
buffer_count += 1;
}
2022-10-30 22:07:36 +00:00
while (n1 != 0) {
const base = radix.base();
2022-10-30 22:07:36 +00:00
buffer[buffer_count] = @intCast(u8, (n1 % base) + '0');
n1 = (n1 / base);
buffer_count += 1;
}
2022-10-30 22:07:36 +00:00
for (buffer[0 .. (buffer_count / 2)]) |_, i|
io.swap(u8, &buffer[i], &buffer[buffer_count - i - 1]);
2022-10-30 22:07:36 +00:00
if ((try writer.write(buffer[0 .. buffer_count])) != buffer_count)
return error.WriteFailure;
}
2022-10-30 22:07:36 +00:00
},
// 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());
2022-10-30 22:07:36 +00:00
}