Skip to content

std/stream

std/stream — byte-stream value backing the eventual HttpRequest.body: Stream migration (docs/STDLIB-DESIGN-RESEARCH.md Rec §1). Phase 1 (this PR): in-memory buffer-backed Stream with a data: u8[] payload + a pos: i32 read cursor. Reads advance the cursor; .read_all_string() / .read_all() consume the remainder. The buffer is allocated up-front; no lazy / chunked reads yet. Phase 2: replace the buffer with a host-side reader that pulls bytes on demand (wasi:http/incoming-body for wasi-http; TCP socket for tcp_serve). The lang-side API shape stays identical — handler code written against the Phase 1 surface keeps working under Phase 2 without changes. bytes is u8[] throughout the codebase (no separate type alias today). Stream constructors / readers all use u8[] for the byte-array side.

pub function stream_from_bytes(bs: u8[]): Stream

stream_from_bytes(bs) — wrap a u8[] buffer as a Stream. The Stream owns the buffer; subsequent reads advance the cursor.

pub function stream_from_string(s: string): Stream

stream_from_string(s) — wrap a string’s bytes as a Stream. Cheap byte conversion (s.bytes() returns the fresh u8[]).

pub function stream_empty(): Stream

stream_empty() — zero-length Stream. Useful as a default / placeholder.

pub function (s: Stream) len(): i32

(s: Stream).len() — total byte count of the Stream’s backing buffer (independent of the read cursor).

pub function (s: Stream) remaining(): i32

(s: Stream).remaining() — bytes between the cursor and the end of the buffer. Zero when the Stream is fully consumed.

pub function (s: Stream) is_empty(): boolean

(s: Stream).is_empty() — whether the Stream has any unread bytes left. The dual of .remaining() == 0; reads better at call sites that gate on “any more data?”

pub function (s: Stream) read_all(): (u8[], Stream)

(s: Stream).read_all() — consume the remaining bytes as a fresh u8[], returning (bytes, advancedStream) per the cursor idiom (docs/CURSOR-IDIOM.md). The returned Stream’s cursor is at the end of the buffer; rebind it: var (bytes, s) = s.read_all();.

Phase 2 will fold an IoError into the result once host- side reads can fail; Phase 1’s in-memory buffer can’t fail so the bare bytes return is fine.

pub function (s: Stream) read_all_string(): (string, Stream)

(s: Stream).read_all_string() — consume the remaining bytes as a UTF-8 string, returning (string, advancedStream). Convenience over string_from_bytes on read_all’s bytes. Non-UTF-8 input round-trips through the string as raw bytes (lang’s string doesn’t enforce UTF-8 validity today).

pub function (s: Stream) read_byte(): (Option[i32], Stream)

(s: Stream).read_byte() — read a single byte from the cursor. Returns (Option[i32], advancedStream): Some(b)

  • a Stream advanced past it, or None + the unchanged Stream when exhausted. Rebind: var (b, s) = s.read_byte();.
pub function (s: Stream) read_n(n: i32): (u8[], Stream)

(s: Stream).read_n(n) — read up to n bytes from the cursor, returning (bytes, advancedStream). The byte array holds the actual bytes consumed (possibly fewer than n at EOF); empty when already exhausted or n <= 0.

pub function (s: Stream) read_line(): (Option[string], Stream)

(s: Stream).read_line() — read up to and including the next \n, returning (Option[string], advancedStream). The line excludes the trailing newline; a trailing \r (Windows line endings) is also stripped. None + unchanged Stream when exhausted at the start of the call (EOF); a final unterminated line returns Some(line) + the advanced Stream, then None on the subsequent call.