Documentation/Reference

Byte buffers, text builders and streams

On this page

These APIs are available without an import. They use ordinary checked calls, independent values and deterministic cleanup. See low-level capacity/I/O and implementation contracts .

builder = StringBuilder(Capacity=256)
Append(BYREF builder, "Hello, ")
AppendLine(BYREF builder, "Fred")
Append(BYREF builder, f"Count: {42:04d}")
text = TakeString(BYREF builder)

packet = ByteWriter(Capacity=64)
WriteUInt32(BYREF packet, UINT32(ByteCount(text)), Order=ByteOrder.BigEndian)
WriteBytes(BYREF packet, EncodeUtf8(text))
bytes = TakeBytes(BYREF packet)
reader = ByteReader(TAKE bytes)
count = ReadUInt32(BYREF reader, Order=ByteOrder.BigEndian)
PRINT DecodeUtf8(ReadBytes(BYREF reader, CINT(count)))

Accumulation

Operation Contract
ByteBuffer(Capacity=0), StringBuilder(Capacity=0) Empty value; reserve at least this many bytes. Invalid capacity raises IndexError; allocation failure is fatal.
ByteBuffer(bytes) BYVAL packed-byte input: independent snapshot, or explicit TAKE bytes to adopt it.
StringBuilder(text) Infallible valid UTF-8 input, including embedded NUL.
Length(buffer), Length(builder) Initialized bytes / Unicode scalar count. ByteCount(builder) returns UTF-8 bytes.
Capacity(value), Reserve(BYREF value,capacity) Byte capacity, including reserved storage; Reserve leaves length/content unchanged and returns0.
Append(BYREF buffer,BYTE(...)) Append one byte; return byte length.
Extend(BYREF buffer,bytes[,start,count]) Append a packed-byte list or checked span; return byte length. All range checks precede mutation.
WriteAt(BYREF buffer,offset,bytes) Replace an initialized byte span; no growth. Return count or IndexError before mutation.
Append(BYREF builder,text), AppendChar(BYREF builder,scalar) Append STRING / checked Unicode scalar (StringError when invalid); return scalar length.
AppendLine(BYREF builder,text="") Append text plus LF; return scalar length. Interpolate values before appending them.
Clear(BYREF value) Empty the accumulator while retaining capacity.
Truncate(BYREF buffer,length) Shrink bytes only; checked IndexError.
ToBytes(buffer), ToString(builder) Independent snapshots. Later source/output mutation and destruction are independent.
TakeBytes(BYREF buffer), TakeString(BYREF builder) Explicitly extract storage and empty the accumulator. Byte extraction swaps in an empty list; text extraction donates UTF-8 storage to an owned STRING header. Extracted capacity is no longer retained by the builder.
Bytes(buffer) Checked read-only borrowed byte-list view. Use it directly in a consumer to avoid a snapshot. Storing a copyable result creates an independent value.

Builders are copyable opaque records. Their storage is private; StringBuilder accepts valid STRINGs/scalars, never arbitrary bytes. Decode external data explicitly with DecodeUtf8 or DecodeText . Positions/counts in byte APIs are bytes, not Python-style negative indices. A borrowed view retains its source against mutation, relocation, transfer and cleanup through the whole consumer call, including later arguments and suspension. Read-only access is not global immutability. Self-appending a borrowed view is rejected; take a snapshot first.

Binary cursors

ByteReader(bytes) takes a BYVAL snapshot (or explicit TAKE); ByteReader(buffer) takes a buffer snapshot. ByteWriter(Capacity=0) uses ByteBuffer storage.

Reader Writer / representation
ReadByte(BYREF reader) WriteByte(BYREF writer,value AS BYTE): unsigned8
ReadInt8(BYREF reader) WriteInt8(BYREF writer,value): signed8, INTEGER result/input
ReadInt16/32/64(BYREF reader[,Order]) WriteInt16/32/64(BYREF writer,value[,Order]): two’s complement, INTEGER result/input
ReadUInt16/32/64(BYREF reader[,Order]) WriteUInt16/32/64(BYREF writer,value[,Order]): UINT32 for16/32, UINT64 for64
ReadDouble(BYREF reader[,Order]) WriteDouble(BYREF writer,value[,Order]): exact IEEE binary64 bits, preserving signed zero and NaN payloads
ReadBytes(BYREF reader,count) WriteBytes(BYREF writer,bytes): independent byte results / appended input
ReadInto(BYREF reader,BYREF bytes,start,count) Copy exactly count bytes into initialized destination storage

Order defaults to ByteOrder.LittleEndian; ByteOrder.BigEndian is explicit. Eight-bit operations have no Order argument. Narrow signed writes and UInt16 validate their range with NumericError before changing output. All reads validate the complete requested range with IndexError before changing cursor/output. They do not return partial fields. Position, Remaining, Length, Seek(BYREF reader,position) and nonnegative Skip(BYREF reader,count) inspect/move the cursor; invalid Seek/Skip leave it unchanged. Writers support Length, Capacity, Reserve, Clear, ToBytes, Bytes and TakeBytes with the same contracts as ByteBuffer. Readers/writers remain independent copyable values. Existing zero-argument ReadByte() and INTEGER WriteByte(value) still access inherited streams.

Buffered files and sockets

writer = BufferedWriter(OpenFile("greeting.txt", "w"))
WriteLine(BYREF writer, "Hello, λ")
file = Finish(TAKE writer)

reader = BufferedReader(OpenFile("greeting.txt"), Capacity=4096)
WHILE TRUE
    IF LET line = ReadLine(BYREF reader) THEN
        PRINT line
    ELSE
        EXIT WHILE
    END IF
WEND

BufferedReader(endpoint,Capacity=8192) and BufferedWriter(endpoint,Capacity=8192) own a File or TCPConnection, including TLS connections. Named endpoints require TAKE; fresh owned results are adopted. Nonpositive capacities raise IndexError and clean the consumed endpoint. The resulting records are noncopyable resources, including inside owning collections or source-free BASIL libraries. Their Endpoint view is borrowed and cannot become another owner. Existing read workers/socket readiness/TLS retry leases and structured cancellation remain in force; buffering adds no thread per stream. File writes/flushes retain the existing synchronous platform boundary. The generic record can hold/release another resource T, but supplied I/O operations require the File/TCPConnection adapters; a public custom-stream protocol is not supplied.

Inherited streams also use these APIs: BufferedReader(Stdin()), BufferedWriter(Stdout()) and BufferedWriter(Stderr()). Each factory returns an owned File duplicate; closing the buffer leaves the process’s original stream open. Explicit Flush/Finish is still required for pending output. Duplicates share stream position, so coordinate read-ahead with any other reads from stdin. See standard-stream ownership and I/O limits .

Reader operations fail with IoError/IndexError (also StringError for ReadLine):

Operation Result
ReadInto(BYREF reader,BYREF bytes,start,count) Checked initialized range; up to count bytes from one buffered segment;0 at EOF or for count0.
ReadByte(BYREF reader) OPTIONAL BYTE: SOME for a byte, NONE at EOF.
ReadBytes(BYREF reader,maximum) Up to maximum bytes, filling across segments until the request is satisfied or EOF.
ReadLine(BYREF reader,MaxBytes=1048576) OPTIONAL STRING: strip LF and its preceding CR. An empty line is SOME(""); empty EOF is NONE. A final unterminated line is returned. Limit counts bytes before LF, including CR; overflow is IoError75.
BufferedBytes, Capacity, EndOfStream Unread lookahead count, buffer size and observed EOF with no remaining lookahead. EOF is learned by a read, not predicted.
Endpoint(reader) Checked read-only endpoint loan. Direct endpoint I/O bypasses lookahead and can change external stream position; use deliberately.
Unbuffer(TAKE reader) Return endpoint ownership, explicitly discarding unread lookahead.

Buffered reads are stream operations: an I/O, line-limit or UTF-8 failure may follow consumed input. They do not roll back external stream position. Zero-size ReadInto/ReadBytes does not refill. Returned bytes/text are independent.

Writer operations:

Operation Contract
Write(BYREF writer,text), WriteLine(BYREF writer,text="") Queue UTF-8 bytes, optionally LF; flush when pending bytes reach BufferSize; return accepted byte count. FAILS IoError.
WriteBytes(BYREF writer,bytes) Same for packed bytes; also FAILS IndexError.
BufferedBytes(writer), BufferSize(writer) Unsent byte count / configured flush threshold. Large input is queued whole before output, so this threshold is not a memory limit.
PendingBytes(writer) Independent snapshot of the unsent suffix; checked IndexError contract.
Flush(BYREF writer) Send pending bytes, return the count pending on entry. A successful short write commits progress before another wait/cancellation/failure. This is buffer draining, not file fsync. FAILS IoError.
Endpoint(writer) Checked read-only endpoint loan. Bypassing the buffer can reorder output; flush before direct endpoint writes.
Finish(TAKE writer) Flush and return endpoint ownership. On failure, clean the consumed writer/endpoint and propagate IoError.

After a failed Write/WriteLine/WriteBytes, its complete argument has already been queued. Retry Flush, rather than resubmitting that argument: the unsent suffix is retained and committed bytes are not replayed. Explicit Flush/Finish observes operational failures. Automatic cleanup discards pending output and closes the endpoint; it does not attempt fallible flushing or claim that pending data was saved. Cancellation remains a distinct task outcome and releases buffers/waits/ endpoints deterministically. Constructors and all recoverable paths follow the same ownership rules as source-compiled resources.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.