bison-js
BISON provides a binary JSON implementation with some extentions that JSON doesn't support, e.g. binary buffers, single-precision Floats
(aka float32
) and BigInt
.
Size and speed
BISON output is 30%...40%
smaller than JSON output (buffers encoded as base64
, BigInt
s encoded as String
).
But BISON takes 30%
more time to encode and 20%
more time to decode if there is no Number
s or BigInt
s larger than 32 bits. If so, BISON is much slower, down to 60%
. Sad but true.
If you can help me to optimise encoding and decoding, feel free to create an issue.
Special thanks
Thanks to @BonsaiDen and his BiSON.js package (that seems abandoned for years). I have got some ideas and inspiration from his docs and code. I wrote my package by myself from ground up, so it is not a fork, it's totally new and incompatible implementation.
How to use
First, install the package with npm install @kirick/bison
.
Then
const BISON = require('@kirick/bison');
const data = {
hello: 'world',
foo: 123456,
bar: 2856.004382,
baz: Buffer.from([ 0xDE, 0xAD, 0xBE, 0xEF ]),
};
const payload = BISON.encode(data);
/*
payload now is
ArrayBuffer {
[Uint8Contents]: <0d 01 5d db dc 9b 19 00 56 86 56 c6 c6 f5 80 00 78 90 00 36 66 f6 f7 40 a6 50 02 3e 5b 85 62 00 d8 98 5c a0 09 bd 5b 7d de 01 b1 30 bd 70>,
byteLength: 46
}
*/
How it works?
BISON supports many types of data:
-
null
; -
boolean
; -
int
anduint
up to 2048 bits; -
float
anddouble
, - buffers like
ArrayBuffer
,TypedArray
and NodeJS'Buffer
; -
string
; -
array
; -
hash
.
Each type has its specific encoding started with 3- or 4-bits of TypeID
followed (or not) by some data bits.
Please note that every BISON payload starts with bit 0
indicates that it's a JSON-like payload contains all necessary data to decode it (like TypeIDs and other data headers).
In future, BISON will support schemas (payload's first bit is 1
) that will allow you to cut out TypeID bits, int
and uint
subtypes, buffer
/string
lengths and much more to save even more data.
null
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0000 (TypeID) |
hash
Type Bit offset | Bits count | Description |
---|---|---|
0 |
3 |
0001 (TypeID) |
4 |
<hash_body_length> |
data; all values until terminator value will be appended to current hash |
4 + <hash_body_length> |
3 |
111 type terminator
|
Hash's body has it's own format:
-
value
of any type (excl.terminator
) -
key
: it's astring
with no TypeID bits.
If the value you've read is terminator
, you don't need to read the key.
array
Type Bit offset | Bits count | Description |
---|---|---|
0 |
3 |
0010 (TypeID) |
4 |
<array_body_length> |
data; all values until terminator value will be appended to the array |
4 + <array_body_length> |
3 |
111 type terminator
|
typed array
Type Bit offset | Bits count | Description |
---|---|---|
0 |
3 |
0011 (TypeID) |
4 |
<type_id_length> |
TypeID (3 or 4 bits) of every elements in the array |
4 + <type_id_length> |
<uint_length> |
unsigned integer of any subtype, but with no TypeID bits; indicates the count of elements in the array |
4 + <type_id_length> + <uint_length> |
<array_body_length> |
data all values will be appended to the array no value has TypeID bits |
int8
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0100 (TypeID) |
4 |
2 |
00 (subtype int8 ) |
6 |
8 |
data |
int16
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0100 (TypeID) |
4 |
2 |
01 (subtype int16 ) |
6 |
16 |
data |
int32
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0100 (TypeID) |
4 |
2 |
10 (subtype int32 ) |
6 |
32 |
data |
int2048
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0100 (TypeID) |
4 |
2 |
11 (subtype int2048 ) |
6 |
8 |
<number_bytes_count> unsigned integer, contains number of bytes that stores the number |
14 |
<number_bytes_count> * 8 |
data |
uint8
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0101 (TypeID) |
4 |
2 |
00 (subtype uint8 ) |
6 |
8 |
data |
uint16
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0101 (TypeID) |
4 |
2 |
01 (subtype uint16 ) |
6 |
16 |
data |
uint32
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0101 (TypeID) |
4 |
2 |
10 (subtype uint32 ) |
6 |
32 |
data |
uint2048
Type Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0101 (TypeID) |
4 |
2 |
11 (subtype uint2048 ) |
6 |
8 |
<number_bytes_count> unsigned integer, contains number of bytes that stores the number |
14 |
<number_bytes_count> * 8 |
data |
float
Type Single-precision float, aka float32
.
Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0110 (TypeID) |
4 |
32 |
data |
double
Type Double-precision float, aka float64
.
Bit offset | Bits count | Description |
---|---|---|
0 |
4 |
0111 (TypeID) |
4 |
64 |
data |
buffer
Type Bit offset | Bits count | Description |
---|---|---|
0 |
3 |
100 (TypeID) |
4 |
<uint_length> |
unsigned integer of any subtype, but with no TypeID bits; indicates the length of the buffer in bytes |
4 + <uint_length> |
<buffer_bytes_count> * 8 |
data |
string
Type Bit offset | Bits count | Description |
---|---|---|
0 |
3 |
101 (TypeID) |
4 |
<uint_length> |
unsigned integer of any subtype, but with no TypeID bits; indicates the length of the string in bytes |
4 + <uint_length> |
<string_bytes_count> * 8 |
data |
boolean
Type Bit offset | Bits count | Description |
---|---|---|
0 |
3 |
110 (TypeID) |
3 |
1 |
0 for false 1 for true
|
terminator
Type Terminator indicates the end of array
or hash
. It's acting like an element of collection, but it's of course virtual.
Bit offset | Bits count | Description |
---|---|---|
0 |
3 |
111 (TypeID) |