Write numbers, strings, booleans and Vector3s into bytes at exact widths, for a file format or a byte stream.
Make one with BinaryWriter.new(bigEndian?): little-endian unless you ask. Integer writes check the range and that the value is whole, and never wrap. WriteString and WriteBuffer put a 4-byte length first; the Fixed forms write only the bytes. The writer grows as you go, and ToBuffer copies what is written so far, so you can keep writing. A BinaryReader reads it all back.
--!strict-- A save record written as bytes: fixed widths, a counted string, a float.local BinaryWriter = require("@shotsystem/BinaryWriter")
local writer = BinaryWriter.new()
writer:WriteFixedString("SAVE") -- four bytes, no count
writer:WriteU16(3) -- a version
writer:WriteString("Apple") -- a u32 byte count, then the bytes
writer:WriteF32(2.5)
writer:WriteBoolean(true) -- a u32 1: four bytesprint(tostring(writer), writer.Length)
-- the bytes so far as hexlocal bytes = writer:ToBuffer()
local hex = table.create(buffer.len(bytes))
for i = 0, buffer.len(bytes) - 1do
hex[i + 1] = string.format("%02x", buffer.readu8(bytes, i))
endprint(table.concat(hex, " "))
-- the same U16 in each byte orderlocal big = BinaryWriter.new(true)
big:WriteU16(3)
local little = BinaryWriter.new()
little:WriteU16(3)
print("big-endian:", big.BigEndian, buffer.readu8(big:ToBuffer(), 1), "little-endian:", buffer.readu8(little:ToBuffer(), 0))
-- integer widths check their range instead of wrappinglocal ok, why = pcall(function() writer:WriteU8(300) end)
print(ok, (string.gsub(tostring(why), "^.-:%d+: ", "")))