stage2 更多的工具库 stage1 修复问题

This commit is contained in:
zzy committed 2026-09-18 10:10:53 +08:00
1 parent 16a1f030d4
commit b3e38ba7e4
28 files changed
+2274 -523

No files matched your search

+75 -23
View File
@@ -4,13 +4,14 @@
// - 输入 ByteView,字符串/数字以“源切片”借用,不做解码,round-trip 保真;
// - 节点池 Vec[JsonNode];数组/对象子项存 Vec[JsonChild],保留插入顺序;
// - 错误带字节位置(error.spl);输出写 ByteBuffer;缩进/数字用 ByteBuffer/fmt。
// 限制:不做字符串转义解码、无递归深度保护、数字不做数值化(原样保存)。
// 限制:不做字符串转义解码、数字不做数值化(原样保存);递归/输出深度上限见 max_depth()。
type .{ .Error, .Status, .make, .at } = @import("error.spl");
type .{ .ByteView, .ViewResult, .FindResult } = @import("byte_view.spl");
type .{ .Vec } = @import("vec.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type .{ .is_digit, .is_hex_digit, .is_space } = @import("ascii.spl");
type .{ .checked_mul } = @import("memory.spl");
type .{ .append_i64, .append_u64 } = @import("fmt.spl");
type JsonKind = enum {
@@ -29,6 +30,10 @@ type JsonChild = struct {
val: usize,
}
// 节点:kind 决定有效载荷字段,其余字段保持空。访问方必须按 kind 读取:
// Null/True/False -> text 空、children 空;
// Number/String -> text 为源切片、children 空;
// Array/Object -> children 保存子项、text 空。
type JsonNode = struct {
kind: JsonKind,
text: ByteView,
@@ -57,6 +62,11 @@ type Parser = struct {
nodes: Vec,
}
// 递归/输出深度上限:防止病态输入耗尽调用栈。256 层足够覆盖正常 JSON。
fn max_depth(): usize {
ret 256;
}
fn blank_node(kind: JsonKind): JsonNode {
ret JsonNode {
.kind = kind,
@@ -78,7 +88,7 @@ fn intern_node(p: *Parser, node: JsonNode): NodeResult {
fn node_at(doc: *Json, index: usize): *JsonNode {
match Vec.get(&doc.nodes, index) {
.Ok(ptr) => { ret ptr as *JsonNode; },
.Err(error) => { ret null; },
.Err(error) => { ret null; }, // 越界/无效池统一返回 null,调用方判空
}
ret null;
}
@@ -122,6 +132,7 @@ fn parse_string_raw(p: *Parser): ViewResult {
}
}
} else if c < 32 {
// JSON 只禁止未转义的 U+0000..U+001F;0x7F 合法,不能用 ascii.is_control。
ret .Err(at(.InvalidFormat, p.pos));
}
p.pos = p.pos + 1;
@@ -192,7 +203,8 @@ fn parse_number(p: *Parser): NodeResult {
}
}
fn parse_array(p: *Parser): NodeResult {
fn parse_array(p: *Parser, depth: usize): NodeResult {
if depth >= max_depth() { ret .Err(at(.OutOfRange, p.pos)); }
p.pos = p.pos + 1; // '['
var kids = Vec.blank(@sizeof(JsonChild), 8);
@@ -201,11 +213,14 @@ fn parse_array(p: *Parser): NodeResult {
p.pos = p.pos + 1;
var empty = blank_node(.Array);
empty.children = kids;
ret intern_node(p, empty);
match intern_node(p, empty) {
.Ok(index) => { ret .Ok(index); },
.Err(error) => { Vec.drop(&empty.children); ret .Err(error); },
}
}
while true {
var element = parse_value(p);
var element = parse_value(p, depth + 1);
var index: usize = 0;
match element {
.Ok(v) => { index = v; },
@@ -232,10 +247,14 @@ fn parse_array(p: *Parser): NodeResult {
var node = blank_node(.Array);
node.children = kids;
ret intern_node(p, node);
match intern_node(p, node) {
.Ok(index) => { ret .Ok(index); },
.Err(error) => { Vec.drop(&node.children); ret .Err(error); },
}
}
fn parse_object(p: *Parser): NodeResult {
fn parse_object(p: *Parser, depth: usize): NodeResult {
if depth >= max_depth() { ret .Err(at(.OutOfRange, p.pos)); }
p.pos = p.pos + 1; // '{'
var kids = Vec.blank(@sizeof(JsonChild), 8);
@@ -244,7 +263,10 @@ fn parse_object(p: *Parser): NodeResult {
p.pos = p.pos + 1;
var empty = blank_node(.Object);
empty.children = kids;
ret intern_node(p, empty);
match intern_node(p, empty) {
.Ok(index) => { ret .Ok(index); },
.Err(error) => { Vec.drop(&empty.children); ret .Err(error); },
}
}
while true {
@@ -262,7 +284,7 @@ fn parse_object(p: *Parser): NodeResult {
}
p.pos = p.pos + 1;
var value = parse_value(p);
var value = parse_value(p, depth + 1);
var index: usize = 0;
match value {
.Ok(v) => { index = v; },
@@ -290,10 +312,13 @@ fn parse_object(p: *Parser): NodeResult {
var node = blank_node(.Object);
node.children = kids;
ret intern_node(p, node);
match intern_node(p, node) {
.Ok(index) => { ret .Ok(index); },
.Err(error) => { Vec.drop(&node.children); ret .Err(error); },
}
}
fn parse_value(p: *Parser): NodeResult {
fn parse_value(p: *Parser, depth: usize): NodeResult {
skip_ws(p);
var c = peek(p);
if c < 0 { ret .Err(at(.UnexpectedEnd, p.pos)); }
@@ -309,8 +334,8 @@ fn parse_value(p: *Parser): NodeResult {
node.text = view;
ret intern_node(p, node);
}
if ch == '{' { ret parse_object(p); } // '{'
if ch == '[' { ret parse_array(p); } // '['
if ch == '{' { ret parse_object(p, depth); } // '{'
if ch == '[' { ret parse_array(p, depth); } // '['
if ch == 't' { ret parse_literal(p, "true", .True); }
if ch == 'f' { ret parse_literal(p, "false", .False); }
if ch == 'n' { ret parse_literal(p, "null", .Null); }
@@ -325,6 +350,7 @@ fn drop_node_pool(pool: *Vec) {
var n = ptr as *JsonNode;
Vec.drop(&n.children);
},
// i < pool.len 且池有效,get 不会失败;失败时无额外资源可释放。
.Err(error) => {},
}
i = i + 1;
@@ -341,7 +367,7 @@ fn parse(src: ByteView): JsonResult {
skip_ws(&p);
var root: usize = 0;
var result = parse_value(&p);
var result = parse_value(&p, 0);
match result {
.Ok(index) => { root = index; },
.Err(error) => {
@@ -367,12 +393,6 @@ fn root_index(doc: *Json): usize {
ret doc.root;
}
fn kind_of(doc: *Json, index: usize): JsonKind {
var n = node_at(doc, index);
if n == null { ret .Null; }
ret n.kind;
}
// 枚举 tag 取整(stage1 枚举 == 有缺陷,用 @enum2int 比较更稳妥)。
fn kind_code(kind: JsonKind): usize {
ret @enum2int(kind) as usize;
@@ -424,6 +444,7 @@ fn object_find(doc: *Json, index: usize, key: ByteView): FindResult {
var child = ptr as *JsonChild;
if ByteView.eq(child.key, key) { ret .Found(child.val); }
},
// i < count == children.len,get 不会失败。
.Err(error) => {},
}
i = i + 1;
@@ -452,10 +473,16 @@ fn emit_indent(out: *ByteBuffer, indent: usize, depth: usize): Status {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret ByteBuffer.append_repeat(out, 32, depth * indent);
var spaces: usize = 0;
match checked_mul(depth, indent) {
.Ok(value) => { spaces = value; },
.Err(error) => { ret .Err(error); },
}
ret ByteBuffer.append_repeat(out, 32, spaces);
}
fn emit(out: *ByteBuffer, doc: *Json, index: usize, indent: usize, depth: usize): Status {
if depth >= max_depth() { ret .Err(make(.OutOfRange)); }
var n = node_at(doc, index);
if n == null { ret .Err(make(.OutOfRange)); }
@@ -738,19 +765,44 @@ fn test_errors() {
expect_parse_error(parse(ByteView.from_slice("01")), 1);
}
fn test_depth_limit() {
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
var i: usize = 0;
while i < max_depth() + 8 {
expect_ok(ByteBuffer.append_byte(&out, '['));
i = i + 1;
}
i = 0;
while i < max_depth() + 8 {
expect_ok(ByteBuffer.append_byte(&out, ']'));
i = i + 1;
}
match parse(ByteView.from_slice(ByteBuffer.as_slice(out))) {
.Ok(doc) => { drop(&doc); @assert(false, "deep input should be rejected"); },
.Err(error) => {
match error.kind {
.OutOfRange => {},
_ => { @assert(false, "depth limit error kind"); },
}
},
}
}
fn test_construction_helpers() {
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
expect_ok(ByteBuffer.append(&out, "{\"id\":"));
expect_ok(write_u64(&out, 18446744073709551615));
expect_ok(ByteBuffer.append(&out, ",\"n\":"));
expect_ok(write_int(&out, -7));
expect_ok(ByteBuffer.append(&out, "}"));
expect_bytes(&out, "{\"id\":18446744073709551615,\"n\":-7}");
ByteBuffer.drop(&out);
}
fn test_large_array() {
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
expect_ok(ByteBuffer.append_byte(&out, '[')); // '['
var i: usize = 0;
while i < 1000 {
@@ -771,7 +823,6 @@ fn test_large_array() {
},
.Err(error) => { @assert(false, "parse large array"); },
}
ByteBuffer.drop(&out);
}
fn main() {
@@ -780,6 +831,7 @@ fn main() {
test_roundtrip_compact();
test_pretty();
test_errors();
test_depth_limit();
test_construction_helpers();
test_large_array();
}