Module: io.stream
Source: ./io/stream.reef
Overview
io/stream - Stream hierarchy (passive objects) for I/O
Stream is the base class. FileStream is fopen-backed (stream_open returns one). MemoryStream is an in-memory buffer with virtual read/write/seek. Prefixed stream_* functions wrap methods so existing call sites keep compiling. Uses the underlying reef_f* builtins.
Opens return Result[Stream, error.Error]: Ok(stream) on success. For
STREAM_READ (and STREAM_READ_WRITE, which requires an existing file):
Err(NotFound) if the path doesn't exist, Err(IoError) for any other
fopen failure. For STREAM_WRITE/STREAM_APPEND: always Err(IoError) on
fopen failure — write/append opens can't distinguish "missing parent
dir" from "permission denied" via an exists()-recheck, since fopen never
creates the file on failure (matches io.file's writeFile/appendFile
precedent). stream_write/stream_write_line return
Result[int, error.Error] carrying the byte count written on success,
Err(IoError) if the underlying write is short or fails.
stream_seek/stream_seek_end return Result[bool, error.Error] (the
void-success convention — Ok(true), never Ok(false)). stream_tell/
stream_size return Result[int64, error.Error], and stream_seek takes an
int64 pos — all three are backed by the genuine 64-bit reef_ftello/
reef_fseeko intrinsics, so offsets on files >2GB are
no longer truncated.
Reads (stream_read, stream_read_line) return Result[Option[string], Error]: Ok(Some(data)) when data was read, Ok(None) on clean EOF (nothing more to read), Err(IoError) on a genuine I/O error (distinguished from EOF via reef_ferror) or if the stream isn't open. stream_read_all returns Result[string, error.Error] instead (Ok(whole remaining content); Err on not-open or a genuine read error) since it has no natural "no more data" case to signal with None.
Example - Read from file: let open_res = stream_open("/tmp/data.txt", STREAM_READ()) if result.is_ok(open_res) let s = result.unwrap_ok(open_res) let read_res = stream_read(s, 1024) if result.is_ok(read_res) let opt = result.unwrap_ok(read_res) if option.is_some(opt) println(option.unwrap(opt)) end if end if stream_close(s) end if
Example - Write to file: let open_res = stream_open("/tmp/output.txt", STREAM_WRITE()) if result.is_ok(open_res) let s = result.unwrap_ok(open_res) stream_write(s, "Hello, World!\n") stream_close(s) end if
Example - Read line by line: let s = result.unwrap_ok(stream_open("file.txt", STREAM_READ())) loop let lr = stream_read_line(s) if result.is_err(lr) break end if let opt = result.unwrap_ok(lr) if option.is_none(opt) break end if println(option.unwrap(opt)) end loop stream_close(s)
Objects
Stream
Fields:
| Name | Type |
|---|---|
handle |
pointer |
mode |
int |
opened |
bool |
eof_flag |
bool |
proc init()
Methods:
shared fn kind(): string
shared fn is_open(): bool
shared fn is_eof(): bool
exclusive proc close()
exclusive fn read(max_len: int): result.Result[option.Option[string], error.Error]
exclusive fn read_line(): result.Result[option.Option[string], error.Error]
exclusive fn read_all(): result.Result[string, error.Error]
exclusive fn write(data: string): result.Result[int, error.Error]
exclusive fn write_line(data: string): result.Result[int, error.Error]
exclusive proc flush()
exclusive fn seek(pos: int64): result.Result[bool, error.Error]
exclusive fn seek_end(): result.Result[bool, error.Error]
exclusive fn tell(): result.Result[int64, error.Error]
exclusive fn size(): result.Result[int64, error.Error]
exclusive proc rewind()
FileStream
Extends: Stream
proc init()
Methods:
override shared fn kind(): string
MemoryStream
Extends: Stream
Fields:
| Name | Type |
|---|---|
data |
string |
pos |
int |
proc init(initial: string)
Methods:
override shared fn kind(): string
override exclusive proc close()
override exclusive proc flush()
override exclusive fn read(max_len: int): result.Result[option.Option[string], error.Error]
override exclusive fn read_line(): result.Result[option.Option[string], error.Error]
override exclusive fn read_all(): result.Result[string, error.Error]
override exclusive fn write(data: string): result.Result[int, error.Error]
override exclusive fn write_line(data: string): result.Result[int, error.Error]
override exclusive fn seek(pos: int64): result.Result[bool, error.Error]
override exclusive fn seek_end(): result.Result[bool, error.Error]
override exclusive fn tell(): result.Result[int64, error.Error]
override exclusive fn size(): result.Result[int64, error.Error]
override exclusive proc rewind()
Functions
fn STREAM_READ(): int
Stream mode constants
fn STREAM_WRITE(): int
fn STREAM_READ_WRITE(): int
fn STREAM_APPEND(): int
fn memory_stream(data: string): Stream
fn stream_kind(s: Stream): string
fn mode_to_fopen_mode(mode: int): string
Helper: Convert mode to fopen mode string
fn mode_to_verb(mode: int): string
Helper: human-readable verb for error messages, keyed off mode.
fn open_failure_kind(path: string): error.ErrorKind
Classifies a failed fopen() for path when opening for READING:
NotFound if the path doesn't exist at all, IoError for any other reason
(e.g. permission denied, or a non-directory component in the path).
Mirrors io.file.open_failure_kind. Only valid for read-mode opens — see
stream_open, which branches on mode before using this.
fn stream_open(path: string, mode: int): result.Result[Stream, error.Error]
Open a file stream with specified mode Ok(stream) on success. For READ-mode opens: Err(NotFound) if the path doesn't exist, Err(IoError) if it exists but cannot be opened for reading. For WRITE/APPEND-mode opens: always Err(IoError) — fopen(path, "w"/"a") never creates the file on failure, so the path is always absent afterwards and an exists()-recheck would misclassify every write/append open failure (including permission-denied on the parent dir) as NotFound. This matches io.file's writeFile/appendFile precedent. STREAM_READ_WRITE ("r+") requires the file to already exist, so it is classified the same way as a read open.
fn stream_open_read(path: string): result.Result[Stream, error.Error]
Convenience: open for reading
fn stream_open_write(path: string): result.Result[Stream, error.Error]
Convenience: open for writing (truncates existing)
fn stream_open_append(path: string): result.Result[Stream, error.Error]
Convenience: open for appending
fn stream_is_open(s: Stream): bool
fn stream_is_eof(s: Stream): bool
fn stream_read(s: Stream, max_len: int): result.Result[option.Option[string], error.Error]
fn stream_read_line(s: Stream): result.Result[option.Option[string], error.Error]
Read a line from stream (up to 4KB). Uses fgets, which reads until newline or buffer full. Ok(Some(line)) — a line was read (may be "" for a genuinely blank line). Ok(None) — clean EOF, nothing more to read. Err(IoError) — stream not open, or a genuine read error (distinguished from EOF via reef_ferror).
NOTE: fgets returns NULL (not an empty C string) on EOF/error, and NULL
can't safely be assigned to a Reef string and then inspected (e.g. via
str.length) — that dereferences a null pointer and crashes. So the fgets
return value is discarded (as a bare statement, like writer_flush's
reef_fwrite call); buf is what gets inspected/returned instead, since
reef_alloc_string_buffer always zero-initializes it (empty string) and
fgets leaves it untouched on failure. This mirrors io.console.readLine's
existing pattern for the same hazard.
fn stream_read_all(s: Stream): result.Result[string, error.Error]
Read entire stream contents. Seeks to beginning, reads all, returns to original position. Ok(content) on success (an empty file yields Ok("")). Err(IoError) if the stream isn't open, or a genuine read error occurred (distinguished from EOF via reef_ferror) — there is no natural "no more data" case here (an empty read result), so unlike stream_read/stream_read_line this returns Result[string, error.Error], not Option-wrapped.
fn stream_write(s: Stream, data: string): result.Result[int, error.Error]
Write data to stream
Ok(count) on success (count is the number of bytes written — 0 for
empty data, which is not an error). Err(IoError) if the stream isn't
open, or if the underlying fwrite is short (wrote fewer bytes than
requested).
fn stream_write_line(s: Stream, data: string): result.Result[int, error.Error]
Write a line to stream (appends newline)
Ok(count) on success (count is the number of bytes written, including
the trailing newline). Err(IoError) if the stream isn't open, or if
either the underlying fputs for data or the trailing-newline fputs
fails — a failure on the second fputs is a real write failure and must
not be reported as Ok.
fn stream_seek(s: Stream, pos: int64): result.Result[bool, error.Error]
Seek to absolute position
Ok(true) on success (the void-success convention). Err(IoError) if the
stream isn't open, or the underlying fseeko fails. pos is int64 and
backed by reef_fseeko, so positions beyond 2GB are genuinely
supported, not truncated.
fn stream_seek_end(s: Stream): result.Result[bool, error.Error]
Seek to end of stream Ok(true) on success. Err(IoError) if the stream isn't open, or the underlying fseek fails.
fn stream_tell(s: Stream): result.Result[int64, error.Error]
Get current position in stream
Ok(pos) on success. Err(IoError) if the stream isn't open. pos is a
genuine int64 sourced from the reef_ftello intrinsic
— no 2GB truncation.
fn stream_size(s: Stream): result.Result[int64, error.Error]
Get total size of stream
Ok(size) on success. Err(IoError) if the stream isn't open. Same genuine
int64-via-reef_ftello note as stream_tell applies to size.
Procedures
proc stream_close(s: Stream)
proc stream_flush(s: Stream)
Flush stream buffer to disk
proc stream_rewind(s: Stream)
Rewind stream to beginning
Generated by reefc doc