Module: nupp.io
Byte buffers, readers and writers#
nupp.io supplies in-memory byte I/O without requiring a stream framework. A buffer holds its bytes in a LuaJIT FFI array, so using it adds no native dependency. Files and processes build on the same reader and writer contracts when their native features are selected; sockets and general asynchronous streams remain separate future layers.
Buffers#
newBuffer() creates an empty growable buffer. A string supplies initial bytes; an integer reserves capacity without changing the length.
Buffer offsets are zero-based. getString(offset, count) copies a range. setString overwrites from an offset and grows the buffer as needed; a gap is filled with zero bytes. clear sets the length to zero without discarding capacity. resize truncates or zero-fills.
Capacity is the allocation, not a recorded number. Growing at least doubles it, so appending through a writer costs amortized constant time per byte and capacity() reports bytes that are actually held. ensureCapacity reserves at least the minimum asked for.
close() releases the buffer, is safe to call repeatedly, and makes later operations raise. isReleased() reports that state.
| Member | Purpose |
|---|---|
length(), capacity() |
Inspect logical and reserved byte counts. |
clear(), resize(length) |
Remove bytes, truncate, or zero-extend. |
ensureCapacity(minimum) |
Reserve without changing logical length. |
getString(offset?, count?) |
Copy all bytes or a zero-based range. |
setString(bytes, offset?) |
Overwrite and grow from a zero-based offset. |
view(offset?, count?) |
Retain an immutable snapshot range. |
newReader(), newWriter() |
Open directional in-memory I/O. |
isReleased(), close() |
Inspect or release ownership. |
Byte views#
view(offset, count) returns an immutable snapshot, not a mutable alias into the buffer. It remains valid if the source buffer changes or closes:
A view can make a smaller view, open its own snapshot reader with newReader, report its byte length, copy to a string, and be closed. Data and UTF-8 functions accept views so callers can avoid coupling those APIs to a mutable buffer.
ByteView provides newReader, length, getString, view, isReleased, and close. Its view offsets are zero-based like Buffer offsets.
Readers#
A nupp.Reader is a forward-only byte source. newStringReader(text) reads a string; buffer:newReader() reads a snapshot of the buffer's current contents.
read(count) returns at most max(1, count) bytes, so zero and negative counts still make progress. It returns an empty string at EOF and nil, reason after close. readInto(buffer, offset, count) returns zero at EOF. Its default count is 64 KiB. transferTo(writer) copies the entire remaining source and returns the byte count.
| Reader member | Result |
|---|---|
read(count) |
string?, reason? |
readInto(buffer, offset?, count?) |
integer?, reason? |
transferTo(writer) |
integer?, reason? |
close() |
boolean, reason? |
Writers#
buffer:newWriter() clears the buffer and returns a forward-only writer targeting it.
write returns a boolean. writeFrom and writeView return the byte count. All return a reason when closed or when the destination was released. Writing a buffer into itself is rejected. flush is a no-op for memory but is part of the common writer contract, allowing a later file or socket writer to implement the same interface.
| Writer member | Result |
|---|---|
write(bytes) |
boolean, reason? |
writeFrom(buffer, offset?, count?) |
integer?, reason? |
writeView(view, offset?, count?) |
integer?, reason? |
flush() |
boolean, reason? |
close() |
boolean, reason? |
For filesystem names rather than file contents, see nupp.io.Path.
Child processes#
nupp.io.process starts a child without exposing descriptors, platform handles, or signals. Reaching the module selects the native process provider and the suspension runtime it needs.
args[1] is the program and the remaining entries are its arguments. cwd changes the child's directory. env overlays inherited variables unless clearEnv starts from an empty environment. Standard streams default to "pipe" and may instead be "inherit" or "null"; stderr alone may be "stdout" to share stdout's actual destination. timeoutMs is measured from spawn, not from the first wait.
communicate({input?, maxOutputBytes?}) is the safe whole-process operation: it feeds stdin while draining stdout and stderr together, closes stdin to deliver EOF, and waits for the exit. Doing those operations sequentially can deadlock when a child fills one output pipe while waiting for more input.
The concrete Reader and Writer satisfy the shared completion-oriented nupp.Reader and nupp.Writer contracts and also expose the nonblocking poll and offer operations needed by that combined drain. asReader and asWriter borrow the same records through their shared interfaces; they do not allocate or duplicate the native handle. The owning Process retains and eventually destroys that handle, so a borrow may not outlive it.
Every wait is contextual. With no suspension handler installed, it sleeps in the platform readiness wait. Under a scheduler handler, the same call parks the current task while the scheduler keeps running. Ready operations do neither.
Suspension explains how the same ordinary call takes those paths and how several waits compose with all, race, or batch.
Process.close() is idempotent and is also its lexical @drop: it attempts every stream release, terminates a child still running, waits for it to finish, and releases the child handle. An Exit reports exitCode, killed, and timedOut; succeeded() is true only for an ordinary zero exit.
Buffers, byte views, readers and writers.
Submodules
| Module | Description |
|---|---|
nupp.io.Path | Constructors and platform operations for immutable filesystem paths. |
nupp.io.URI | Parsing and inspection operations for immutable absolute URIs. |
nupp.io.files | Filesystem metadata, directory contents, and the operations that move names rather than bytes. |
nupp.io.http | An optional asynchronous HTTP client whose sockets and TLS run in the native Reqwest/Tokio provider. |
nupp.io.process | Running a child process. |
nupp.io.processtypes | What a child process is, and what a platform has to provide to run one. |
Module contents
Constructors
| Constructor | Description |
|---|---|
newBuffer | Creates an owned growable byte buffer. |
newStringReader | Creates a forward-only reader over a string. |
Constructors#
newBufferconstructor#
Creates an owned growable byte buffer.
Arguments
| Name | Type | Description |
|---|---|---|
initial | integer | string? | initial bytes, an initial capacity, or nothing for an empty buffer |
Returns
| Type | Description |
|---|---|
nupp.Buffer | the new buffer |
newStringReaderconstructor#
Creates a forward-only reader over a string.
local newStringReader: function(text: string): nupp.ReaderArguments
| Name | Type | Description |
|---|---|---|
text | string | the bytes to read |
Returns
| Type | Description |
|---|---|
nupp.Reader | the new reader |