feat: add bounded read-to-end and compatible file paths

This commit is contained in:
2026-10-22 23:21:41 +02:00
parent 98f3a04daf
commit 31c2cad9f9
5 changed files with 176 additions and 35 deletions
+36 -1
View File
@@ -1,5 +1,6 @@
import "@ffi/c"
import "@std/mem"
import "@std/arraylist"
# typeinfo! requires this import even without a qualified meta reference.
import "@std/meta"
@@ -8,6 +9,12 @@ ReadError :: enum {
read_failed
}
ReadLimitError :: enum {
limit_exceeded
}
ReadAllError :: alias (mem.AllocError | ReadError | ReadLimitError)
WriteError :: enum {
not_open_for_writing
write_failed
@@ -34,7 +41,7 @@ Io :: struct {
IoVTable :: struct {
read @proc(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
write @proc(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError
open @proc(context ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError
open @proc(context ?@mut anyopaque, path [*;0]u8, mode FileMode) Handle ! OpenError
close @proc(context ?@mut anyopaque, handle Handle) void ! CloseError
stdin @proc(context ?@mut anyopaque) Handle
stdout @proc(context ?@mut anyopaque) Handle
@@ -64,6 +71,34 @@ read proc(input Reader, buffer []mut u8) usize ! ReadError {
return count
}
#! read to EOF, accepting at most `maximum` bytes; the reader remains caller-owned.
#! exceeding the limit consumes one extra byte and returns limit_exceeded.
#! returned storage retains `allocator`; release it with arraylist.deinit.
#! all allocated storage is released on error.
read_all proc(allocator mem.Allocator, input Reader, maximum usize) arraylist.ArrayList(u8) ! ReadAllError {
bytes := arraylist.init(u8, allocator)
errdefer arraylist.deinit(&bytes)
while true {
length :: bytes.items.len
if (length == maximum) {
probe [1]mut u8 := undefined
if (try read(input, (&probe)[..]) != 0) return .limit_exceeded
return bytes
}
if (length == bytes.capacity) {
growth := usize(4096)
if (growth > maximum - length) growth = maximum - length
try arraylist.reserve(&bytes, length + growth)
}
storage :: bytes.items.ptr[..bytes.capacity]
end := bytes.capacity
if (end > maximum) end = maximum
count :: try read(input, storage[length..end])
if (count == 0) return bytes
bytes.items = storage[..length + count]
}
}
write proc(output Writer, bytes []u8) usize ! WriteError {
if (bytes.len == 0) return 0