Bytes and lossless Unix paths
Raw transport uses owned list[u8] values. These are ordinary mutable lists,
with checked indexing, borrowing, and fallible growth; the current representation
uses general list slots rather than a compact byte buffer. A Unix path is a byte
list used without decoding. Filesystem calls reject embedded NUL and otherwise
preserve every byte, including CR, LF, tabs, and invalid UTF-8.
All operations below are prelude builtins. Arguments are observed in source
order and remain available. Results use IoError; allocation failures are
IoError.Data(DataError.Allocation(...)).
| Operation | Result | Contract |
|---|---|---|
encode_utf8(text) | Result[list[u8], IoError] | Independent owned copy of the exact UTF-8 bytes |
decode_utf8(bytes) | Result[str, IoError] | Strict validation and independent text; invalid input returns DataError.InvalidUtf8 |
args_bytes() | Result[list[list[u8]], IoError] | Independent snapshot including invocation name at index zero; no decoding |
input_bytes(delimiter: u8) | Result[Option[list[u8]], IoError] | Read one stdin record, removing only its delimiter |
write_stdout_bytes(source: &[u8]) | Result[i64, IoError] | Exact bytes with no added framing; returns byte count |
write_stderr_bytes(source: &[u8]) | Result[i64, IoError] | Same contract on stderr |
read_bytes(path) | Result[list[u8], IoError] | Read whole file without decoding or newline translation |
write_bytes(path, source: &[u8]) | Result[i64, IoError] | Create or truncate, write exact bytes, check close, return byte count |
The writers read source as a slice view:
pass &bytes for a list[u8] or std.bytes.Bytes, &buffer[a:b] for part of
one, or a b"..." literal. Nothing is copied or converted.
input_bytes(0u8) provides NUL framing for Unix paths, whose names cannot contain
NUL. Some(empty) represents an empty record; Nothing means EOF before any
record byte. A final unterminated nonempty record is accepted. CR and LF have no
special meaning. Input reads no bytes beyond the delimiter and retries interrupted
reads. Allocation or I/O failure can consume part of a record; no rollback or
resynchronization is promised. Construction may fail even at EOF.
Writers read the view in place without allocating an output buffer. They retry
interrupted writes and complete all bytes on success, but errors can follow a
written prefix. Use the existing flush_stdout() or flush_stderr() to report
deferred output errors. Calls and NUL records are not atomic against concurrent
writers.
Binary file helpers use the same Linux open/creation/symlink rules as
text helpers. Path conversion is fallible and precedes opening,
so allocation failure cannot truncate a destination. No output allocation follows
opening. Reads release partial buffers and descriptors on error; successful file
operations check close errors. Success does not imply atomic replacement or durable
storage. File helpers on other hosts return IoError.Unsupported; raw stdin uses
the Unix process descriptor.
For display, decode explicitly or format the byte list with str.repr; keep the
original bytes for filesystem operations. Replacement decoding is never used.
The imported std.path module provides Linux Path owners over this
transport, and std.fs provides directory enumeration and
metadata. std.bytes provides compact buffers that convert
to and from list[u8], and std.io reads and writes them
through files and the process streams without that conversion. First-class byte
views and portable path representations remain within issue #49’s broader scope.
The finder example exercises raw arguments, matching,
and NUL input/output against real Unix filenames.