Generate typescript data binary serialization and deserialization code given typescript types.
This project created was for the specific use case of real-time data transfer for a multiplayer mod for a fast-paced rpg game.
The mod is called cc-multibakery, and this project is called in this file.
This means:
- so only basic types work, most more complex types will likely not work
- there's no need for binary format versioning, so when typescript types change newly generated decoders will most likely crash when attempting to read data generated with an old encoder
- data is packed as tightly as possible
booleanis one bitf64is a 8 byte floating point number (double)f32is a 4 byte floating point number (float)numberisf64by default, can be changed tof32in configu8is 1 byte unsigned integer (0 to 255)u4is 4 bits, not 1 byte with wasted space (0 to 15)i8is 8 bits (-128 to 127)- there are types from
u2andi2up tou32andi32 - note: value range checks are enabled by default and can be disabled
stringis encoded usingTextEncoder, length is stored first (max lenght 2^16), then data followsanyandunknownare parsed into JSON and saved asstringwith max length of 2^24- arrays are supported, default max length is 2^16 max length can be extended with:
import type { RecordSize, u24 } from 'ts-binarifier/src/type-aliases'
type MyType = number[] & RecordSize<u24>- records are supported, default max length is 2^8 max length can be extended with:
import type { RecordSize, u24 } from 'ts-binarifier/src/type-aliases'
type MyType = Record<number, string> & RecordSize<u24>- interfaces (objects with known field names) are supported, the field names are not encoded
- known size arrays like
[number, string]are supported and encoded like interfaces - optional types are supported:
- if the value exist, it takes up 1 bit (1) + T bits
- if the value is undefined, it takes up only 1 bit (0)
You also can look at what's supported in the test file: ./src/test/index.test.ts
Input type:
import type { i2, u2, f32, u32, i32 } from 'ts-binarifier/src/type-aliases'
type MyType = {
a: i2
b: u2
c: f32
d: u32
e: i32
}Output encoding/decoding class:
import { Encoder } from 'ts-binarifier/src/encoder'
import { Decoder } from 'ts-binarifier/src/decoder'
import type { MyType } from '/path/to/file/that/containts/MyType'
export class Gen {
static encode(data: MyType): Uint8Array {
const encoder = new Encoder()
if (data.a < -2 || data.a > 1) throw new Error(`Number of value: ${data.a} does not fit into i2`)
encoder.i8(data.a, 2)
if (data.b < 0 || data.b > 3) throw new Error(`Number of value: ${data.b} does not fit into u2`)
encoder.u8(data.b, 2)
encoder.f32(data.c)
if (data.d < 0 || data.d > 4294967295) throw new Error(`Number of value: ${data.d} does not fit into u32`)
encoder.u32(data.d, 32)
if (data.e < -2147483648 || data.e > 2147483647) throw new Error(`Number of value: ${data.e} does not fit into i32`)
encoder.i32(data.e, 32)
return encoder.getBuffer()
}
static decode(buf: Uint8Array): MyType {
const decoder = new Decoder(buf)
return {
a: decoder.i(2),
b: decoder.u(2),
c: decoder.f32(),
d: decoder.u32(),
e: decoder.i32()
}
}
}Test with:
npm test
npm testWatch