Buffer
Low-level serialization module for encoding/decoding Roblox data types to binary buffers
Core Encoding
Buffer.Encode
Buffer.Encode(value: any): (buffer?, string?)Serialize any supported Roblox value to buffer.
Parameters:
value: Any encodable value (primitives, Roblox types, tables)
Returns:
buffer?: Encoded buffer on successstring?: Error message on failure
Example:
local Buffer = require(ReplicatedStorage.Wisp.modules.Buffer)
local buf, err = Buffer.Encode({
position = Vector3.new(10, 5, 0),
health = 100,
name = "Player1"
})
if buf then
print("Encoded size:", buffer.len(buf))
endBuffer.Decode
Buffer.Decode(b: buffer): anyDeserialize buffer to original value. Throws on error.
Parameters:
b: Buffer to decode
Returns:
any: Decoded value
Example:
local value = Buffer.Decode(buf)
print(value.position) -- Vector3(10, 5, 0)Buffer.SafeDecode
Buffer.SafeDecode(b: buffer): (any, string?)Safe decode with error handling (doesn't throw).
Parameters:
b: Buffer to decode
Returns:
any: Decoded value on success,nilon failurestring?: Error message on failure
Example:
local value, err = Buffer.SafeDecode(buf)
if err then
warn("Decode failed:", err)
else
print("Decoded:", value)
endAdvanced Encoding
Buffer.EncodeWith
Buffer.EncodeWith(value: any, priority: Priority): (buffer?, string?)Encode with LRU cache tier for frequently encoded values.
Parameters:
value: Value to encodepriority:"hot"(100-slot) |"warm"(500-slot) |"cold"(2000-slot)
Returns:
buffer?: Encoded buffer on successstring?: Error message on failure
Example:
-- Frequently changing data
local buf = Buffer.EncodeWith(playerPosition, "hot")
-- Static config
local buf = Buffer.EncodeWith(gameConfig, "cold")Buffer.EncodeCompressed
Buffer.EncodeCompressed(value: any, mode: CompressionMode?, hint: DataHint?): (buffer?, string?)Encode with compression.
Parameters:
value: Value to encodemode(optional):"auto"|"lz4"|"deflate"|"zstd"|"none"(default:"auto")hint(optional):"text"|"numbers"|"spatial"|"binary"|"auto"(default:"auto")
Returns:
buffer?: Compressed buffer on successstring?: Error message on failure
Example:
-- Large text data
local buf = Buffer.EncodeCompressed(longText, "deflate", "text")
-- Numeric arrays
local buf = Buffer.EncodeCompressed(heights, "lz4", "numbers")
-- Auto-detect best compression
local buf = Buffer.EncodeCompressed(data, "auto")Buffer.EncodeSafe
Buffer.EncodeSafe(value: any): (buffer?, string?)Encode with cyclic reference detection.
Parameters:
value: Value to encode
Returns:
buffer?: Encoded buffer on successstring?: Error message (including cyclic reference details)
Example:
local tbl = {a = 1}
tbl.self = tbl -- Cyclic reference
local buf, err = Buffer.EncodeSafe(tbl)
-- err: "cyclic reference at root.self"Schema Encoding
Buffer.ValidateSchema
Buffer.ValidateSchema(schema: Schema): (boolean, string?)Validate schema structure before use.
Parameters:
schema: Schema definition to validate
Returns:
boolean:trueif valid,falseif invalidstring?: Error message on failure
Example:
local ok, err = Buffer.ValidateSchema(mySchema)
if not ok then
error("Invalid schema: " .. err)
endBuffer.EncodeSchema
Buffer.EncodeSchema(value: {[string]: any}, schema: Schema): (buffer?, string?)Encode using schema (field names omitted from wire for efficiency).
Parameters:
value: Table with fields matching schemaschema: Schema definition
Returns:
buffer?: Encoded buffer on successstring?: Error message on failure
Example:
local schema = {
fields = {
{name = "hp", type = "number"},
{name = "mp", type = "number"}
}
}
local buf = Buffer.EncodeSchema({hp = 100, mp = 50}, schema)
-- Wire: [version:1] [100] [50] (no field names!)Buffer.DecodeSchema
Buffer.DecodeSchema(b: buffer, schema: Schema): ({[string]: any}?, string?)Decode schema-encoded buffer.
Parameters:
b: Schema-encoded bufferschema: Schema definition (must match encoding schema)
Returns:
{[string]: any}?: Decoded table on successstring?: Error message on failure
Example:
local data, err = Buffer.DecodeSchema(buf, schema)
if data then
print(data.hp, data.mp) -- 100, 50
endDelta Encoding
Buffer.EncodeDelta
Buffer.EncodeDelta(old: {[string]: any}, new: {[string]: any}): (buffer?, string?)Encode only changed fields (table diff).
Parameters:
old: Previous table statenew: New table state
Returns:
buffer?: Delta patch buffer on successstring?: Error message on failure
Example:
local old = {hp = 100, mp = 50, xp = 0}
local new = {hp = 80, mp = 50, xp = 10}
local delta = Buffer.EncodeDelta(old, new)
-- Wire: only {hp = 80, xp = 10}Buffer.ApplyDelta
Buffer.ApplyDelta(target: {[string]: any}, delta: buffer): {[string]: any}Apply delta patch to table.
Parameters:
target: Current table to patchdelta: Delta buffer fromEncodeDelta
Returns:
{[string]: any}: Patched table
Example:
local current = {hp = 100, mp = 50, xp = 0}
current = Buffer.ApplyDelta(current, delta)
print(current.hp, current.xp) -- 80, 10Buffer.EncodeSchemaDelta
Buffer.EncodeSchemaDelta(old: {[string]: any}, new: {[string]: any}, schema: Schema): (buffer?, string?)Schema-aware delta encoding using bitmask (max 32 fields).
Parameters:
old: Previous table statenew: New table stateschema: Schema definition
Returns:
buffer?: Delta buffer on successstring?: Error message on failure
Example:
local statsSchema = {
fields = {
{name = "hp", type = "number"},
{name = "mp", type = "number"},
{name = "xp", type = "number"}
}
}
local delta = Buffer.EncodeSchemaDelta(oldStats, newStats, statsSchema)
-- Wire: [version:1] [bitmask:u32] [changed_values...]Buffer.DecodeSchemaDelta
Buffer.DecodeSchemaDelta(b: buffer, current: {[string]: any}, schema: Schema): ({[string]: any}?, string?)Decode schema delta and merge with current state.
Parameters:
b: Delta buffercurrent: Current table state to merge intoschema: Schema definition
Returns:
{[string]: any}?: Merged table on successstring?: Error message on failure
Example:
local current = {hp = 100, mp = 50, xp = 0}
local updated, err = Buffer.DecodeSchemaDelta(deltaBuffer, current, statsSchema)
if updated then
print(updated.hp) -- Updated value
endMigration
Buffer.MigrateSchema
Buffer.MigrateSchema(
data: {[string]: any},
fromVersion: number,
toVersion: number,
migrations: MigrationMap
): ({[string]: any}?, string?)Upgrade data through version chain.
Parameters:
data: Data at old versionfromVersion: Current data versiontoVersion: Target versionmigrations: Map of version upgrade functions
Returns:
{[string]: any}?: Upgraded data on successstring?: Error message on failure
Example:
local migrations = {
[1] = function(data) -- v1 → v2
data.newField = data.oldField * 2
data.oldField = nil
return data
end,
[2] = function(data) -- v2 → v3
data.anotherField = true
return data
end
}
local upgraded = Buffer.MigrateSchema(
oldData, -- version 1
1, -- from
3, -- to
migrations
)Instance Encoding
Buffer.RegisterInstance
Buffer.RegisterInstance(instance: Instance, id: number)Register instance for encoding. Must be called before encoding instances.
Parameters:
instance: Instance to registerid: Unique ID (u32, 0-4,294,967,295)
Example:
local workspace = game.Workspace
Buffer.RegisterInstance(workspace, 1)
local buf = Buffer.Encode(workspace)
-- Wire: [T_INSTANCE:1] [id:4] → total 5 bytesBuffer.UnregisterInstance
Buffer.UnregisterInstance(instance: Instance)Remove instance from registry.
Parameters:
instance: Instance to unregister
Example:
Buffer.UnregisterInstance(workspace)Custom Types
Buffer.RegisterType
Buffer.RegisterType(id: number, ct: CustomType)Register custom encoder/decoder for user-defined types.
Parameters:
id: Type ID (0-254)ct: CustomType withcheck,encode,decodefunctions
Example:
local MyType = {}
MyType.__index = MyType
function MyType.new(x, y)
return setmetatable({x = x, y = y}, MyType)
end
Buffer.RegisterType(1, {
check = function(v)
return getmetatable(v) == MyType
end,
encode = function(value, offset)
buffer.writef32(BB, offset, value.x)
buffer.writef32(BB, offset + 4, value.y)
return offset + 8, nil
end,
decode = function(b, offset)
local x = buffer.readf32(b, offset)
local y = buffer.readf32(b, offset + 4)
return MyType.new(x, y), offset + 8
end
})
-- Now you can encode/decode MyType instances
local obj = MyType.new(10, 20)
local buf = Buffer.Encode(obj)Utilities
Buffer.EncodedSize
Buffer.EncodedSize(value: any): (number?, string?)Calculate encoded size without allocating buffer.
Parameters:
value: Value to measure
Returns:
number?: Size in bytes on successstring?: Error message on failure
Example:
local size = Buffer.EncodedSize(largeTable)
print("Will encode to", size, "bytes")Buffer.Invalidate
Buffer.Invalidate(value: any)Remove value from all LRU caches (hot/warm/cold).
Parameters:
value: Value to invalidate
Example:
-- Config changed, invalidate cache
Buffer.Invalidate(gameConfig)Buffer.SetCompressionDict
Buffer.SetCompressionDict(dict: buffer)Set Deflate dictionary for domain-specific compression boost.
Parameters:
dict: Dictionary buffer (max 32KB)
Example:
local dict = buffer.fromstring("common game words player health mana...")
Buffer.SetCompressionDict(dict)Buffer.Visualize
Buffer.Visualize(b: buffer): stringRender buffer contents as human-readable tag list.
Parameters:
b: Buffer to visualize
Returns:
string: Tag listing with offsets
Example:
print(Buffer.Visualize(buf))
-- [0000] 11 TABLE
-- [0001] 03 U8
-- [0002] 64 (value: 100)Buffer.VisualizeEntropy
Buffer.VisualizeEntropy(b: buffer): stringRender entropy distribution for compression analysis.
Parameters:
b: Buffer to analyze
Returns:
string: ASCII entropy visualization
Example:
print(Buffer.VisualizeEntropy(buf))
-- [::::----====++++####]
-- Low entropy (left) = compressible
-- High entropy (right) = already compressed/randomBuffer.DetectDataClass
Buffer.DetectDataClass(b: buffer): DataHintDetect data type from first tag byte.
Parameters:
b: Buffer to analyze
Returns:
DataHint:"numbers"|"text"|"spatial"|"binary"|"already_compressed"
Example:
local hint = Buffer.DetectDataClass(buf)
if hint == "text" then
print("Use Deflate compression")
elseif hint == "numbers" then
print("Use LZ4 compression")
endBuffer.ToJSON
Buffer.ToJSON(b: buffer): (string?, string?)Convert buffer to JSON string.
Parameters:
b: Buffer to convert
Returns:
string?: JSON string on successstring?: Error message on failure
Example:
local json, err = Buffer.ToJSON(buf)
if json then
print(json) -- {"hp":100,"mp":50}
endBuffer.FromJSON
Buffer.FromJSON(json: string): (buffer?, string?)Parse JSON and encode to buffer.
Parameters:
json: JSON string to parse
Returns:
buffer?: Encoded buffer on successstring?: Error message on failure
Example:
local buf, err = Buffer.FromJSON('{"hp":100,"mp":50}')
if buf then
local data = Buffer.Decode(buf)
print(data.hp) -- 100
endAsync Operations
Buffer.EncodeAsync
Buffer.EncodeAsync(value: any): AsyncHandleEncode large tables without blocking (yields every 256 fields).
Parameters:
value: Value to encode (typically large table)
Returns:
AsyncHandle: Handle with:Await()and:Cancel()methods
Example:
local handle = Buffer.EncodeAsync(hugeTable)
-- Wait for completion
local buf, err = handle:Await()
-- Or cancel if needed
handle:Cancel()Buffer.DecodeAsync
Buffer.DecodeAsync(b: buffer): AsyncHandleAsync decode for large buffers.
Parameters:
b: Buffer to decode
Returns:
AsyncHandle: Handle with:Await()and:Cancel()methods
Example:
local handle = Buffer.DecodeAsync(largeBuffer)
local value, err = handle:Await()Buffer.CreateStream
Buffer.CreateStream(): StreamCreate write-only stream for incremental encoding.
Returns:
Stream: Stream with:write()and:finalize()methods
Example:
local stream = Buffer.CreateStream()
stream:write(value1)
stream:write(value2)
stream:write(value3)
local buf = stream:finalize()
print("Encoded", buffer.len(buf), "bytes")Type Constructors
Schema validation helpers accessed via Buffer.Types.
Types.any()
Types.any(): SchemaTypeAccept any value (no validation).
Returns:
SchemaType: Type validator
Example:
{name = "data", type = Types.any()}Types.exact()
Types.exact(typeofStr: string): SchemaTypeExact typeof() match.
Parameters:
typeofStr: Expected typeof() string
Returns:
SchemaType: Type validator
Example:
{name = "part", type = Types.exact("Instance")}Types.number()
Types.number(): SchemaTypeAny number (including NaN, Infinity).
Returns:
SchemaType: Type validator
Example:
{name = "score", type = Types.number()}Types.numberRange()
Types.numberRange(min: number, max: number): SchemaTypeNumber within range [min, max].
Parameters:
min: Minimum value (inclusive)max: Maximum value (inclusive)
Returns:
SchemaType: Type validator
Example:
{name = "health", type = Types.numberRange(0, 100)}
{name = "temperature", type = Types.numberRange(-273.15, 1000)}Types.integer()
Types.integer(): SchemaTypeInteger (no decimals).
Returns:
SchemaType: Type validator
Example:
{name = "level", type = Types.integer()}Types.string()
Types.string(maxLen: number?): SchemaTypeString with optional max length.
Parameters:
maxLen(optional): Maximum string length
Returns:
SchemaType: Type validator
Example:
{name = "username", type = Types.string(20)}
{name = "description", type = Types.string()} -- No limitTypes.boolean()
Types.boolean(): SchemaTypeBoolean value.
Returns:
SchemaType: Type validator
Example:
{name = "isActive", type = Types.boolean()}Types.array()
Types.array(elementType: SchemaType, maxLen: number?): SchemaTypeArray (sequence table) with typed elements.
Parameters:
elementType: Type validator for array elementsmaxLen(optional): Maximum array length
Returns:
SchemaType: Type validator
Example:
{
name = "scores",
type = Types.array(Types.integer(), 100)
}
{
name = "players",
type = Types.array(Types.string(20))
}Types.union()
Types.union(...: SchemaType): SchemaTypeValue must match one of the types.
Parameters:
...: Variable number of SchemaType validators
Returns:
SchemaType: Type validator
Example:
{
name = "value",
type = Types.union(
Types.number(),
Types.string()
)
}Types.optional()
Types.optional(inner: SchemaType): SchemaTypeAllow nil or inner type.
Parameters:
inner: Type validator for non-nil values
Returns:
SchemaType: Type validator
Example:
{
name = "nickname",
type = Types.optional(Types.string(30))
}Types.literal()
Types.literal(...: any): SchemaTypeValue must be one of the literals.
Parameters:
...: Variable number of literal values
Returns:
SchemaType: Type validator
Example:
{
name = "team",
type = Types.literal("Red", "Blue", "Green")
}
{
name = "status",
type = Types.literal("pending", "active", "completed")
}