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