Files
spl/stage2/utils/fmt.spl
T

242 lines
6.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Expect retcode = 0
// 数值/布尔格式化,写入 ByteBuffer。radix 取值 2..36;uppercase 只影响 10..35 的字母形式。
// 不分配额外内存(除目标 ByteBuffer 自身扩容),失败不产生部分输出。
type .{ .Error, .Status, .make } = @import("error.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type .{ .digit_char } = @import("ascii.spl");
// 注意:radix 边界直接用字面量。stage1 刻意不做常量求值/折叠,
// 顶层 const 不应用于运行时表达式。
fn valid_radix(radix: u8): bool {
ret radix >= 2 && radix <= 36;
}
fn append_u64(out: *ByteBuffer, value: u64, radix: u8, uppercase: bool): Status {
if !valid_radix(radix) { ret .Err(make(.InvalidArgument)); }
var digits: [64]u8 = undefined;
var count: usize = 0;
if value == 0 {
digits[0] = 48;
count = 1;
} else {
var v = value;
var base = radix as u64;
while v != 0 {
var d = (v % base) as u8;
match digit_char(d, uppercase) {
.Ok(c) => { digits[count] = c; },
.Err(error) => { ret .Err(error); },
}
count = count + 1;
v = v / base;
}
}
// digits 是低位在前,反向写入得到正常顺序。
var i = count;
while i > 0 {
i = i - 1;
match ByteBuffer.append_byte(out, digits[i]) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
}
ret .Ok;
}
fn append_i64(out: *ByteBuffer, value: i64, radix: u8, uppercase: bool): Status {
if !valid_radix(radix) { ret .Err(make(.InvalidArgument)); }
if value < 0 {
match ByteBuffer.append_byte(out, 45) { // '-'
.Ok => {},
.Err(error) => { ret .Err(error); },
}
// value 为补码,(0 - (value as u64)) 得到绝对值(含 i64 最小值)。
var magnitude = 0 - (value as u64);
ret append_u64(out, magnitude, radix, uppercase);
}
ret append_u64(out, value as u64, radix, uppercase);
}
fn append_usize(out: *ByteBuffer, value: usize, radix: u8, uppercase: bool): Status {
ret append_u64(out, value as u64, radix, uppercase);
}
fn append_isize(out: *ByteBuffer, value: isize, radix: u8, uppercase: bool): Status {
ret append_i64(out, value as i64, radix, uppercase);
}
fn append_bool(out: *ByteBuffer, value: bool): Status {
if value { ret ByteBuffer.append(out, "true"); }
ret ByteBuffer.append(out, "false");
}
// ---- 测试辅助 ----
fn expect_ok(result: Status) {
match result {
.Ok => {},
.Err(error) => { @assert(false, "unexpected fmt error"); },
}
}
fn expect_invalid(result: Status) {
match result {
.Ok => { @assert(false, "expected fmt error"); },
.Err(error) => {
match error.kind {
.InvalidArgument => {},
_ => { @assert(false, "unexpected fmt error kind"); },
}
},
}
}
fn expect_text(buffer: *ByteBuffer, expected: []u8) {
var actual = ByteBuffer.as_slice(buffer.*);
@assert(actual.len == expected.len, "formatted length");
var i: usize = 0;
while i < expected.len {
@assert(actual.ptr[i] == expected.ptr[i], "formatted byte");
i = i + 1;
}
}
fn test_unsigned() {
var b = ByteBuffer.new();
expect_ok(append_u64(&b, 0, 10, false));
expect_text(&b, "0");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 42, 10, false));
expect_text(&b, "42");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 1234567890, 10, false));
expect_text(&b, "1234567890");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 18446744073709551615, 10, false));
expect_text(&b, "18446744073709551615");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 255, 16, false));
expect_text(&b, "ff");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 255, 16, true));
expect_text(&b, "FF");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 4096, 16, false));
expect_text(&b, "1000");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 170, 2, false));
expect_text(&b, "10101010");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 35, 36, false));
expect_text(&b, "z");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 18446744073709551615, 2, false));
expect_text(&b, "1111111111111111111111111111111111111111111111111111111111111111");
ByteBuffer.clear(&b);
ByteBuffer.drop(&b);
}
fn test_signed() {
var b = ByteBuffer.new();
expect_ok(append_i64(&b, 0, 10, false));
expect_text(&b, "0");
ByteBuffer.clear(&b);
expect_ok(append_i64(&b, -123, 10, false));
expect_text(&b, "-123");
ByteBuffer.clear(&b);
expect_ok(append_i64(&b, 9223372036854775807, 10, false));
expect_text(&b, "9223372036854775807");
ByteBuffer.clear(&b);
expect_ok(append_i64(&b, -9223372036854775808, 10, false));
expect_text(&b, "-9223372036854775808");
ByteBuffer.clear(&b);
expect_ok(append_isize(&b, -16, 16, false));
expect_text(&b, "-10");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 48879, 16, true));
expect_text(&b, "BEEF");
ByteBuffer.clear(&b);
ByteBuffer.drop(&b);
}
fn test_usize_bool_and_errors() {
var b = ByteBuffer.new();
expect_ok(append_usize(&b, 1000 as usize, 10, false));
expect_text(&b, "1000");
ByteBuffer.clear(&b);
expect_ok(append_bool(&b, true));
expect_text(&b, "true");
ByteBuffer.clear(&b);
expect_ok(append_bool(&b, false));
expect_text(&b, "false");
ByteBuffer.clear(&b);
expect_invalid(append_u64(&b, 1, 0, false));
expect_invalid(append_u64(&b, 1, 1, false));
expect_invalid(append_u64(&b, 1, 37, false));
expect_invalid(append_i64(&b, -1, 0, false));
@assert(ByteBuffer.size(b) == 0, "invalid radix writes nothing");
ByteBuffer.drop(&b);
}
fn test_composition_and_growth() {
var b = ByteBuffer.new();
expect_ok(ByteBuffer.append(&b, "x="));
expect_ok(append_i64(&b, -42, 10, false));
expect_ok(ByteBuffer.append(&b, " hex="));
expect_ok(append_u64(&b, 255, 16, false));
expect_ok(ByteBuffer.append(&b, " ok="));
expect_ok(append_bool(&b, true));
expect_text(&b, "x=-42 hex=ff ok=true");
ByteBuffer.clear(&b);
// 反复写入触发 ByteBuffer 扩容,校验累积内容。
var i: i32 = 0;
while i < 200 {
expect_ok(append_i64(&b, i as i64, 10, false));
expect_ok(ByteBuffer.append_byte(&b, 44)); // ','
i = i + 1;
}
expect_ok(append_i64(&b, 200, 10, false));
@assert(ByteBuffer.size(b) > 0, "growth content");
var s = ByteBuffer.as_slice(b);
@assert(s.ptr[0] == 48, "starts with 0");
@assert(s.ptr[1] == 44, "0 then comma");
ByteBuffer.drop(&b);
}
fn main() {
test_unsigned();
test_signed();
test_usize_bool_and_errors();
test_composition_and_growth();
}