stage2 更多的工具库
This commit is contained in:
1 parent
4141e7d574
commit
16a1f030d4
15 files changed
+1914
-109
No files matched your search
@@ -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");
|
||||
}
|
||||
Reference in new issue
Block a user