This module is internal. It is documented so you can reason about how Flux behaves
and performs, but it is not re-exported from the root
Flux module and its surface
may change without notice. For everyday use, reach for Flux.Server
and Flux.Client.--!strict --!native --!optimize 2, and every buffer
and bit32 function is localised at the top of the module.
Types
buffer
The backing store. Reassigned on the writer when it grows.
number
Current byte offset. Advanced by every read and write.
number
On a writer, the allocated capacity. On a reader, the buffer’s actual length, used as the loop
bound when decoding a batch.
{ Instance }
The side array carrying instance references out of band.
{ [Instance]: number }
Writer only. Reverse lookup for deduplicating repeat references.
Lifecycle
CreateWriter / CreateReader
capacity defaults to ALLOC_SIZE (4096) and is
ignored when a pooled writer is available.
FreeWriter / FreeReader
FreeWriter zeroes the cursor and clears both
instance tables. A writer whose capacity exceeded MAX_REUSE_SIZE (64 KB) is discarded rather
than pooled.
Finalize
cursor bytes into an exactly-sized buffer and clones the instance array.
Returns both, ready to pass to a remote.
The copy is necessary because the writer’s backing buffer is about to be reused. This is the
only allocation steady-state Flux performs, and it is unavoidable, since the remote needs a
buffer it owns.
Growth
Write* checks
cursor + size > len first, so overflow is impossible.
Primitives
Each pair reads and writes a fixed width, advancing the cursor by that many bytes.Varints
Strings and buffers
WriteString uses buffer.writestring, so
binary content is safe and lengths are in bytes rather than characters.
Roblox datatypes
WriteUDim, WriteUDim2, WriteRegion3, WriteBrickColor, WriteTweenInfo and
WriteBuffer are reachable only through WriteAny, because the Types
namespace has no constructor for them. Call them directly from a custom type if you need them
under a schema.Instance handling
WriteInstance appends to w.insts, or reuses the existing w.instIndex[v], and writes a
varint index. Nothing about the instance enters the buffer.
Enum caching
WriteEnum memoises tostring(item) in EnumCache, and ReadEnum memoises the reverse in
StringToEnumCache, resolving cache misses by splitting on . and indexing Enum. An
unresolvable name returns nil rather than erroring.
Packet header
63 as an escape and follows with a varint.
See Packets.
Dynamic encoder
typeof(v). Used for every
unschematised event argument and for all invoke traffic.
Integer narrowing
Integer narrowing
Integers are written at the narrowest width that fits:
u8, u16 or u32 when
non-negative, i16 or i32 when negative, and f64 otherwise. WriteI8 is never
selected, so small negatives cost two bytes.Table classification
Table classification
A single
pairs walk decides array against dictionary, breaking on the first key that is not
an integer in 1..n. Arrays get TYPE_ARRAY plus a varint count; dictionaries get
TYPE_TABLE and alternating key and value pairs terminated by a nil key.Circular references overflow
Circular references overflow
Recursion has no visited-set and no depth limit, so a table referencing itself recurses until
the Luau stack overflows.
Unsupported types degrade silently
Unsupported types degrade silently
An unrecognised
typeof logs warn("Unsupported type: " .. t) on the sender and writes
TYPE_NIL. The receiver gets nil with no indication anything went wrong, and the argument
count still matches.Pack / Unpack
WriteAny values. Unpack returns them as an
array.
Public but unused by Flux itself, because the
Server and Client modules write their argument
counts through the packet header instead. Available if you are building your own framing on top
of Buffers.Constants
4 KB comfortably holds a typical frame of traffic, which is why steady-state Flux performs no
buffer allocation. Writers cycle between the pool and the bridge, and only
Finalize
allocates.Next steps
Custom types
Using these primitives in a schema entry.
Type tags
Every tag and its byte cost.
