file io
This commit is contained in:
+1
-1
@@ -177,7 +177,7 @@ as `math.divfloor(a, b)` resolve to ordinary functions.
|
|||||||
- root `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
|
- root `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
|
||||||
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
|
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
|
||||||
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
||||||
- `std/io` explicit `Io` capabilities, `Reader`/`Writer` stream values, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, and `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
- `std/io` explicit `Io` capabilities, provider-bound `Reader`/`Writer` handles, existing-file open/close operations, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, and `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
||||||
- entry points are either `main func() ...` or `main func(init process.Init) ...`; `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
|
- entry points are either `main func() ...` or `main func(init process.Init) ...`; `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
|
||||||
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.Init`
|
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.Init`
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,15 @@ odin build . -out:build/brolang
|
|||||||
./build/prototype
|
./build/prototype
|
||||||
```
|
```
|
||||||
|
|
||||||
Programs may receive the system I/O capability explicitly. Readers and writers
|
Programs may receive the system I/O capability explicitly. Standard-stream
|
||||||
pair that implementation with a stream; `main func() ...` remains valid.
|
helpers bind the provider, handle, and callback; `main func() ...` remains valid.
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
io :: import "@std/io"
|
io :: import "@std/io"
|
||||||
process :: import "@std/process"
|
process :: import "@std/process"
|
||||||
|
|
||||||
main func(init process.Init) void {
|
main func(init process.Init) void {
|
||||||
io.print(io.Writer {
|
io.print(io.stdout(init.io), "hello {s} {d}\n", {"bro", 37}) catch |_| {
|
||||||
impl = init.io,
|
|
||||||
stream = .stdout,
|
|
||||||
}, "hello {s} {d}\n", {"bro", 37}) catch |_| {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -627,7 +627,7 @@
|
|||||||
parameters
|
parameters
|
||||||
|
|
||||||
25. dynamic heap allocation (implemented; v1)
|
25. dynamic heap allocation (implemented; v1)
|
||||||
- `std/mem` exposes a plain-data `Allocator` contract with `?*mut anyopaque` context and `alloc`, `realloc`, and `free` `@func` pointers
|
- `std/mem` exposes a plain-data `Allocator` contract with `?@mut anyopaque` context and `alloc`, `realloc`, and `free` `@func` pointers
|
||||||
- `mem.c_allocator` is the libc-backed allocator; `mem.alloc(mem.c_allocator, size, alignment)` returns nullable mutable byte memory
|
- `mem.c_allocator` is the libc-backed allocator; `mem.alloc(mem.c_allocator, size, alignment)` returns nullable mutable byte memory
|
||||||
- `mem.free(mem.c_allocator, ptr, size, alignment)` frees with the same allocator; `malloc` handles default-aligned requests and `posix_memalign` handles larger power-of-two alignments
|
- `mem.free(mem.c_allocator, ptr, size, alignment)` frees with the same allocator; `malloc` handles default-aligned requests and `posix_memalign` handles larger power-of-two alignments
|
||||||
- `mem.realloc` preserves alignment and the original allocation on failure; zero size frees, and over-aligned blocks use allocate/copy/free
|
- `mem.realloc` preserves alignment and the original allocation on failure; zero size frees, and over-aligned blocks use allocate/copy/free
|
||||||
@@ -811,9 +811,11 @@
|
|||||||
33. explicit I/O provider (implemented)
|
33. explicit I/O provider (implemented)
|
||||||
- `main` may take one canonical `@std/process Init`; parameterless entry points remain valid
|
- `main` may take one canonical `@std/process Init`; parameterless entry points remain valid
|
||||||
- the compiler supplies a `hide system` macOS provider and constructs `Init` in the external C wrapper
|
- the compiler supplies a `hide system` macOS provider and constructs `Init` in the external C wrapper
|
||||||
- readers and writers pair an explicit provider with `stdin`, `stdout`, or `stderr`
|
- readers and writers bind provider context, a generalized handle, and a direct callback
|
||||||
|
- standard-stream helpers and existing-file operations use the injected provider; files retain it
|
||||||
- `read` and `write` validate provider counts; `write_all` handles partial writes and no progress
|
- `read` and `write` validate provider counts; `write_all` handles partial writes and no progress
|
||||||
- the system provider uses unbuffered libc `read`/`write`, retries interruption, and allocates nothing
|
- the system provider uses unbuffered POSIX file descriptors, retries interrupted open/read/write,
|
||||||
|
and allocates nothing
|
||||||
|
|
||||||
34. package declaration aliases and root `std.ArrayList` (implemented)
|
34. package declaration aliases and root `std.ArrayList` (implemented)
|
||||||
- bare qualified aliases use `Name :: alias package.Member` without adding a keyword
|
- bare qualified aliases use `Name :: alias package.Member` without adding a keyword
|
||||||
|
|||||||
+116
-2
@@ -2266,6 +2266,120 @@ milestone_33_injects_explicit_io_provider_and_runs_std_io :: proc(t: ^testing.T)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
std_io_opens_existing_files_through_the_captured_provider :: proc(t: ^testing.T) {
|
||||||
|
directory := "/tmp/brolang-test-file-io"
|
||||||
|
main_path := "/tmp/brolang-test-file-io/main.bro"
|
||||||
|
data_path := "/tmp/brolang-test-file-io/data"
|
||||||
|
output := "/tmp/brolang-test-file-io/app"
|
||||||
|
seed := "xxxxx"
|
||||||
|
text := `io :: import "@std/io"
|
||||||
|
process :: import "@std/process"
|
||||||
|
|
||||||
|
missing_fails func(system io.Io) bool {
|
||||||
|
_ = io.open(system, "/tmp/brolang-test-file-io/missing", .read_only) catch |err| {
|
||||||
|
return err == .open_failed
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
wrong_read_fails func(file io.File) bool {
|
||||||
|
buffer [1]mut u8 = [0]
|
||||||
|
_ = io.read(io.reader(file), buffer[..]) catch |err| {
|
||||||
|
return err == .not_open_for_reading
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
wrong_write_fails func(file io.File) bool {
|
||||||
|
_ = io.write(io.writer(file), "x") catch |err| {
|
||||||
|
return err == .not_open_for_writing
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
main func(init process.Init) i32 {
|
||||||
|
if !missing_fails(init.io) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
read_file io.File :: io.open(init.io, "/tmp/brolang-test-file-io/data", .read_only) catch |_| {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
buffer [5]mut u8 = [0, 0, 0, 0, 0]
|
||||||
|
count usize = io.read(io.reader(read_file), buffer[..]) catch |_| {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
if count != 5 or buffer[0] != 'x' or !wrong_write_fails(read_file) {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
io.close(read_file) catch |_| {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
|
||||||
|
write_file io.File :: io.open(init.io, "/tmp/brolang-test-file-io/data", .write_only) catch |_| {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
if !wrong_read_fails(write_file) {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
io.write_all(io.writer(write_file), "bro") catch |_| {
|
||||||
|
return 9
|
||||||
|
}
|
||||||
|
io.close(write_file) catch |_| {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
read_write_file io.File :: io.open(init.io, "/tmp/brolang-test-file-io/data", .read_write) catch |_| {
|
||||||
|
return 11
|
||||||
|
}
|
||||||
|
count = io.read(io.reader(read_write_file), buffer[..]) catch |_| {
|
||||||
|
return 12
|
||||||
|
}
|
||||||
|
if count != 5 or buffer[0] != 'b' or buffer[1] != 'r' or buffer[2] != 'o' {
|
||||||
|
return 13
|
||||||
|
}
|
||||||
|
io.write_all(io.writer(read_write_file), "!") catch |_| {
|
||||||
|
return 14
|
||||||
|
}
|
||||||
|
io.close(read_write_file) catch |_| {
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||||
|
testing.expect(t, os.write_entire_file(data_path, transmute([]byte)seed))
|
||||||
|
|
||||||
|
status := compiler_core.compile_package(
|
||||||
|
directory,
|
||||||
|
output,
|
||||||
|
nil,
|
||||||
|
target.DEFAULT,
|
||||||
|
cimport.Options{},
|
||||||
|
".",
|
||||||
|
)
|
||||||
|
testing.expect_value(t, status, 0)
|
||||||
|
state, stdout, stderr, _ := os2.process_exec(
|
||||||
|
os2.Process_Desc{command=[]string{output}},
|
||||||
|
context.allocator,
|
||||||
|
)
|
||||||
|
defer delete(stdout)
|
||||||
|
defer delete(stderr)
|
||||||
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
|
testing.expect_value(t, len(stdout), 0)
|
||||||
|
testing.expect_value(t, len(stderr), 0)
|
||||||
|
|
||||||
|
data, ok := os.read_entire_file(data_path)
|
||||||
|
defer delete(data)
|
||||||
|
testing.expect(t, ok)
|
||||||
|
testing.expect_value(t, string(data), "broxx!")
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
milestone_33_rejects_non_io_and_extra_main_parameters :: proc(t: ^testing.T) {
|
milestone_33_rejects_non_io_and_extra_main_parameters :: proc(t: ^testing.T) {
|
||||||
cases := [?]string{
|
cases := [?]string{
|
||||||
@@ -2549,7 +2663,7 @@ milestone_37_format_errors_are_reported_at_comptime :: proc(t: ^testing.T) {
|
|||||||
process :: import "@std/process"
|
process :: import "@std/process"
|
||||||
|
|
||||||
main func(init process.Init) void {
|
main func(init process.Init) void {
|
||||||
writer io.Writer :: io.Writer {impl = init.io, stream = .stdout}
|
writer io.Writer :: io.stdout(init.io)
|
||||||
stored :: typeinfo!(i32)
|
stored :: typeinfo!(i32)
|
||||||
_ = stored
|
_ = stored
|
||||||
io.print(writer, "", 1) catch |_| {}
|
io.print(writer, "", 1) catch |_| {}
|
||||||
@@ -4582,7 +4696,7 @@ allocator_contract_c_allocator_global_lowers :: proc(t: ^testing.T) {
|
|||||||
found_anyopaque_context = pointer_ok &&
|
found_anyopaque_context = pointer_ok &&
|
||||||
pointer_item.kind == .Pointer &&
|
pointer_item.kind == .Pointer &&
|
||||||
pointer_item.mutable &&
|
pointer_item.mutable &&
|
||||||
pointer_item.many &&
|
!pointer_item.many &&
|
||||||
pointer_item.child == types.ANYOPAQUE
|
pointer_item.child == types.ANYOPAQUE
|
||||||
}
|
}
|
||||||
} else if name == "vtable" {
|
} else if name == "vtable" {
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ arraylist :: import "@std/arraylist"
|
|||||||
mem :: import "@std/mem"
|
mem :: import "@std/mem"
|
||||||
std :: import "@std"
|
std :: import "@std"
|
||||||
|
|
||||||
hide fail_alloc func(_ ?*mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
hide fail_alloc func(_ ?@mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
hide fail_realloc func(_ ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
hide fail_realloc func(_ ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
hide fail_free func(_ ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {}
|
hide fail_free func(_ ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {}
|
||||||
|
|
||||||
hide fail_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
hide fail_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
||||||
alloc = fail_alloc,
|
alloc = fail_alloc,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
io :: import "@std/io"
|
io :: import "@std/io"
|
||||||
process :: import "@std/process"
|
process :: import "@std/process"
|
||||||
|
|
||||||
hide read_ok func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! io.ReadError {
|
hide read_ok func(_ ?@mut anyopaque, _ io.Handle, buffer []mut u8) usize ! io.ReadError {
|
||||||
if buffer.len == 0 {
|
if buffer.len == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -13,85 +13,94 @@ hide read_ok func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! i
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
hide read_too_much func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! io.ReadError {
|
hide read_too_much func(_ ?@mut anyopaque, _ io.Handle, buffer []mut u8) usize ! io.ReadError {
|
||||||
return buffer.len + 1
|
return buffer.len + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
hide read_eof func(_ ?*mut anyopaque, _ io.ReadStream, _ []mut u8) usize ! io.ReadError {
|
hide read_eof func(_ ?@mut anyopaque, _ io.Handle, _ []mut u8) usize ! io.ReadError {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_short func(_ ?*mut anyopaque, _ io.WriteStream, bytes []u8) usize ! io.WriteError {
|
hide write_short func(_ ?@mut anyopaque, _ io.Handle, bytes []u8) usize ! io.WriteError {
|
||||||
if bytes.len > 2 {
|
if bytes.len > 2 {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
return bytes.len
|
return bytes.len
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_none func(_ ?*mut anyopaque, _ io.WriteStream, _ []u8) usize ! io.WriteError {
|
hide write_none func(_ ?@mut anyopaque, _ io.Handle, _ []u8) usize ! io.WriteError {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_too_much func(_ ?*mut anyopaque, _ io.WriteStream, bytes []u8) usize ! io.WriteError {
|
hide write_too_much func(_ ?@mut anyopaque, _ io.Handle, bytes []u8) usize ! io.WriteError {
|
||||||
return bytes.len + 1
|
return bytes.len + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
hide ok_vtable io.IoVTable :: io.IoVTable {
|
ok_reader func() io.Reader {
|
||||||
read = read_ok,
|
|
||||||
write = write_short,
|
|
||||||
}
|
|
||||||
|
|
||||||
hide read_bad_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = read_too_much,
|
|
||||||
write = write_short,
|
|
||||||
}
|
|
||||||
|
|
||||||
hide eof_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = read_eof,
|
|
||||||
write = write_short,
|
|
||||||
}
|
|
||||||
|
|
||||||
hide write_none_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = read_ok,
|
|
||||||
write = write_none,
|
|
||||||
}
|
|
||||||
|
|
||||||
hide write_bad_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = read_ok,
|
|
||||||
write = write_too_much,
|
|
||||||
}
|
|
||||||
|
|
||||||
reader_for func(vtable @io.IoVTable) io.Reader {
|
|
||||||
return io.Reader {
|
return io.Reader {
|
||||||
impl = io.Io {context = none, vtable = vtable},
|
context = none,
|
||||||
stream = .stdin,
|
handle = io.Handle {file_desc = 0},
|
||||||
|
read = read_ok,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer_for func(vtable @io.IoVTable) io.Writer {
|
bad_reader func() io.Reader {
|
||||||
|
return io.Reader {
|
||||||
|
context = none,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
read = read_too_much,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eof_reader func() io.Reader {
|
||||||
|
return io.Reader {
|
||||||
|
context = none,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
read = read_eof,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
short_writer func() io.Writer {
|
||||||
return io.Writer {
|
return io.Writer {
|
||||||
impl = io.Io {context = none, vtable = vtable},
|
context = none,
|
||||||
stream = .stdout,
|
handle = io.Handle {file_desc = 0},
|
||||||
|
write = write_short,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
none_writer func() io.Writer {
|
||||||
|
return io.Writer {
|
||||||
|
context = none,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
write = write_none,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bad_writer func() io.Writer {
|
||||||
|
return io.Writer {
|
||||||
|
context = none,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
write = write_too_much,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rejects_bad_read func() bool {
|
rejects_bad_read func() bool {
|
||||||
buffer [1]mut u8 = [0]
|
buffer [1]mut u8 = [0]
|
||||||
_ = io.read(reader_for(&read_bad_vtable), buffer[..]) catch |err| {
|
_ = io.read(bad_reader(), buffer[..]) catch |err| {
|
||||||
return err == .read_failed
|
return err == .read_failed
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rejects_no_progress func() bool {
|
rejects_no_progress func() bool {
|
||||||
io.print(writer_for(&write_none_vtable), "{s}", {"x",}) catch |err| {
|
io.print(none_writer(), "{s}", {"x",}) catch |err| {
|
||||||
return err == .no_progress
|
return err == .no_progress
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rejects_bad_write func() bool {
|
rejects_bad_write func() bool {
|
||||||
_ = io.write(writer_for(&write_bad_vtable), "x") catch |err| {
|
_ = io.write(bad_writer(), "x") catch |err| {
|
||||||
return err == .write_failed
|
return err == .write_failed
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -100,32 +109,26 @@ rejects_bad_write func() bool {
|
|||||||
main func(init process.Init) i32 {
|
main func(init process.Init) i32 {
|
||||||
system io.Io :: init.io
|
system io.Io :: init.io
|
||||||
buffer [2]mut u8 = [0, 0]
|
buffer [2]mut u8 = [0, 0]
|
||||||
count usize :: io.read(reader_for(&ok_vtable), buffer[..]) catch 0
|
count usize :: io.read(ok_reader(), buffer[..]) catch 0
|
||||||
if count != 2 or buffer[0] != 'o' or buffer[1] != 'k' {
|
if count != 2 or buffer[0] != 'o' or buffer[1] != 'k' {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
eof usize :: io.read(reader_for(&eof_vtable), buffer[..]) catch 1
|
eof usize :: io.read(eof_reader(), buffer[..]) catch 1
|
||||||
empty_read usize :: io.read(reader_for(&read_bad_vtable), buffer[0..0]) catch 1
|
empty_read usize :: io.read(bad_reader(), buffer[0..0]) catch 1
|
||||||
empty_write usize :: io.write(writer_for(&write_bad_vtable), "") catch 1
|
empty_write usize :: io.write(bad_writer(), "") catch 1
|
||||||
if eof != 0 or empty_read != 0 or empty_write != 0 {
|
if eof != 0 or empty_read != 0 or empty_write != 0 {
|
||||||
return 5
|
return 5
|
||||||
}
|
}
|
||||||
io.print(writer_for(&ok_vtable), "{s}{d}", {"partial", 37}) catch |_| {
|
io.print(short_writer(), "{s}{d}", {"partial", 37}) catch |_| {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
if !rejects_bad_read() or !rejects_no_progress() or !rejects_bad_write() {
|
if !rejects_bad_read() or !rejects_no_progress() or !rejects_bad_write() {
|
||||||
return 3
|
return 3
|
||||||
}
|
}
|
||||||
io.print(io.Writer {
|
io.print(io.stdout(system), "io-ok {d} {{bro}}\n", {37,}) catch |_| {
|
||||||
impl = system,
|
|
||||||
stream = .stdout,
|
|
||||||
}, "io-ok {d} {{bro}}\n", {37,}) catch |_| {
|
|
||||||
return 4
|
return 4
|
||||||
}
|
}
|
||||||
io.print(io.Writer {
|
io.print(io.stdout(system), "bounds={d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d}\n", {
|
||||||
impl = system,
|
|
||||||
stream = .stdout,
|
|
||||||
}, "bounds={d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d}\n", {
|
|
||||||
minval!(i8), maxval!(u8),
|
minval!(i8), maxval!(u8),
|
||||||
minval!(i16), maxval!(u16),
|
minval!(i16), maxval!(u16),
|
||||||
minval!(i32), maxval!(u32),
|
minval!(i32), maxval!(u32),
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
mem :: import "@std/mem"
|
mem :: import "@std/mem"
|
||||||
|
|
||||||
hide probe_count func(context ?*mut anyopaque) void {
|
hide probe_count func(context ?@mut anyopaque) void {
|
||||||
if context |raw| {
|
if context |raw| {
|
||||||
counts *mut usize :: ptrcast!(usize, raw)
|
count @mut usize :: ptrcast!(usize, raw)
|
||||||
counts[0] += 1
|
count^ += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hide probe_alloc func(context ?*mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
hide probe_alloc func(context ?@mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
||||||
probe_count(context)
|
probe_count(context)
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
hide probe_realloc func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
hide probe_realloc func(context ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
||||||
probe_count(context)
|
probe_count(context)
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
hide probe_free func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {
|
hide probe_free func(context ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {
|
||||||
probe_count(context)
|
probe_count(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,11 +33,11 @@ typed_allocator_test func() i32 {
|
|||||||
first_calls [1]mut usize = [0]
|
first_calls [1]mut usize = [0]
|
||||||
second_calls [1]mut usize = [0]
|
second_calls [1]mut usize = [0]
|
||||||
first_allocator mem.Allocator :: mem.Allocator {
|
first_allocator mem.Allocator :: mem.Allocator {
|
||||||
context = (&first_calls).ptr,
|
context = &first_calls,
|
||||||
vtable = &probe_vtable,
|
vtable = &probe_vtable,
|
||||||
}
|
}
|
||||||
second_allocator mem.Allocator :: mem.Allocator {
|
second_allocator mem.Allocator :: mem.Allocator {
|
||||||
context = (&second_calls).ptr,
|
context = &second_calls,
|
||||||
vtable = &probe_vtable,
|
vtable = &probe_vtable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ main func(init process.Init) i32 {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
writer io.Writer :: io.Writer {impl = init.io, stream = .stdout}
|
writer io.Writer :: io.stdout(init.io)
|
||||||
positive f64 = 1.0
|
positive f64 = 1.0
|
||||||
zero f64 = 0.0
|
zero f64 = 0.0
|
||||||
infinity f64 :: positive / zero
|
infinity f64 :: positive / zero
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
O_RDONLY c_int :: 0
|
||||||
|
O_WRONLY c_int :: 1
|
||||||
|
O_RDWR c_int :: 2
|
||||||
|
|
||||||
|
STDIN_FILENO c_int :: 0
|
||||||
|
STDOUT_FILENO c_int :: 1
|
||||||
|
STDERR_FILENO c_int :: 2
|
||||||
|
|
||||||
|
EINTR c_int :: 4
|
||||||
|
EBADF c_int :: 9
|
||||||
|
|
||||||
|
open c_func(_ *c_char, _ c_int, ...) c_int
|
||||||
|
close c_func(_ c_int) c_int
|
||||||
read c_func(_ c_int, _ ?*mut anyopaque, _ c_ulong) c_long
|
read c_func(_ c_int, _ ?*mut anyopaque, _ c_ulong) c_long
|
||||||
write c_func(_ c_int, _ ?*anyopaque, _ c_ulong) c_long
|
write c_func(_ c_int, _ ?*anyopaque, _ c_ulong) c_long
|
||||||
__error c_func() *mut c_int
|
__error c_func() *mut c_int
|
||||||
|
|||||||
+12
-20
@@ -1,36 +1,28 @@
|
|||||||
import "@ffi/c"
|
import "@ffi/c"
|
||||||
import "@std/io"
|
import "@std/io"
|
||||||
|
|
||||||
hide write func(_ ?*mut anyopaque, _ io.WriteStream, bytes []u8) usize ! io.WriteError {
|
print func($format []u8, $Args type, args Args) void {
|
||||||
|
writer io.Writer :: io.Writer{
|
||||||
|
context = none,
|
||||||
|
handle = io.Handle{ file_desc = c_int(io.Stream.stderr) },
|
||||||
|
write = write,
|
||||||
|
}
|
||||||
|
io.print(writer, format, Args, args) catch |_| {}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide write func(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError {
|
||||||
request usize = bytes.len
|
request usize = bytes.len
|
||||||
maximum usize :: usize(maxval!(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
if request > maximum {
|
if request > maximum {
|
||||||
request = maximum
|
request = maximum
|
||||||
}
|
}
|
||||||
while true {
|
while true {
|
||||||
count c_long :: c.write(2, bytes.ptr, c_ulong(request))
|
count c_long :: c.write(handle.file_desc, bytes.ptr, c_ulong(request))
|
||||||
if count >= 0 {
|
if count >= 0 {
|
||||||
return usize(count)
|
return usize(count)
|
||||||
}
|
}
|
||||||
if c.__error()^ != 4 {
|
if c.__error()^ != c.EINTR {
|
||||||
return .write_failed
|
return .write_failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hide read func(_ ?*mut anyopaque, _ io.ReadStream, _ []mut u8) usize ! io.ReadError {
|
|
||||||
return .read_failed
|
|
||||||
}
|
|
||||||
|
|
||||||
hide vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = read,
|
|
||||||
write = write,
|
|
||||||
}
|
|
||||||
|
|
||||||
print func($format []u8, $Args type, args Args) void {
|
|
||||||
writer io.Writer :: io.Writer {
|
|
||||||
impl = io.Io {context = none, vtable = &vtable},
|
|
||||||
stream = .stderr,
|
|
||||||
}
|
|
||||||
io.print(writer, format, Args, args) catch |_| {}
|
|
||||||
}
|
|
||||||
|
|||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
import "@ffi/c"
|
||||||
|
|
||||||
|
File :: struct {
|
||||||
|
io Io
|
||||||
|
handle Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
FileMode :: enum {
|
||||||
|
read_only
|
||||||
|
write_only
|
||||||
|
read_write
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenError :: enum {
|
||||||
|
open_failed
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseError :: enum {
|
||||||
|
close_failed
|
||||||
|
}
|
||||||
|
|
||||||
|
open func(io Io, path [;0]u8, mode FileMode) File ! OpenError {
|
||||||
|
handle Handle :: io.vtable.open(io.context, path, mode) catch |err| {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return File {io = io, handle = handle}
|
||||||
|
}
|
||||||
|
|
||||||
|
close func(file File) void ! CloseError {
|
||||||
|
try file.io.vtable.close(file.io.context, file.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader func(file File) Reader {
|
||||||
|
return Reader {
|
||||||
|
context = file.io.context,
|
||||||
|
handle = file.handle,
|
||||||
|
read = file.io.vtable.read,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer func(file File) Writer {
|
||||||
|
return Writer {
|
||||||
|
context = file.io.context,
|
||||||
|
handle = file.handle,
|
||||||
|
write = file.io.vtable.write,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_read func(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
|
||||||
|
request usize = buffer.len
|
||||||
|
maximum usize :: usize(maxval!(c_long))
|
||||||
|
if request > maximum {
|
||||||
|
request = maximum
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
count c_long :: c.read(handle.file_desc, buffer.ptr, c_ulong(request))
|
||||||
|
if count >= 0 {
|
||||||
|
return usize(count)
|
||||||
|
}
|
||||||
|
errno c_int :: c.__error()^
|
||||||
|
if errno == c.EINTR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if errno == c.EBADF {
|
||||||
|
return .not_open_for_reading
|
||||||
|
}
|
||||||
|
return .read_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_write func(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
|
||||||
|
request usize = bytes.len
|
||||||
|
maximum usize :: usize(maxval!(c_long))
|
||||||
|
if request > maximum {
|
||||||
|
request = maximum
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
count c_long :: c.write(handle.file_desc, bytes.ptr, c_ulong(request))
|
||||||
|
if count >= 0 {
|
||||||
|
return usize(count)
|
||||||
|
}
|
||||||
|
errno c_int :: c.__error()^
|
||||||
|
if errno == c.EINTR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if errno == c.EBADF {
|
||||||
|
return .not_open_for_writing
|
||||||
|
}
|
||||||
|
return .write_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_open func(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
|
||||||
|
flags c_int = c.O_RDONLY
|
||||||
|
match mode {
|
||||||
|
.read_only: flags = c.O_RDONLY
|
||||||
|
.write_only: flags = c.O_WRONLY
|
||||||
|
.read_write: flags = c.O_RDWR
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
fd c_int :: c.open(ptrcast!(c_char, path.ptr), flags)
|
||||||
|
if fd >= 0 {
|
||||||
|
return Handle {file_desc = fd}
|
||||||
|
}
|
||||||
|
if c.__error()^ != c.EINTR {
|
||||||
|
return .open_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_close func(_ ?@mut anyopaque, handle Handle) void ! CloseError {
|
||||||
|
if c.close(handle.file_desc) != 0 {
|
||||||
|
return .close_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_stdin func(_ ?@mut anyopaque) Handle {
|
||||||
|
return Handle {file_desc = c_int(Stream.stdin)}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_stdout func(_ ?@mut anyopaque) Handle {
|
||||||
|
return Handle {file_desc = c_int(Stream.stdout)}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_stderr func(_ ?@mut anyopaque) Handle {
|
||||||
|
return Handle {file_desc = c_int(Stream.stderr)}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system_vtable IoVTable :: IoVTable {
|
||||||
|
read = system_read,
|
||||||
|
write = system_write,
|
||||||
|
open = system_open,
|
||||||
|
close = system_close,
|
||||||
|
stdin = system_stdin,
|
||||||
|
stdout = system_stdout,
|
||||||
|
stderr = system_stderr,
|
||||||
|
}
|
||||||
|
|
||||||
|
hide system func() Io {
|
||||||
|
return Io {
|
||||||
|
context = none,
|
||||||
|
vtable = &system_vtable,
|
||||||
|
}
|
||||||
|
}
|
||||||
+99
-111
@@ -2,69 +2,80 @@ import "@ffi/c"
|
|||||||
import "@std/meta"
|
import "@std/meta"
|
||||||
|
|
||||||
ReadError :: enum {
|
ReadError :: enum {
|
||||||
|
not_open_for_reading
|
||||||
read_failed
|
read_failed
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteError :: enum {
|
WriteError :: enum {
|
||||||
|
not_open_for_writing
|
||||||
write_failed
|
write_failed
|
||||||
no_progress
|
no_progress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle :: union {
|
||||||
|
file_desc c_int
|
||||||
|
ptr @mut anyopaque
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream :: enum(c_int) {
|
||||||
|
stdin = c.STDIN_FILENO
|
||||||
|
stdout = c.STDOUT_FILENO
|
||||||
|
stderr = c.STDERR_FILENO
|
||||||
|
}
|
||||||
|
|
||||||
Io :: struct {
|
Io :: struct {
|
||||||
context ?*mut anyopaque
|
context ?@mut anyopaque
|
||||||
vtable @IoVTable
|
vtable @IoVTable
|
||||||
}
|
}
|
||||||
|
|
||||||
IoVTable :: struct {
|
IoVTable :: struct {
|
||||||
read @func(context ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError
|
read @func(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
|
||||||
write @func(context ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError
|
write @func(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError
|
||||||
}
|
open @func(context ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError
|
||||||
|
close @func(context ?@mut anyopaque, handle Handle) void ! CloseError
|
||||||
ReadStream :: enum(c_int) {
|
stdin @func(context ?@mut anyopaque) Handle
|
||||||
stdin = 0
|
stdout @func(context ?@mut anyopaque) Handle
|
||||||
}
|
stderr @func(context ?@mut anyopaque) Handle
|
||||||
|
|
||||||
WriteStream :: enum(c_int) {
|
|
||||||
stdout = 1
|
|
||||||
stderr = 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Reader :: struct {
|
Reader :: struct {
|
||||||
impl Io
|
context ?@mut anyopaque
|
||||||
stream ReadStream
|
handle Handle
|
||||||
|
read @func(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
|
||||||
}
|
}
|
||||||
|
|
||||||
Writer :: struct {
|
Writer :: struct {
|
||||||
impl Io
|
context ?@mut anyopaque
|
||||||
stream WriteStream
|
handle Handle
|
||||||
|
write @func(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError
|
||||||
}
|
}
|
||||||
|
|
||||||
read func(reader Reader, buffer []mut u8) usize ! ReadError {
|
read func(input Reader, buffer []mut u8) usize ! ReadError {
|
||||||
if buffer.len == 0 {
|
if buffer.len == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
count usize :: try reader.impl.vtable.read(reader.impl.context, reader.stream, buffer)
|
count usize :: try input.read(input.context, input.handle, buffer)
|
||||||
if count > buffer.len {
|
if count > buffer.len {
|
||||||
return .read_failed
|
return .read_failed
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
write func(writer Writer, bytes []u8) usize ! WriteError {
|
write func(output Writer, bytes []u8) usize ! WriteError {
|
||||||
if bytes.len == 0 {
|
if bytes.len == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
count usize :: try writer.impl.vtable.write(writer.impl.context, writer.stream, bytes)
|
count usize :: try output.write(output.context, output.handle, bytes)
|
||||||
if count > bytes.len {
|
if count > bytes.len {
|
||||||
return .write_failed
|
return .write_failed
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
write_all func(writer Writer, bytes []u8) void ! WriteError {
|
write_all func(output Writer, bytes []u8) void ! WriteError {
|
||||||
offset usize = 0
|
offset usize = 0
|
||||||
while offset < bytes.len {
|
while offset < bytes.len {
|
||||||
count usize :: write(writer, bytes[offset..]) catch |err| {
|
count usize :: write(output, bytes[offset..]) catch |err| {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
@@ -75,7 +86,49 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_integer_signed func(writer Writer, value i64, base u64, uppercase bool) void ! WriteError {
|
stdin func(io Io) Reader {
|
||||||
|
return Reader {
|
||||||
|
context = io.context,
|
||||||
|
handle = io.vtable.stdin(io.context),
|
||||||
|
read = io.vtable.read,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout func(io Io) Writer {
|
||||||
|
return Writer {
|
||||||
|
context = io.context,
|
||||||
|
handle = io.vtable.stdout(io.context),
|
||||||
|
write = io.vtable.write,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr func(io Io) Writer {
|
||||||
|
return Writer {
|
||||||
|
context = io.context,
|
||||||
|
handle = io.vtable.stderr(io.context),
|
||||||
|
write = io.vtable.write,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
||||||
|
expand for parse_format(format.len, format, Args) |token| {
|
||||||
|
match token.kind {
|
||||||
|
.unused: break
|
||||||
|
.literal: try write_all(output, format[token.start..token.end])
|
||||||
|
.string: try write_all(output, field!(args, token.field))
|
||||||
|
.default: try write_default(output, field!(args, token.field))
|
||||||
|
.decimal: try write_decimal(output, field!(args, token.field))
|
||||||
|
.binary: try write_integer(output, field!(args, token.field), 2, false)
|
||||||
|
.octal: try write_integer(output, field!(args, token.field), 8, false)
|
||||||
|
.hex_lower: try write_integer(output, field!(args, token.field), 16, false)
|
||||||
|
.hex_upper: try write_integer(output, field!(args, token.field), 16, true)
|
||||||
|
.character: try write_character(output, field!(args, token.field))
|
||||||
|
else: try write_float(output, field!(args, token.field), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide write_integer_signed func(output Writer, value i64, base u64, uppercase bool) void ! WriteError {
|
||||||
buffer [65]mut u8 = undefined
|
buffer [65]mut u8 = undefined
|
||||||
end usize = buffer.len
|
end usize = buffer.len
|
||||||
current i64 = value
|
current i64 = value
|
||||||
@@ -104,11 +157,11 @@ hide write_integer_signed func(writer Writer, value i64, base u64, uppercase boo
|
|||||||
end -= 1
|
end -= 1
|
||||||
buffer[end] = '-'
|
buffer[end] = '-'
|
||||||
}
|
}
|
||||||
try write_all(writer, buffer[end..])
|
try write_all(output, buffer[end..])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_integer_unsigned func(writer Writer, value u64, base u64, uppercase bool) void ! WriteError {
|
hide write_integer_unsigned func(output Writer, value u64, base u64, uppercase bool) void ! WriteError {
|
||||||
buffer [65]mut u8 = undefined
|
buffer [65]mut u8 = undefined
|
||||||
end usize = buffer.len
|
end usize = buffer.len
|
||||||
current u64 = value
|
current u64 = value
|
||||||
@@ -127,7 +180,7 @@ hide write_integer_unsigned func(writer Writer, value u64, base u64, uppercase b
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try write_all(writer, buffer[end..])
|
try write_all(output, buffer[end..])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,12 +317,12 @@ hide format_field_name func($T type, index usize) []u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_integer func(writer Writer, $T type, value T, base u64, uppercase bool) void ! WriteError {
|
hide write_integer func(output Writer, $T type, value T, base u64, uppercase bool) void ! WriteError {
|
||||||
match typeinfo!(T) {
|
match typeinfo!(T) {
|
||||||
.integer: if minval!(T) < 0 {
|
.integer: if minval!(T) < 0 {
|
||||||
try write_integer_signed(writer, i64(value), base, uppercase)
|
try write_integer_signed(output, i64(value), base, uppercase)
|
||||||
} else {
|
} else {
|
||||||
try write_integer_unsigned(writer, u64(value), base, uppercase)
|
try write_integer_unsigned(output, u64(value), base, uppercase)
|
||||||
}
|
}
|
||||||
else: compile_error!("io.print integer format requires an integer argument")
|
else: compile_error!("io.print integer format requires an integer argument")
|
||||||
}
|
}
|
||||||
@@ -277,7 +330,7 @@ hide write_integer func(writer Writer, $T type, value T, base u64, uppercase boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
# note: libc keeps float formatting small; replace it with a native shortest-roundtrip writer if locale independence matters.
|
# note: libc keeps float formatting small; replace it with a native shortest-roundtrip writer if locale independence matters.
|
||||||
hide write_float func(writer Writer, $T type, value T, scientific bool) void ! WriteError {
|
hide write_float func(output Writer, $T type, value T, scientific bool) void ! WriteError {
|
||||||
match typeinfo!(T) {
|
match typeinfo!(T) {
|
||||||
.float: {
|
.float: {
|
||||||
buffer [64]mut u8 = undefined
|
buffer [64]mut u8 = undefined
|
||||||
@@ -296,53 +349,53 @@ hide write_float func(writer Writer, $T type, value T, scientific bool) void ! W
|
|||||||
if count < 0 or usize(count) >= buffer.len {
|
if count < 0 or usize(count) >= buffer.len {
|
||||||
return .write_failed
|
return .write_failed
|
||||||
}
|
}
|
||||||
try write_all(writer, buffer[0..usize(count)])
|
try write_all(output, buffer[0..usize(count)])
|
||||||
}
|
}
|
||||||
else: compile_error!("io.print float format requires a float argument")
|
else: compile_error!("io.print float format requires a float argument")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_decimal func(writer Writer, $T type, value T) void ! WriteError {
|
hide write_decimal func(output Writer, $T type, value T) void ! WriteError {
|
||||||
match typeinfo!(T) {
|
match typeinfo!(T) {
|
||||||
.integer: try write_integer(writer, value, 10, false)
|
.integer: try write_integer(output, value, 10, false)
|
||||||
.float: try write_float(writer, value, false)
|
.float: try write_float(output, value, false)
|
||||||
else: compile_error!("io.print '{d}' requires an integer or float argument")
|
else: compile_error!("io.print '{d}' requires an integer or float argument")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_character func(writer Writer, $T type, value T) void ! WriteError {
|
hide write_character func(output Writer, $T type, value T) void ! WriteError {
|
||||||
match typeinfo!(T) {
|
match typeinfo!(T) {
|
||||||
.integer: {
|
.integer: {
|
||||||
if minval!(T) < 0 or maxval!(T) > 255 {
|
if minval!(T) < 0 or maxval!(T) > 255 {
|
||||||
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||||
}
|
}
|
||||||
buffer [1]u8 = [u8(value)]
|
buffer [1]u8 = [u8(value)]
|
||||||
try write_all(writer, buffer[..])
|
try write_all(output, buffer[..])
|
||||||
}
|
}
|
||||||
else: compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
else: compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hide write_default func(writer Writer, $T type, value T) void ! WriteError {
|
hide write_default func(output Writer, $T type, value T) void ! WriteError {
|
||||||
match typeinfo!(T) {
|
match typeinfo!(T) {
|
||||||
.bool: if value {
|
.bool: if value {
|
||||||
try write_all(writer, "true")
|
try write_all(output, "true")
|
||||||
} else {
|
} else {
|
||||||
try write_all(writer, "false")
|
try write_all(output, "false")
|
||||||
}
|
}
|
||||||
.integer: try write_integer(writer, value, 10, false)
|
.integer: try write_integer(output, value, 10, false)
|
||||||
.float: try write_float(writer, value, false)
|
.float: try write_float(output, value, false)
|
||||||
.array: try write_all(writer, value)
|
.array: try write_all(output, value)
|
||||||
.pointer: try write_all(writer, value)
|
.pointer: try write_all(output, value)
|
||||||
.slice: try write_all(writer, value)
|
.slice: try write_all(output, value)
|
||||||
.enum |enum_info|: {
|
.enum |enum_info|: {
|
||||||
expand for enum_info.fields |field| {
|
expand for enum_info.fields |field| {
|
||||||
if value == field!(T, field.name) {
|
if value == field!(T, field.name) {
|
||||||
try write_all(writer, ".")
|
try write_all(output, ".")
|
||||||
try write_all(writer, field.name)
|
try write_all(output, field.name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,68 +405,3 @@ hide write_default func(writer Writer, $T type, value T) void ! WriteError {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print func(writer Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
|
||||||
expand for parse_format(format.len, format, Args) |token| {
|
|
||||||
match token.kind {
|
|
||||||
.unused: break
|
|
||||||
.literal: try write_all(writer, format[token.start..token.end])
|
|
||||||
.string: try write_all(writer, field!(args, token.field))
|
|
||||||
.default: try write_default(writer, field!(args, token.field))
|
|
||||||
.decimal: try write_decimal(writer, field!(args, token.field))
|
|
||||||
.binary: try write_integer(writer, field!(args, token.field), 2, false)
|
|
||||||
.octal: try write_integer(writer, field!(args, token.field), 8, false)
|
|
||||||
.hex_lower: try write_integer(writer, field!(args, token.field), 16, false)
|
|
||||||
.hex_upper: try write_integer(writer, field!(args, token.field), 16, true)
|
|
||||||
.character: try write_character(writer, field!(args, token.field))
|
|
||||||
else: try write_float(writer, field!(args, token.field), true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
|
||||||
request usize = buffer.len
|
|
||||||
maximum usize :: usize(maxval!(c_long))
|
|
||||||
if request > maximum {
|
|
||||||
request = maximum
|
|
||||||
}
|
|
||||||
while true {
|
|
||||||
count c_long :: c.read(c_int(stream), buffer.ptr, c_ulong(request))
|
|
||||||
if count >= 0 {
|
|
||||||
return usize(count)
|
|
||||||
}
|
|
||||||
if c.__error()^ != 4 {
|
|
||||||
return .read_failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hide system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError {
|
|
||||||
fd c_int :: c_int(stream)
|
|
||||||
request usize = bytes.len
|
|
||||||
maximum usize :: usize(maxval!(c_long))
|
|
||||||
if request > maximum {
|
|
||||||
request = maximum
|
|
||||||
}
|
|
||||||
while true {
|
|
||||||
count c_long :: c.write(fd, bytes.ptr, c_ulong(request))
|
|
||||||
if count >= 0 {
|
|
||||||
return usize(count)
|
|
||||||
}
|
|
||||||
if c.__error()^ != 4 {
|
|
||||||
return .write_failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hide system_vtable IoVTable :: IoVTable {
|
|
||||||
read = system_read,
|
|
||||||
write = system_write,
|
|
||||||
}
|
|
||||||
|
|
||||||
hide system func() Io {
|
|
||||||
return Io {
|
|
||||||
context = none,
|
|
||||||
vtable = &system_vtable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+7
-7
@@ -5,14 +5,14 @@ AllocError :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Allocator :: struct {
|
Allocator :: struct {
|
||||||
context ?*mut anyopaque
|
context ?@mut anyopaque
|
||||||
vtable @AllocatorVTable
|
vtable @AllocatorVTable
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocatorVTable :: struct {
|
AllocatorVTable :: struct {
|
||||||
alloc @func(context ?*mut anyopaque, size usize, alignment usize) ?*mut u8
|
alloc @func(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8
|
||||||
realloc @func(context ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
|
realloc @func(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
|
||||||
free @func(context ?*mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
|
free @func(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
|
||||||
}
|
}
|
||||||
|
|
||||||
raw_alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 {
|
raw_alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 {
|
||||||
@@ -135,7 +135,7 @@ hide power_of_two func(value usize) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
||||||
if power_of_two(alignment) == false {
|
if power_of_two(alignment) == false {
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
return ptrcast!(u8, memory[0])
|
return ptrcast!(u8, memory[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
||||||
if power_of_two(alignment) == false {
|
if power_of_two(alignment) == false {
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size
|
|||||||
return c_alloc(none, new_size, alignment)
|
return c_alloc(none, new_size, alignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
hide c_free func(_ ?*mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
|
hide c_free func(_ ?@mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
|
||||||
c.free(memory)
|
c.free(memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user