File

struct File

An open host file descriptor with synchronous read, write, seek, and stat operations.

Methods return operation-specific Error values carrying negative errno codes. The handle is not automatically invalidated after close, so callers must avoid subsequent operations and repeated closes.

Methods

static func open(path: Path, mode: Mode) -> Result<File, Error>

Open the file at path: .r reads, .w truncates or creates, .rw reads and writes, creating the file when missing.

View Source
pub static func open(path: Path, mode: Mode) -> Result<File, Error> {
	let flags = match mode {
		.r -> O_RDONLY,
		.w -> O_WRONLY + O_CREAT + O_TRUNC,
		.rw -> O_RDWR + O_CREAT
	}
	let permissions = S_IRUSR + S_IWUSR
	let fd = open_path(path: path.to_string(), flags: flags, mode: permissions)
	if fd < 0 { return .error(.open(fd)) }
	.ok(File(fd: fd, mode: mode))
}

func read() -> Result<String, Error>

Reads from the current offset until the host reports end-of-file.

The buffer starts at 4096 bytes or the current file size, whichever is larger, and doubles as needed. On error, partial bytes are discarded and returned storage is freed.

View Source
pub func read() -> Result<String, Error> {
	#unsafe {
		let capacity = 4096
		let size = _io_file_size(fd: self.fd)
		if size > capacity { capacity = size }
		let buf = _alloc<Byte>(count: capacity)
		let total = 0
		loop {
			let n = _io_read(
				fd: self.fd,
				buf: _ptr_add(ptr: buf, offset: total),
				count: capacity - total
			)
			if n < 0 {
				_free(ptr: buf)
				return .error(.read(n))
			}
			if n == 0 { break }
			total = total + n
			if total == capacity {
				let grown = _alloc<Byte>(count: capacity * 2)
				_copy(from: buf, to: grown, length: total)
				_free(ptr: buf)
				buf = grown
				capacity = capacity * 2
			}
		}
		.ok(String(base: ByteStorage(base: buf), byte_count: total, capacity: capacity))
	}
}

func read(count: Int) -> Result<String, Error>

Performs one host read of at most count bytes from the current offset.

count must be non-negative. A short read is successful and advances the descriptor by only the bytes received. Unlike read(), this does not loop to fill the requested count.

View Source
pub func read(count: Int) -> Result<String, Error> {
	#unsafe {
		let buf = _alloc<Byte>(count: count)
		let n = _io_read(fd: self.fd, buf: buf, count: count)
		if n < 0 {
			_free(ptr: buf)
			return .error(.read(n))
		}
		let clamped = _io_clamp(count: n, capacity: count)
		return .ok(String(base: ByteStorage(base: buf), byte_count: clamped, capacity: count))
	}
}

func write(_ string: String) -> Result<Int, Error>

Write the whole string, looping over partial writes. The answer is the byte count written.

View Source
pub func write(_ string: String) -> Result<Int, Error> {
	#unsafe {
		let total = 0
		loop total < string.byte_count {
			let n = _io_write(
				fd: self.fd,
				buf: _ptr_add(ptr: string.storage.base, offset: total),
				count: string.byte_count - total
			)
			if n < 0 { return .error(.write(n)) }
			total = total + n
		}
		.ok(total)
	}
}

func seek(from: SeekFrom) -> Result<Int, Error>

Moves the descriptor offset relative to the selected origin.

Success returns the new absolute byte offset. Host failure preserves the negative errno in Error.seek.

View Source
pub func seek(from: SeekFrom) -> Result<Int, Error> {
	match from {
		.start(offset) -> self.seek(offset: offset, whence: SEEK_SET),
		.current(offset) -> self.seek(offset: offset, whence: SEEK_CUR),
		.end(offset) -> self.seek(offset: offset, whence: SEEK_END)
	}
}

func tell() -> Result<Int, Error>

Queries the current byte offset by seeking zero bytes from .current.

View Source
pub func tell() -> Result<Int, Error> {
	self.seek(from: .current(0))
}

func size() -> Result<Int, Error>

Returns the descriptor's current file size from host metadata.

This does not change the file offset. Metadata failure is returned as Error.stat with the host's negative errno.

View Source
pub func size() -> Result<Int, Error> {
	let size = _io_file_size(fd: self.fd)
	if size < 0 { return .error(.stat(size)) }
	.ok(size)
}

func close() -> Int

Requests one host close and returns zero or a negative errno.

The stored descriptor is not invalidated, so repeated closes or later I/O remain the caller's responsibility.

View Source
pub func close() -> Int {
	_io_close(fd: self.fd)
}

func is_valid() -> Bool

Returns whether the stored descriptor is non-negative.

This reflects construction only; it remains true after close because the handle does not mutate its descriptor.

View Source
pub func is_valid() -> Bool { self.fd >= 0 }