Buffer
local Buffer = Quark.Import("Buffer")
A buffer is a mutable byte array for binary data: network packets, file formats, bulk numbers for native functions. Every Quark function that takes binary data (hashing, encryption, encoding, ...) accepts a buffer as well as a string.
Reading outside of the buffer raises an error, writing past the end grows the buffer and fills the gap with zeros. Buffers are limited to 256 MiB.
Index base
Offsets are zero based by default, as in binary format specifications. A package that prefers Lua style one based offsets switches for itself, other packages are not affected:
Buffer.SetIndexBase(1)
| Base | First byte | Ranges (start, finish) | Like |
|---|---|---|---|
0 (default) | 0 | finish is exclusive | DataView / slice in JavaScript |
1 | 1 | finish is inclusive | string.sub |
Every buffer takes the base of the package creating it and keeps it, buffers from Slice and Clone take the base of their source. buffer:SetIndexBase(base) changes the base of a single buffer, for example one received from another package, and returns the buffer. Buffer.GetIndexBase() and buffer:GetIndexBase() return the current setting.
Creating buffers
| Function | |
|---|---|
Buffer.New(size?, fill?) | buffer of size bytes (default 0), filled with fill (default 0) |
Buffer.FromString(data) | copy of a string or buffer |
Buffer.FromArray(type, values) | numbers encoded one after another |
Buffer.Concat(parts) | strings and buffers joined into a new buffer |
Buffer.IsBuffer(value) | whether the value is a buffer |
Buffer.SetIndexBase(base) / Buffer.GetIndexBase() | index base for buffers created by the calling package |
Number types
u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, little endian by default. Append be for big endian or le to be explicit: u32be, i16le. Writing a value that does not fit into the type (for example 256 as u8, or 1.5 as an integer type) raises an error. u64 values above the Lua integer range are read as floats.
Methods
| Method | |
|---|---|
:Read(type, offset) / :Write(type, offset, value) | single number |
:ReadArray(type, offset, count) / :WriteArray(type, offset, values) | several numbers |
:Append(type, value) | write a number at the end, returns the buffer for chaining |
:ReadString(offset, length?) / :WriteString(offset, data) | raw bytes, length defaults to the rest of the buffer |
:AppendString(data) | append raw bytes, returns the buffer for chaining |
:GetBytes(start?, finish?) | contents as a Lua string, see Index base for ranges |
:Slice(start?, finish?) | copy of a range as a new buffer |
:CopyFrom(source, offset?, source_start?, source_finish?) | copy from a string or buffer into this buffer |
:Fill(byte, start?, finish?) | set a range to one byte value |
:Resize(size, fill?) | grow or shrink |
:GetSize() or #buffer | size in bytes |
:ToHex() | lowercase hex |
:Equals(other) or == | byte wise comparison |
:Clone() | independent copy |
:SetIndexBase(base) / :GetIndexBase() | index base of this buffer |
local packet = Buffer.New()
:Append("u8", 1)
:Append("u16be", player_id)
:Append("f32", health)
:AppendString(name)
local kind = packet:Read("u8", 0)
local id = packet:Read("u16be", 1)
Views
buffer:View(type, offset?, count?) looks at the buffer as an array of numbers of one type, like "f32" or "u16be". offset is where the view starts (the beginning), count the number of values (as many as fit). Views read and write the buffer directly, changes are visible through both.
local heights = noise:Fill(256, 256)
local view = heights:View("f32le")
local highest, position = view:Max()
view[view:GetBuffer():GetIndexBase()] = 0
Values are indexed like the buffer, starting at its index base. view[index] returns nil outside of the view and assigning raises an error, #view is the number of values.
| Method | |
|---|---|
:Get(index), :Set(index, value) | single values, errors outside of the view |
:ToTable(), :FromTable(list) | all values as a list / write a list from the start |
:Fill(value) | set every value |
:Scale(factor, offset?) | multiply every value and add offset, integers are rounded |
:Sum(), :Average() | sum and average, Average is nil for empty views |
:Min(), :Max() | smallest / largest value and its index, nil for empty views |
:Sort(descending?) | sort the values in place |
:GetCount(), :GetType(), :GetBuffer() |
Bulk operations run natively and are much faster than looping in Lua over large buffers, for example to analyze a generated height map. A view notices when its buffer becomes too small after Resize and raises an error, create a new view then.