Read bytes back as numbers, strings and Vector3s, one value at a time, even while more bytes are still arriving.
BinaryReader.new(data, offset?, bigEndian?) reads a buffer in place, or a string. Reading past the end errors with the offset and moves nothing, so a stream reader reads a length, checks Contains, and Seeks back when the frame isn't complete yet. Append adds the bytes that came later. It reads what a BinaryWriter writes, width for width.
--!strict-- Reading a save record back: each read moves Offset on; a read past the end errors and moves nothing.local BinaryWriter = require("@shotsystem/BinaryWriter")
local BinaryReader = require("@shotsystem/BinaryReader")
local writer = BinaryWriter.new()
writer:WriteFixedString("SAVE")
writer:WriteU16(3)
writer:WriteString("Apple")
writer:WriteF32(2.5)
local reader = BinaryReader.new(writer:ToBuffer())
print(tostring(reader))
print("tag:", reader:ReadFixedString(4), "version:", reader:ReadU16())
print("name:", reader:ReadString(), "price:", reader:ReadF32())
print("offset:", reader.Offset, "length:", reader.Length, "remaining:", reader.Remaining)
reader:Seek(4)
print("version again:", reader:ReadU16())
reader:Skip(9) -- the name's count and its five bytesprint("price again:", reader:ReadF32())
local ok, why = pcall(function() reader:ReadU8() end)
print(ok, (string.gsub(tostring(why), "^.-:%d+: ", "")), reader.Offset)
-- a stream: Contains says whether the next bytes have arrived, Append adds the ones that came laterlocal stream = BinaryReader.new("\5\0\0\0Ap")
print("whole name here:", stream:Contains(4 + 5))
stream:Append("ple")
print("after Append:", stream:Contains(4 + 5), stream:ReadString())
print("big-endian:", BinaryReader.new("\0\7", 0, true):ReadU16())
What it printedOutput
BinaryReader(offset 0 of 19 bytes)
tag: SAVE version: 3
name: Apple price: 2.5
offset: 19 length: 19 remaining: 0
version again: 3
price again: 2.5
false BinaryReader: ReadU8 needs 1 byte at offset 19, 0 left 19
whole name here: false
after Append: true Apple
big-endian: 7