UNPKG

superbuffer

Version:

A simple way to serialize JavaScript objects into ArrayBuffers for high compression on the wire.

194 lines (151 loc) 5.86 kB
<h1 align="center">Superbuffer</h1> <p align="center"> A simple way to serialize JavaScript objects into ArrayBuffers for high compression on the wire. </p> <br> <br> <p align="center"> <a href="#introduction">Introduction</a> • <a href="#introduction">Usage</a> • <a href="#introduction">Data Types</a> • <a href="#introduction">Caveats</a> </p> <p align="center"> <a href=""> <img src="https://badgen.net/bundlephobia/minzip/superbuffer?color=green&label=size"> </a> <a href=""> <img src="https://badgen.net/npm/v/superbuffer?color=blue&label=version"> </a> <a> <img src="https://badgen.net/npm/node/superbuffer?color=blue"> </a> </p> --- ## Introduction Superbuffer makes it easy to serialize and deserialize data from ArrayBuffers matching the interfaces you define. Its Schema-Model API was inspired by [typed-array-buffer-schema][tabs-url]. Superbuffer was designed to balance the readability and flexibility JSON while maximizing compression of your data on the wire. It is especially useful in situations where a high volume and frequency of network traffic between clients is necessary (such as multiplayer networked games). ## Installation **NPM** ``` npm i superbuffer ``` **Yarn** ``` yarn add superbuffer ``` ## Usage API Superbuffer allows you to define the shape of the data you want to serialize: ```typescript import {Schema, Model, uint32, uint64, int16, string} from 'superbuffer'; const commentSchema = new Schema({ message: string, timestamp: uint64, votes: int16, }); const postSchema = new Schema({ id: uint32, title: string, tags: [string], author: { id: uint32, name: string, }, comments: [commentSchema], }); const post = new Model(postSchema); ``` If you use Typescript, Superbuffer automatically ensures your data has proper typings: ```typescript import {Model, float32} from 'superbuffer'; const position = Model.fromSchemaDefinition({ x: float32, y: float32, }); // Typescript knows that only objects with type // {x: number, y: number} can be serialized by the `position` model const buffer = position.toBuffer({x: 32.1, y: 12.3}); // Typescript knows `result` is of type {x: number, y: number} const result = position.fromBuffer(buffer); ``` Superbuffer can serialize any non-circular JSON structure: ```typescript import {Schema, Model, views, ExtractSchemaObject} from 'superbuffer'; const {int16, int32, uint8, uint32, uint64, float32, boolean, string} = views; const playerSchema = new Schema({ id: uint8, x: float32, y: float32, health: uint8, alive: boolean, }); const inputSchema = new Schema({action: int16, movement: int16}); const listSchema = new Schema({value: int32}); const snapshotModel = Model.fromSchemaDefinition({ time: uint64, sequenceNumber: uint32, input: inputSchema, messages: [string], data: { list: [listSchema], players: [playerSchema], }, }); type Snapshot = ExtractSchemaObject<typeof snapshotModel>; const snapshot: Snapshot = { time: BigInt(Date.now()), sequenceNumber: 438923, input: { action: -12, movement: -4, }, messages: ['hello', 'hi', 'how are you', 'im good, how are you', 'fine thank you'], data: { list: [{value: 1}, {value: 2}, {value: 3}, {value: 4}, {value: 5}], players: [ {id: 14, x: 145.32, y: 98.1123, health: 99, alive: true}, {id: 15, x: 218.46, y: -14.0934, health: 100, alive: true}, {id: 0, x: 3289.554, y: -1432.0, health: 0, alive: false}, ], }, }; /** Client */ // JSON.stringify(snapshot) = 430 bytes // model.toBuffer(snapshot) = 139 bytes // 68% compression! const buffer = snapshotModel.toBuffer(snapshot); // Object to ArrayBuffer network.emit(buffer); /** Server */ network.on('message', (buffer) => { const id = Model.getIdFromBuffer(buffer); if (id === snapshotModel.schema.id) { const result = snapshotModel.fromBuffer(buffer); // ArrayBuffer to object } }); ``` ## Data Types Superbuffer's views are mapped to their respective [TypedArray][typed-array-url] countparts: | Superbuffer View | TypedArray Equivalent | Value Range | Byte Size | | ---------------- | --------------------- | ---------------------------- | --------- | | uint8 | Uint8Array | 0 to 255 | 1 | | uint16 | Uint16Array | 0 to 65535 | 2 | | uint32 | Uint32Array | 0 to 4294967295 | 4 | | uint64 | Uint64Array | 0 to 2^64\-1 | 8 | | int8 | Int8Array | \-128 to 127 | 1 | | int16 | Int16Array | \-32768 to 32767 | 2 | | int32 | Int32Array | \-2147483648 to 2147483647 | 4 | | int64 | Int64Array | \-2^63 to 2^63\-1 | 8 | | float32 | Float32Array | 1\.2×10^\-38 to 3\.4×10^38 | 4 | | float64 | Float64Array | 5\.0×10^\-324 to 1\.8×10^308 | 8 | | string | Uint8Array | UTF\-8 encoding | variable | | boolean | Uint8Array | 0 or 1 | 1 | ## Caveats - Max array length of 65535 (uint16) - Max number of schema ids is 255 (uint8) - No `"` character in strings - No `null/undefined` properties: all properties of the schema definition must be present on an object to be serialized (empty arrays are allowed) ## License [MIT License][mit-url] [tabs-url]: https://github.com/geckosio/typed-array-buffer-schema [typed-array-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays [mit-url]: https://github.com/DanielHZhang/superbuffer/blob/main/license.md