npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@lifaon/jbson

v1.1.3

Published

Encodes and decodes complex javascript objects and types. May be used to transmit or clone objects

Downloads

7

Readme

npm (scoped) npm bundle size (scoped) npm NPM npm type definitions

JBSON

Javascript Binary Structured Object Notation

This library provides tools to :

  • encode javascript values like objects, Map, numbers, ArrayBuffer, etc... into a sequence of bytes
  • decode JBSON bytes' sequence into js values
  • clone complex variables (structured clone)

It may be used to transmit complex data structure than JSON doesn't support like:

  • binary data (ArrayBuffer)
  • built-in types: Map, Set, RegExp, Date, BigInt, etc...
  • circular references and pointers

Or you may use it to clone a complex variable.

To install:

yarn add @lifaon/jbson
# or 
npm i @lifaon/jbson --save

Entry point: index.js. I recommend you to use rollup to import/bundle the package, but you may use an already bundled version in bundles/.

You may also use unpkg: https://unpkg.com/@lifaon/jbson

WARN: This implementation is different than mongoDB's BSON.

INFO: This implementation doesn't aim to compress data:

  • in some cases the size may be strongly reduced (10~30% of the size of the JSON equivalent)
  • in other cases data may be bigger than JSON (a few)
  • moreover, strings are not compressed, so you'll still benefit of gziping the bytes when sending them.

INFO: suggested mime-type: application/jbson

Related:

  • https://msgpack.org/
  • https://en.wikipedia.org/wiki/BSON
  • https://en.wikipedia.org/wiki/UBJSON

Usage

Encoding

// WARN the returned Uint8Array is shared, use .slice() to clone its values
function EncodeToJBSON<T>(value: T): Uint8Array;

Example:

const obj: any = {};
obj.obj = obj;
console.log(EncodeToJBSON(obj)); // output Uint8Array([17, 1, 4, 3, 111, 98, 106, 127, 0])

Decoding

function DecodeFromJBSON<T>(buffer: Uint8Array): T;

Example:

console.log(DecodeFromJBSON(new Uint8Array([17, 1, 4, 3, 111, 98, 106, 127, 0]))); // output { obj: { obj: { ... } } }

Cloning

function StructuredClone<T>(value: T): T;

Example:

console.log(StructuredClone({ a: 1 })); // output { a: 1 }

JBSON Spec

Assuming than:

  • write is a function which writes a byte into a buffer and increment the write index.
  • read is a function which reads a byte from a buffer and increment the read index.
export type WriteFunction  = (value: number) => void;
export type ReadFunction  = () => number;

Pointers:

export type Pointer = number;
export type GetPointerFunction = () => Pointer;

Some structures may include some circular or shared references like: const obj = {}; obj.obj = obj;

JBSON supports such conditions by creating a Pointer which is nothing more than the index where is reference as been encoded.

Notations:

  • 0b[bit 7, bit 6, ... bit 0] => represents a byte
  • [0b[...], 0b[...]] or [1, 2, 3, ...] => represents a sequence of bytes

Size

The size is a variable length number, encoded in 7 bits every bytes, where the 8th bit is used to notify than the next byte is part of this number.

[0b[<bit 7>: 1 if more bits are required to encode the number, <bit 6-0>: number bits (little endian)], ...repeat{0,}]

Example: encoding 1234 (0b 0000 0100 1101 0010)

[0b11010010, 0b00000100]

export function EncodeSize(size: number, write: WriteFunction): void {
  let byte: number;
  do {
    byte = (size & 0b01111111);
    size >>= 7;
    byte |= ((size !== 0) as any) << 7;
    write(byte);
  } while (size !== 0);
}

export function DecodeSize(read: ReadFunction): number {
  let size: number = 0;
  let byte: number;
  let offset: number = 0;
  do {
    byte = read();
    size |= (byte & 0b01111111) << offset;
    offset += 7;
  } while (byte & 0b10000000);
  return size;
}

export function EncodeBigSize(size: bigint, write: WriteFunction): void {
  let byte: number;
  do {
    byte = Number(size & 0b01111111n);
    size >>= 7n;
    byte |= ((size !== 0n) as any) << 7;
    write(byte);
  } while (size !== 0n);
}

export function DecodeBigSize(read: ReadFunction): bigint {
  let size: bigint = 0n;
  let byte: number;
  let offset: bigint = 0n;
  do {
    byte = read();
    size |= BigInt(byte & 0b01111111) << offset;
    offset += 7n;
  } while (byte & 0b10000000);
  return size;
}

Types

The following encoders/decoders wont specify the type byte preceding the encoded bits. The type byte values are specified into the any section.

Boolean

The booleans are simply stored as 0 if false and 1 if true.

[0b[<bit 7-1>: 0, boolean ? 1 : 0]]

Example:

  • true: [0b00000001] = [1]
  • false: [0b00000000] = [0]
export function EncodeBoolean(boolean: boolean, write: WriteFunction): void {
  write(boolean ? 1 : 0);
}

export function DecodeBoolean(read: ReadFunction): boolean {
  return (read() !== 0);
}

Number

Numbers may have the following types:

export enum NUMBER_TYPES {
  INT8 = 0x00,
  UINT8 = 0x01,
  INT16 = 0x02,
  UINT16 = 0x03,
  INT32 = 0x04,
  UINT32 = 0x05,
  INT64 = 0x06,
  UINT64 = 0x07,
  FLOAT32 = 0x08,
  FLOAT64 = 0x09,
}

They are stored like that:

[0b[<bit 7-0>: NUMBER_TYPES[type of the number]], ...number bits stored as big-endian{1-8}]

Example: encoding 1234

  1. Inferred type: NUMBER_TYPES.UINT16
  2. Bytes: [3 /* number type (uint16) */, 4 /* high byte of the number */, 210 /* low byte of the number */] = [3, 4, 210]
const dataView = new DataView(new ArrayBuffer(8));

export function EncodeNumber(number: number, write: WriteFunction): void {
  const type: NUMBER_TYPES = InferNumberTypeOfNumber(number);
  write(type);
  SetNumberInDataView(number, type, dataView, 0, false);
  for (let i = 0, l = NumberTypeByteLength(type); i < l; i++) {
    write(dataView.getUint8(i));
  }
}

export function DecodeNumber(read: ReadFunction): number {
  const type: NUMBER_TYPES = read();
  for (let i = 0, l = NumberTypeByteLength(type); i < l; i++) {
    dataView.setUint8(i, read());
  }
  return GetNumberInDataView(type, dataView, 0, false);
}

String

Strings are converted into an utf8 encoded Uint8Array, then the array length is encoded using EncodeSize and finally the content is written just after.

[...size of the string{1,}, ...content of the string{0,}]

Example: encoding 'abc'

[3 /* string's length */, 97 /* 'a' */, 98 /* 'b' */, 99 /* 'c' */] = [3, 97, 98, 99]

export function EncodeString(string: string, write: WriteFunction): void {
  const bytes: Uint8Array = textEncoder.encode(string);
  EncodeSize(bytes.length, write);
  for (let i = 0, l = bytes.length; i < l; i++) {
    write(bytes[i]);
  }
}

export function DecodeString(read: ReadFunction): string {
  const size: number = DecodeSize(read);
  const bytes: Uint8Array = (size < tempUint8Array.length) ? tempUint8Array : new Uint8Array(size);
  for (let i = 0; i < size; i++) {
    bytes[i] = read();
  }
  return textDecoder.decode(bytes.subarray(0, size));
}

BigInt

BigInts are simply stored as if they where size

Example: encoding 1234n

[210, 9]

export function EncodeBigInt(number: bigint, write: WriteFunction): void {
  return EncodeBigSize(number, write);
}

export function DecodeBigInt(read: ReadFunction): bigint {
  return DecodeBigSize(read);
}

Date

Dates are stored as number (timestamp in milliseconds) using EncodeNumber.

[...timestamp of the date in milliseconds encoded as number{2,9}]

Example: encoding new Date('04 Dec 1995 00:12:00 GMT')

[7 /* number type (uint64) */, 0, 0, 0, 190, 118, 189, 140, 128 /* ... number bits */] = [7, 0, 0, 0, 190, 118, 189, 140, 128]

export function EncodeDate(date: Date, write: WriteFunction): void {
  EncodeNumber(date.valueOf(), write);
}

export function DecodeDate(read: ReadFunction): Date {
  return new Date(DecodeNumber(read));
}

RegExp

RegExps are stored as a tuple of string composed of the source and the flags using EncodeString.

[...regexp.source encoded as string{1,}, ...regexp.flags encoded as string{1,}]

Example: encoding new RegExp(/abc/g)

[3 /* regex.source's length */, 97, 98, 99 /* ... 'abc' */, 1 /* regex.flags' length */, 103 /* 'g' */] = [3, 97, 98, 99, 1, 103]

export function EncodeRegExp(regexp: RegExp, write: WriteFunction): void {
  EncodeString(regexp.source, write);
  EncodeString(regexp.flags, write);
}

export function DecodeRegExp(read: ReadFunction): RegExp {
  return new RegExp(DecodeString(read), DecodeString(read));
}

ArrayBuffer

ArrayBuffers are stored as a tuple composed of its size and its content bytes.

[...size of the buffer{1,}, ...buffer bytes{0,}]

Example: encoding new Uint8Array([0, 1, 2]).buffer

[3 /* buffer's size */, 0, 1, 2 /* ... buffer's content */] = [3, 0, 1, 2]

export function EncodeArrayBuffer(buffer: ArrayBuffer | SharedArrayBuffer, write: WriteFunction, byteOffset: number = 0, byteLength: number = buffer.byteLength): void {
  EncodeSize(byteLength, write);
  const bytes: Uint8Array = new Uint8Array(buffer, byteOffset, byteLength);
  for (let i = 0, l = bytes.length; i < l; i++) {
    write(bytes[i]);
  }
}

export function DecodeArrayBuffer(read: ReadFunction): ArrayBuffer {
  const bytes: Uint8Array = new Uint8Array(DecodeSize(read));
  for (let i = 0; i < bytes.length; i++) {
    bytes[i] = read();
  }
  return bytes.buffer;
}

ArrayBufferView

ArrayBufferView (Uint8Array, Uint16Array, ...) are stored as a tuple composed of its number type (uint8, uint16, etc... see NUMBER_TYPES) and its content encoded as an ArrayBuffer with EncodeArrayBuffer.

[buffer type {1}, ...buffer size and bytes{1,}]

Example: encoding new Uint8Array([0, 1, 2])

[1 /* buffer's type (uint8) */, 3 /* buffer's size */, 0, 1, 2 /* ... buffer's content */] = [1, 3, 0, 1, 2]

export function EncodeArrayBufferView(buffer: ArrayBufferView, write: WriteFunction): void {
  write(ArrayBufferViewToNumberType(buffer));
  EncodeArrayBuffer(buffer.buffer, write, buffer.byteOffset, buffer.byteLength);
}

export function DecodeArrayBufferView(read: ReadFunction): ArrayBufferView {
  return new (NumberTypeToArrayBufferViewConstructor(read()))(DecodeArrayBuffer(read));
}

Map

Maps are stored as:

[map entries' size {1,}, ...for each entries: tuple<EncodeAny(key), EncodeAny(value)>]

Example: encoding new Map([['a', 1]])

[
    1 /* number of entries in the map */,

    /** entry 0: **/

        4 /* string type */,
        1 /* string's length */,
        97 /* 'a' */,

        3 /* number type */,
        1 /* (uint8) */,
        1 /* value */
]

= [1, 4, 1, 97, 3, 1, 1]

export function EncodeMap(
  map: Map<any, any>,
  write: WriteFunction,
  getPointer: GetPointerFunction,
  memory: Map<any, Pointer> = new Map<any, Pointer>()
): void {
  EncodeSize(map.size, write);

  for (const entry of map.entries()) {
    EncodeAny(entry[0], write, getPointer, memory);
    EncodeAny(entry[1], write, getPointer, memory);
  }
}

export function DecodeMap(
  read: ReadFunction,
  getPointer: GetPointerFunction,
  memory: Map<Pointer, any> = new Map<Pointer, any>(),
  pointer: Pointer = getPointer()
): Map<any, any> {
  const size: number = DecodeSize(read);
  const map: Map<any, any> = new Map<any, any>();
  memory.set(pointer, map);
  for (let i = 0; i < size; i++) {
    const key: any = DecodeAny(read, getPointer, memory);
    const value: any = DecodeAny(read, getPointer, memory);
    map.set(key, value);
  }
  return map;
}

Set

Sets are stored as:

[set values' size {1,}, ...for each values: EncodeAny(value)]

Example: encoding new Set(['a', 1])

[
    2 /* number of values in the set */,

    /** entry 0: **/
        4 /* string type */,
        1 /* string's length */,
        97 /* 'a' */,

    /** entry 1: **/
        3 /* number type */,
        1 /* (uint8) */,
        1 /* value */
]

= [2, 4, 1, 97, 3, 1, 1]

export function EncodeSet(
  set: Set<any>,
  write: WriteFunction,
  getPointer: GetPointerFunction,
  memory: Map<any, Pointer> = new Map<any, Pointer>()
): void {
  EncodeSize(set.size, write);

  for (const value of set.values()) {
    EncodeAny(value, write, getPointer, memory);
  }
}

export function DecodeSet(
  read: ReadFunction,
  getPointer: GetPointerFunction,
  memory: Map<Pointer, any> = new Map<Pointer, any>(),
  pointer: Pointer = getPointer()
): Set<any> {
  const size: number = DecodeSize(read);
  const set: Set<any> = new Set<any>();
  memory.set(pointer, set);
  for (let i = 0; i < size; i++) {
    set.add(DecodeAny(read, getPointer, memory));
  }
  return set;
}

Array

Arrays are stored exactly as Set

Example: encoding ['a', 1]

[
    2 /* number of values in the array */,

    /** entry 0: **/
        4 /* string type */,
        1 /* string's length */,
        97 /* 'a' */,

    /** entry 1: **/
        3 /* number type */,
        1 /* (uint8) */,
        1 /* value */
]

= [2, 4, 1, 97, 3, 1, 1]

export function EncodeArray(
  array: any[],
  write: WriteFunction,
  getPointer: GetPointerFunction,
  memory: Map<any, Pointer> = new Map<any, Pointer>()
): void {
  EncodeSize(array.length, write);

  for (let i = 0, l = array.length; i < l; i++) {
    EncodeAny(array[i], write, getPointer, memory);
  }
}

export function DecodeArray(
  read: ReadFunction,
  getPointer: GetPointerFunction,
  memory: Map<Pointer, any> = new Map<Pointer, any>(),
  pointer: Pointer = getPointer()
): any[] {
  const size: number = DecodeSize(read);
  const array: any[] = new Array<any>(size);
  memory.set(pointer, array);
  for (let i = 0; i < size; i++) {
    array[i] = DecodeAny(read, getPointer, memory);
  }
  return array;
}

Object

Objects are stored exactly as Map

Example: encoding { a: 1 }

[
    1 /* number of properties in the object */,

    /** property 0: **/

        /** property's key: **/
            4 /* string type */,
            1 /* string's length */,
            97 /* 'a' */,

        /** property's value: **/
            3 /* number type */,
            1 /* (uint8) */,
            1 /* value */
]

= [1, 4, 1, 97, 3, 1, 1]

export function EncodeObject(
  object: any,
  write: WriteFunction,
  getPointer: GetPointerFunction,
  memory: Map<any, Pointer> = new Map<any, Pointer>()
): void {
  const entries: [any, any][] = Object.entries(object);
  EncodeSize(entries.length, write);

  for (let i = 0, l = entries.length; i < l; i++) {
    EncodeAny(entries[i][0], write, getPointer, memory);
    EncodeAny(entries[i][1], write, getPointer, memory);
  }
}

export function DecodeObject(
  read: ReadFunction,
  getPointer: GetPointerFunction,
  memory: Map<Pointer, any> = new Map<Pointer, any>(),
  pointer: Pointer = getPointer()
): object {
  const size: number = DecodeSize(read);
  const object: any = {};
  memory.set(pointer, object);
  for (let i = 0; i < size; i++) {
    const key: any = DecodeAny(read, getPointer, memory);
    object[key] = DecodeAny(read, getPointer, memory);
  }
  return object;
}

Pointer

export type Pointer = number;
export type GetPointerFunction = () => Pointer;

Some structures may include circular or shared references like: const obj = {}; obj.obj = obj;

JBSON supports such conditions by creating a Pointer which is nothing more than the index where is reference as been encoded.

Pointers are stored as size

Example: full encoding of const obj = {}; obj.obj = obj;

[
    17 /* object type */,
    1 /* number of properties in the object */,

    /** property 0: **/

        /** property's key: **/
            4 /* string type */,
            3/* property key's length */,
            111, 98, 106, /* 'obj' */

        /** property's value: **/
            127 /* pointer type */,
            0 /* index where is stored the reference's value => 0 which is the index of the object */
]

= [17, 1, 4, 3, 111, 98, 106, 127, 0]

export function EncodePointer(pointer: Pointer, write: WriteFunction): void {
  return EncodeSize(pointer, write);
}

export function DecodePointer(read: ReadFunction): Pointer {
  return DecodeSize(read);
}

Any

Any is the entry point for every value you want to encode / decode. The encoder will convert a value into a sequence of bytes composed of the type of the value and its encoded bytes.

export enum ANY_TYPES {
  UNDEFINED = 0x00,
  NULL = 0x01,
  BOOLEAN = 0x02,
  NUMBER = 0x03,
  STRING = 0x04,
  SYMBOL = 0x05,
  BOOLEAN_OBJECT = 0x06,
  NUMBER_OBJECT = 0x07,
  STRING_OBJECT = 0x08,
  DATE = 0x09,
  REGEXP = 0x0a,
  SHARED_ARRAY_BUFFER = 0x0b,
  ARRAY_BUFFER = 0x0c,
  ARRAY_BUFFER_VIEW = 0x0d,
  MAP = 0x0e,
  SET = 0x0f,
  ARRAY = 0x10,
  OBJECT = 0x11,
  BIGINT = 0x12,

  POINTER = 0x7f,
}

[value's type {1}, ...encoded value's bytes{1,}]

export function EncodeAny(
  value: any,
  write: WriteFunction,
  getPointer: GetPointerFunction,
  memory: Map<any, Pointer> = new Map<any, Pointer>()
): void {
  if (memory.has(value)) {
    write(ANY_TYPES.POINTER);
    EncodePointer(memory.get(value) as Pointer, write);
  } else {
    if ((value !== null) && (value !== void 0) && (typeof value.toJBSON === 'function')) {
      EncodeAny(value.toJBSON(), write, getPointer, memory);
    } else {
      const type: string = typeof value;

      // p4
      if (type === 'undefined') {
        write(ANY_TYPES.UNDEFINED);

      } else if (value === null) {
        write(ANY_TYPES.NULL);

      } else if (type === 'boolean') {
        write(ANY_TYPES.BOOLEAN);
        EncodeBoolean(value, write);

      } else if (type === 'number') {
        write(ANY_TYPES.NUMBER);
        EncodeNumber(value, write);

      } else if (type === 'string') {
        write(ANY_TYPES.STRING);
        EncodeString(value, write);

      } else if (type === 'symbol') {  // p5
        throw new Error(`Value could not be cloned: ${ value.toString() } is a Symbol`);

      } else if (type === 'bigint') {
        write(ANY_TYPES.BIGINT);
        EncodeBigInt(value, write);

      } else if (type === 'object') {
        memory.set(value, getPointer()); // p6 & p23

        if (value instanceof Boolean) { // p7
          write(ANY_TYPES.BOOLEAN_OBJECT);
          EncodeBoolean(value.valueOf(), write);

        } else if (value instanceof Number) { // p8
          write(ANY_TYPES.NUMBER_OBJECT);
          EncodeNumber(value.valueOf(), write);

        } else if (value instanceof String) { // p9
          write(ANY_TYPES.STRING_OBJECT);
          EncodeString(value.valueOf(), write);

        } else if (value instanceof Date) { // p10
          write(ANY_TYPES.DATE);
          EncodeDate(value, write);

        } else if (value instanceof RegExp) { // p11
          write(ANY_TYPES.REGEXP);
          EncodeRegExp(value, write);

        } else if ((typeof SharedArrayBuffer !== 'undefined') && (value instanceof SharedArrayBuffer)) { // p12.2
          // if(forStorage) throw new DataCloneError('Value could not be cloned: is a SharedArrayBuffer');
          write(ANY_TYPES.SHARED_ARRAY_BUFFER);
          EncodeArrayBuffer(value, write);

        } else if (value instanceof ArrayBuffer) { // p12.3
          write(ANY_TYPES.ARRAY_BUFFER);
          EncodeArrayBuffer(value, write);

        } else if (ArrayBuffer.isView(value)) { // p13
          write(ANY_TYPES.ARRAY_BUFFER_VIEW);
          EncodeArrayBufferView(value, write);

        } else if (value instanceof Map) { // p14
          write(ANY_TYPES.MAP);
          EncodeMap(value, write, getPointer, memory);

        } else if (value instanceof Set) { // p15
          write(ANY_TYPES.SET);
          EncodeSet(value, write, getPointer, memory);

        } else if (Array.isArray(value)) { // p16
          write(ANY_TYPES.ARRAY);
          EncodeArray(value, write, getPointer, memory);

        } else if (!IsPlainObject(value)) { // p18
          if (typeof value.toJSON === 'function') {
            EncodeAny(value.toJSON(), write, getPointer, memory);
          } else {
            // INFO super hard to implement
            let string: string = String(value);
            if (string.length > 200) {
              string = string.substring(0, 150) + '\n[...]\n' + string.slice(-50);
            }
            console.log(value);
            throw new TypeError(`Unsupported type : ${ string }`);
          }
        } else {
          write(ANY_TYPES.OBJECT);
          EncodeObject(value, write, getPointer, memory);
        }
      } else {
        throw new TypeError(`Unsupported type : ${ type }`);
      }
    }
  }
}
export function DecodeAny(
  read: ReadFunction,
  getPointer: GetPointerFunction,
  memory: Map<Pointer, any> = new Map<Pointer, any>()
): any {

  const pointer: Pointer = getPointer();
  const type: number = read();
  let value: any;
  switch (type) {

    case ANY_TYPES.UNDEFINED:
      return void 0;
    case ANY_TYPES.NULL:
      return null;
    case ANY_TYPES.BOOLEAN:
      return DecodeBoolean(read);
    case ANY_TYPES.NUMBER:
      return DecodeNumber(read);
    case ANY_TYPES.STRING:
      return DecodeString(read);
    case ANY_TYPES.BIGINT:
      return DecodeBigInt(read);

    case ANY_TYPES.BOOLEAN_OBJECT:
      value = Boolean(DecodeBoolean(read));
      break;
    case ANY_TYPES.NUMBER_OBJECT:
      value = Number(DecodeNumber(read));
      break;
    case ANY_TYPES.STRING_OBJECT:
      value = String(DecodeString(read));
      break;
    case ANY_TYPES.DATE:
      value = DecodeDate(read);
      break;
    case ANY_TYPES.REGEXP:
      value = DecodeRegExp(read);
      break;
    case ANY_TYPES.SHARED_ARRAY_BUFFER:
      value = DecodeArrayBuffer(read);
      break;
    case ANY_TYPES.ARRAY_BUFFER:
      value = DecodeArrayBuffer(read);
      break;
    case ANY_TYPES.ARRAY_BUFFER_VIEW:
      value = DecodeArrayBufferView(read);
      break;
    case ANY_TYPES.MAP:
      value = DecodeMap(read, getPointer, memory, pointer);
      break;
    case ANY_TYPES.SET:
      value = DecodeSet(read, getPointer, memory, pointer);
      break;
    case ANY_TYPES.ARRAY:
      value = DecodeArray(read, getPointer, memory, pointer);
      break;
    case ANY_TYPES.OBJECT:
      value = DecodeObject(read, getPointer, memory, pointer);
      break;
    case ANY_TYPES.POINTER:
      const address: Pointer = DecodePointer(read);
      if (memory.has(address)) {
        return memory.get(address);
      } else {
        throw new TypeError(`Find a pointer without valid pointed value`);
      }
    default:
      throw new TypeError(`Invalid type found : ${ type }`);
  }

  memory.set(pointer, value);

  return value;
}