# tecs.io Input, output, and nonblocking network transport. A [`Stream`](/modules/io/#tecs.io.Stream) describes binary storage without keeping a cursor. A [`Reader`](/modules/io/#tecs.io.Reader) and [`Writer`](/modules/io/#tecs.io.Writer) are the caller-owned directional endpoints opened from it. Strings, buffers, borrowed bytes, paths, and handles all use that one descriptor contract. A Lua `FILE` is also a basic reader and writer. `transfer` accepts files directly and uses `readInto` or `writeFrom` accelerators when an endpoint provides them. Transform constructors take ownership of either a Tecs endpoint or a file and close it with the wrapper. Whole-source operations run synchronously and return their result directly. They return nil and a reason when an endpoint cannot be opened, read, written, flushed, or closed: ```teal tecs.scoped(function(scope: tecs.Scope) local save = scope:own(tecs.io.newFileStream("save.bin")) local bytes = assert(save:transferToBuffer()) scope:own(bytes) print(bytes:length()) end) ``` TCP is an ordered byte stream: one write may take several reads to receive, several writes may arrive in one read, and message framing belongs to the caller. UDP preserves one send as one datagram, but the network may lose, duplicate, or reorder it. General immutable URIs live under `tecs.io.URI`; HTTP and HTTPS live under `tecs.io.http`; the MCP debug server lives under `tecs.io.mcp`. Hot TCP loops can reuse a `Buffer`. `TCPSocket:readInto` lets the native socket fill its FFI allocation directly, and `TCPSocket:writeFrom` queues a buffer range without constructing an intermediate Lua string. `Buffer:view` retains an immutable zero-copy snapshot. `writeView` on sockets, writers, and writable streams consumes that retained range directly, while `ByteView:newStream` gives it replayable stream behavior without copying payload bytes. Transform readers and writers compose those endpoints without materializing a whole source. `newInflateReader`, `newBase64DecodeReader`, `newHexDecodeReader`, and `newTranscodeReader` pull transformed bytes as their consumer asks. The corresponding deflate, encode, and transcode writers push transformed chunks toward a destination and finish format trailers or partial character state on `close`. Each wrapper owns the endpoint it wraps. Memory endpoints lend source pointers and reserve destination ranges directly; other endpoints reuse pooled chunks, so a warmed transfer allocates no intermediate payload storage. Ownership is explicit at construction boundaries. A stream over a buffer or Lua file handle borrows that resource and never closes it. A stream over a byte view retains its own immutable view. Transform readers and writers take ownership of the endpoint they wrap and close it with themselves. Opening a buffer or byte-view reader retains an immutable snapshot that outlives later closure; opening a buffer writer borrows the mutable buffer, clears its logical length, and requires it to remain open through the writer's close. Resolution and client connection return asynchronous [`Future`](/modules/Future/) values. An [`Application`](/modules/Application/) polls [`runtime`](/modules/runtime/) once per iteration, so a game waits on status without driving I/O itself: ```teal local address = tecs.io.resolve("game.example") local function update() if address.status == "ready" then print(address.value.text) end end ``` An `Application` calls `tecs.runtime.poll` automatically once per host iteration. Game systems and update functions must not drive either pump. A headless program normally calls `tecs.runtime.poll` once per manual loop; `tecs.io.poll` remains the narrower operation for code deliberately driving only generic network futures. A future's bounded `wait` is available when blocking is the honest operation. Stream transfers already block until their bytes have moved. Listening, accepting, reading, writing, and receiving are nonblocking. Every address, TCP socket, listener, UDP socket, and UDP packet owns a resource. Close it explicitly. A GC finalizer releases an abandoned network handle as a last resort, but cannot report a close error. Closing is idempotent; using a closed value returns a failure. Naming `tecs.io` loads none of its children. Reading `files`, `http`, `mcp`, `Path`, `Process`, `URI`, or `watcher` loads only that child. Declares the directional binary interfaces published by `tecs.io`. This module is the cycle-safe home of `Reader`, `Writer`, and the stream descriptor interfaces. It is not a second public namespace: `tecs.io` re-exports the contracts, while resource modules require them without requiring their public parent. ## Module contents ### Submodules | Submodule | Description | | --- | --- | | [`tecs.io.Path`](/modules/io/Path/) | Immutable UTF-8 filesystem paths with platform-native semantics. | | [`tecs.io.Process`](/modules/io/Process/) | Streaming child processes with backpressured standard I/O. | | [`tecs.io.URI`](/modules/io/URI/) | Immutable absolute URIs with component-aware modification. | | [`tecs.io.files`](/modules/io/files/) | Asset, persistent, and cache paths plus synchronous file operations | | [`tecs.io.http`](/modules/io/http/) | Asynchronous HTTP requests, streaming bodies, connection pools, and ECS request entities | | [`tecs.io.mcp`](/modules/io/mcp/) | MCP server setup, world inspection, safe mutation, custom tools, and transport | | [`tecs.io.watcher`](/modules/io/watcher/) | Development-time polling and reload dispatch for loaded content | ### Constructors | Constructor | Description | | --- | --- | | [`newBase64DecodeReader`](/modules/io/#tecs.io.newBase64DecodeReader) | Creates a reader that incrementally decodes RFC 4648 Base64 text. | | [`newBase64EncodeWriter`](/modules/io/#tecs.io.newBase64EncodeWriter) | Creates a writer that incrementally encodes bytes as RFC 4648 Base64. | | [`newBuffer`](/modules/io/#tecs.io.newBuffer) | Creates an owned growable byte buffer. | | [`newByteReader`](/modules/io/#tecs.io.newByteReader) | Creates a reader over borrowed FFI memory. | | [`newByteStream`](/modules/io/#tecs.io.newByteStream) | Creates a replayable read-only stream over borrowed FFI bytes. | | [`newDeflateWriter`](/modules/io/#tecs.io.newDeflateWriter) | Creates a writer that incrementally deflates bytes into a destination. | | [`newEmptyStream`](/modules/io/#tecs.io.newEmptyStream) | Creates a replayable readable empty stream. | | [`newFileStream`](/modules/io/#tecs.io.newFileStream) | Creates a replayable file stream. | | [`newHandleStream`](/modules/io/#tecs.io.newHandleStream) | Creates a one-shot stream over a borrowed Lua file handle. | | [`newHexDecodeReader`](/modules/io/#tecs.io.newHexDecodeReader) | Creates a reader that incrementally decodes hexadecimal text. | | [`newHexEncodeWriter`](/modules/io/#tecs.io.newHexEncodeWriter) | Creates a writer that incrementally encodes bytes as lowercase hexadecimal text. | | [`newInflateReader`](/modules/io/#tecs.io.newInflateReader) | Creates a reader that incrementally inflates a compressed source. | | [`newStringReader`](/modules/io/#tecs.io.newStringReader) | Creates a reader over an immutable Lua string. | | [`newStringStream`](/modules/io/#tecs.io.newStringStream) | Creates a replayable read-only string stream. | | [`newTranscodeReader`](/modules/io/#tecs.io.newTranscodeReader) | Creates a reader that incrementally converts character encodings. | | [`newTranscodeWriter`](/modules/io/#tecs.io.newTranscodeWriter) | Creates a writer that incrementally converts character encodings. | ### Types | Type | Kind | Description | | --- | --- | --- | | [`Address`](/modules/io/#tecs.io.Address) | record | An Address owns one resolved network address. | | [`Buffer`](/modules/io/#tecs.io.Buffer) | interface | A Buffer is owned growable FFI-backed byte storage. | | [`ByteView`](/modules/io/#tecs.io.ByteView) | interface | A ByteView retains an immutable zero-copy range from a buffer. | | [`DeflateWriterOptions`](/modules/io/#tecs.io.DeflateWriterOptions) | record | Options control an incremental deflate writer. | | [`InflateReaderOptions`](/modules/io/#tecs.io.InflateReaderOptions) | record | Options control an incremental inflate reader. | | [`ReadableStream`](/modules/io/#tecs.io.ReadableStream) | interface | A ReadableStream opens readers and supplies whole-source transfers. | | [`Reader`](/modules/io/#tecs.io.Reader) | interface | A Reader supplies bytes in order and releases its owned state on close. | | [`ReadWriteStream`](/modules/io/#tecs.io.ReadWriteStream) | interface | A ReadWriteStream supports both directional interfaces. | | [`Seekable`](/modules/io/#tecs.io.Seekable) | interface | Seekable supplies random-access cursor operations shared by readers and writers. | | [`SeekableReader`](/modules/io/#tecs.io.SeekableReader) | interface | A SeekableReader supplies bytes through a repositionable cursor. | | [`SeekableWriter`](/modules/io/#tecs.io.SeekableWriter) | interface | A SeekableWriter patches a destination through a repositionable cursor. | | [`Stream`](/modules/io/#tecs.io.Stream) | interface | A Stream describes binary storage without retaining a cursor. | | [`TCPListener`](/modules/io/#tecs.io.TCPListener) | record | A TCPListener listens for TCP clients. | | [`TCPSocket`](/modules/io/#tecs.io.TCPSocket) | record | A TCPSocket owns one connected TCP byte stream. | | [`UDPPacket`](/modules/io/#tecs.io.UDPPacket) | record | A UDPPacket owns one received UDP datagram and its source address. | | [`UDPSocket`](/modules/io/#tecs.io.UDPSocket) | record | A UDPSocket sends and receives UDP packets. | | [`WritableStream`](/modules/io/#tecs.io.WritableStream) | interface | A WritableStream opens writers and supplies whole-destination transfers. | | [`Writer`](/modules/io/#tecs.io.Writer) | interface | A Writer accepts bytes in order and finishes its destination on close. | ### Functions | Function | Kind | Description | | --- | --- | --- | | [`bind`](/modules/io/#tecs.io.bind) | Static | Binds a UDP socket. | | [`connect`](/modules/io/#tecs.io.connect) | Static | Begins connecting to a resolved address. | | [`init`](/modules/io/#tecs.io.init) | Static | Starts networking for this module. | | [`listen`](/modules/io/#tecs.io.listen) | Static | Binds a TCP listener. | | [`pending`](/modules/io/#tecs.io.pending) | Static | Returns the number of pending resolution or connection futures. | | [`poll`](/modules/io/#tecs.io.poll) | Static | Advances pending I/O in a headless loop without waiting. | | [`resolve`](/modules/io/#tecs.io.resolve) | Static | Begins resolving a hostname. | | [`shutdown`](/modules/io/#tecs.io.shutdown) | Static | Stops this module's networking instance. | | [`transfer`](/modules/io/#tecs.io.transfer) | Static | Transfers every remaining byte between directional endpoints. | ## Constructors ### tecs.io.newBase64DecodeReader Static Creates a reader that incrementally decodes RFC 4648 Base64 text. The reader owns `source` and retains at most one undecoded quantum between source reads. ```teal function tecs.io.newBase64DecodeReader( source: types.Reader | FILE ): types.Reader ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `source` | [`types.Reader`](/modules/io/#tecs.io.Reader) | FILE | The caller supplies the reader of Base64 text. | #### Returns | Type | Description | | --- | --- | | [`types.Reader`](/modules/io/#tecs.io.Reader) | Returns a caller-owned reader of decoded bytes. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local reader = scope:own( tecs.io.newBase64DecodeReader( tecs.io.newStringReader("c25hcHNob3QgYnl0ZXM=") ) ) local decoded , reason = reader:read(1024) assert(decoded, reason) assert(decoded == "snapshot bytes") end) ``` ### tecs.io.newBase64EncodeWriter Static Creates a writer that incrementally encodes bytes as RFC 4648 Base64. The writer owns `destination`. `close` emits padding for the final partial quantum before closing the destination. ```teal function tecs.io.newBase64EncodeWriter( destination: types.Writer | FILE ): types.Writer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `destination` | [`types.Writer`](/modules/io/#tecs.io.Writer) | FILE | The caller supplies the writer receiving Base64 text. | #### Returns | Type | Description | | --- | --- | | [`types.Writer`](/modules/io/#tecs.io.Writer) | Returns a caller-owned writer that accepts unencoded bytes. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local encoded = scope:own(tecs.io.newBuffer()) local writer = scope:own( tecs.io.newBase64EncodeWriter(encoded:newWriter()) ) assert(writer:write("snapshot bytes")) assert(writer:close()) assert(encoded:getString() == "c25hcHNob3QgYnl0ZXM=") end) ``` ### tecs.io.newBuffer Static Creates an owned growable byte buffer. ```teal function tecs.io.newBuffer(initial: integer | string): IOBuffer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `initial` | integer | string | The caller supplies a non-negative zero-filled logical length, a string to copy, or nil for an empty buffer. | #### Returns | Type | Description | | --- | --- | | [`IOBuffer`](/modules/io/#tecs.io.Buffer) | Returns a caller-owned buffer. | #### Examples ```teal local bytes = tecs.io.newBuffer("save data") print(bytes:length()) bytes:close() ``` ### tecs.io.newByteReader Static Creates a reader over borrowed FFI memory. The reader never frees or modifies the allocation. The caller keeps its owner reachable and the bytes unchanged until the reader closes. ```teal function tecs.io.newByteReader( pointer: loader.CValue, length: integer ): types.Reader ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `pointer` | `loader.CValue` | The caller supplies the first readable byte. | | `length` | `integer` | The caller supplies the non-negative readable byte count. | #### Returns | Type | Description | | --- | --- | | [`types.Reader`](/modules/io/#tecs.io.Reader) | Returns a caller-owned reader whose cursor starts at zero. | #### Examples ```teal local ffi = require("ffi") local bytes = "borrowed bytes" local pointer = ffi.cast("const uint8_t *", bytes) local reader = tecs.io.newByteReader(pointer as any, #bytes) assert(reader:read(1024) == bytes) reader:close() ``` ### tecs.io.newByteStream Static Creates a replayable read-only stream over borrowed FFI bytes. Closing the stream or its readers never frees the allocation. ```teal function tecs.io.newByteStream( pointer: loader.CValue, length: integer, contentType: string ): types.ReadableStream ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `pointer` | `loader.CValue` | The caller keeps the backing allocation alive and unchanged until every reader closes and every transfer returns. | | `length` | `integer` | The caller supplies the non-negative byte count. | | `contentType` | `string` | The caller supplies optional media type metadata. | #### Returns | Type | Description | | --- | --- | | [`types.ReadableStream`](/modules/io/#tecs.io.ReadableStream) | Returns a readable stream. | #### Examples ```teal local ffi = require("ffi") local bytes = "borrowed bytes" local pointer = ffi.cast("const uint8_t *", bytes) local source = tecs.io.newByteStream(pointer as any, #bytes) print(source:readAll()) source:close() ``` ### tecs.io.newDeflateWriter Static Creates a writer that incrementally deflates bytes into a destination. The writer owns `destination`. `flush` emits a synchronization point without ending the compressed stream, and `close` emits its trailer before closing the destination. ```teal function tecs.io.newDeflateWriter( destination: types.Writer | FILE, options: types.DeflateWriterOptions ): types.Writer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `destination` | [`types.Writer`](/modules/io/#tecs.io.Writer) | FILE | The caller supplies the writer receiving compressed bytes. | | `options` | [`types.DeflateWriterOptions`](/modules/io/#tecs.io.DeflateWriterOptions) | The caller selects raw framing and a compression level or omits it for zlib framing and zlib's default level. | #### Returns | Type | Description | | --- | --- | | [`types.Writer`](/modules/io/#tecs.io.Writer) | Returns a caller-owned writer that accepts uncompressed bytes. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local compressed = scope:own(tecs.io.newBuffer()) local writer = scope:own( tecs.io.newDeflateWriter(compressed:newWriter()) ) assert(writer:write("snapshot ")) assert(writer:write("bytes")) assert(writer:close()) assert(compressed:length() > 0) end) ``` ### tecs.io.newEmptyStream Static Creates a replayable readable empty stream. ```teal function tecs.io.newEmptyStream( contentType: string ): types.ReadableStream ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `contentType` | `string` | The caller supplies optional media type metadata. | #### Returns | Type | Description | | --- | --- | | [`types.ReadableStream`](/modules/io/#tecs.io.ReadableStream) | Returns a source whose length is zero. | #### Examples ```teal local source = tecs.io.newEmptyStream( "application/octet-stream" ) print(source:readAll()) source:close() ``` ### tecs.io.newFileStream Static Creates a replayable file stream. Readers open the current path through `tecs.io.files`; writers replace it. A descriptor keeps no open file between endpoints. ```teal function tecs.io.newFileStream( path: string | Path, contentType: string ): types.ReadWriteStream ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `path` | string | [`Path`](/modules/io/Path/) | The caller supplies the source or destination as a string or [`Path`](/modules/io/Path/). | | `contentType` | `string` | The caller supplies optional media type metadata. | #### Returns | Type | Description | | --- | --- | | [`types.ReadWriteStream`](/modules/io/#tecs.io.ReadWriteStream) | Returns a readable and writable stream. | #### Examples ```teal local path = tecs.io.Path.new("save.bin") local save = tecs.io.newFileStream( path, "application/octet-stream" ) assert(save:writeAll("save data")) print(save:readAll()) save:close() ``` ### tecs.io.newHandleStream Static Creates a one-shot stream over a borrowed Lua file handle. The first reader or writer claims its current cursor. Closing the stream or endpoint does not close the caller's handle. ```teal function tecs.io.newHandleStream( handle: FILE, length: integer, contentType: string ): types.ReadWriteStream ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `handle` | `FILE` | The caller keeps the Lua file handle open through the endpoint's close. | | `length` | `integer` | The caller supplies the remaining byte count when known. | | `contentType` | `string` | The caller supplies optional media type metadata. | #### Returns | Type | Description | | --- | --- | | [`types.ReadWriteStream`](/modules/io/#tecs.io.ReadWriteStream) | Returns a non-replayable readable and writable stream. | #### Examples ```teal local handle = assert(io.open("save.bin", "rb")) local source = tecs.io.newHandleStream(handle) print(source:readAll()) source:close() handle:close() ``` ### tecs.io.newHexDecodeReader Static Creates a reader that incrementally decodes hexadecimal text. The reader owns `source` and carries one unmatched nibble across source reads. ```teal function tecs.io.newHexDecodeReader( source: types.Reader | FILE ): types.Reader ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `source` | [`types.Reader`](/modules/io/#tecs.io.Reader) | FILE | The caller supplies the reader of hexadecimal text. | #### Returns | Type | Description | | --- | --- | | [`types.Reader`](/modules/io/#tecs.io.Reader) | Returns a caller-owned reader of decoded bytes. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local reader = scope:own( tecs.io.newHexDecodeReader(tecs.io.newStringReader("73617665")) ) local decoded , reason = reader:read(1024) assert(decoded, reason) assert(decoded == "save") end) ``` ### tecs.io.newHexEncodeWriter Static Creates a writer that incrementally encodes bytes as lowercase hexadecimal text. The writer owns `destination` and forwards each encoded chunk without retaining input bytes. ```teal function tecs.io.newHexEncodeWriter( destination: types.Writer | FILE ): types.Writer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `destination` | [`types.Writer`](/modules/io/#tecs.io.Writer) | FILE | The caller supplies the writer receiving hexadecimal text. | #### Returns | Type | Description | | --- | --- | | [`types.Writer`](/modules/io/#tecs.io.Writer) | Returns a caller-owned writer that accepts unencoded bytes. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local encoded = scope:own(tecs.io.newBuffer()) local writer = scope:own( tecs.io.newHexEncodeWriter(encoded:newWriter()) ) assert(writer:write("save")) assert(writer:close()) assert(encoded:getString() == "73617665") end) ``` ### tecs.io.newInflateReader Static Creates a reader that incrementally inflates a compressed source. The reader owns `source` and closes it when the wrapper closes. Reads retain only bounded compressed and decompressed chunks. A raw stream has no zlib header, trailer, or checksum. ```teal function tecs.io.newInflateReader( source: types.Reader | FILE, options: types.InflateReaderOptions ): types.Reader ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `source` | [`types.Reader`](/modules/io/#tecs.io.Reader) | FILE | The caller supplies the reader of compressed bytes. | | `options` | [`types.InflateReaderOptions`](/modules/io/#tecs.io.InflateReaderOptions) | The caller selects raw framing and an output ceiling or omits it for a zlib stream and the default ceiling. | #### Returns | Type | Description | | --- | --- | | [`types.Reader`](/modules/io/#tecs.io.Reader) | Returns a caller-owned reader of decompressed bytes. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local compressed = scope:own(tecs.io.newBuffer()) local restored = scope:own(tecs.io.newBuffer()) local compressor = scope:own( tecs.io.newDeflateWriter(compressed:newWriter()) ) assert(compressor:write("snapshot bytes")) assert(compressor:close()) local reader = scope:own( tecs.io.newInflateReader(compressed:newReader()) ) local sink = scope:own(restored:newWriter()) local count , reason = tecs.io.transfer(reader, sink) assert(count, reason) assert(restored:getString() == "snapshot bytes") end) ``` ### tecs.io.newStringReader Static Creates a reader over an immutable Lua string. The reader retains `bytes` without copying it. Closing the reader drops that retained reference; there is no separate resource to close. ```teal function tecs.io.newStringReader(bytes: string): types.Reader ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `bytes` | `string` | The caller supplies complete binary contents. | #### Returns | Type | Description | | --- | --- | | [`types.Reader`](/modules/io/#tecs.io.Reader) | Returns a caller-owned reader whose cursor starts at zero. | #### Examples ```teal local reader = tecs.io.newStringReader("snapshot bytes") assert(reader:read(1024) == "snapshot bytes") reader:close() ``` ### tecs.io.newStringStream Static Creates a replayable read-only string stream. The stream retains the immutable string without copying it. ```teal function tecs.io.newStringStream( bytes: string, contentType: string ): types.ReadableStream ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `bytes` | `string` | The caller supplies complete binary contents. | | `contentType` | `string` | The caller supplies optional media type metadata. | #### Returns | Type | Description | | --- | --- | | [`types.ReadableStream`](/modules/io/#tecs.io.ReadableStream) | Returns a readable stream. | #### Examples ```teal local source = tecs.io.newStringStream("hello", "text/plain") print(source:readAll()) source:close() ``` ### tecs.io.newTranscodeReader Static Creates a reader that incrementally converts character encodings. The reader owns `source`. It preserves conversion state and carries an incomplete multibyte character across source reads. ```teal function tecs.io.newTranscodeReader( source: types.Reader | FILE, fromEncoding: string, toEncoding: string ): types.Reader ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `source` | [`types.Reader`](/modules/io/#tecs.io.Reader) | FILE | The caller supplies the reader of encoded text. | | `fromEncoding` | `string` | The caller supplies an SDL iconv source encoding name such as `UTF-8`. | | `toEncoding` | `string` | The caller supplies an SDL iconv destination encoding name such as `UTF-16LE`. | #### Returns | Type | Description | | --- | --- | | [`types.Reader`](/modules/io/#tecs.io.Reader) | Returns a caller-owned reader of converted text. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local reader = scope:own( tecs.io.newTranscodeReader( tecs.io.newStringReader("A\0\233\0"), "UTF-16LE", "UTF-8" ) ) local utf8 , reason = reader:read(1024) assert(utf8, reason) assert(utf8 == "A\195\169") end) ``` ### tecs.io.newTranscodeWriter Static Creates a writer that incrementally converts character encodings. The writer owns `destination`. It preserves conversion state and carries an incomplete multibyte character across writes; `close` rejects a truncated final character and emits any target shift state. ```teal function tecs.io.newTranscodeWriter( destination: types.Writer | FILE, fromEncoding: string, toEncoding: string ): types.Writer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `destination` | [`types.Writer`](/modules/io/#tecs.io.Writer) | FILE | The caller supplies the writer receiving converted text. | | `fromEncoding` | `string` | The caller supplies an SDL iconv source encoding name such as `UTF-8`. | | `toEncoding` | `string` | The caller supplies an SDL iconv destination encoding name such as `UTF-16LE`. | #### Returns | Type | Description | | --- | --- | | [`types.Writer`](/modules/io/#tecs.io.Writer) | Returns a caller-owned writer that accepts bytes in `fromEncoding`. | #### Examples ```teal tecs.scoped(function(scope: tecs.Scope) local utf16 = scope:own(tecs.io.newBuffer()) local writer = scope:own( tecs.io.newTranscodeWriter( utf16:newWriter(), "UTF-8", "UTF-16LE" ) ) assert(writer:write("A\195")) assert(writer:write("\169")) assert(writer:close()) assert(utf16:getString() == "A\0\233\0") end) ``` ## Types ### tecs.io.Address record An `Address` owns one resolved network address. Closing it releases the address and remains safe to repeat. ```teal record tecs.io.Address is Closeable host: string text: string close: function(self): boolean, string isClosed: function(self): boolean end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.Address.host field Read-only. Networking sets `host` at resolution or peer lookup. It contains the requested hostname or numeric peer address. ```teal tecs.io.Address.host: string ``` #### tecs.io.Address.text field Read-only. Networking sets `text` to the printable numeric address when it creates the object. ```teal tecs.io.Address.text: string ``` #### tecs.io.Address:close Instance ```teal function tecs.io.Address.close(self): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Address` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | | | `string` | | #### tecs.io.Address:isClosed Instance Returns whether `close` has released this address. ```teal function tecs.io.Address.isClosed(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Address` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true after `close`. | ### tecs.io.Buffer interface A `Buffer` is owned growable FFI-backed byte storage. ```teal interface tecs.io.Buffer is Closeable interface WriteRange is Closeable capacity: function(self): integer commit: function(self, used: integer) getFFIPointer: function(self): loader.BytePointer end capacity: function(self): integer clear: function(self) ensureCapacity: function(self, minimum: integer) getFFIPointer: function(self): loader.BytePointer getString: function(self, offset: integer, count: integer): string isReleased: function(self): boolean length: function(self): integer newReader: function(self): Reader newStream: function(self, contentType: string): ReadWriteStream newWriter: function(self): Writer reserveRange: function( self, offset: integer, minimum: integer ): WriteRange resize: function(self, length: integer) setString: function(self, bytes: string, offset: integer) view: function(self, offset: integer, count: integer): ByteView end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.Buffer.WriteRange interface An exclusive, zero-copy writable range reserved from a [`Buffer`](/modules/io/#tecs.io.Buffer). Closing abandons the range without changing the buffer's logical length. ```teal interface tecs.io.Buffer.WriteRange is Closeable capacity: function(self): integer commit: function(self, used: integer) getFFIPointer: function(self): loader.BytePointer end ``` ##### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | ##### tecs.io.Buffer.WriteRange:capacity Instance Returns the maximum byte count the native writer may fill. ```teal function tecs.io.Buffer.WriteRange.capacity(self): integer ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `WriteRange` | The open range to inspect. | ###### Returns | Type | Description | | --- | --- | | `integer` | Returns the reserved byte capacity. | ##### tecs.io.Buffer.WriteRange:commit Instance Commits bytes written through the borrowed pointer. The call closes the range. A gap before the committed bytes is zero-filled. ```teal function tecs.io.Buffer.WriteRange.commit(self, used: integer) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `WriteRange` | The open range to commit. | | `used` | `integer` | The caller supplies the written count up to `capacity`. | ###### Returns None. ##### tecs.io.Buffer.WriteRange:getFFIPointer Instance Borrows the reserved range's writable address. ```teal function tecs.io.Buffer.WriteRange.getFFIPointer( self ): loader.BytePointer ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `WriteRange` | The open range whose storage to borrow. | ###### Returns | Type | Description | | --- | --- | | `loader.BytePointer` | Returns a pointer valid until `commit` or `close`. | #### tecs.io.Buffer:capacity Instance Returns the allocated byte capacity. ```teal function tecs.io.Buffer.capacity(self): integer ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The buffer to inspect. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the byte count available without reallocating. | #### Examples ```teal local bytes = tecs.io.newBuffer("data") assert(bytes:capacity() >= bytes:length()) bytes:close() ``` #### tecs.io.Buffer:clear Instance Removes every logical byte without releasing the allocation. ```teal function tecs.io.Buffer.clear(self) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The buffer to clear. | ##### Returns None. #### Examples ```teal local bytes = tecs.io.newBuffer("reusable") local capacity = bytes:capacity() bytes:clear() assert(bytes:length() == 0) assert(bytes:capacity() == capacity) bytes:close() ``` #### tecs.io.Buffer:ensureCapacity Instance Ensures at least `minimum` bytes fit without another allocation. Growth is geometric. A call at or below the current capacity leaves pointers returned by `getFFIPointer` valid. ```teal function tecs.io.Buffer.ensureCapacity(self, minimum: integer) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The buffer to grow. | | `minimum` | `integer` | The caller supplies a non-negative byte count. | ##### Returns None. #### Examples ```teal local bytes = tecs.io.newBuffer("data") bytes:ensureCapacity(1024) assert(bytes:capacity() >= 1024) assert(bytes:length() == 4) bytes:close() ``` #### tecs.io.Buffer:getFFIPointer Instance Borrows the address of the first byte. The pointer is invalid after capacity grows or `close` runs. The buffer must remain reachable for the whole native call that uses it. ```teal function tecs.io.Buffer.getFFIPointer(self): loader.BytePointer ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The buffer whose storage to borrow. | ##### Returns | Type | Description | | --- | --- | | `loader.BytePointer` | Returns a mutable FFI byte pointer. | #### Examples ```teal local bytes = tecs.io.newBuffer("native bytes") local pointer = bytes:getFFIPointer() print(pointer) bytes:close() ``` #### tecs.io.Buffer:getString Instance Copies a range into a Lua string. ```teal function tecs.io.Buffer.getString( self, offset: integer, count: integer ): string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The source buffer. | | `offset` | `integer` | The caller supplies a zero-based offset or omits it for zero. | | `count` | `integer` | The caller supplies a byte count or omits it for the remainder. | ##### Returns | Type | Description | | --- | --- | | `string` | Returns a fresh binary string. | #### Examples ```teal local bytes = tecs.io.newBuffer("headerpayload") print(bytes:getString(6, 7)) bytes:close() ``` #### tecs.io.Buffer:isReleased Instance Returns whether `close` has given up the allocation. ```teal function tecs.io.Buffer.isReleased(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The buffer to inspect. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true after `close`. | #### Examples ```teal local bytes = tecs.io.newBuffer() assert(not bytes:isReleased()) bytes:close() assert(bytes:isReleased()) ``` #### tecs.io.Buffer:length Instance Returns the number of logical bytes. ```teal function tecs.io.Buffer.length(self): integer ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The buffer to inspect. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes visible to `getString`. | #### Examples ```teal local bytes = tecs.io.newBuffer("four") print(bytes:length()) bytes:close() ``` #### tecs.io.Buffer:newReader Instance Opens a reader over an immutable snapshot of the current bytes. The reader retains the allocation independently, so the caller may close or mutate the buffer after this call. Its cursor starts at zero. ```teal function tecs.io.Buffer.newReader(self): Reader ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The open source buffer. | ##### Returns | Type | Description | | --- | --- | | `Reader` | Returns a caller-owned reader. | #### Examples ```teal local bytes = tecs.io.newBuffer("snapshot") local reader = bytes:newReader() bytes:close() assert(reader:read(1024) == "snapshot") reader:close() ``` #### tecs.io.Buffer:newStream Instance Creates a stream sharing this buffer. The stream borrows this buffer. The caller keeps it open while using the stream and closes it separately. Closing the stream never closes the buffer. `transferToBuffer` returns this same borrowed object without transferring ownership. ```teal function tecs.io.Buffer.newStream( self, contentType: string ): ReadWriteStream ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The open buffer the stream borrows. | | `contentType` | `string` | The caller supplies optional media type metadata. | ##### Returns | Type | Description | | --- | --- | | `ReadWriteStream` | Returns a caller-owned readable and writable stream. | #### Examples ```teal local bytes = tecs.io.newBuffer("old") local stream = bytes:newStream() assert(stream:writeAll("new")) print(bytes:getString()) stream:close() bytes:close() ``` #### tecs.io.Buffer:newWriter Instance Opens a writer that replaces the current bytes. The call clears the logical length while retaining capacity. The caller keeps the buffer open until the writer closes. ```teal function tecs.io.Buffer.newWriter(self): Writer ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The open destination buffer. | ##### Returns | Type | Description | | --- | --- | | `Writer` | Returns a caller-owned writer. | #### Examples ```teal local bytes = tecs.io.newBuffer("old") local writer = bytes:newWriter() assert(writer:write("new")) assert(writer:close()) assert(bytes:getString() == "new") bytes:close() ``` #### tecs.io.Buffer:reserveRange Instance Reserves an exclusive range for a native writer without an intermediate string. The buffer detaches from retained views before exposing mutable storage. ```teal function tecs.io.Buffer.reserveRange( self, offset: integer, minimum: integer ): WriteRange ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The open buffer to reserve. | | `offset` | `integer` | The caller supplies a zero-based write offset. | | `minimum` | `integer` | The caller supplies the minimum writable capacity. | ##### Returns | Type | Description | | --- | --- | | [`WriteRange`](/modules/io/#tecs.io.Buffer.WriteRange) | Returns a caller-owned exclusive range. | #### tecs.io.Buffer:resize Instance Changes the logical length. Expansion fills every newly visible byte with zero. Shrinking keeps the allocation for reuse. ```teal function tecs.io.Buffer.resize(self, length: integer) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The buffer to resize. | | `length` | `integer` | The caller supplies a non-negative byte count. | ##### Returns None. #### Examples ```teal local bytes = tecs.io.newBuffer("data") bytes:resize(6) assert(bytes:getString() == "data\0\0") bytes:resize(2) assert(bytes:getString() == "da") bytes:close() ``` #### tecs.io.Buffer:setString Instance Copies a Lua string into the buffer. The write extends the logical length as needed. A gap between the old end and `offset` is zero-filled. ```teal function tecs.io.Buffer.setString(self, bytes: string, offset: integer) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The destination buffer. | | `bytes` | `string` | The caller supplies a binary string. | | `offset` | `integer` | The caller supplies a zero-based offset or omits it for zero. | ##### Returns None. #### Examples ```teal local bytes = tecs.io.newBuffer("abcdefgh") bytes:setString("XY", 3) assert(bytes:getString() == "abcXYfgh") bytes:setString("!", 10) assert(bytes:getString(8, 3) == "\0\0!") bytes:close() ``` #### tecs.io.Buffer:view Instance Returns a zero-copy immutable snapshot of a byte range. ```teal function tecs.io.Buffer.view( self, offset: integer, count: integer ): ByteView ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | The open buffer to retain. | | `offset` | `integer` | The caller supplies a zero-based offset or omits it for zero. | | `count` | `integer` | The caller supplies a byte count or omits it for the remainder. | ##### Returns | Type | Description | | --- | --- | | [`ByteView`](/modules/io/#tecs.io.ByteView) | Returns a caller-owned retained view. | ### tecs.io.ByteView interface A `ByteView` retains an immutable zero-copy range from a buffer. ```teal interface tecs.io.ByteView is Closeable getFFIPointer: function(self): loader.BytePointer getString: function(self): string isReleased: function(self): boolean length: function(self): integer newReader: function(self): Reader newStream: function(self, contentType: string): ReadableStream view: function(self, offset: integer, count: integer): ByteView end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.ByteView:getFFIPointer Instance Borrows the retained range's read-only address. LuaJIT FFI cannot enforce constness. Writing through a cast pointer is unsafe and breaks the snapshot guarantee. ```teal function tecs.io.ByteView.getFFIPointer(self): loader.BytePointer ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | The open view whose storage to borrow. | ##### Returns | Type | Description | | --- | --- | | `loader.BytePointer` | Returns a pointer valid until `close`. | #### tecs.io.ByteView:getString Instance Copies the retained range into a Lua string. ```teal function tecs.io.ByteView.getString(self): string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | The open view to copy. | ##### Returns | Type | Description | | --- | --- | | `string` | Returns a fresh binary string. | #### tecs.io.ByteView:isReleased Instance Returns whether the view has released its retained allocation. ```teal function tecs.io.ByteView.isReleased(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | The view to inspect. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true after `close`. | #### tecs.io.ByteView:length Instance Returns the retained range's byte length. ```teal function tecs.io.ByteView.length(self): integer ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | The open view to inspect. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the number of readable bytes. | #### tecs.io.ByteView:newReader Instance Opens a reader over this immutable range. The reader retains the allocation independently, so the caller may close this view immediately after the call. Its cursor starts at zero. ```teal function tecs.io.ByteView.newReader(self): Reader ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | The open source view. | ##### Returns | Type | Description | | --- | --- | | `Reader` | Returns a caller-owned reader. | #### Examples ```teal local bytes = tecs.io.newBuffer("headerpayload") local payload = bytes:view(6) local reader = payload:newReader() payload:close() bytes:close() assert(reader:read(1024) == "payload") reader:close() ``` #### tecs.io.ByteView:newStream Instance Creates a replayable read-only stream retaining this view. The stream retains its own view, so the caller may close this view immediately after construction. Closing the stream closes its retained view after already-open readers close theirs. ```teal function tecs.io.ByteView.newStream( self, contentType: string ): ReadableStream ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | The open immutable byte view. | | `contentType` | `string` | The caller supplies optional media type metadata. | ##### Returns | Type | Description | | --- | --- | | `ReadableStream` | Returns a caller-owned readable stream. | #### Examples ```teal local bytes = tecs.io.newBuffer("headerpayload") local payload = bytes:view(6) local source = payload:newStream("application/octet-stream") payload:close() bytes:close() assert(source:readAll() == "payload") source:close() ``` #### tecs.io.ByteView:view Instance Retains a zero-copy subrange. ```teal function tecs.io.ByteView.view( self, offset: integer, count: integer ): ByteView ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | The open source view. | | `offset` | `integer` | The caller supplies a zero-based offset or omits it for zero. | | `count` | `integer` | The caller supplies a byte count or omits it for the remainder. | ##### Returns | Type | Description | | --- | --- | | [`ByteView`](/modules/io/#tecs.io.ByteView) | Returns a caller-owned view retaining the same allocation. | ### tecs.io.DeflateWriterOptions record Options control an incremental deflate writer. ```teal record tecs.io.DeflateWriterOptions raw: boolean level: integer end ``` #### tecs.io.DeflateWriterOptions.raw field Caller-writable. Selects raw DEFLATE when true and zlib framing when false or omitted. ```teal tecs.io.DeflateWriterOptions.raw: boolean ``` #### tecs.io.DeflateWriterOptions.level field Caller-writable. Sets zlib's compression level from minus one through nine or uses its default when omitted. ```teal tecs.io.DeflateWriterOptions.level: integer ``` ### tecs.io.InflateReaderOptions record Options control an incremental inflate reader. ```teal record tecs.io.InflateReaderOptions raw: boolean maxBytes: integer end ``` #### tecs.io.InflateReaderOptions.raw field Caller-writable. Selects raw DEFLATE when true and zlib framing when false or omitted. ```teal tecs.io.InflateReaderOptions.raw: boolean ``` #### tecs.io.InflateReaderOptions.maxBytes field Caller-writable. Sets the hard decompressed-byte ceiling or uses 268,435,456 bytes when omitted. ```teal tecs.io.InflateReaderOptions.maxBytes: integer ``` ### tecs.io.ReadableStream interface A `ReadableStream` opens readers and supplies whole-source transfers. ```teal interface tecs.io.ReadableStream is Stream, Closeable discard: function(self): integer, string newReader: function(self): Reader, string readAll: function(self, maxBytes: integer): string, string transferTo: function( self, destination: WritableStream ): integer, string transferToBuffer: function(self, maxBytes: integer): Buffer, string transferToFile: function(self, path: string): integer, string end ``` #### Interfaces | Interface | | --- | | [`Stream`](/modules/io/#tecs.io.Stream) | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.ReadableStream:discard Instance Reads and discards every source byte. The operation opens, consumes, and closes one reader. On a non-replayable stream, this claims its one endpoint. ```teal function tecs.io.ReadableStream.discard(self): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ReadableStream` | The readable descriptor. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes discarded. | | `string` | Returns the source's reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newFileStream("download.tmp") local count = source:discard() print(count) source:close() ``` #### tecs.io.ReadableStream:newReader Instance Opens a reader with a new cursor. ```teal function tecs.io.ReadableStream.newReader(self): Reader, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ReadableStream` | The readable descriptor. | ##### Returns | Type | Description | | --- | --- | | [`Reader`](/modules/io/#tecs.io.Reader) | Returns a caller-owned reader, or nil when unavailable. | | `string` | Returns the reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newStringStream("data") local reader = assert(source:newReader()) print(reader:read(4)) reader:close() source:close() ``` #### tecs.io.ReadableStream:readAll Instance Reads the complete source into a Lua string. The operation opens, consumes, and closes one reader. On a non-replayable stream, this claims its one endpoint. ```teal function tecs.io.ReadableStream.readAll( self, maxBytes: integer ): string, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ReadableStream` | The readable descriptor. | | `maxBytes` | `integer` | The caller supplies a non-negative limit or omits it for no explicit limit. | ##### Returns | Type | Description | | --- | --- | | `string` | Returns the complete bytes. | | `string` | Returns the source's reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newStringStream("data") local bytes = source:readAll(1024) print(bytes) source:close() ``` #### tecs.io.ReadableStream:transferTo Instance Copies every source byte into `destination`. The operation opens and closes one source reader and one destination writer. Either non-replayable descriptor is claimed by the call. ```teal function tecs.io.ReadableStream.transferTo( self, destination: WritableStream ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ReadableStream` | The readable descriptor. | | `destination` | `WritableStream` | The caller supplies an available writable stream. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes written. | | `string` | Returns the source or destination reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newStringStream("data") local output = tecs.io.newBuffer() local destination = output:newStream() local count = source:transferTo(destination) print(count, output:getString()) source:close() destination:close() output:close() ``` #### tecs.io.ReadableStream:transferToBuffer Instance Transfers the complete source into an owned buffer. A stream created by `Buffer:newStream` returns that same borrowed buffer without copying and does not transfer ownership. Other streams return a newly allocated buffer owned by the caller. The operation opens, consumes, and closes one reader when it must copy. On a non-replayable stream, this claims its one endpoint. ```teal function tecs.io.ReadableStream.transferToBuffer( self, maxBytes: integer ): Buffer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ReadableStream` | The readable descriptor. | | `maxBytes` | `integer` | The caller supplies a non-negative limit or omits it for no explicit limit. | ##### Returns | Type | Description | | --- | --- | | [`Buffer`](/modules/io/#tecs.io.Buffer) | Returns a caller-owned buffer. | | `string` | Returns the source's reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newFileStream("save.bin") local bytes = source:transferToBuffer(1024 * 1024) print(bytes:length()) bytes:close() source:close() ``` #### tecs.io.ReadableStream:transferToFile Instance Copies every source byte into a file. The operation opens and closes one source reader. On a non-replayable stream, this claims its one endpoint. ```teal function tecs.io.ReadableStream.transferToFile( self, path: string ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ReadableStream` | The readable descriptor. | | `path` | `string` | The caller supplies the destination path to replace. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes written. | | `string` | Returns the source or file reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newStringStream("save data") local count = source:transferToFile("save.bin") print(count) source:close() ``` ### tecs.io.Reader interface A `Reader` supplies bytes in order and releases its owned state on `close`. ```teal interface tecs.io.Reader is Closeable read: function(self, count: integer): string, string readInto: function( self, destination: Buffer, offset: integer, count: integer ): integer, string end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.Reader:read Instance Caller-writable. Supplies the operation that reads the next bytes. ```teal function tecs.io.Reader.read(self, count: integer): string, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Reader` | The reader whose cursor advances. | | `count` | `integer` | The maximum number of bytes to return when positive. Zero and negative counts are treated as one so the reader makes progress. | ##### Returns | Type | Description | | --- | --- | | `string` | Returns up to `max(1, count)` bytes, fewer at the end, or an empty string after the source is exhausted. | | `string` | Returns the source's reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newStringStream("abcdefgh") local reader = assert(source:newReader()) local chunk , reason = reader:read(4) assert(chunk, reason) print(chunk) reader:close() source:close() ``` #### tecs.io.Reader:readInto Instance Reads bytes directly into a buffer and advances the cursor. The operation grows the destination as needed and preserves bytes outside the written range. It returns zero after the source is exhausted. ```teal function tecs.io.Reader.readInto( self, destination: Buffer, offset: integer, count: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Reader` | The open reader whose cursor advances. | | `destination` | [`Buffer`](/modules/io/#tecs.io.Buffer) | The caller supplies an open destination buffer and keeps ownership of it. | | `offset` | `integer` | The caller supplies a non-negative zero-based destination offset or omits it for zero. | | `count` | `integer` | The caller supplies the maximum non-negative byte count or omits it for 16,384. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the number of bytes written into the destination. | | `string` | Returns the source's reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newStringStream("abcdefgh") local reader = assert(source:newReader()) local bytes = tecs.io.newBuffer("prefix:") local count , reason = reader:readInto(bytes, bytes:length(), 4) assert(count, reason) print(bytes:getString()) reader:close() source:close() bytes:close() ``` ### tecs.io.ReadWriteStream interface A `ReadWriteStream` supports both directional interfaces. ```teal interface tecs.io.ReadWriteStream is ReadableStream, Stream, Closeable, WritableStream end ``` #### Interfaces | Interface | | --- | | [`ReadableStream`](/modules/io/#tecs.io.ReadableStream) | | [`Stream`](/modules/io/#tecs.io.Stream) | | [`Closeable`](/modules/#tecs.Closeable) | | [`WritableStream`](/modules/io/#tecs.io.WritableStream) | ### tecs.io.Seekable interface `Seekable` supplies random-access cursor operations shared by readers and writers. ```teal interface tecs.io.Seekable enum Origin "current" "end" "start" end seek: function( self, origin: Origin, offset: integer ): integer, string size: function(self): integer, string tell: function(self): integer, string end ``` #### tecs.io.Seekable.Origin enum `Origin` selects the reference point for a seek. ```teal enum tecs.io.Seekable.Origin "current" "end" "start" end ``` #### tecs.io.Seekable:seek Instance Repositions the cursor and returns its new absolute position. `"start"` measures `offset` from byte zero, `"current"` measures it from the cursor, and `"end"` measures it from the current byte length. The offset defaults to zero and may be negative for the latter two origins. A result before byte zero or past the current end fails without moving the cursor. ```teal function tecs.io.Seekable.seek( self, origin: Origin, offset: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Seekable` | The open cursor to reposition. | | `origin` | [`Origin`](/modules/io/#tecs.io.Seekable.Origin) | The caller selects the reference point. | | `offset` | `integer` | The caller supplies a signed integer byte offset or omits it for zero. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the new zero-based cursor position. | | `string` | Returns the storage reason when the first return is nil. | #### tecs.io.Seekable:size Instance Returns the storage's current byte length. The result includes every write already accepted even when buffered bytes have not reached the underlying destination yet. ```teal function tecs.io.Seekable.size(self): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Seekable` | The open cursor to inspect. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the current byte length. | | `string` | Returns the storage reason when the first return is nil. | #### tecs.io.Seekable:tell Instance Returns the current zero-based cursor position. ```teal function tecs.io.Seekable.tell(self): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Seekable` | The open cursor to inspect. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the cursor's byte position. | | `string` | Returns the storage reason when the first return is nil. | ### tecs.io.SeekableReader interface A `SeekableReader` supplies bytes through a repositionable cursor. ```teal interface tecs.io.SeekableReader is Reader, Closeable, Seekable end ``` #### Interfaces | Interface | | --- | | [`Reader`](/modules/io/#tecs.io.Reader) | | [`Closeable`](/modules/#tecs.Closeable) | | [`Seekable`](/modules/io/#tecs.io.Seekable) | ### tecs.io.SeekableWriter interface A `SeekableWriter` patches a destination through a repositionable cursor. ```teal interface tecs.io.SeekableWriter is Writer, Closeable, Seekable end ``` #### Interfaces | Interface | | --- | | [`Writer`](/modules/io/#tecs.io.Writer) | | [`Closeable`](/modules/#tecs.Closeable) | | [`Seekable`](/modules/io/#tecs.io.Seekable) | ### tecs.io.Stream interface A `Stream` describes binary storage without retaining a cursor. ```teal interface tecs.io.Stream is Closeable contentLength: function(self): integer | nil contentType: function(self): string | nil isReadable: function(self): boolean isReplayable: function(self): boolean isWritable: function(self): boolean withMetadata: function( self, contentType: string, contentLength: integer, isReplayable: boolean ): T end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.Stream:contentLength Instance Returns the byte length when it is known. ```teal function tecs.io.Stream.contentLength(self): integer | nil ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Stream` | The descriptor to inspect. | ##### Returns | Type | Description | | --- | --- | | integer | nil | Returns the current byte length, or nil when it cannot be known before reading. | #### Examples ```teal local source = tecs.io.newStringStream("four") local length = source:contentLength() if length ~= nil then print(length) end source:close() ``` #### tecs.io.Stream:contentType Instance Returns the media type supplied at construction. ```teal function tecs.io.Stream.contentType(self): string | nil ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Stream` | The descriptor to inspect. | ##### Returns | Type | Description | | --- | --- | | string | nil | Returns the media type, or nil when none was supplied. | #### Examples ```teal local source = tecs.io.newStringStream("{}", "application/json") print(source:contentType()) source:close() ``` #### tecs.io.Stream:isReadable Instance Returns whether the descriptor can open a reader. ```teal function tecs.io.Stream.isReadable(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Stream` | The descriptor to inspect. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when `newReader` is supported. | #### Examples ```teal local source = tecs.io.newStringStream("data") print(source:isReadable()) source:close() ``` #### tecs.io.Stream:isReplayable Instance Returns whether each reader starts from the beginning. True means the descriptor can open independent readers repeatedly. False means opening an endpoint or running a whole-source operation may claim the descriptor permanently; `newReader` reports when it can no longer open one. ```teal function tecs.io.Stream.isReplayable(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Stream` | The descriptor to inspect. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when opening a later reader replays all bytes. | #### Examples ```teal local source = tecs.io.newStringStream("data") print(source:isReplayable()) source:close() ``` #### tecs.io.Stream:isWritable Instance Returns whether the descriptor can open a writer. ```teal function tecs.io.Stream.isWritable(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Stream` | The descriptor to inspect. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when `newWriter` is supported. | #### Examples ```teal local save = tecs.io.newFileStream("save.bin") print(save:isWritable()) save:close() ``` #### tecs.io.Stream:withMetadata Instance Creates a lazy view that overrides this descriptor's metadata. Nil inherits each value from the receiver. The view retains and delegates to the receiver, preserves its static and runtime directional interface, and closes the receiver when it closes. If every supplied value already matches, this returns the receiver. ```teal function tecs.io.Stream.withMetadata( self, contentType: string, contentLength: integer, isReplayable: boolean ): T ``` ##### Type Parameters | Name | Constraint | Description | | --- | --- | --- | | `T` | [`Stream`](/modules/io/#tecs.io.Stream) | | ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `T` | The stream whose storage and operations the view retains. | | `contentType` | `string` | The caller overrides the media type or omits it. | | `contentLength` | `integer` | The caller overrides the non-negative byte length or omits it. | | `isReplayable` | `boolean` | The caller overrides replayability or omits it. | ##### Returns | Type | Description | | --- | --- | | `T` | Returns a metadata view with the receiver's stream type. | #### Examples ```teal local source = tecs.io.newStringStream("{}") local json = source:withMetadata("application/json") print(json:contentType()) json:close() ``` ### tecs.io.TCPListener record A `TCPListener` listens for TCP clients. Closing it stops listening, leaves accepted streams open, and remains safe to repeat. ```teal record tecs.io.TCPListener is Closeable port: integer accept: function(self): TCPSocket, string close: function(self): boolean, string isClosed: function(self): boolean wait: function(self, timeoutMs: integer): boolean, string end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.TCPListener.port field Read-only. Networking sets `port` from the value passed to `listen` and never changes it. Zero remains zero. ```teal tecs.io.TCPListener.port: integer ``` #### tecs.io.TCPListener:accept Instance Takes one pending client without waiting. Returns nil with no error when nobody waits. The caller owns the returned stream. ```teal function tecs.io.TCPListener.accept(self): TCPSocket, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPListener` | | ##### Returns | Type | Description | | --- | --- | | [`TCPSocket`](/modules/io/#tecs.io.TCPSocket) | Returns one caller-owned client, or nil when none waits or acceptance fails. | | `string` | Returns the reason only when acceptance fails. | #### tecs.io.TCPListener:close Instance ```teal function tecs.io.TCPListener.close(self): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPListener` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | | | `string` | | #### tecs.io.TCPListener:isClosed Instance Returns whether `close` has released this listener. ```teal function tecs.io.TCPListener.isClosed(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPListener` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true after `close`. | #### tecs.io.TCPListener:wait Instance Waits up to `timeoutMs` for a client to become ready to accept. Returns false without an error on timeout. This does not accept the client; call `accept` afterwards. ```teal function tecs.io.TCPListener.wait( self, timeoutMs: integer ): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPListener` | | | `timeoutMs` | `integer` | The caller supplies 0 to 2147483647 milliseconds or omits it for zero. Zero polls without blocking. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when at least one client waits. | | `string` | Returns the reason on failure. Timeout returns false without a reason. | ### tecs.io.TCPSocket record A `TCPSocket` owns one connected TCP byte stream. Closing it may discard queued writes, so a caller drains delivery-sensitive output first. Repeated closure remains safe. ```teal record tecs.io.TCPSocket is Closeable close: function(self): boolean, string drain: function(self, timeoutMs: integer): boolean, string isClosed: function(self): boolean peer: function(self): Address, string pendingWrites: function(self): integer, string read: function(self, maxBytes: integer): string, string readInto: function( self, destination: IOBuffer, offset: integer, maxBytes: integer ): integer, string wait: function(self, timeoutMs: integer): boolean, string write: function(self, bytes: string): boolean, string writeFrom: function( self, source: IOBuffer, offset: integer, count: integer ): integer, string writeView: function( self, source: types.ByteView, offset: integer, count: integer ): integer, string end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.TCPSocket:close Instance ```teal function tecs.io.TCPSocket.close(self): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | | | `string` | | #### tecs.io.TCPSocket:drain Instance Waits up to `timeoutMs` for queued writes to be sent. Returns false without an error on timeout. ```teal function tecs.io.TCPSocket.drain( self, timeoutMs: integer ): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | | `timeoutMs` | `integer` | The caller supplies 0 to 2147483647 milliseconds or omits it for 5000. Zero polls without blocking. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when the local send queue reaches zero before timeout. | | `string` | Returns the reason on failure. Timeout returns false without a reason. | #### tecs.io.TCPSocket:isClosed Instance Returns whether `close` has released this connection. ```teal function tecs.io.TCPSocket.isClosed(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true after `close`. | #### tecs.io.TCPSocket:peer Instance Returns a newly owned address for the remote peer. The caller closes the returned address. ```teal function tecs.io.TCPSocket.peer(self): Address, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | ##### Returns | Type | Description | | --- | --- | | [`Address`](/modules/io/#tecs.io.Address) | Returns a caller-owned address that outlives the connection, or nil on failure. | | `string` | Returns the reason when the first return is nil. | #### tecs.io.TCPSocket:pendingWrites Instance Returns bytes accepted but not yet sent. ```teal function tecs.io.TCPSocket.pendingWrites(self): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the queued byte count, zero after sending, or nil on failure. | | `string` | Returns the reason when the first return is nil. | #### tecs.io.TCPSocket:read Instance Reads up to `maxBytes` without waiting. Returns nil with no error when no bytes are ready. Returns nil with an error after the peer disconnects or the read fails. A successful read may contain fewer bytes than requested. ```teal function tecs.io.TCPSocket.read( self, maxBytes: integer ): string, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | | `maxBytes` | `integer` | The caller supplies a ceiling from 1 to 65536 bytes or omits it for 16384. Invalid values raise. | ##### Returns | Type | Description | | --- | --- | | `string` | Returns a fresh string of available bytes, or nil when no bytes arrived or the read failed. | | `string` | Returns the reason only when reading fails or the peer disconnects. | #### tecs.io.TCPSocket:readInto Instance Reads available bytes directly into reusable FFI memory. The call writes at a zero-based offset and extends the buffer through the last byte read. It returns nil without a reason when no bytes are ready and leaves the logical length and bytes unchanged; reserving the requested range may still grow capacity. A zero count returns zero without consuming input. ```teal function tecs.io.TCPSocket.readInto( self, destination: IOBuffer, offset: integer, maxBytes: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | | `destination` | [`IOBuffer`](/modules/io/#tecs.io.Buffer) | The caller supplies an open [`Buffer`](/modules/io/#tecs.io.Buffer). | | `offset` | `integer` | The caller supplies a zero-based destination offset or omits it for zero. | | `maxBytes` | `integer` | The caller supplies a ceiling from 0 to 65536 bytes or omits it for 16384. Invalid values raise. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes read, zero for a zero-byte request, or nil when no bytes arrived or the read failed. | | `string` | Returns the reason only when reading fails or the peer disconnects. | #### tecs.io.TCPSocket:wait Instance Waits up to `timeoutMs` for input or disconnection. Returns false without an error on timeout. This does not consume input; call `read` afterwards. ```teal function tecs.io.TCPSocket.wait( self, timeoutMs: integer ): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | | `timeoutMs` | `integer` | The caller supplies 0 to 2147483647 milliseconds or omits it for zero. Zero polls without blocking. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when input arrives or the peer disconnects. | | `string` | Returns the reason on failure. Timeout returns false without a reason. | #### tecs.io.TCPSocket:write Instance Queues one chunk for reliable ordered delivery. This accepting the bytes does not mean the peer has received them; use `pendingWrites` or `drain` when that distinction matters. ```teal function tecs.io.TCPSocket.write(self, bytes: string): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | | `bytes` | `string` | The caller supplies at most 16777216 bytes. Larger values raise; an empty string succeeds without queuing data. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true only when the connection accepts the whole chunk. | | `string` | Returns the reason when the first return is false. | #### tecs.io.TCPSocket:writeFrom Instance Queues a buffer range for reliable ordered delivery. The native send queue copies the range before this call returns, so the caller may mutate or close the buffer afterwards. This avoids constructing an intermediate Lua string but does not bypass the queue's ownership copy. ```teal function tecs.io.TCPSocket.writeFrom( self, source: IOBuffer, offset: integer, count: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | | `source` | [`IOBuffer`](/modules/io/#tecs.io.Buffer) | The caller supplies an open [`Buffer`](/modules/io/#tecs.io.Buffer). | | `offset` | `integer` | The caller supplies a zero-based source offset or omits it for zero. | | `count` | `integer` | The caller supplies at most 16777216 bytes or omits it for the remainder. Invalid ranges raise. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the number of bytes accepted. | | `string` | Returns the reason when the first return is nil. | #### tecs.io.TCPSocket:writeView Instance Queues a retained byte view without constructing an intermediate string. The native send queue copies the range before this call returns. ```teal function tecs.io.TCPSocket.writeView( self, source: types.ByteView, offset: integer, count: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TCPSocket` | | | `source` | [`types.ByteView`](/modules/io/#tecs.io.ByteView) | The caller supplies an open [`ByteView`](/modules/io/#tecs.io.ByteView). | | `offset` | `integer` | The caller supplies a zero-based source offset or omits it for zero. | | `count` | `integer` | The caller supplies at most 16777216 bytes or omits it for the remainder. Invalid ranges raise. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the number of bytes accepted. | | `string` | Returns the reason when the first return is nil. | ### tecs.io.UDPPacket record A `UDPPacket` owns one received UDP datagram and its source address. Closing it releases both the address and byte buffer and remains safe to repeat. ```teal record tecs.io.UDPPacket is Closeable address: Address port: integer bytes: IOBuffer close: function(self): boolean, string end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.UDPPacket.address field Read-only. Networking sets `address` when receiving the packet. The packet owns it until `close`. ```teal tecs.io.UDPPacket.address: Address ``` #### tecs.io.UDPPacket.port field Read-only. Networking sets `port` to the remote source port when receiving the packet. ```teal tecs.io.UDPPacket.port: integer ``` #### tecs.io.UDPPacket.bytes field Read-only. Networking sets `bytes` to one caller-owned [`Buffer`](/modules/io/#tecs.io.Buffer) containing the complete datagram. A zero-length datagram uses an empty buffer. `close` releases it. ```teal tecs.io.UDPPacket.bytes: IOBuffer ``` #### tecs.io.UDPPacket:close Instance ```teal function tecs.io.UDPPacket.close(self): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `UDPPacket` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | | | `string` | | ### tecs.io.UDPSocket record A `UDPSocket` sends and receives UDP packets. Closing it releases the socket and remains safe to repeat. ```teal record tecs.io.UDPSocket is Closeable port: integer close: function(self): boolean, string isClosed: function(self): boolean receive: function(self): UDPPacket, string send: function( self, address: Address, port: integer, bytes: string ): boolean, string wait: function(self, timeoutMs: integer): boolean, string end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.UDPSocket.port field Read-only. Networking sets `port` from the value passed to `bind` and never changes it. Zero remains zero. ```teal tecs.io.UDPSocket.port: integer ``` #### tecs.io.UDPSocket:close Instance ```teal function tecs.io.UDPSocket.close(self): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `UDPSocket` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | | | `string` | | #### tecs.io.UDPSocket:isClosed Instance Returns whether `close` has released this socket. ```teal function tecs.io.UDPSocket.isClosed(self): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `UDPSocket` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true after `close`. | #### tecs.io.UDPSocket:receive Instance Takes one packet without waiting. Returns nil with no error when no packet is ready. The packet owns its source address, which the caller closes. ```teal function tecs.io.UDPSocket.receive(self): UDPPacket, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `UDPSocket` | | ##### Returns | Type | Description | | --- | --- | | [`UDPPacket`](/modules/io/#tecs.io.UDPPacket) | Returns one caller-owned complete packet, or nil when none waits or reception fails. | | `string` | Returns the reason only when reception fails. | #### tecs.io.UDPSocket:send Instance Sends one packet without waiting. ```teal function tecs.io.UDPSocket.send( self, address: Address, port: integer, bytes: string ): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `UDPSocket` | | | `address` | [`Address`](/modules/io/#tecs.io.Address) | The caller supplies an open address from this module. | | `port` | `integer` | The caller supplies a remote port from 1 to 65535. | | `bytes` | `string` | The caller supplies one datagram of at most 65507 bytes. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when the network accepts the complete datagram. | | `string` | Returns the reason when the first return is false. | #### tecs.io.UDPSocket:wait Instance Waits up to `timeoutMs` for a packet. Returns false without an error on timeout. This does not consume a packet; call `receive` afterwards. ```teal function tecs.io.UDPSocket.wait( self, timeoutMs: integer ): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `UDPSocket` | | | `timeoutMs` | `integer` | The caller supplies 0 to 2147483647 milliseconds or omits it for zero. Zero polls without blocking. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when at least one packet waits. | | `string` | Returns the reason on failure. Timeout returns false without a reason. | ### tecs.io.WritableStream interface A `WritableStream` opens writers and supplies whole-destination transfers. ```teal interface tecs.io.WritableStream is Stream, Closeable newWriter: function(self): Writer, string writeAll: function(self, bytes: string): integer, string writeBuffer: function( self, buffer: Buffer, offset: integer, count: integer ): integer, string writeView: function( self, view: ByteView, offset: integer, count: integer ): integer, string end ``` #### Interfaces | Interface | | --- | | [`Stream`](/modules/io/#tecs.io.Stream) | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.WritableStream:newWriter Instance Opens a writer with a new cursor. Opening replaces the destination's previous bytes. ```teal function tecs.io.WritableStream.newWriter(self): Writer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `WritableStream` | The writable descriptor. | ##### Returns | Type | Description | | --- | --- | | [`Writer`](/modules/io/#tecs.io.Writer) | Returns a caller-owned writer, or nil when unavailable. | | `string` | Returns the reason when the first return is nil. | #### Examples ```teal local bytes = tecs.io.newBuffer() local destination = bytes:newStream() local writer = assert(destination:newWriter()) assert(writer:write("data")) assert(writer:close()) destination:close() bytes:close() ``` #### tecs.io.WritableStream:writeAll Instance Replaces the destination with a Lua string. The operation opens, finishes, and closes one writer. On a non-replayable stream, this claims its one endpoint. ```teal function tecs.io.WritableStream.writeAll( self, bytes: string ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `WritableStream` | The writable descriptor. | | `bytes` | `string` | The caller supplies the complete binary contents. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes written. | | `string` | Returns the destination's reason when the first return is nil. | #### Examples ```teal local destination = tecs.io.newFileStream("save.bin") local count = destination:writeAll("save data") print(count) destination:close() ``` #### tecs.io.WritableStream:writeBuffer Instance Replaces the destination with a buffer range. The operation opens, finishes, and closes one writer. On a non-replayable stream, this claims its one endpoint. ```teal function tecs.io.WritableStream.writeBuffer( self, buffer: Buffer, offset: integer, count: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `WritableStream` | The writable descriptor. | | `buffer` | [`Buffer`](/modules/io/#tecs.io.Buffer) | The caller keeps the source buffer open and unchanged through the call. | | `offset` | `integer` | The caller supplies a zero-based offset or omits it for zero. | | `count` | `integer` | The caller supplies a byte count or omits it for the remainder. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes written. | | `string` | Returns the destination's reason when the first return is nil. | #### Examples ```teal local bytes = tecs.io.newBuffer("save data") local destination = tecs.io.newFileStream("save.bin") local count = destination:writeBuffer(bytes) print(count) destination:close() bytes:close() ``` #### tecs.io.WritableStream:writeView Instance Replaces the destination with a retained immutable byte range. The operation opens, finishes, and closes one writer. On a non-replayable stream, this claims its one endpoint. The caller keeps `view` open through the call. ```teal function tecs.io.WritableStream.writeView( self, view: ByteView, offset: integer, count: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `WritableStream` | The writable descriptor. | | `view` | [`ByteView`](/modules/io/#tecs.io.ByteView) | The caller supplies an open [`ByteView`](/modules/io/#tecs.io.ByteView). | | `offset` | `integer` | The caller supplies a zero-based offset or omits it for zero. | | `count` | `integer` | The caller supplies a byte count or omits it for the remainder. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the bytes written. | | `string` | Returns the destination's reason when the first return is nil. | ### tecs.io.Writer interface A `Writer` accepts bytes in order and finishes its destination on `close`. ```teal interface tecs.io.Writer is Closeable flush: function(self): boolean, string write: function(self, bytes: string): boolean, string writeFrom: function( self, source: Buffer, offset: integer, count: integer ): integer, string writeView: function( self, source: ByteView, offset: integer, count: integer ): integer, string end ``` #### Interfaces | Interface | | --- | | [`Closeable`](/modules/#tecs.Closeable) | #### tecs.io.Writer:flush Instance Flushes buffered bytes without closing the destination. ```teal function tecs.io.Writer.flush(self): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | The writer to flush. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns whether buffered bytes reached the destination. | | `string` | Returns the destination's reason when the first return is false. | #### Examples ```teal local destination = tecs.io.newFileStream("save.bin") local writer = assert(destination:newWriter()) assert(writer:write("checkpoint")) assert(writer:flush()) assert(writer:close()) destination:close() ``` #### tecs.io.Writer:write Instance Caller-writable. Supplies the operation that writes the next bytes. ```teal function tecs.io.Writer.write(self, bytes: string): boolean, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | The writer receiving the bytes. | | `bytes` | `string` | The binary string to append, including any embedded NUL bytes. | ##### Returns | Type | Description | | --- | --- | | `boolean` | Returns whether every byte reached the destination. | | `string` | Returns the destination's reason when the first return is false. | #### Examples ```teal local bytes = tecs.io.newBuffer() local writer = bytes:newWriter() local wrote , reason = writer:write("data") assert(wrote, reason) assert(writer:close()) print(bytes:getString()) bytes:close() ``` #### tecs.io.Writer:writeFrom Instance Writes a complete buffer range and advances the cursor. ```teal function tecs.io.Writer.writeFrom( self, source: Buffer, offset: integer, count: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | The open writer whose cursor advances. | | `source` | [`Buffer`](/modules/io/#tecs.io.Buffer) | The caller supplies an open source buffer and retains ownership. | | `offset` | `integer` | The caller supplies a zero-based source offset or omits it for zero. | | `count` | `integer` | The caller supplies a byte count or omits it for the remainder. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the complete byte count. | | `string` | Returns the destination's reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newBuffer("header:data") local destination = tecs.io.newBuffer() local writer = destination:newWriter() local count , reason = writer:writeFrom(source, 7, 4) assert(count, reason) assert(writer:close()) print(destination:getString()) source:close() destination:close() ``` #### tecs.io.Writer:writeView Instance Writes a complete immutable-view range and advances the cursor. ```teal function tecs.io.Writer.writeView( self, source: ByteView, offset: integer, count: integer ): integer, string ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | The open writer whose cursor advances. | | `source` | [`ByteView`](/modules/io/#tecs.io.ByteView) | The caller supplies an open source view and retains ownership. | | `offset` | `integer` | The caller supplies a zero-based source offset or omits it for zero. | | `count` | `integer` | The caller supplies a byte count or omits it for the remainder. | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the complete byte count. | | `string` | Returns the destination's reason when the first return is nil. | #### Examples ```teal local source = tecs.io.newBuffer("header:data") local view = source:view(7, 4) local destination = tecs.io.newBuffer() local writer = destination:newWriter() local count , reason = writer:writeView(view) assert(count, reason) assert(writer:close()) print(destination:getString()) view:close() source:close() destination:close() ``` ## Functions ### tecs.io.bind Static Binds a UDP socket. A nil address listens on all local interfaces. ```teal function tecs.io.bind( port: integer, address: Address ): UDPSocket, string ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `port` | `integer` | The caller supplies a local port from 0 to 65535. Zero lets the platform choose one but leaves `UDPSocket.port` at zero. | | `address` | [`Address`](/modules/io/#tecs.io.Address) | The caller supplies a local address or omits it for all interfaces. | #### Returns | Type | Description | | --- | --- | | [`UDPSocket`](/modules/io/#tecs.io.UDPSocket) | Returns a caller-owned socket, or nil on failure. | | `string` | Returns the reason when the first return is nil. | ### tecs.io.connect Static Begins connecting to a resolved address. ```teal function tecs.io.connect( address: Address, port: integer ): FutureType ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `address` | [`Address`](/modules/io/#tecs.io.Address) | The caller supplies an address from this module. A closed address returns an already-failed future. | | `port` | `integer` | The caller supplies a remote port from 1 to 65535. | #### Returns | Type | Description | | --- | --- | | [`FutureType`](/modules/Future/)`<`[`TCPSocket`](/modules/io/#tecs.io.TCPSocket)`>` | Returns a future over a caller-owned connection. `tecs.io.poll` or `Future:wait` settles it. | ### tecs.io.init Static Starts networking for this module. Safe to call repeatedly. `resolve`, `connect`, `listen` and `bind` start it automatically. ```teal function tecs.io.init(): boolean, string ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `boolean` | Returns true when networking runs. | | `string` | Returns the startup reason when the first return is false. | ### tecs.io.listen Static Binds a TCP listener. A nil address listens on all local interfaces. ```teal function tecs.io.listen( port: integer, address: Address ): TCPListener, string ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `port` | `integer` | The caller supplies a local port from 0 to 65535. Zero lets the platform choose one but leaves `TCPListener.port` at zero. | | `address` | [`Address`](/modules/io/#tecs.io.Address) | The caller supplies a local address or omits it for all interfaces. | #### Returns | Type | Description | | --- | --- | | [`TCPListener`](/modules/io/#tecs.io.TCPListener) | Returns a caller-owned listener, or nil on failure. | | `string` | Returns the reason when the first return is nil. | #### Examples ```teal local listener , listenReason = tecs.io.listen(8080) assert(listener, listenReason) local client: tecs.io.TCPSocket = nil local request = "" -- Call this once per frame. It serves one HTTP/1.1 request at a time; a real -- server keeps one request buffer per connected socket. local function pollWebServer() if client == nil then client = listener:accept() return end local chunk , readReason = client:read() if readReason ~= nil then client:close() client = nil request = "" return end if chunk == nil then return end request = request .. chunk if request:find("\r\n\r\n", 1, true) == nil then return end local body = "Hello from Tecs\n" local response = ( "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" .. "Content-Type: text/plain\r\nConnection: close\r\n\r\n%s" ):format(#body, body) local sent , sendReason = client:write(response) assert(sent, sendReason) assert(client:drain(1000)) client:close() client = nil request = "" end pollWebServer() listener:close() ``` ### tecs.io.pending Static Returns the number of pending resolution or connection futures. ```teal function tecs.io.pending(): integer ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `integer` | Returns zero after every operation settles or cancels. | ### tecs.io.poll Static Advances pending I/O in a headless loop without waiting. [`Application`](/modules/Application/) calls this automatically once per host iteration, including while suspended or crashed. A game system, plugin, or update function must not call it. Only a headless program without an application owns a manual call, normally once per loop while `pending()` is greater than zero. ```teal function tecs.io.poll(): integer ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `integer` | Returns how many resolution or connection futures settled during this call, including failures. | #### Examples ```teal while tecs.io.pending() > 0 do tecs.io.poll() end ``` ### tecs.io.resolve Static Begins resolving a hostname. ```teal function tecs.io.resolve(host: string): FutureType
``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `host` | `string` | The caller supplies a DNS name or numeric address of at most 253 bytes. Empty strings and embedded NUL bytes raise. | #### Returns | Type | Description | | --- | --- | | [`FutureType`](/modules/Future/)`<`[`Address`](/modules/io/#tecs.io.Address)`>` | Returns a future over a caller-owned address. `tecs.io.poll` or `Future:wait` settles it. | ### tecs.io.shutdown Static Stops this module's networking instance. It refuses while an address, TCP socket, listener, UDP socket, packet or asynchronous operation remains live. ```teal function tecs.io.shutdown(): boolean, string ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `boolean` | Returns true after shutdown or when networking never started. | | `string` | Returns the live-resource reason when shutdown refuses. | ### tecs.io.transfer Static Transfers every remaining byte between directional endpoints. Lua files satisfy the basic endpoint protocol directly. Tecs readers and writers may additionally provide `readInto` and `writeFrom` fast paths, which this call selects without changing the source code using them. The call borrows both endpoints and closes neither one. ```teal function tecs.io.transfer( source: types.Reader | FILE, destination: types.Writer | FILE ): integer, string ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `source` | [`types.Reader`](/modules/io/#tecs.io.Reader) | FILE | The caller supplies an open reader or Lua file positioned at its first byte to transfer. | | `destination` | [`types.Writer`](/modules/io/#tecs.io.Writer) | FILE | The caller supplies an open writer or Lua file positioned where transferred bytes should begin. | #### Returns | Type | Description | | --- | --- | | `integer` | Returns the number of bytes written. | | `string` | Returns the source or destination reason when the first return is nil. | #### Examples ```teal local source = assert(tecs.io.files.open("save.bin")) local destination = assert(tecs.io.files.open("save.copy", "w")) local count , reason = tecs.io.transfer(source, destination) assert(count, reason) source:close() destination:close() ```