/development/std/io.nct
io.nct
//! Common user-facing I/O API.
//!
//! `std/io` exposes ordinary file and text APIs. Fallible functions return `T!`
//! and fail with the compiler built-in `error` payload. Target-specific syscall
//! results and errno wrappers stay behind `#target`-gated std internals.
use std/error.Error
use std/mem.alloc_pages
use std/mem.free_pages
use std/os.{OSError, OSErrorKind}
use std/path.Utf8Path
use std/ptr.copy_str_to_ptr
use std/ptr.from_addr
use std/ptr.store_u8_to_ptr
#target: "arm64-darwin"
pub(nocter) primitive open_read_raw(path: *u8): i32!
#target: "arm64-darwin"
pub(nocter) primitive create_raw(path: *u8): i32!
#target: "arm64-darwin"
pub(nocter) primitive append_raw(path: *u8): i32!
#target: "arm64-darwin"
pub(nocter) primitive write_text_raw(fd: i32, text: &str): void!
#target: "arm64-darwin"
pub(nocter) primitive write_bytes_raw(fd: i32, bytes: &[u8]): void!
#target: "arm64-darwin"
pub(nocter) primitive read_bytes_raw(fd: i32, buffer: &+[u8]): usize!
#target: "arm64-darwin"
pub(nocter) primitive close_fd_raw(fd: i32): void
#target: "arm64-darwin"
pub(nocter) func stdout_fd(): i32 {
return 1
}
#target: "arm64-darwin"
pub(nocter) func stderr_fd(): i32 {
return 2
}
#target: "arm64-darwin"
pub(nocter) func open_read_fd(path: &str): i32! {
let allocation_size: usize = path.len() + 1
let address: usize = alloc_pages(allocation_size)?
copy_str_to_ptr(from_addr(address), 0, path)
store_u8_to_ptr(from_addr(address), allocation_size - 1, 0)
let fd = open_read_raw(from_addr(address)) catch error {
free_pages(address, allocation_size)
return error
}
free_pages(address, allocation_size)
return fd
}
#target: "arm64-darwin"
pub(nocter) func create_fd(path: &str): i32! {
let allocation_size: usize = path.len() + 1
let address: usize = alloc_pages(allocation_size)?
copy_str_to_ptr(from_addr(address), 0, path)
store_u8_to_ptr(from_addr(address), allocation_size - 1, 0)
let fd = create_raw(from_addr(address)) catch error {
free_pages(address, allocation_size)
return error
}
free_pages(address, allocation_size)
return fd
}
#target: "arm64-darwin"
pub(nocter) func append_fd(path: &str): i32! {
let allocation_size: usize = path.len() + 1
let address: usize = alloc_pages(allocation_size)?
copy_str_to_ptr(from_addr(address), 0, path)
store_u8_to_ptr(from_addr(address), allocation_size - 1, 0)
let fd = append_raw(from_addr(address)) catch error {
free_pages(address, allocation_size)
return error
}
free_pages(address, allocation_size)
return fd
}
#target: "arm64-darwin"
pub(nocter) func read_fd(fd: i32, buffer: &+[u8]): usize! {
return read_bytes_raw(fd, buffer)?
}
#target: "arm64-darwin"
pub(nocter) func write_fd(fd: i32, bytes: &[u8]): void! {
write_bytes_raw(fd, bytes)?
return
}
#target: "arm64-darwin"
pub(nocter) func write_text_fd(fd: i32, text: &str): void! {
write_text_raw(fd, text)?
return
}
#target: "arm64-darwin"
pub(nocter) func close_fd(fd: i32): void {
close_fd_raw(fd)
return
}
// Compatibility classification type. It is an ordinary enum, not the fallible
// payload type.
pub(nocter) enum IOError {
interrupted(error: OSError)
would_block(error: OSError)
not_found(error: OSError)
permission_denied(error: OSError)
already_exists(error: OSError)
invalid_input(error: OSError)
broken_pipe(error: OSError)
timed_out(error: OSError)
unsupported(error: OSError)
unexpected_os_error(error: OSError)
}
pub struct File {
fd: i32
close_on_drop: bool
}
pub interface Reader {
pub method &+self.read(buffer: &+[u8]): usize! from static
}
pub interface Writer {
pub method &+self.write(bytes: &[u8]): void! from static
pub method &+self.flush(): void! { return }
}
pub(nocter) func from_os_error(error: OSError): error {
if error.kind is OSErrorKind.interrupted {
return Error.new("std.io.interrupted", "operation interrupted")
}
if error.kind is OSErrorKind.would_block {
return Error.new("std.io.would_block", "operation would block")
}
if error.kind is OSErrorKind.not_found {
return Error.new("std.io.not_found", "file not found")
}
if error.kind is OSErrorKind.permission_denied {
return Error.new("std.io.permission_denied", "permission denied")
}
if error.kind is OSErrorKind.already_exists {
return Error.new("std.io.already_exists", "file already exists")
}
if error.kind is OSErrorKind.invalid_input {
return Error.new("std.io.invalid_input", "invalid I/O input")
}
if error.kind is OSErrorKind.broken_pipe {
return Error.new("std.io.broken_pipe", "broken pipe")
}
if error.kind is OSErrorKind.timed_out {
return Error.new("std.io.timed_out", "operation timed out")
}
if error.kind is OSErrorKind.unsupported {
return Error.new("std.io.unsupported", "unsupported I/O operation")
}
return Error.new("std.io.os_error", "OS operation failed")
}
pub func stdout(): File {
return File { fd: stdout_fd(), close_on_drop: false }
}
pub func stderr(): File {
return File { fd: stderr_fd(), close_on_drop: false }
}
pub func open(path: &str): File! {
return File.open(path)?
}
pub func create(path: &str): File! { return File.create(path)? }
pub func append(path: &str): File! { return File.append(path)? }
pub func open_path(path: &Utf8Path): File! { return File.open(path.view())? }
pub func create_path(path: &Utf8Path): File! { return File.create(path.view())? }
pub func append_path(path: &Utf8Path): File! { return File.append(path.view())? }
construct File {
pub default func open(path: &str): Self! {
let fd = open_read_fd(path)?
return File { fd: fd, close_on_drop: true }
}
pub func create(path: &str): Self! {
let fd = create_fd(path)?
return File { fd: fd, close_on_drop: true }
}
pub func append(path: &str): Self! {
let fd = append_fd(path)?
return File { fd: fd, close_on_drop: true }
}
}
pub func read(file: &+File, buffer: &+[u8]): usize! {
return read_fd(file.fd, buffer)?
}
pub func write(file: &+File, bytes: &[u8]): void! {
write_fd(file.fd, bytes)?
return
}
pub func write_text(file: &+File, text: &str): void! {
write_text_fd(file.fd, text)?
return
}
pub func close(file: &+File): void {
if file.close_on_drop {
close_fd(file.fd)
file.close_on_drop = false
}
return
}
impl File {
pub method &+self.write_text(text: &str): void! {
write_text(self, text)?
return
}
pub method &+self.close(): void {
close(self)
return
}
drop &+self {
close(self)
return
}
}
impl Reader for File {
method &+self.read(buffer: &+[u8]): usize! from static { return read(self, buffer)? }
}
impl Writer for File {
method &+self.write(bytes: &[u8]): void! from static { write(self, bytes)? return }
method &+self.flush(): void! { return }
}
pub func print(text: &str): void! {
write_text_fd(1, text)?
return
}
func unsupported(): error {
return Error.new("std.io.unsupported", "unsupported I/O operation")
}