272 lines
8.2 KiB
Plaintext
272 lines
8.2 KiB
Plaintext
// 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();
|
||
}
|