// 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(); }