{"id":23953900,"url":"https://github.com/lifaon74/jbson","last_synced_at":"2026-05-16T09:05:33.725Z","repository":{"id":66198233,"uuid":"211275234","full_name":"lifaon74/jbson","owner":"lifaon74","description":"Encodes and decodes complex javascript objects and types. May be used to transmit or clone objects","archived":false,"fork":false,"pushed_at":"2022-09-21T00:53:13.000Z","size":26021,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-15T17:37:31.575Z","etag":null,"topics":["binary","jbson","json","structured-clone"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lifaon74.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-27T08:43:04.000Z","updated_at":"2020-03-14T16:10:35.000Z","dependencies_parsed_at":"2023-02-22T13:01:02.709Z","dependency_job_id":null,"html_url":"https://github.com/lifaon74/jbson","commit_stats":{"total_commits":10,"total_committers":3,"mean_commits":"3.3333333333333335","dds":"0.30000000000000004","last_synced_commit":"e58a195c938455ad3089ea860dbfddd820b95a8d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lifaon74%2Fjbson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lifaon74%2Fjbson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lifaon74%2Fjbson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lifaon74%2Fjbson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lifaon74","download_url":"https://codeload.github.com/lifaon74/jbson/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240457994,"owners_count":19804489,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binary","jbson","json","structured-clone"],"created_at":"2025-01-06T14:48:53.419Z","updated_at":"2026-05-16T09:05:33.695Z","avatar_url":"https://github.com/lifaon74.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm (scoped)](https://img.shields.io/npm/v/@lifaon/jbson.svg)](https://www.npmjs.com/package/@lifaon/jbson)\n![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@lifaon/jbson.svg)\n![npm](https://img.shields.io/npm/dm/@lifaon/jbson.svg)\n![NPM](https://img.shields.io/npm/l/@lifaon/jbson.svg)\n![npm type definitions](https://img.shields.io/npm/types/@lifaon/jbson.svg)\n\n\n\n# JBSON #\n\nJavascript Binary Structured Object Notation\n\nThis library provides tools to :\n- encode javascript values like `objects`, `Map`, `numbers`, `ArrayBuffer`, etc... into a sequence of bytes\n- decode JBSON bytes' sequence into js values\n- clone complex variables (structured clone)\n\nIt may be used to transmit complex data structure than JSON doesn't support like:\n- binary data (ArrayBuffer)\n- built-in types: Map, Set, RegExp, Date, BigInt, etc...\n- circular references and pointers\n\nOr you may use it to clone a complex variable.\n\nTo install:\n```bash\nyarn add @lifaon/jbson\n# or \nnpm i @lifaon/jbson --save\n```\n\nEntry point: `index.js`. I recommend you to use rollup to import/bundle the package,\nbut you may use an already bundled version in `bundles/`.\n\nYou may also use unpkg: `https://unpkg.com/@lifaon/jbson`\n\n\n**WARN:** This implementation is different than mongoDB's BSON.\n\n**INFO:** This implementation doesn't aim to compress data:\n- in some cases the size may be strongly reduced (10~30% of the size of the JSON equivalent)\n- in other cases data may be bigger than JSON (a few)\n- moreover, strings are not compressed, so you'll still benefit of gziping the bytes when sending them.\n\n**INFO:** suggested mime-type: `application/jbson`\n\nRelated:\n- https://msgpack.org/\n- https://en.wikipedia.org/wiki/BSON\n- https://en.wikipedia.org/wiki/UBJSON\n\n## Usage ##\n\n### Encoding ###\n```ts\n// WARN the returned Uint8Array is shared, use .slice() to clone its values\nfunction EncodeToJBSON\u003cT\u003e(value: T): Uint8Array;\n```\n\n**Example:**\n\n```ts\nconst obj: any = {};\nobj.obj = obj;\nconsole.log(EncodeToJBSON(obj)); // output Uint8Array([17, 1, 4, 3, 111, 98, 106, 127, 0])\n```\n\n### Decoding ###\n```ts\nfunction DecodeFromJBSON\u003cT\u003e(buffer: Uint8Array): T;\n```\n\n**Example:**\n\n```ts\nconsole.log(DecodeFromJBSON(new Uint8Array([17, 1, 4, 3, 111, 98, 106, 127, 0]))); // output { obj: { obj: { ... } } }\n```\n\n### Cloning ###\n```ts\nfunction StructuredClone\u003cT\u003e(value: T): T;\n```\n\n**Example:**\n\n```ts\nconsole.log(StructuredClone({ a: 1 })); // output { a: 1 }\n```\n\n\n## JBSON Spec ##\n\nAssuming than:\n- `write` is a function which writes a byte into a buffer and increment the write index.\n- `read` is a function which reads a byte from a buffer and increment the read index.\n\n```ts\nexport type WriteFunction  = (value: number) =\u003e void;\nexport type ReadFunction  = () =\u003e number;\n```\n\nPointers:\n```ts\nexport type Pointer = number;\nexport type GetPointerFunction = () =\u003e Pointer;\n```\nSome structures may include some circular or shared references like: `const obj = {}; obj.obj = obj;`\n\nJBSON supports such conditions by creating a `Pointer` which is nothing more than the index where is reference as been encoded.\n\nNotations:\n- `0b[bit 7, bit 6, ... bit 0]` =\u003e represents a byte\n- `[0b[...], 0b[...]]` or `[1, 2, 3, ...]` =\u003e represents a sequence of bytes\n\n### Size ###\nThe 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.\n\n`[0b[\u003cbit 7\u003e: 1 if more bits are required to encode the number, \u003cbit 6-0\u003e: number bits (little endian)], ...repeat{0,}]`\n\n**Example:** encoding `1234` (0b 0000 0100 1101 0010)\n\n`[0b11010010, 0b00000100]`\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeSize(size: number, write: WriteFunction): void {\n  let byte: number;\n  do {\n    byte = (size \u0026 0b01111111);\n    size \u003e\u003e= 7;\n    byte |= ((size !== 0) as any) \u003c\u003c 7;\n    write(byte);\n  } while (size !== 0);\n}\n\nexport function DecodeSize(read: ReadFunction): number {\n  let size: number = 0;\n  let byte: number;\n  let offset: number = 0;\n  do {\n    byte = read();\n    size |= (byte \u0026 0b01111111) \u003c\u003c offset;\n    offset += 7;\n  } while (byte \u0026 0b10000000);\n  return size;\n}\n\nexport function EncodeBigSize(size: bigint, write: WriteFunction): void {\n  let byte: number;\n  do {\n    byte = Number(size \u0026 0b01111111n);\n    size \u003e\u003e= 7n;\n    byte |= ((size !== 0n) as any) \u003c\u003c 7;\n    write(byte);\n  } while (size !== 0n);\n}\n\nexport function DecodeBigSize(read: ReadFunction): bigint {\n  let size: bigint = 0n;\n  let byte: number;\n  let offset: bigint = 0n;\n  do {\n    byte = read();\n    size |= BigInt(byte \u0026 0b01111111) \u003c\u003c offset;\n    offset += 7n;\n  } while (byte \u0026 0b10000000);\n  return size;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### Types ###\n\nThe following encoders/decoders wont specify the **type byte** preceding the encoded bits. The type byte values are specified into the [any](#any) section.\n\n\n#### Boolean ####\n\nThe booleans are simply stored as 0 if false and 1 if true.\n\n`[0b[\u003cbit 7-1\u003e: 0, boolean ? 1 : 0]]`\n\n**Example:**\n\n- true: `[0b00000001]` = `[1]`\n- false: `[0b00000000]` = `[0]`\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeBoolean(boolean: boolean, write: WriteFunction): void {\n  write(boolean ? 1 : 0);\n}\n\nexport function DecodeBoolean(read: ReadFunction): boolean {\n  return (read() !== 0);\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### Number ####\n\nNumbers may have the following types:\n```ts\nexport enum NUMBER_TYPES {\n  INT8 = 0x00,\n  UINT8 = 0x01,\n  INT16 = 0x02,\n  UINT16 = 0x03,\n  INT32 = 0x04,\n  UINT32 = 0x05,\n  INT64 = 0x06,\n  UINT64 = 0x07,\n  FLOAT32 = 0x08,\n  FLOAT64 = 0x09,\n}\n```\nThey are stored like that:\n\n`[0b[\u003cbit 7-0\u003e: NUMBER_TYPES[type of the number]], ...number bits stored as big-endian{1-8}]`\n\n**Example:** encoding `1234`\n1) Inferred type: `NUMBER_TYPES.UINT16`\n2) Bytes: `[3 /* number type (uint16) */, 4 /* high byte of the number */, 210 /* low byte of the number */]` = `[3, 4, 210]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nconst dataView = new DataView(new ArrayBuffer(8));\n\nexport function EncodeNumber(number: number, write: WriteFunction): void {\n  const type: NUMBER_TYPES = InferNumberTypeOfNumber(number);\n  write(type);\n  SetNumberInDataView(number, type, dataView, 0, false);\n  for (let i = 0, l = NumberTypeByteLength(type); i \u003c l; i++) {\n    write(dataView.getUint8(i));\n  }\n}\n\nexport function DecodeNumber(read: ReadFunction): number {\n  const type: NUMBER_TYPES = read();\n  for (let i = 0, l = NumberTypeByteLength(type); i \u003c l; i++) {\n    dataView.setUint8(i, read());\n  }\n  return GetNumberInDataView(type, dataView, 0, false);\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### String ####\n\nStrings are converted into an utf8 encoded Uint8Array, then the array [length](#size) is encoded using `EncodeSize` and finally the content is written just after.\n\n`[...size of the string{1,}, ...content of the string{0,}]`\n\n**Example:** encoding `'abc'`\n\n`[3 /* string's length */, 97 /* 'a' */, 98 /* 'b' */, 99 /* 'c' */]` =  `[3, 97, 98, 99]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeString(string: string, write: WriteFunction): void {\n  const bytes: Uint8Array = textEncoder.encode(string);\n  EncodeSize(bytes.length, write);\n  for (let i = 0, l = bytes.length; i \u003c l; i++) {\n    write(bytes[i]);\n  }\n}\n\nexport function DecodeString(read: ReadFunction): string {\n  const size: number = DecodeSize(read);\n  const bytes: Uint8Array = (size \u003c tempUint8Array.length) ? tempUint8Array : new Uint8Array(size);\n  for (let i = 0; i \u003c size; i++) {\n    bytes[i] = read();\n  }\n  return textDecoder.decode(bytes.subarray(0, size));\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### BigInt ####\n\nBigInts are simply stored as if they where [size](#size)\n\n**Example:** encoding `1234n`\n\n`[210, 9]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeBigInt(number: bigint, write: WriteFunction): void {\n  return EncodeBigSize(number, write);\n}\n\nexport function DecodeBigInt(read: ReadFunction): bigint {\n  return DecodeBigSize(read);\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### Date ####\n\nDates are stored as [number](#number) (timestamp in milliseconds) using `EncodeNumber`.\n\n`[...timestamp of the date in milliseconds encoded as number{2,9}]`\n\n**Example:** encoding `new Date('04 Dec 1995 00:12:00 GMT')`\n\n`[7 /* number type (uint64) */, 0, 0, 0, 190, 118, 189, 140, 128 /* ... number bits */]` =  `[7, 0, 0, 0, 190, 118, 189, 140, 128]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeDate(date: Date, write: WriteFunction): void {\n  EncodeNumber(date.valueOf(), write);\n}\n\nexport function DecodeDate(read: ReadFunction): Date {\n  return new Date(DecodeNumber(read));\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### RegExp ####\n\nRegExps are stored as a tuple of [string](#string) composed of the source and the flags using `EncodeString`.\n\n`[...regexp.source encoded as string{1,}, ...regexp.flags encoded as string{1,}]`\n\n**Example:** encoding `new RegExp(/abc/g)`\n\n`[3 /* regex.source's length */, 97, 98, 99 /* ... 'abc' */, 1 /* regex.flags' length */, 103 /* 'g' */]` = `[3, 97, 98, 99, 1, 103]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeRegExp(regexp: RegExp, write: WriteFunction): void {\n  EncodeString(regexp.source, write);\n  EncodeString(regexp.flags, write);\n}\n\nexport function DecodeRegExp(read: ReadFunction): RegExp {\n  return new RegExp(DecodeString(read), DecodeString(read));\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### ArrayBuffer ####\n\nArrayBuffers are stored as a tuple composed of its [size](#size) and its content bytes.\n\n`[...size of the buffer{1,}, ...buffer bytes{0,}]`\n\n**Example:** encoding `new Uint8Array([0, 1, 2]).buffer`\n\n`[3 /* buffer's size */, 0, 1, 2 /* ... buffer's content */]` = `[3, 0, 1, 2]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeArrayBuffer(buffer: ArrayBuffer | SharedArrayBuffer, write: WriteFunction, byteOffset: number = 0, byteLength: number = buffer.byteLength): void {\n  EncodeSize(byteLength, write);\n  const bytes: Uint8Array = new Uint8Array(buffer, byteOffset, byteLength);\n  for (let i = 0, l = bytes.length; i \u003c l; i++) {\n    write(bytes[i]);\n  }\n}\n\nexport function DecodeArrayBuffer(read: ReadFunction): ArrayBuffer {\n  const bytes: Uint8Array = new Uint8Array(DecodeSize(read));\n  for (let i = 0; i \u003c bytes.length; i++) {\n    bytes[i] = read();\n  }\n  return bytes.buffer;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### ArrayBufferView ####\n\nArrayBufferView (Uint8Array, Uint16Array, ...) are stored as a tuple composed of its [number type](#number) (uint8, uint16, etc... see `NUMBER_TYPES`) and its content encoded as an [ArrayBuffer](#array-buffer) with `EncodeArrayBuffer`.\n\n`[buffer type {1}, ...buffer size and bytes{1,}]`\n\n**Example:** encoding `new Uint8Array([0, 1, 2])`\n\n`[1 /* buffer's type (uint8) */, 3 /* buffer's size */, 0, 1, 2 /* ... buffer's content */]` =  `[1, 3, 0, 1, 2]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeArrayBufferView(buffer: ArrayBufferView, write: WriteFunction): void {\n  write(ArrayBufferViewToNumberType(buffer));\n  EncodeArrayBuffer(buffer.buffer, write, buffer.byteOffset, buffer.byteLength);\n}\n\nexport function DecodeArrayBufferView(read: ReadFunction): ArrayBufferView {\n  return new (NumberTypeToArrayBufferViewConstructor(read()))(DecodeArrayBuffer(read));\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### Map ####\n\nMaps are stored as:\n\n`[map entries' size {1,}, ...for each entries: tuple\u003cEncodeAny(key), EncodeAny(value)\u003e]`\n\n**Example:** encoding `new Map([['a', 1]])`\n\n```ts\n[\n    1 /* number of entries in the map */,\n\n    /** entry 0: **/\n\n        4 /* string type */,\n        1 /* string's length */,\n        97 /* 'a' */,\n\n        3 /* number type */,\n        1 /* (uint8) */,\n        1 /* value */\n]\n```\n= `[1, 4, 1, 97, 3, 1, 1]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeMap(\n  map: Map\u003cany, any\u003e,\n  write: WriteFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cany, Pointer\u003e = new Map\u003cany, Pointer\u003e()\n): void {\n  EncodeSize(map.size, write);\n\n  for (const entry of map.entries()) {\n    EncodeAny(entry[0], write, getPointer, memory);\n    EncodeAny(entry[1], write, getPointer, memory);\n  }\n}\n\nexport function DecodeMap(\n  read: ReadFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cPointer, any\u003e = new Map\u003cPointer, any\u003e(),\n  pointer: Pointer = getPointer()\n): Map\u003cany, any\u003e {\n  const size: number = DecodeSize(read);\n  const map: Map\u003cany, any\u003e = new Map\u003cany, any\u003e();\n  memory.set(pointer, map);\n  for (let i = 0; i \u003c size; i++) {\n    const key: any = DecodeAny(read, getPointer, memory);\n    const value: any = DecodeAny(read, getPointer, memory);\n    map.set(key, value);\n  }\n  return map;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### Set ####\n\nSets are stored as:\n\n`[set values' size {1,}, ...for each values: EncodeAny(value)]`\n\n**Example:** encoding `new Set(['a', 1])`\n\n```ts\n[\n    2 /* number of values in the set */,\n\n    /** entry 0: **/\n        4 /* string type */,\n        1 /* string's length */,\n        97 /* 'a' */,\n\n    /** entry 1: **/\n        3 /* number type */,\n        1 /* (uint8) */,\n        1 /* value */\n]\n```\n= `[2, 4, 1, 97, 3, 1, 1]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeSet(\n  set: Set\u003cany\u003e,\n  write: WriteFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cany, Pointer\u003e = new Map\u003cany, Pointer\u003e()\n): void {\n  EncodeSize(set.size, write);\n\n  for (const value of set.values()) {\n    EncodeAny(value, write, getPointer, memory);\n  }\n}\n\nexport function DecodeSet(\n  read: ReadFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cPointer, any\u003e = new Map\u003cPointer, any\u003e(),\n  pointer: Pointer = getPointer()\n): Set\u003cany\u003e {\n  const size: number = DecodeSize(read);\n  const set: Set\u003cany\u003e = new Set\u003cany\u003e();\n  memory.set(pointer, set);\n  for (let i = 0; i \u003c size; i++) {\n    set.add(DecodeAny(read, getPointer, memory));\n  }\n  return set;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### Array ####\n\nArrays are stored exactly as [Set](#set)\n\n**Example:** encoding `['a', 1]`\n\n```ts\n[\n    2 /* number of values in the array */,\n\n    /** entry 0: **/\n        4 /* string type */,\n        1 /* string's length */,\n        97 /* 'a' */,\n\n    /** entry 1: **/\n        3 /* number type */,\n        1 /* (uint8) */,\n        1 /* value */\n]\n```\n= `[2, 4, 1, 97, 3, 1, 1]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeArray(\n  array: any[],\n  write: WriteFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cany, Pointer\u003e = new Map\u003cany, Pointer\u003e()\n): void {\n  EncodeSize(array.length, write);\n\n  for (let i = 0, l = array.length; i \u003c l; i++) {\n    EncodeAny(array[i], write, getPointer, memory);\n  }\n}\n\nexport function DecodeArray(\n  read: ReadFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cPointer, any\u003e = new Map\u003cPointer, any\u003e(),\n  pointer: Pointer = getPointer()\n): any[] {\n  const size: number = DecodeSize(read);\n  const array: any[] = new Array\u003cany\u003e(size);\n  memory.set(pointer, array);\n  for (let i = 0; i \u003c size; i++) {\n    array[i] = DecodeAny(read, getPointer, memory);\n  }\n  return array;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### Object ####\n\nObjects are stored exactly as [Map](#map)\n\n**Example:** encoding `{ a: 1 }`\n\n```ts\n[\n    1 /* number of properties in the object */,\n\n    /** property 0: **/\n\n        /** property's key: **/\n            4 /* string type */,\n            1 /* string's length */,\n            97 /* 'a' */,\n\n        /** property's value: **/\n            3 /* number type */,\n            1 /* (uint8) */,\n            1 /* value */\n]\n```\n= `[1, 4, 1, 97, 3, 1, 1]`\n\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeObject(\n  object: any,\n  write: WriteFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cany, Pointer\u003e = new Map\u003cany, Pointer\u003e()\n): void {\n  const entries: [any, any][] = Object.entries(object);\n  EncodeSize(entries.length, write);\n\n  for (let i = 0, l = entries.length; i \u003c l; i++) {\n    EncodeAny(entries[i][0], write, getPointer, memory);\n    EncodeAny(entries[i][1], write, getPointer, memory);\n  }\n}\n\nexport function DecodeObject(\n  read: ReadFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cPointer, any\u003e = new Map\u003cPointer, any\u003e(),\n  pointer: Pointer = getPointer()\n): object {\n  const size: number = DecodeSize(read);\n  const object: any = {};\n  memory.set(pointer, object);\n  for (let i = 0; i \u003c size; i++) {\n    const key: any = DecodeAny(read, getPointer, memory);\n    object[key] = DecodeAny(read, getPointer, memory);\n  }\n  return object;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n#### Pointer ####\n\n```ts\nexport type Pointer = number;\nexport type GetPointerFunction = () =\u003e Pointer;\n```\nSome structures may include circular or shared references like: `const obj = {}; obj.obj = obj;`\n\nJBSON supports such conditions by creating a `Pointer` which is nothing more than the index where is reference as been encoded.\n\nPointers are stored as [size](#size)\n\n\n**Example:** full encoding of `const obj = {}; obj.obj = obj;`\n\n```ts\n[\n    17 /* object type */,\n    1 /* number of properties in the object */,\n\n    /** property 0: **/\n\n        /** property's key: **/\n            4 /* string type */,\n            3/* property key's length */,\n            111, 98, 106, /* 'obj' */\n\n        /** property's value: **/\n            127 /* pointer type */,\n            0 /* index where is stored the reference's value =\u003e 0 which is the index of the object */\n]\n```\n= `[17, 1, 4, 3, 111, 98, 106, 127, 0]`\n\n\u003cdetails\u003e\n\u003csummary\u003eshow\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodePointer(pointer: Pointer, write: WriteFunction): void {\n  return EncodeSize(pointer, write);\n}\n\nexport function DecodePointer(read: ReadFunction): Pointer {\n  return DecodeSize(read);\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n#### Any ####\n\n*Any* is the entry point for every value you want to encode / decode.\nThe encoder will convert a value into a sequence of bytes composed of the type of the value and its encoded bytes.\n\n```ts\nexport enum ANY_TYPES {\n  UNDEFINED = 0x00,\n  NULL = 0x01,\n  BOOLEAN = 0x02,\n  NUMBER = 0x03,\n  STRING = 0x04,\n  SYMBOL = 0x05,\n  BOOLEAN_OBJECT = 0x06,\n  NUMBER_OBJECT = 0x07,\n  STRING_OBJECT = 0x08,\n  DATE = 0x09,\n  REGEXP = 0x0a,\n  SHARED_ARRAY_BUFFER = 0x0b,\n  ARRAY_BUFFER = 0x0c,\n  ARRAY_BUFFER_VIEW = 0x0d,\n  MAP = 0x0e,\n  SET = 0x0f,\n  ARRAY = 0x10,\n  OBJECT = 0x11,\n  BIGINT = 0x12,\n\n  POINTER = 0x7f,\n}\n```\n\n`[value's type {1}, ...encoded value's bytes{1,}]`\n\n\u003cdetails\u003e\n\u003csummary\u003eshow encoding\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function EncodeAny(\n  value: any,\n  write: WriteFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cany, Pointer\u003e = new Map\u003cany, Pointer\u003e()\n): void {\n  if (memory.has(value)) {\n    write(ANY_TYPES.POINTER);\n    EncodePointer(memory.get(value) as Pointer, write);\n  } else {\n    if ((value !== null) \u0026\u0026 (value !== void 0) \u0026\u0026 (typeof value.toJBSON === 'function')) {\n      EncodeAny(value.toJBSON(), write, getPointer, memory);\n    } else {\n      const type: string = typeof value;\n\n      // p4\n      if (type === 'undefined') {\n        write(ANY_TYPES.UNDEFINED);\n\n      } else if (value === null) {\n        write(ANY_TYPES.NULL);\n\n      } else if (type === 'boolean') {\n        write(ANY_TYPES.BOOLEAN);\n        EncodeBoolean(value, write);\n\n      } else if (type === 'number') {\n        write(ANY_TYPES.NUMBER);\n        EncodeNumber(value, write);\n\n      } else if (type === 'string') {\n        write(ANY_TYPES.STRING);\n        EncodeString(value, write);\n\n      } else if (type === 'symbol') {  // p5\n        throw new Error(`Value could not be cloned: ${ value.toString() } is a Symbol`);\n\n      } else if (type === 'bigint') {\n        write(ANY_TYPES.BIGINT);\n        EncodeBigInt(value, write);\n\n      } else if (type === 'object') {\n        memory.set(value, getPointer()); // p6 \u0026 p23\n\n        if (value instanceof Boolean) { // p7\n          write(ANY_TYPES.BOOLEAN_OBJECT);\n          EncodeBoolean(value.valueOf(), write);\n\n        } else if (value instanceof Number) { // p8\n          write(ANY_TYPES.NUMBER_OBJECT);\n          EncodeNumber(value.valueOf(), write);\n\n        } else if (value instanceof String) { // p9\n          write(ANY_TYPES.STRING_OBJECT);\n          EncodeString(value.valueOf(), write);\n\n        } else if (value instanceof Date) { // p10\n          write(ANY_TYPES.DATE);\n          EncodeDate(value, write);\n\n        } else if (value instanceof RegExp) { // p11\n          write(ANY_TYPES.REGEXP);\n          EncodeRegExp(value, write);\n\n        } else if ((typeof SharedArrayBuffer !== 'undefined') \u0026\u0026 (value instanceof SharedArrayBuffer)) { // p12.2\n          // if(forStorage) throw new DataCloneError('Value could not be cloned: is a SharedArrayBuffer');\n          write(ANY_TYPES.SHARED_ARRAY_BUFFER);\n          EncodeArrayBuffer(value, write);\n\n        } else if (value instanceof ArrayBuffer) { // p12.3\n          write(ANY_TYPES.ARRAY_BUFFER);\n          EncodeArrayBuffer(value, write);\n\n        } else if (ArrayBuffer.isView(value)) { // p13\n          write(ANY_TYPES.ARRAY_BUFFER_VIEW);\n          EncodeArrayBufferView(value, write);\n\n        } else if (value instanceof Map) { // p14\n          write(ANY_TYPES.MAP);\n          EncodeMap(value, write, getPointer, memory);\n\n        } else if (value instanceof Set) { // p15\n          write(ANY_TYPES.SET);\n          EncodeSet(value, write, getPointer, memory);\n\n        } else if (Array.isArray(value)) { // p16\n          write(ANY_TYPES.ARRAY);\n          EncodeArray(value, write, getPointer, memory);\n\n        } else if (!IsPlainObject(value)) { // p18\n          if (typeof value.toJSON === 'function') {\n            EncodeAny(value.toJSON(), write, getPointer, memory);\n          } else {\n            // INFO super hard to implement\n            let string: string = String(value);\n            if (string.length \u003e 200) {\n              string = string.substring(0, 150) + '\\n[...]\\n' + string.slice(-50);\n            }\n            console.log(value);\n            throw new TypeError(`Unsupported type : ${ string }`);\n          }\n        } else {\n          write(ANY_TYPES.OBJECT);\n          EncodeObject(value, write, getPointer, memory);\n        }\n      } else {\n        throw new TypeError(`Unsupported type : ${ type }`);\n      }\n    }\n  }\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eshow decoding\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nexport function DecodeAny(\n  read: ReadFunction,\n  getPointer: GetPointerFunction,\n  memory: Map\u003cPointer, any\u003e = new Map\u003cPointer, any\u003e()\n): any {\n\n  const pointer: Pointer = getPointer();\n  const type: number = read();\n  let value: any;\n  switch (type) {\n\n    case ANY_TYPES.UNDEFINED:\n      return void 0;\n    case ANY_TYPES.NULL:\n      return null;\n    case ANY_TYPES.BOOLEAN:\n      return DecodeBoolean(read);\n    case ANY_TYPES.NUMBER:\n      return DecodeNumber(read);\n    case ANY_TYPES.STRING:\n      return DecodeString(read);\n    case ANY_TYPES.BIGINT:\n      return DecodeBigInt(read);\n\n    case ANY_TYPES.BOOLEAN_OBJECT:\n      value = Boolean(DecodeBoolean(read));\n      break;\n    case ANY_TYPES.NUMBER_OBJECT:\n      value = Number(DecodeNumber(read));\n      break;\n    case ANY_TYPES.STRING_OBJECT:\n      value = String(DecodeString(read));\n      break;\n    case ANY_TYPES.DATE:\n      value = DecodeDate(read);\n      break;\n    case ANY_TYPES.REGEXP:\n      value = DecodeRegExp(read);\n      break;\n    case ANY_TYPES.SHARED_ARRAY_BUFFER:\n      value = DecodeArrayBuffer(read);\n      break;\n    case ANY_TYPES.ARRAY_BUFFER:\n      value = DecodeArrayBuffer(read);\n      break;\n    case ANY_TYPES.ARRAY_BUFFER_VIEW:\n      value = DecodeArrayBufferView(read);\n      break;\n    case ANY_TYPES.MAP:\n      value = DecodeMap(read, getPointer, memory, pointer);\n      break;\n    case ANY_TYPES.SET:\n      value = DecodeSet(read, getPointer, memory, pointer);\n      break;\n    case ANY_TYPES.ARRAY:\n      value = DecodeArray(read, getPointer, memory, pointer);\n      break;\n    case ANY_TYPES.OBJECT:\n      value = DecodeObject(read, getPointer, memory, pointer);\n      break;\n    case ANY_TYPES.POINTER:\n      const address: Pointer = DecodePointer(read);\n      if (memory.has(address)) {\n        return memory.get(address);\n      } else {\n        throw new TypeError(`Find a pointer without valid pointed value`);\n      }\n    default:\n      throw new TypeError(`Invalid type found : ${ type }`);\n  }\n\n  memory.set(pointer, value);\n\n  return value;\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flifaon74%2Fjbson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flifaon74%2Fjbson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flifaon74%2Fjbson/lists"}