Files
spl/stage2/utils/fmt.spl
T
2026-09-18 11:17:33 +08:00

272 lines
8.2 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 的字母形式。
// 整数转换统一复用 MPA。先在临时缓冲区完成转换,再一次性追加,失败不产生部分输出。
type .{ .Error, .Status, .make } = @import("error.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type .{ .BigInt, .Bits } = @import("mp.spl");
// 注意:radix 边界直接用字面量。stage1 刻意不做常量求值/折叠,
// 顶层 const 不应用于运行时表达式。
fn valid_radix(radix: u8): bool {
ret radix >= 2 && radix <= 36;
}
fn append_big_int(out: *ByteBuffer, value: BigInt,
radix: u8, uppercase: bool): Status {
if !valid_radix(radix) { ret .Err(make(.InvalidArgument)); }
var text = ByteBuffer.new();
defer { ByteBuffer.drop(&text); }
match BigInt.format(&text, value, radix, uppercase) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret ByteBuffer.append(out, ByteBuffer.as_slice(text));
}
fn append_bits(out: *ByteBuffer, value: Bits, signed: bool,
radix: u8, uppercase: bool): Status {
var integer = BigInt.zero();
defer { BigInt.drop(&integer); }
match Bits.to_big(&integer, value, signed) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_big_int(out, integer, radix, uppercase);
}
fn append_u64(out: *ByteBuffer, value: u64, radix: u8, uppercase: bool): Status {
var integer = BigInt.zero();
defer { BigInt.drop(&integer); }
match BigInt.from_u64(&integer, value) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_big_int(out, integer, radix, uppercase);
}
fn append_i64(out: *ByteBuffer, value: i64, radix: u8, uppercase: bool): Status {
var integer = BigInt.zero();
defer { BigInt.drop(&integer); }
match BigInt.from_i64(&integer, value) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_big_int(out, integer, 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();
defer { ByteBuffer.drop(&b); }
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);
}
fn test_signed() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
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);
}
fn test_usize_bool_and_errors() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
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");
expect_ok(ByteBuffer.append(&b, "prefix"));
expect_invalid(append_i64(&b, -123, 1, false));
expect_text(&b, "prefix");
}
fn test_mpa_integer_formatting() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
var huge = BigInt.zero();
defer { BigInt.drop(&huge); }
expect_ok(BigInt.parse(&huge, "1234567890123456789012345678901234567890", 10));
expect_ok(append_big_int(&b, huge, 10, false));
expect_text(&b, "1234567890123456789012345678901234567890");
ByteBuffer.clear(&b);
expect_ok(BigInt.parse(&huge, "-ffffffffffffffffffffffffffffffff", 16));
expect_ok(append_big_int(&b, huge, 16, true));
expect_text(&b, "-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
ByteBuffer.clear(&b);
var raw = BigInt.zero();
defer { BigInt.drop(&raw); }
expect_ok(BigInt.from_u64(&raw, 255));
var bits: Bits = undefined;
match Bits.new(8) {
.Ok(value) => { bits = value; },
.Err(error) => { @assert(false, "Bits construction"); },
}
defer { Bits.drop(&bits); }
expect_ok(Bits.from_big_checked(&bits, raw, 8, false));
expect_ok(append_bits(&b, bits, false, 10, false));
expect_text(&b, "255");
ByteBuffer.clear(&b);
expect_ok(append_bits(&b, bits, true, 10, false));
expect_text(&b, "-1");
}
fn test_composition_and_growth() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
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, ',')); // ','
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] == '0', "starts with 0");
@assert(s.ptr[1] == ',', "0 then comma");
}
fn main() {
test_unsigned();
test_signed();
test_usize_bool_and_errors();
test_mpa_integer_formatting();
test_composition_and_growth();
}