{"id":"bajsucks/bufferizer","name":"bufferizer","scope":"bajsucks","platform":"roblox","description":"A serdes library for homogeneous arrays","version":"0.3.0","latest":"0.3.0","versions":["0.1.0","0.1.1","0.1.2","0.1.3","0.2.0","0.2.1","0.3.0"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"e3db553e51ae5eb0997384050956d70c620dc5570e1b59f4cc2414c88c0427d4","likes":0,"downloads":2,"install":"forest install bajsucks/bufferizer","url":"https://forest.dev/p/roblox/bajsucks/bufferizer","files":"https://api.forest.dev/ai/package/roblox/bajsucks/bufferizer/files","readme":"# Bufferizer\nLight, zero-dependency Luau SerDes library for homogeneous arrays.\n\n## Install\n### Forest\n`forest i bajsucks/bufferizer`\n\n### Wally\nWally package is deprecated, but an older version is available:\n\n`bufferizer = \"bajsucks/bufferizer@^0.2.0\"`\n\n### .rbxm\n[Download the latest .rbxm release from Releases](https://github.com/bajsucks/bufferizer/releases/latest), or build from source via `rojo build`\n\n## Use cases\n- Networking and replication (especially ECS)\n- Data saves, I guess?\n\n## Usage\n```luau\nBufferizer.Encode<T>(Payload: {T}, Schema: Schema<T>): buffer\nBufferizer.Decode<T>(buffer, Schema: Schema<T>): {T}\nBufferizer.Size<T>(Schema: Schema<T>, Payload: {T}?): number?\nBufferizer.schemas: {Schema<any>} -- out-of-the-box schemas\nBufferizer.primitives.types: {any} -- primitive column types (u8, i16, f32, string, etc.)\n```\n\nThere is support for dynamic width data types:\n- `varint` (int `0` to `2^53`)\n- `zigzag` (int `-2^52` to `2^52`)\n- `string`\n\nOut-of-the-box schemas:\n- `Vector2`\n- `Vector3`\n- `CFrame`\n- `Color3`\n- `UDim`\n- `UDim2`\n- `number` (f64)\n- `jecs_id` (i24)\n\n## Defining new schemas\nIf you want a custom type, you will have to create a schema.\nFor example, Vector3 schema:\n```luau\n-- p = Bufferizer.primitives.types\n\nBufferizer.Schema(\n\t{ p.f32, p.f32, p.f32 }, -- `map`, maps the primitives returned by `encode`\n\tfunction(v: Vector3) -- `encode`, converts the type into primitives\n\t\treturn v.X, v.Y, v.Z\n\tend,\n\tVector3.new -- `decode` converts the primitives back into the type\n)\n```\nIt's as simple as that!\n\nYou can also skip encode/decode when the schema has a single column:\n```luau\n-- p = Bufferizer.primitives.types\n\nBufferizer.Schema( -- Luau number\n\t{ p.f64 },\n)\n```\n\n## Dynamic width types, Array, FixedArray and Anchored\n`varint`, `zigzag` and `string` only write as many bytes as a value needs:\n```luau\n-- p = Bufferizer.primitives.types\n\nlocal event = Bufferizer.Schema(\n\t{ p.string, p.u32, p.zigzag },\n\tfunction(e)\n\t\treturn e.name, e.actor, e.score\n\tend,\n\tfunction(name: string, actor: number, score: number)\n\t\treturn { name = name, actor = actor, score = score }\n\tend\n)\n\nlocal buf = Bufferizer.Encode(\n\t{\n\t\t{ name = \"hit\", actor = 12, score = -5 },\n\t\t{ name = \"died\", actor = 12, score = 2 ^ 40 },\n\t}, event\n)\n\nBufferizer.Decode(buf, event) -- the same 2 rows\n```\n\n`Array` is a list per row. Every row reads its own count back, so rows may hold different amounts, and an empty list is fine:\n```luau\n-- p = Bufferizer.primitives.types\n\nlocal inventory = Bufferizer.Schema(\n\t{ p.string, Bufferizer.Array(p.u16) },\n\tfunction(player)\n\t\treturn player.name, player.items\n\tend,\n\tfunction(name: string, items: {number})\n\t\treturn { name = name, items = items }\n\tend\n)\n\nlocal buf = Bufferizer.Encode({\n\t{ name = \"pupa\", items = {} },\n\t{ name = \"lupa\", items = { 4096, 4095, 4094 } },\n}, inventory)\n\nBufferizer.Decode(buf, inventory) -- 2 rows, 2 different lengths\n```\nElements of .Array() are primitives.\n\n`FixedArray` is a list with a known count and no count prefix, so it stays fixed width:\n```luau\n-- p = Bufferizer.primitives.types\n\nlocal box = Bufferizer.Schema(\n\t{ Bufferizer.FixedArray(p.f32, 3), Bufferizer.FixedArray(p.f32, 3) },\n\tfunction(b)\n\t\treturn b.center, b.size\n\tend,\n\tfunction(center: {number}, size: {number})\n\t\treturn { center = center, size = size }\n\tend\n)\n\nBufferizer.Size(box) -- 24 bytes per row\n\nlocal buf = Bufferizer.Encode({ { center = { 0, 0, 0 }, size = { 1, 2, 3 } } }, box)\nBufferizer.Decode(buf, box) -- { center = { 0, 0, 0 }, size = { 1, 2, 3 } }\n```\nA row with the wrong element count will throw an error.\n\n`Anchored` writes the first row in full and then only how much each row changed from the one before it, which makes slow-moving columns (timestamps, ids) efficient:\n```luau\n-- p = Bufferizer.primitives.types\nlocal log = Bufferizer.Schema({ p.string, Bufferizer.Anchored() }, function(entry)\n\treturn entry.action, entry.tick\nend, function(action, tick)\n\treturn { action = action, tick = tick }\nend)\n\nlocal buf = Bufferizer.Encode({\n\t{ action = \"spawn\", tick = 1000000 },\n\t{ action = \"move\", tick = 1000001 },\n\t{ action = \"move\", tick = 1000002 },\n}, log)\n\nbuffer.len(buf) -- 21 bytes, against 28 with i32 ticks\nBufferizer.Decode(buf, log) -- the same three rows\n```\nIt can't be nested inside .Array() and takes ints.\n\nFirst row can be `-2^52` to `2^52`, and each step between rows must stay within `2^52`.","readmeTruncated":false}