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

+63 -27
View File
@@ -1,4 +1,5 @@
// Expect retcode = 0
// 路径工具(不访问文件系统):同时接受 '/' 与 '\\';支持 Windows 盘符 "C:"。
// 输入为借用 ByteView,输出写入调用方的 ByteBuffer。
// 注意:normalize 对相对路径的前导 ".." 采取丢弃(不保留),适用于导入路径场景。
@@ -7,13 +8,14 @@ type .{ .Error, .Status, .make } = @import("error.spl");
type .{ .ByteView } = @import("byte_view.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type .{ .Vec } = @import("vec.spl");
type .{ .is_alpha } = @import("ascii.spl");
fn is_sep(c: u8): bool {
ret c == '/' || c == '\\'; // '/' '\\'
}
fn is_drive(path: ByteView): bool {
ret path.len >= 2 && path.ptr[1] == ':'; // ':'
ret path.len >= 2 && is_alpha(path.ptr[0]) && path.ptr[1] == ':'; // 盘符首字符必须是字母
}
fn is_absolute(path: ByteView): bool {
@@ -33,6 +35,7 @@ fn last_sep(path: ByteView): usize {
}
// 目录部分(不含末尾分隔符);根 "/" 返回 "/";无分隔符返回空。
// 下标均由 last_sep 保证在界内,slice 不会失败;Err 分支仅作防御。
fn dirname(path: ByteView): ByteView {
var idx = last_sep(path);
if idx == ~(0 as usize) { ret ByteView.empty(); }
@@ -40,13 +43,13 @@ fn dirname(path: ByteView): ByteView {
var r = ByteView.slice(path, 0, 1);
match r {
.Ok(v) => { ret v; },
.Err(error) => { ret ByteView.empty(); },
.Err(error) => { ret ByteView.empty(); }, // 防御:索引已界内
}
}
var r = ByteView.slice(path, 0, idx);
match r {
.Ok(v) => { ret v; },
.Err(error) => { ret ByteView.empty(); },
.Err(error) => { ret ByteView.empty(); }, // 防御:索引已界内
}
ret ByteView.empty();
}
@@ -57,7 +60,7 @@ fn basename(path: ByteView): ByteView {
var r = ByteView.slice(path, idx + 1, path.len);
match r {
.Ok(v) => { ret v; },
.Err(error) => { ret ByteView.empty(); },
.Err(error) => { ret ByteView.empty(); }, // 防御:idx+1 <= len
}
ret ByteView.empty();
}
@@ -73,39 +76,68 @@ fn extension(path: ByteView): ByteView {
var r = ByteView.slice(base, i + 1, base.len);
match r {
.Ok(v) => { ret v; },
.Err(error) => { ret ByteView.empty(); },
.Err(error) => { ret ByteView.empty(); }, // 防御:i+1 <= base.len
}
}
}
ret ByteView.empty();
}
// out = dir + sep + rel(rel 绝对时直接使用 rel);先清空 out。
// out = dir + sep + rel(rel 绝对时直接使用 rel);成功后才替换 out,失败保持 out 不变。
fn join_into(out: *ByteBuffer, dir: ByteView, rel: ByteView): Status {
ByteBuffer.clear(out);
var fresh = ByteBuffer.new();
if is_absolute(rel) {
ret ByteBuffer.append(out, ByteView.as_slice(rel));
match ByteBuffer.append(&fresh, ByteView.as_slice(rel)) {
.Ok => {},
.Err(error) => { ByteBuffer.drop(&fresh); ret .Err(error); },
}
ByteBuffer.drop(out);
out.* = ByteBuffer.take(&fresh);
ret .Ok;
}
match ByteBuffer.append(&fresh, ByteView.as_slice(dir)) {
.Ok => {},
.Err(error) => { ByteBuffer.drop(&fresh); ret .Err(error); },
}
match ByteBuffer.append(out, ByteView.as_slice(dir)) { .Ok => {}, .Err(e) => { ret .Err(e); } }
if dir.len != 0 && !is_sep(dir.ptr[dir.len - 1]) {
match ByteBuffer.append_byte(out, '/') { .Ok => {}, .Err(e) => { ret .Err(e); } }
match ByteBuffer.append_byte(&fresh, '/') {
.Ok => {},
.Err(error) => { ByteBuffer.drop(&fresh); ret .Err(error); },
}
}
ret ByteBuffer.append(out, ByteView.as_slice(rel));
match ByteBuffer.append(&fresh, ByteView.as_slice(rel)) {
.Ok => {},
.Err(error) => { ByteBuffer.drop(&fresh); ret .Err(error); },
}
ByteBuffer.drop(out);
out.* = ByteBuffer.take(&fresh);
ret .Ok;
}
// 折叠重复分隔符、'.',及'..'(回退上一段;相对路径前导 '..' 丢弃)。
// 成功后才替换 out,失败保持 out 不变。
fn normalize_into(out: *ByteBuffer, path: ByteView): Status {
ByteBuffer.clear(out);
if path.len == 0 { ret .Ok; }
var fresh = ByteBuffer.new();
if path.len == 0 {
ByteBuffer.drop(out);
out.* = ByteBuffer.take(&fresh);
ret .Ok;
}
var starts = Vec.blank(@sizeof(usize), 8);
var i: usize = 0;
if is_sep(path.ptr[0]) {
match ByteBuffer.append_byte(out, '/') { .Ok => {}, .Err(e) => { Vec.drop(&starts); ret .Err(e); } }
match ByteBuffer.append_byte(&fresh, '/') {
.Ok => {}, .Err(e) => { Vec.drop(&starts); ByteBuffer.drop(&fresh); ret .Err(e); },
}
} else if is_drive(path) {
match ByteBuffer.append_byte(out, path.ptr[0]) { .Ok => {}, .Err(e) => { Vec.drop(&starts); ret .Err(e); } }
match ByteBuffer.append_byte(out, ':') { .Ok => {}, .Err(e) => { Vec.drop(&starts); ret .Err(e); } }
match ByteBuffer.append_byte(&fresh, path.ptr[0]) {
.Ok => {}, .Err(e) => { Vec.drop(&starts); ByteBuffer.drop(&fresh); ret .Err(e); },
}
match ByteBuffer.append_byte(&fresh, ':') {
.Ok => {}, .Err(e) => { Vec.drop(&starts); ByteBuffer.drop(&fresh); ret .Err(e); },
}
i = 2; // 跳过盘符
}
@@ -119,7 +151,7 @@ fn normalize_into(out: *ByteBuffer, path: ByteView): Status {
var seg = ByteView.empty();
match seg_result {
.Ok(v) => { seg = v; },
.Err(error) => { Vec.drop(&starts); ret .Err(error); },
.Err(error) => { Vec.drop(&starts); ByteBuffer.drop(&fresh); ret .Err(error); },
}
if ByteView.eq(seg, ByteView.from_slice(".")) { continue; }
@@ -127,30 +159,34 @@ fn normalize_into(out: *ByteBuffer, path: ByteView): Status {
if starts.len > 0 {
var idx = starts.len - 1;
match Vec.get(&starts, idx) {
.Ok(p) => { out.storage.len = (p as *usize).*; },
.Err(error) => {},
.Ok(p) => { fresh.storage.len = (p as *usize).*; },
.Err(error) => {}, // starts.len 已保证 idx 在界内,get 不会失败
}
starts.len = starts.len - 1;
}
continue;
}
var comp_start = out.storage.len;
if comp_start > 0 && !is_sep(out.storage.data[comp_start - 1]) {
match ByteBuffer.append_byte(out, '/') { .Ok => {}, .Err(e) => { Vec.drop(&starts); ret .Err(e); } }
var comp_start = fresh.storage.len;
if comp_start > 0 && !is_sep(fresh.storage.data[comp_start - 1]) {
match ByteBuffer.append_byte(&fresh, '/') {
.Ok => {}, .Err(e) => { Vec.drop(&starts); ByteBuffer.drop(&fresh); ret .Err(e); },
}
}
var recorded = comp_start;
match Vec.push(&starts, &recorded as *u8) {
.Ok => {},
.Err(error) => { Vec.drop(&starts); ret .Err(error); },
.Err(error) => { Vec.drop(&starts); ByteBuffer.drop(&fresh); ret .Err(error); },
}
match ByteBuffer.append(out, ByteView.as_slice(seg)) {
match ByteBuffer.append(&fresh, ByteView.as_slice(seg)) {
.Ok => {},
.Err(error) => { Vec.drop(&starts); ret .Err(error); },
.Err(error) => { Vec.drop(&starts); ByteBuffer.drop(&fresh); ret .Err(error); },
}
}
Vec.drop(&starts);
ByteBuffer.drop(out);
out.* = ByteBuffer.take(&fresh);
ret .Ok;
}
@@ -200,17 +236,18 @@ fn test_parts() {
fn test_join() {
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
expect_ok(join_into(&out, view("stage2/utils"), view("vec.spl")));
expect_out(&out, "stage2/utils/vec.spl");
expect_ok(join_into(&out, view("a/"), view("b.spl")));
expect_out(&out, "a/b.spl");
expect_ok(join_into(&out, view("ignored"), view("/abs/x")));
expect_out(&out, "/abs/x");
ByteBuffer.drop(&out);
}
fn test_normalize() {
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
expect_ok(normalize_into(&out, view("a/b/../c")));
expect_out(&out, "a/c");
expect_ok(normalize_into(&out, view("./a//b")));
@@ -221,7 +258,6 @@ fn test_normalize() {
expect_out(&out, "C:/b");
expect_ok(normalize_into(&out, view("a/..")));
expect_out(&out, "");
ByteBuffer.drop(&out);
}
fn main() {