EMILUA(3em)
πΈ EMILUA(3em) β emilua, section 3em, the Emilua reference πΈ
okay so bestie, welcome to emilua(3em) β this is the page for byte_span, and honestly? it's iconic. it's Emilua's Lua execution engine territory, so grab a snack and let's get into it π
β¨ NAME β¨
Emilua β Lua execution engine. that's the whole name section, short and slay π
π¦ DESCRIPTION π¦
π‘ Note:byte_spanis a global, so you don't need torequire()it. it's just there for you, like a good bestie π«Ά
so what even is it? it's a span of bytes. and in Emilua, these are used as network buffers. periodt. β¨
π Note: plugin authors, this one's for you This class is intended for network buffers in a proactor-based network API β and by that we mean true asynchronous IO. a NIC could literally be writing to this memory region while the program is running π like, that's a real thing that can happen. the effect is the same as another thread writing to the same memory region. no cap. if you're writing state machines, do not construct the state machine on top of the memory region that a byte_span points to. it is not safe to store state here, because buggy Lua applications could mutate this area in a racy way π only use the memory region as the result of operations. that's it. that's the rule. a future Emilua release could introduce read-write locks, but as of right now I'm unconvinced of their advantages here π€·ββοΈ
and the lore? it's modeled after Golang's slices β http://blog.golang.org/2011/01/go-slices-usage-and-internals.html β however, the plot twist: 1-indexed access is used π as it should be, lbr.
π FUNCTIONS π
new(length: integer[, capacity: integer]) β byte_spanβ the constructor, the main character π when you leave out the capacity argument, it just defaults to the length you specified. easy.with_zeros(length: integer[, capacity: integer]) β byte_spanβ also a constructor π it initializes capacity bytes with zero (the NUL byte). and if length and capacity are different, then it goes beyond the first length bytes to initialize the whole memory region pointed to by the returnedbyte_spanβ yes, the whole thing, she's thorough π
and if length and capacity are equal? then it's identical to new(length):fill(0). same vibe, same result.
sub(self[, start: integer, end: integer]) β byte_spanβ returns a brand newbyte_spanthat points to a slice of the same memory region. sharing is caring π«Ά
start and end are optional, obvi β they default to 1 and the byte_span's length respectively.
fun fact: we can grow a byte_span to its capacity by slicing it again. glow-up behavior, honestly β¨
β οΈ invalid ranges β like start below1, abyte_spanrunning beyond its capacity, negative indexes, the usual suspects β will raiseEINVAL. no exceptions. well, except that one.
first(self, count: integer) β byte_spanβ returns a newbyte_spanthat points to the first count bytes of the same memory region. front of the line energy πlast(self, count: integer) β byte_spanβ returns a newbyte_spanthat points to the last count bytes of the same memory region. saving the best for last, literally πcopy(self, src: byte_span|string) β integerβ copies src into self π₯ and it returns the number of elements copied, so you always know what happened.
copying between slices of different lengths is supported β it'll copy only up to the smaller number of elements. and better yet, it can handle source and destination spans that share the same underlying memory, handling overlapping spans correctly π like, overlap-aware. we love a considerate function.
append() β byte_spanβ two overloads, bestie, keep up π
function append(self, ...: byte_span|string|nil) -> byte_span (1)
function append(...: byte_span|string|nil) -> byte_span (2)
returns a new byte_span by appending the trailing arguments into self. and here's the iykyk part: if self's capacity is enough to hold all the data, the underlying memory is modified in place. otherwise, the returned byte_span will point to newly allocated memory[1] π§
for the second overload β that's the non-member function β a new byte span is created from scratch. fresh start, new me πΈ
fill(self, byte: integer) β byte_spanβ as in C'smemset(), it fills the memory area pointed to by self with byte. and it returns self, because of course it does, she's generous π
π FUNCTIONS (STRING ALGORITHMS) π
quick vibe check before we start: these functions operate in terms of octets/bytes β kinda like an 8-bit ASCII situation β and they have no concept of UTF-8 encoding. so don't come here expecting unicode magic, she's byte-pilled and that's the tea π
starts_with(self, prefix: string|byte_span) β booleanβ returns whether self begins with prefix. yes/no, that's the whole gig.ends_with(self, suffix: string|byte_span) β booleanβ returns whether self ends with suffix. every story needs a good ending πfind(self, tgt: string|byte_span[, start: integer]) β integer|nilβ finds the first substring equal to tgt and returns its index, ornilif it's not found. no luck?nil. bye πrfind(self, tgt: string|byte_span[, end_: integer]) β integer|nilβ finds the last substring equal to tgt and returns its index, ornilif not found. reverse search, reverse card πfind_first_of(self, strlist: string|byte_span[, start: integer]) β integer|nilβ finds the first octet equal to any of the octets within strlist and returns its index, ornilif not found. it's a whole cast of characters and she's looking for the first one on set β¨find_last_of(self, strlist: string|byte_span[, end_: integer]) β integer|nilβ finds the last octet equal to any of the octets within strlist and returns its index, ornilif not found. last one to leave the party πfind_first_not_of(self, strlist: string|byte_span[, start: integer]) β integer|nilβ finds the first octet not equal to any of the octets within strlist and returns its index, ornilif not found. the not-like-the-other-girls search πfind_last_not_of(self, strlist: string|byte_span[, end: integer]) β integer|nilβ finds the last octet not equal to any of the octets within strlist and returns its index, ornilif not found. same energy, other end π¦trimmed(self[, lws: string|byte_span = " \f\n\r\t\v"]) β byte_spanβ returns a slice from self that doesn't start nor end with any octet from lws. the default lws is" \f\n\r\t\v"β like, space, form feed, newline, carriage return, tab and vertical tab. she's neat, she's tidy, she's trimmed π§inplace_lower(self)β converts every upper ASCII character from self to its lower version. lowercase era πinplace_upper(self)β converts every lower ASCII character from self to its upper version. SHOUTING ERA π£π₯
FUNCTIONS (PRIMITIVE TYPES SERIALIZATION)
okay bestie, these ones are the byte-nerd girlies π
they operate in terms of bytes, and they're endianness-aware (yes they know the difference, they're not messy). Hand them a byte_span of the wrong size and they will throw EINVAL at you, no cap. The cute part? Data doesn't need to be aligned β¨ lowkey iconic.
the unsigned getters πΌ
get_u16be(self) β integerβ readsself(has to be 2 bytes long, she checks) as an unsigned 16-bit integer in big endian order and gives you the result π«Άget_u16le(self) β integerβ same 2-byte requirement, but this time it's an unsigned 16-bit integer in little endian order, and you get the result backget_u24be(self) β integerβself(3 bytes long, periodt) interpreted as an unsigned 24-bit integer, big endian order, returns the result β¨get_u24le(self) β integerβ 3 bytes ofself, unsigned 24-bit integer, little endian order, result returnedget_u32be(self) β integerβself(4 bytes long, exactly) as an unsigned 32-bit integer, big endian order, and boom, the result πΈget_u32le(self) β integerβ 4 bytes long, unsigned 32-bit integer, little endian order, result returnedget_u40be(self) β integerβ 5 bytes long, unsigned 40-bit integer, big endian order, result returned π¦get_u40le(self) β integerβ 5 bytes long, unsigned 40-bit integer, little endian order, result returnedget_u48be(self) β integerβ 6 bytes long, unsigned 48-bit integer, big endian order, result returned πget_u48le(self) β integerβ 6 bytes long, unsigned 48-bit integer, little endian order, result returned
the signed getters π
get_i8(self) β integerβself(must be 1 byte long) interpreted as a signed 8-bit integer, result returned
π note β get_u8() doesnβt exist as you can just index instead.
get_i16be(self) β integerβ 2 bytes long, signed 16-bit integer, big endian order, result returnedget_i16le(self) β integerβ 2 bytes long, signed 16-bit integer, little endian order, result returnedget_i24be(self) β integerβ 3 bytes long, signed 24-bit integer, big endian order, result returnedget_i24le(self) β integerβ 3 bytes long, signed 24-bit integer, little endian order, result returnedget_i32be(self) β integerβ 4 bytes long, signed 32-bit integer, big endian order, result returned π₯get_i32le(self) β integerβ 4 bytes long, signed 32-bit integer, little endian order, result returnedget_i40be(self) β integerβ 5 bytes long, signed 40-bit integer, big endian order, result returnedget_i40le(self) β integerβ 5 bytes long, signed 40-bit integer, little endian order, result returnedget_i48be(self) β integerβ 6 bytes long, signed 48-bit integer, big endian order, result returnedget_i48le(self) β integerβ 6 bytes long, signed 48-bit integer, little endian order, result returned
the float getters π§
get_f32be(self) β numberβself(4 bytes long) read as a 32-bit floating point number, big endian order, result returned β¨get_f32le(self) β numberβ 4 bytes long, 32-bit floating point number, little endian order, result returnedget_f64be(self) β numberβ 8 bytes long, 64-bit floating point number, big endian order, result returned πget_f64le(self) β numberβ 8 bytes long, 64-bit floating point number, little endian order, result returned
the unsigned setters π
bestie, these ones write into the stored byte sequence instead of reading it. same energy, opposite direction.
set_u16be(self, n: integer)β sets the stored byte sequence (must be 2 bytes long) to represent the unsigned 16-bit integern, big endian order π«Άset_u16le(self, n: integer)β 2 bytes long, unsigned 16-bit integern, little endian orderset_u24be(self, n: integer)β 3 bytes long, unsigned 24-bit integern, big endian order β¨set_u24le(self, n: integer)β 3 bytes long, unsigned 24-bit integern, little endian orderset_u32be(self, n: integer)β 4 bytes long, unsigned 32-bit integern, big endian order πΈset_u32le(self, n: integer)β 4 bytes long, unsigned 32-bit integern, little endian orderset_u40be(self, n: integer)β 5 bytes long, unsigned 40-bit integern, big endian order π¦set_u40le(self, n: integer)β 5 bytes long, unsigned 40-bit integern, little endian orderset_u48be(self, n: integer)β 6 bytes long, unsigned 48-bit integern, big endian order πset_u48le(self, n: integer)β 6 bytes long, unsigned 48-bit integern, little endian order
the signed setters π
set_i8(self, n: integer)β sets the stored byte sequence (must be 1 bytes long) to represent the signed byten
π note β set_u8() doesnβt exist as you can just index instead.
set_i16be(self, n: integer)β 2 bytes long, signed 16-bit integern, big endian orderset_i16le(self, n: integer)β 2 bytes long, signed 16-bit integern, little endian orderset_i24be(self, n: integer)β 3 bytes long, signed 24-bit integern, big endian orderset_i24le(self, n: integer)β 3 bytes long, signed 24-bit integern, little endian orderset_i32be(self, n: integer)β 4 bytes long, signed 32-bit integern, big endian order π₯set_i32le(self, n: integer)β 4 bytes long, signed 32-bit integern, little endian orderset_i40be(self, n: integer)β 5 bytes long, signed 40-bit integern, big endian orderset_i40le(self, n: integer)β 5 bytes long, signed 40-bit integern, little endian orderset_i48be(self, n: integer)β 6 bytes long, signed 48-bit integern, big endian orderset_i48le(self, n: integer)β 6 bytes long, signed 48-bit integern, little endian order
the float setters π
set_f32be(self, n: number)β sets the stored byte sequence (must be 4 bytes long) to represent the 32-bit floating point numbern, big endian order β¨set_f32le(self, n: number)β 4 bytes long, 32-bit floating point numbern, little endian orderset_f64be(self, n: number)β 8 bytes long, 64-bit floating point numbern, big endian order πset_f64le(self, n: number)β 8 bytes long, 64-bit floating point numbern, little endian order
β¨ PROPERTIES β¨
capacity : integer
the capacity. that's it. that's the whole property π she said "I contain multitudes but today I'm just gonna tell you how much I can hold".
π¦ METAMETHODS π¦
she's got the full set, bestie, the whole personality:
__tostring()__len()__index()__newindex()__eq()
π note β you can index the spans by numerical valued keys, and the numerical (ASCII) value for the underlying byte will be returned (or assigned on __newindex()). iykyk π«Ά
πΈ NOTES πΈ
- [1] Allocation strategy (the new
byte_span's capacity) is left unspecified and may change among Emilua releases. so don't build your whole life plan around it, fr fr π
and that's the whole page, bestie!! π mwah π
this was Emilua 0.12.1-nixpkgs1, dated 1980-01-01, and yeah β it's EMILUA(3em), the one and only π
π¦ where this page comes from
- nixos 26.05: emilua 0.12.1
- nixpkgs unstable: emilua 0.12.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms