stage2 更多的工具库

This commit is contained in:
zzy committed 2026-09-17 22:32:36 +08:00
1 parent 4141e7d574
commit 16a1f030d4
15 files changed
+1914 -109

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
// Expect retcode = 0
// I/O 便利层(构建在 sys/file 之上):
// - C 字符串路径/模式的 open 包装
// - 整块读写(write_all / read_all)
// - stdout/stderr 打印
// 说明:read_all 会按文件大小一次性读入 ByteBuffer;不处理增长中的文件。
type .{ .Error, .Status, .make } = @import("error.spl");
type .{ .ByteView } = @import("byte_view.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type File = @import("../sys/file.spl");
// 把 view 复制到以 NUL 结尾的临时缓冲,返回其 data(归属 tmp,调用后勿再用)。
fn cstr(tmp: *ByteBuffer, view: ByteView): *u8 {
ByteBuffer.clear(tmp);
if view.len != 0 {
match ByteBuffer.append(tmp, ByteView.as_slice(view)) {
.Ok => {},
.Err(error) => { ret null; },
}
}
match ByteBuffer.append_byte(tmp, '\0') {
.Ok => {},
.Err(error) => { ret null; },
}
ret tmp.storage.data;
}
fn open_cstr(path: ByteView, mode: ByteView): File {
var pbuf = ByteBuffer.new();
var mbuf = ByteBuffer.new();
var p = cstr(&pbuf, path);
var m = cstr(&mbuf, mode);
var f = File.open(p, m);
ByteBuffer.drop(&pbuf);
ByteBuffer.drop(&mbuf);
ret f;
}
// 反复写入直到写完;返回实际写入字节数(!= bytes.len 表示中途失败)。
fn write_all(f: *File, bytes: []u8): u64 {
var total: u64 = 0;
var want = bytes.len as u64;
while total < want {
var offset = total as usize;
var w = File.write(f, bytes.ptr + offset, want - total);
if w == 0 { break; }
total = total + w;
}
ret total;
}
fn print(bytes: []u8) {
var out = File.stdout();
write_all(&out, bytes);
}
fn println(bytes: []u8) {
print(bytes);
print("\n");
}
fn eprint(bytes: []u8) {
var err = File.stderr();
write_all(&err, bytes);
}
// 读取整个文件到 out(先清空)。打开失败或不完整返回 Err(.Io)。
fn read_all(path: ByteView, out: *ByteBuffer): Status {
var f = open_cstr(path, ByteView.from_slice("rb"));
if !File.valid(&f) { ret .Err(make(.Io)); }
var sz = File.size(&f);
ByteBuffer.clear(out);
if sz != 0 {
match ByteBuffer.reserve(out, sz as usize) {
.Ok => {},
.Err(error) => { File.close(&f); ret .Err(error); },
}
var got = File.read(&f, out.storage.data, sz);
out.storage.len = got as usize;
File.close(&f);
if got != sz { ret .Err(make(.Io)); }
ret .Ok;
}
File.close(&f);
ret .Ok;
}
// ---- 测试辅助 ----
fn view(text: []u8): ByteView {
ret ByteView.from_slice(text);
}
fn test_roundtrip() {
var path = view("build/_io_roundtrip.tmp");
var f = open_cstr(path, view("wb"));
@assert(File.valid(&f), "open wb");
var w = write_all(&f, "hello\nworld");
@assert(w == 11, "write_all length");
File.close(&f);
var out = ByteBuffer.new();
match read_all(path, &out) {
.Ok => {},
.Err(error) => { @assert(false, "read_all"); },
}
var got = ByteBuffer.as_slice(out);
@assert(got.len == 11, "roundtrip length");
@assert(got.ptr[0] == 'h' && got.ptr[5] == '\n' && got.ptr[10] == 'd', "roundtrip bytes");
ByteBuffer.drop(&out);
}
fn test_append_and_missing() {
var path = view("build/_io_append.tmp");
var f = open_cstr(path, view("wb"));
@assert(File.valid(&f), "open wb");
write_all(&f, "ab");
File.close(&f);
f = open_cstr(path, view("ab"));
@assert(File.valid(&f), "open ab");
var w2 = write_all(&f, "cd");
File.close(&f);
@assert(w2 == 2, "append length");
var out = ByteBuffer.new();
match read_all(path, &out) {
.Ok => {},
.Err(error) => { @assert(false, "read append"); },
}
var got = ByteBuffer.as_slice(out);
@assert(got.len == 4 && got.ptr[2] == 'c' && got.ptr[3] == 'd', "append content");
// 不存在的文件
match read_all(view("build/_io_no_such_file.tmp"), &out) {
.Ok => { @assert(false, "missing should fail"); },
.Err(error) => {
match error.kind { .Io => {}, _ => { @assert(false, "error kind"); } }
},
}
ByteBuffer.drop(&out);
}
fn main() {
test_roundtrip();
test_append_and_missing();
println("io: ok");
}