diff --git a/.gitignore b/.gitignore
index f06235c..3c3629e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
node_modules
-dist
diff --git a/dist/bert.d.ts b/dist/bert.d.ts
new file mode 100644
index 0000000..bd49274
--- /dev/null
+++ b/dist/bert.d.ts
@@ -0,0 +1,406 @@
+///
+export declare const Types: {
+ BERT_START: number;
+ SMALL_ATOM: number;
+ ATOM: number;
+ BINARY: number;
+ SMALL_INTEGER: number;
+ INTEGER: number;
+ SMALL_BIG: number;
+ LARGE_BIG: number;
+ FLOAT: number;
+ STRING: number;
+ LIST: number;
+ SMALL_TUPLE: number;
+ LARGE_TUPLE: number;
+ MAP: number;
+ NIL: number;
+ NEW_FLOAT: number;
+ ZERO: number;
+};
+export declare const Lang: {
+ ELIXIR: number;
+ ERLANG: number;
+};
+export declare class Bert {
+ #private;
+ private allBinariesAsString;
+ private mapKeyAsAtom;
+ private decodeUndefinedValues;
+ private convention;
+ private outputBuffer;
+ constructor(allBinariesAsString?: boolean, mapKeyAsAtom?: boolean, decodeUndefinedValues?: boolean, convention?: number);
+ toAtom: (str: string) => {
+ type: string;
+ value: string;
+ toString: () => string;
+ };
+ toTuple: (arr: any[]) => {
+ type: string;
+ length: number;
+ value: any[];
+ toString: () => string;
+ toLocaleString(): string;
+ pop(): any;
+ push(...items: any[]): number;
+ concat(...items: ConcatArray[]): any[];
+ concat(...items: any[]): any[];
+ join(separator?: string | undefined): string;
+ reverse(): any[];
+ shift(): any;
+ slice(start?: number | undefined, end?: number | undefined): any[];
+ sort(compareFn?: ((a: any, b: any) => number) | undefined): any[];
+ splice(start: number, deleteCount?: number | undefined): any[];
+ splice(start: number, deleteCount: number, ...items: any[]): any[];
+ unshift(...items: any[]): number;
+ indexOf(searchElement: any, fromIndex?: number | undefined): number;
+ lastIndexOf(searchElement: any, fromIndex?: number | undefined): number;
+ every(predicate: (value: any, index: number, array: any[]) => value is S, thisArg?: any): this is S[];
+ every(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean;
+ some(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean;
+ forEach(callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any): void;
+ map(callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any): U[];
+ filter(predicate: (value: any, index: number, array: any[]) => value is S_1, thisArg?: any): S_1[];
+ filter(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): any[];
+ reduce(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any;
+ reduce(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any;
+ reduce(callbackfn: (previousValue: U_1, currentValue: any, currentIndex: number, array: any[]) => U_1, initialValue: U_1): U_1;
+ reduceRight(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any;
+ reduceRight(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any;
+ reduceRight(callbackfn: (previousValue: U_2, currentValue: any, currentIndex: number, array: any[]) => U_2, initialValue: U_2): U_2;
+ find(predicate: (value: any, index: number, obj: any[]) => value is S_2, thisArg?: any): S_2 | undefined;
+ find(predicate: (value: any, index: number, obj: any[]) => unknown, thisArg?: any): any;
+ findIndex(predicate: (value: any, index: number, obj: any[]) => unknown, thisArg?: any): number;
+ fill(value: any, start?: number | undefined, end?: number | undefined): any[];
+ copyWithin(target: number, start: number, end?: number | undefined): any[];
+ entries(): IterableIterator<[number, any]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
+ includes(searchElement: any, fromIndex?: number | undefined): boolean;
+ flatMap(callback: (this: This, value: any, index: number, array: any[]) => U_3 | readonly U_3[], thisArg?: This | undefined): U_3[];
+ flat(this: A, depth?: D | undefined): FlatArray[];
+ at(index: number): any;
+ findLast(predicate: (value: any, index: number, array: any[]) => value is S_3, thisArg?: any): S_3 | undefined;
+ findLast(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): any;
+ findLastIndex(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): number;
+ toReversed(): any[];
+ toSorted(compareFn?: ((a: any, b: any) => number) | undefined): any[];
+ toSpliced(start: number, deleteCount: number, ...items: any[]): any[];
+ toSpliced(start: number, deleteCount?: number | undefined): any[];
+ with(index: number, value: any): any[];
+ [Symbol.iterator](): IterableIterator;
+ [Symbol.unscopables]: {
+ [x: number]: boolean | undefined;
+ length?: boolean | undefined;
+ toString?: boolean | undefined;
+ toLocaleString?: boolean | undefined;
+ pop?: boolean | undefined;
+ push?: boolean | undefined;
+ concat?: boolean | undefined;
+ join?: boolean | undefined;
+ reverse?: boolean | undefined;
+ shift?: boolean | undefined;
+ slice?: boolean | undefined;
+ sort?: boolean | undefined;
+ splice?: boolean | undefined;
+ unshift?: boolean | undefined;
+ indexOf?: boolean | undefined;
+ lastIndexOf?: boolean | undefined;
+ every?: boolean | undefined;
+ some?: boolean | undefined;
+ forEach?: boolean | undefined;
+ map?: boolean | undefined;
+ filter?: boolean | undefined;
+ reduce?: boolean | undefined;
+ reduceRight?: boolean | undefined;
+ find?: boolean | undefined;
+ findIndex?: boolean | undefined;
+ fill?: boolean | undefined;
+ copyWithin?: boolean | undefined;
+ entries?: boolean | undefined;
+ keys?: boolean | undefined;
+ values?: boolean | undefined;
+ includes?: boolean | undefined;
+ flatMap?: boolean | undefined;
+ flat?: boolean | undefined;
+ at?: boolean | undefined;
+ findLast?: boolean | undefined;
+ findLastIndex?: boolean | undefined;
+ toReversed?: boolean | undefined;
+ toSorted?: boolean | undefined;
+ toSpliced?: boolean | undefined;
+ with?: boolean | undefined;
+ [Symbol.iterator]?: boolean | undefined;
+ readonly [Symbol.unscopables]?: boolean | undefined;
+ };
+ };
+ encode: (obj: any, noCopy?: boolean) => Buffer;
+ decode: (buffer: Buffer) => any;
+ encode_string: (obj: string, buffer: Buffer) => Buffer;
+ encode_boolean: (obj: boolean, buffer: Buffer) => any;
+ encode_number: (obj: number, buffer: Buffer) => Buffer;
+ encode_float: (obj: number, buffer: Buffer) => Buffer;
+ encode_object: (obj: any, buffer: Buffer) => any;
+ encode_atom: (obj: any, buffer: Buffer) => Buffer;
+ encode_binary: (obj: Buffer, buffer: Buffer) => Buffer;
+ encode_undefined: (_obj: Buffer, buffer: Buffer) => any;
+ encode_tuple: (obj: Buffer, buffer: Buffer) => Buffer;
+ encode_array: (obj: any[], buffer: Buffer) => Buffer;
+ encode_map: (obj: Record, buffer: Buffer) => Buffer;
+ decode_atom: (buffer: Buffer, count: 1 | 2 | 4) => {
+ value: any;
+ rest: Buffer;
+ };
+ decode_binary: (buffer: Buffer) => {
+ value: string | Buffer;
+ rest: Buffer;
+ };
+ decode_integer: (buffer: Buffer, count: 1 | 2 | 4, unsigned?: boolean) => {
+ value: number;
+ rest: Buffer;
+ };
+ decode_big: (buffer: Buffer, count: 1 | 2 | 4) => {
+ value: number;
+ rest: Buffer;
+ };
+ decode_float: (buffer: Buffer) => {
+ value: number;
+ rest: Buffer;
+ };
+ decode_new_float: (buffer: Buffer) => {
+ value: number;
+ rest: Buffer;
+ };
+ decode_string: (buffer: Buffer) => {
+ value: string;
+ rest: Buffer;
+ };
+ decode_list: (buffer: Buffer) => {
+ value: any[];
+ rest: Buffer;
+ };
+ decode_map: (buffer: Buffer) => {
+ value: Record;
+ rest: Buffer;
+ };
+ decode_tuple: (buffer: Buffer, count: 1 | 2 | 4) => {
+ value: {
+ type: string;
+ length: number;
+ value: any[];
+ toString: () => string;
+ toLocaleString(): string;
+ pop(): any;
+ push(...items: any[]): number;
+ concat(...items: ConcatArray[]): any[];
+ concat(...items: any[]): any[];
+ join(separator?: string | undefined): string;
+ reverse(): any[];
+ shift(): any;
+ slice(start?: number | undefined, end?: number | undefined): any[];
+ sort(compareFn?: ((a: any, b: any) => number) | undefined): any[];
+ splice(start: number, deleteCount?: number | undefined): any[];
+ splice(start: number, deleteCount: number, ...items: any[]): any[];
+ unshift(...items: any[]): number;
+ indexOf(searchElement: any, fromIndex?: number | undefined): number;
+ lastIndexOf(searchElement: any, fromIndex?: number | undefined): number;
+ every(predicate: (value: any, index: number, array: any[]) => value is S, thisArg?: any): this is S[];
+ every(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean;
+ some(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean;
+ forEach(callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any): void;
+ map(callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any): U[];
+ filter(predicate: (value: any, index: number, array: any[]) => value is S_1, thisArg?: any): S_1[];
+ filter(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): any[];
+ reduce(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any;
+ reduce(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any;
+ reduce(callbackfn: (previousValue: U_1, currentValue: any, currentIndex: number, array: any[]) => U_1, initialValue: U_1): U_1;
+ reduceRight(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any;
+ reduceRight(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any;
+ reduceRight(callbackfn: (previousValue: U_2, currentValue: any, currentIndex: number, array: any[]) => U_2, initialValue: U_2): U_2;
+ find(predicate: (value: any, index: number, obj: any[]) => value is S_2, thisArg?: any): S_2 | undefined;
+ find(predicate: (value: any, index: number, obj: any[]) => unknown, thisArg?: any): any;
+ findIndex(predicate: (value: any, index: number, obj: any[]) => unknown, thisArg?: any): number;
+ fill(value: any, start?: number | undefined, end?: number | undefined): any[];
+ copyWithin(target: number, start: number, end?: number | undefined): any[];
+ entries(): IterableIterator<[number, any]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
+ includes(searchElement: any, fromIndex?: number | undefined): boolean;
+ flatMap(callback: (this: This, value: any, index: number, array: any[]) => U_3 | readonly U_3[], thisArg?: This | undefined): U_3[];
+ flat(this: A, depth?: D | undefined): FlatArray[];
+ at(index: number): any;
+ findLast(predicate: (value: any, index: number, array: any[]) => value is S_3, thisArg?: any): S_3 | undefined;
+ findLast(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): any;
+ findLastIndex(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): number;
+ toReversed(): any[];
+ toSorted(compareFn?: ((a: any, b: any) => number) | undefined): any[];
+ toSpliced(start: number, deleteCount: number, ...items: any[]): any[];
+ toSpliced(start: number, deleteCount?: number | undefined): any[];
+ with(index: number, value: any): any[];
+ [Symbol.iterator](): IterableIterator;
+ [Symbol.unscopables]: {
+ [x: number]: boolean | undefined;
+ length?: boolean | undefined;
+ toString?: boolean | undefined;
+ toLocaleString?: boolean | undefined;
+ pop?: boolean | undefined;
+ push?: boolean | undefined;
+ concat?: boolean | undefined;
+ join?: boolean | undefined;
+ reverse?: boolean | undefined;
+ shift?: boolean | undefined;
+ slice?: boolean | undefined;
+ sort?: boolean | undefined;
+ splice?: boolean | undefined;
+ unshift?: boolean | undefined;
+ indexOf?: boolean | undefined;
+ lastIndexOf?: boolean | undefined;
+ every?: boolean | undefined;
+ some?: boolean | undefined;
+ forEach?: boolean | undefined;
+ map?: boolean | undefined;
+ filter?: boolean | undefined;
+ reduce?: boolean | undefined;
+ reduceRight?: boolean | undefined;
+ find?: boolean | undefined;
+ findIndex?: boolean | undefined;
+ fill?: boolean | undefined;
+ copyWithin?: boolean | undefined;
+ entries?: boolean | undefined;
+ keys?: boolean | undefined;
+ values?: boolean | undefined;
+ includes?: boolean | undefined;
+ flatMap?: boolean | undefined;
+ flat?: boolean | undefined;
+ at?: boolean | undefined;
+ findLast?: boolean | undefined;
+ findLastIndex?: boolean | undefined;
+ toReversed?: boolean | undefined;
+ toSorted?: boolean | undefined;
+ toSpliced?: boolean | undefined;
+ with?: boolean | undefined;
+ [Symbol.iterator]?: boolean | undefined;
+ readonly [Symbol.unscopables]?: boolean | undefined;
+ };
+ };
+ rest: Buffer;
+ };
+ decode_nil: (buffer: Buffer) => {
+ value: never[];
+ rest: Buffer;
+ };
+ bytesToInt: (buffer: Buffer, length: 1 | 2 | 4, unsigned: boolean) => number;
+ binary_to_list: (str: string) => string[];
+}
+/**
+ * Convert object to atom.
+ *
+ */
+export declare const toAtom: (str: string) => {
+ type: string;
+ value: string;
+ toString: () => string;
+};
+/**
+ * Convert array of items to tuple.
+ *
+ */
+export declare const toTuple: (arr: any[]) => {
+ type: string;
+ length: number;
+ value: any[];
+ toString: () => string;
+ toLocaleString(): string;
+ pop(): any;
+ push(...items: any[]): number;
+ concat(...items: ConcatArray[]): any[];
+ concat(...items: any[]): any[];
+ join(separator?: string | undefined): string;
+ reverse(): any[];
+ shift(): any;
+ slice(start?: number | undefined, end?: number | undefined): any[];
+ sort(compareFn?: ((a: any, b: any) => number) | undefined): any[];
+ splice(start: number, deleteCount?: number | undefined): any[];
+ splice(start: number, deleteCount: number, ...items: any[]): any[];
+ unshift(...items: any[]): number;
+ indexOf(searchElement: any, fromIndex?: number | undefined): number;
+ lastIndexOf(searchElement: any, fromIndex?: number | undefined): number;
+ every(predicate: (value: any, index: number, array: any[]) => value is S, thisArg?: any): this is S[];
+ every(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean;
+ some(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean;
+ forEach(callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any): void;
+ map(callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any): U[];
+ filter(predicate: (value: any, index: number, array: any[]) => value is S_1, thisArg?: any): S_1[];
+ filter(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): any[];
+ reduce(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any;
+ reduce(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any;
+ reduce(callbackfn: (previousValue: U_1, currentValue: any, currentIndex: number, array: any[]) => U_1, initialValue: U_1): U_1;
+ reduceRight(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any;
+ reduceRight(callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any;
+ reduceRight(callbackfn: (previousValue: U_2, currentValue: any, currentIndex: number, array: any[]) => U_2, initialValue: U_2): U_2;
+ find(predicate: (value: any, index: number, obj: any[]) => value is S_2, thisArg?: any): S_2 | undefined;
+ find(predicate: (value: any, index: number, obj: any[]) => unknown, thisArg?: any): any;
+ findIndex(predicate: (value: any, index: number, obj: any[]) => unknown, thisArg?: any): number;
+ fill(value: any, start?: number | undefined, end?: number | undefined): any[];
+ copyWithin(target: number, start: number, end?: number | undefined): any[];
+ entries(): IterableIterator<[number, any]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
+ includes(searchElement: any, fromIndex?: number | undefined): boolean;
+ flatMap(callback: (this: This, value: any, index: number, array: any[]) => U_3 | readonly U_3[], thisArg?: This | undefined): U_3[];
+ flat(this: A, depth?: D | undefined): FlatArray[];
+ at(index: number): any;
+ findLast(predicate: (value: any, index: number, array: any[]) => value is S_3, thisArg?: any): S_3 | undefined;
+ findLast(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): any;
+ findLastIndex(predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any): number;
+ toReversed(): any[];
+ toSorted(compareFn?: ((a: any, b: any) => number) | undefined): any[];
+ toSpliced(start: number, deleteCount: number, ...items: any[]): any[];
+ toSpliced(start: number, deleteCount?: number | undefined): any[];
+ with(index: number, value: any): any[];
+ [Symbol.iterator](): IterableIterator;
+ [Symbol.unscopables]: {
+ [x: number]: boolean | undefined;
+ length?: boolean | undefined;
+ toString?: boolean | undefined;
+ toLocaleString?: boolean | undefined;
+ pop?: boolean | undefined;
+ push?: boolean | undefined;
+ concat?: boolean | undefined;
+ join?: boolean | undefined;
+ reverse?: boolean | undefined;
+ shift?: boolean | undefined;
+ slice?: boolean | undefined;
+ sort?: boolean | undefined;
+ splice?: boolean | undefined;
+ unshift?: boolean | undefined;
+ indexOf?: boolean | undefined;
+ lastIndexOf?: boolean | undefined;
+ every?: boolean | undefined;
+ some?: boolean | undefined;
+ forEach?: boolean | undefined;
+ map?: boolean | undefined;
+ filter?: boolean | undefined;
+ reduce?: boolean | undefined;
+ reduceRight?: boolean | undefined;
+ find?: boolean | undefined;
+ findIndex?: boolean | undefined;
+ fill?: boolean | undefined;
+ copyWithin?: boolean | undefined;
+ entries?: boolean | undefined;
+ keys?: boolean | undefined;
+ values?: boolean | undefined;
+ includes?: boolean | undefined;
+ flatMap?: boolean | undefined;
+ flat?: boolean | undefined;
+ at?: boolean | undefined;
+ findLast?: boolean | undefined;
+ findLastIndex?: boolean | undefined;
+ toReversed?: boolean | undefined;
+ toSorted?: boolean | undefined;
+ toSpliced?: boolean | undefined;
+ with?: boolean | undefined;
+ [Symbol.iterator]?: boolean | undefined;
+ readonly [Symbol.unscopables]?: boolean | undefined;
+ };
+};
diff --git a/dist/bert.js b/dist/bert.js
new file mode 100644
index 0000000..85bcc7e
--- /dev/null
+++ b/dist/bert.js
@@ -0,0 +1,440 @@
+export const Types = {
+ BERT_START: 131,
+ SMALL_ATOM: 115,
+ ATOM: 100,
+ BINARY: 109,
+ SMALL_INTEGER: 97,
+ INTEGER: 98,
+ SMALL_BIG: 110,
+ LARGE_BIG: 111,
+ FLOAT: 99,
+ STRING: 107,
+ LIST: 108,
+ SMALL_TUPLE: 104,
+ LARGE_TUPLE: 105, // TODO: Implement.
+ MAP: 116,
+ NIL: 106,
+ NEW_FLOAT: 70,
+ ZERO: 0,
+};
+export const Lang = {
+ ELIXIR: 0,
+ ERLANG: 1,
+};
+export class Bert {
+ allBinariesAsString;
+ mapKeyAsAtom;
+ decodeUndefinedValues;
+ convention;
+ outputBuffer;
+ constructor(allBinariesAsString = false, mapKeyAsAtom = true, decodeUndefinedValues = true, convention = Lang.ELIXIR) {
+ this.allBinariesAsString = allBinariesAsString;
+ this.mapKeyAsAtom = mapKeyAsAtom;
+ this.decodeUndefinedValues = decodeUndefinedValues;
+ this.convention = convention;
+ this.outputBuffer = Buffer.alloc(10000000);
+ this.outputBuffer[0] = Types.BERT_START;
+ }
+ toAtom = toAtom;
+ toTuple = toTuple;
+ #encode = (obj, buffer) => this[`encode_${typeof obj}`](obj, buffer);
+ encode = (obj, noCopy = false) => {
+ const tailBuffer = this.#encode(obj, Buffer.from(this.outputBuffer, 1));
+ if (tailBuffer.length === 0) {
+ throw new Error("Bert encode a too big term, encoding buffer overflow");
+ }
+ else if (noCopy) {
+ return Buffer.from(this.outputBuffer, 0, this.outputBuffer.length - tailBuffer.length);
+ }
+ else {
+ const res = Buffer.alloc(this.outputBuffer.length - tailBuffer.length);
+ this.outputBuffer.copy(res, 0, 0, res.length);
+ return res;
+ }
+ };
+ #decode = (buffer) => {
+ const t = buffer[0];
+ buffer = Buffer.from(buffer, 1);
+ switch (t) {
+ case Types.SMALL_ATOM:
+ return this.decode_atom(buffer, 1);
+ case Types.ATOM:
+ return this.decode_atom(buffer, 2);
+ case Types.BINARY:
+ return this.decode_binary(buffer);
+ case Types.SMALL_INTEGER:
+ return this.decode_integer(buffer, 1, true);
+ case Types.INTEGER:
+ return this.decode_integer(buffer, 4);
+ case Types.SMALL_BIG:
+ return this.decode_big(buffer, 1);
+ case Types.LARGE_BIG:
+ return this.decode_big(buffer, 4);
+ case Types.FLOAT:
+ return this.decode_float(buffer);
+ case Types.NEW_FLOAT:
+ return this.decode_new_float(buffer);
+ case Types.STRING:
+ return this.decode_string(buffer);
+ case Types.LIST:
+ return this.decode_list(buffer);
+ case Types.SMALL_TUPLE:
+ return this.decode_tuple(buffer, 1);
+ // case Types.LARGE_TUPLE:
+ // return this.decode_large_tuple(buffer, 4)
+ case Types.NIL:
+ return this.decode_nil(buffer);
+ case Types.MAP:
+ return this.decode_map(buffer);
+ default:
+ throw new Error(`Unexpected BERT type: ${t}`);
+ }
+ };
+ decode = (buffer) => {
+ if (buffer[0] !== Types.BERT_START) {
+ throw new Error("Not a valid BERT");
+ }
+ const obj = this.#decode(Buffer.from(buffer, 1));
+ if (obj.rest.length !== 0) {
+ throw new Error("Invalid BERT");
+ }
+ return obj.value;
+ };
+ encode_string = (obj, buffer) => {
+ if (this.convention === Lang.ELIXIR) {
+ return this.encode_binary(Buffer.from(obj), buffer);
+ }
+ else {
+ buffer[0] = Types.STRING;
+ buffer.writeUInt16BE(obj.length, 1);
+ const len = buffer.write(obj, 3);
+ return Buffer.from(buffer, 3 + len);
+ }
+ };
+ encode_boolean = (obj, buffer) => {
+ if (obj) {
+ return this.#encode(this.toAtom("true"), buffer);
+ }
+ else {
+ return this.#encode(this.toAtom("false"), buffer);
+ }
+ };
+ encode_number = (obj, buffer) => {
+ const isInteger = obj % 1 === 0;
+ // Handle floats...
+ if (!isInteger) {
+ return this.encode_float(obj, buffer);
+ }
+ // Small int...
+ if (isInteger && obj >= 0 && obj < 256) {
+ buffer[0] = Types.SMALL_INTEGER;
+ buffer.writeUInt8(obj, 1);
+ return Buffer.from(buffer, 2);
+ }
+ // 4 byte int...
+ if (isInteger && obj >= -134217728 && obj <= 134217727) {
+ buffer[0] = Types.INTEGER;
+ buffer.writeInt32BE(obj, 1);
+ return Buffer.from(buffer, 5);
+ }
+ // Bignum...
+ const numBuffer = Buffer.alloc(buffer.length);
+ if (obj < 0) {
+ obj *= -1;
+ numBuffer[0] = 1;
+ }
+ else {
+ numBuffer[0] = 0;
+ }
+ let offset = 1;
+ while (obj !== 0) {
+ numBuffer[offset] = obj % 256;
+ obj = Math.floor(obj / 256);
+ offset++;
+ }
+ if (offset < 256) {
+ buffer[0] = Types.SMALL_BIG;
+ buffer.writeUInt8(offset - 1, 1);
+ numBuffer.copy(buffer, 2, 0, offset);
+ return Buffer.from(buffer, 2 + offset);
+ }
+ else {
+ buffer[0] = Types.LARGE_BIG;
+ buffer.writeUInt32BE(offset - 1, 1);
+ numBuffer.copy(buffer, 5, 0, offset);
+ return Buffer.from(buffer, 5 + offset);
+ }
+ };
+ encode_float = (obj, buffer) => {
+ // float...
+ buffer[0] = Types.NEW_FLOAT;
+ buffer.writeDoubleBE(obj, 1);
+ return Buffer.from(buffer, 9);
+ };
+ encode_object = (obj, buffer) => {
+ // Check if it's an atom, binary, or tuple...
+ if (obj === null) {
+ const undefinedAtom = this.convention === Lang.ELIXIR ? "nil" : "undefined";
+ return this.#encode(this.toAtom(undefinedAtom), buffer);
+ }
+ if (Buffer.isBuffer(obj)) {
+ return this.encode_binary(obj, buffer);
+ }
+ if (Array.isArray(obj)) {
+ return this.encode_array(obj, buffer);
+ }
+ if (obj.type === "Atom") {
+ return this.encode_atom(obj, buffer);
+ }
+ if (obj.type === "Tuple") {
+ return this.encode_tuple(obj, buffer);
+ }
+ // Treat the object as an associative array...
+ return this.encode_map(obj, buffer);
+ };
+ encode_atom = (obj, buffer) => {
+ buffer[0] = Types.ATOM;
+ buffer.writeUInt16BE(obj.value.length, 1);
+ const len = buffer.write(obj.value, 3);
+ return Buffer.from(buffer, 3 + len);
+ };
+ encode_binary = (obj, buffer) => {
+ buffer[0] = Types.BINARY;
+ buffer.writeUInt32BE(obj.length, 1);
+ obj.copy(buffer, 5);
+ return Buffer.from(buffer, 5 + obj.length);
+ };
+ encode_undefined = (_obj, buffer) => {
+ return this.#encode(null, buffer);
+ };
+ encode_tuple = (obj, buffer) => {
+ if (obj.length < 256) {
+ buffer[0] = Types.SMALL_TUPLE;
+ buffer.writeUInt8(obj.length, 1);
+ buffer = Buffer.from(buffer, 2);
+ }
+ else {
+ buffer[0] = Types.LARGE_TUPLE;
+ buffer.writeUInt32BE(obj.length, 1);
+ buffer = buffer.slice(5);
+ }
+ for (let i = 0; i < obj.length; ++i) {
+ buffer = this.#encode(obj[i], buffer);
+ }
+ return buffer;
+ };
+ encode_array = (obj, buffer) => {
+ if (obj.length === 0) {
+ buffer[0] = Types.NIL;
+ return Buffer.from(buffer, 1);
+ }
+ buffer[0] = Types.LIST;
+ buffer.writeUInt32BE(obj.length, 1);
+ buffer = Buffer.from(buffer, 5);
+ for (let i = 0; i < obj.length; ++i) {
+ buffer = this.#encode(obj[i], buffer);
+ }
+ buffer[0] = Types.NIL;
+ return Buffer.from(buffer, 1);
+ };
+ encode_map = (obj, buffer) => {
+ const keys = Object.keys(obj);
+ buffer[0] = Types.MAP;
+ buffer.writeUInt32BE(keys.length, 1);
+ buffer = Buffer.from(buffer, 5);
+ for (let i = 0; i < keys.length; ++i) {
+ const key = this.mapKeyAsAtom ? this.toAtom(keys[i]) : keys[i];
+ buffer = this.#encode(key, buffer);
+ buffer = this.#encode(obj[keys[i]], buffer);
+ }
+ return buffer;
+ };
+ decode_atom = (buffer, count) => {
+ const size = this.bytesToInt(buffer, count, true);
+ buffer = Buffer.from(buffer, count);
+ let value = buffer.toString("utf8", 0, size);
+ if (value === "true") {
+ value = true;
+ }
+ else if (value === "false") {
+ value = false;
+ }
+ else if (this.decodeUndefinedValues &&
+ this.convention === Lang.ELIXIR &&
+ value === "nil") {
+ value = null;
+ }
+ else if (this.decodeUndefinedValues &&
+ this.convention === Lang.ERLANG &&
+ value === "undefined") {
+ value = null;
+ }
+ else {
+ value = this.toAtom(value);
+ }
+ return {
+ value,
+ rest: Buffer.from(buffer, size),
+ };
+ };
+ decode_binary = (buffer) => {
+ const size = this.bytesToInt(buffer, 4, true);
+ buffer = Buffer.from(buffer, 4);
+ const bin = Buffer.alloc(size);
+ buffer.copy(bin, 0, 0, size);
+ return {
+ value: this.convention === Lang.ELIXIR && this.allBinariesAsString
+ ? bin.toString()
+ : bin,
+ rest: Buffer.from(buffer, size),
+ };
+ };
+ decode_integer = (buffer, count, unsigned = false) => {
+ return {
+ value: this.bytesToInt(buffer, count, unsigned),
+ rest: Buffer.from(buffer, count),
+ };
+ };
+ decode_big = (buffer, count) => {
+ const size = this.bytesToInt(buffer, count, false);
+ buffer = Buffer.from(buffer, count);
+ let num = 0;
+ const isNegative = buffer[0] === 1;
+ buffer = Buffer.from(buffer, 1);
+ for (let i = size - 1; i >= 0; --i) {
+ const n = buffer[i];
+ if (num === 0) {
+ num = n;
+ }
+ else {
+ num = num * 256 + n;
+ }
+ }
+ if (isNegative) {
+ num = num * -1;
+ }
+ return {
+ value: num,
+ rest: Buffer.from(buffer, size),
+ };
+ };
+ decode_float = (buffer) => {
+ const size = 31;
+ return {
+ value: parseFloat(buffer.toString("utf8", 0, size)),
+ rest: Buffer.from(buffer, size),
+ };
+ };
+ decode_new_float = (buffer) => {
+ return {
+ value: buffer.readDoubleBE(0),
+ rest: Buffer.from(buffer, 8),
+ };
+ };
+ decode_string = (buffer) => {
+ const size = this.bytesToInt(buffer, 2, true);
+ buffer = Buffer.from(buffer, 2);
+ return {
+ value: buffer.toString("utf8", 0, size),
+ rest: Buffer.from(buffer, size),
+ };
+ };
+ decode_list = (buffer) => {
+ const arr = [];
+ const size = this.bytesToInt(buffer, 4, true);
+ buffer = Buffer.from(buffer, 4);
+ for (let i = 0; i < size; ++i) {
+ const el = this.#decode(buffer);
+ arr.push(el.value);
+ buffer = el.rest;
+ }
+ const lastChar = buffer[0];
+ if (lastChar !== Types.NIL) {
+ throw new Error("List does not end with NIL");
+ }
+ buffer = Buffer.from(buffer, 1);
+ return {
+ value: arr,
+ rest: buffer,
+ };
+ };
+ decode_map = (buffer) => {
+ const map = {};
+ const size = this.bytesToInt(buffer, 4, true);
+ buffer = Buffer.from(buffer, 4);
+ for (let i = 0; i < size; ++i) {
+ let el = this.#decode(buffer);
+ const key = el.value;
+ el = this.#decode(el.rest);
+ const value = el.value;
+ map[key] = value;
+ buffer = el.rest;
+ }
+ return {
+ value: map,
+ rest: buffer,
+ };
+ };
+ decode_tuple = (buffer, count) => {
+ const arr = [];
+ const size = this.bytesToInt(buffer, count, true);
+ buffer = Buffer.from(buffer, count);
+ for (let i = 0; i < size; ++i) {
+ const el = this.#decode(buffer);
+ arr.push(el.value);
+ buffer = el.rest;
+ }
+ return {
+ value: this.toTuple(arr),
+ rest: buffer,
+ };
+ };
+ decode_nil = (buffer) => {
+ // nil is an empty list
+ return {
+ value: [],
+ rest: buffer,
+ };
+ };
+ bytesToInt = (buffer, length, unsigned) => {
+ switch (length) {
+ case 1:
+ return unsigned ? buffer.readUInt8(0) : buffer.readInt8(0);
+ case 2:
+ return unsigned ? buffer.readUInt16BE(0) : buffer.readInt16BE(0);
+ case 4:
+ return unsigned ? buffer.readUInt32BE(0) : buffer.readInt32BE(0);
+ }
+ };
+ binary_to_list = (str) => {
+ const ret = [];
+ for (let i = 0; i < str.length; ++i)
+ ret.push(str[i]);
+ return ret;
+ };
+}
+/**
+ * Convert object to atom.
+ *
+ */
+export const toAtom = (str) => ({
+ type: "Atom",
+ value: str,
+ toString: () => str,
+});
+/**
+ * Convert array of items to tuple.
+ *
+ */
+export const toTuple = (arr) => ({
+ ...arr,
+ type: "Tuple",
+ length: arr.length,
+ value: arr,
+ toString: () => "{" +
+ arr
+ .filter((i) => i !== "")
+ .map((i) => i.toString())
+ .join(", ") +
+ "}",
+});
diff --git a/dist/main.d.ts b/dist/main.d.ts
new file mode 100644
index 0000000..4d51085
--- /dev/null
+++ b/dist/main.d.ts
@@ -0,0 +1,9 @@
+///
+///
+import { Duplex } from "node:stream";
+export declare class Port extends Duplex {
+ constructor();
+ _read(): void;
+ _write(obj: Buffer, _encoding: any, callback: Function): void;
+ _final(): void;
+}
diff --git a/dist/main.js b/dist/main.js
new file mode 100644
index 0000000..b554e87
--- /dev/null
+++ b/dist/main.js
@@ -0,0 +1,45 @@
+import { Bert } from "./bert.js";
+import { Duplex } from "node:stream";
+const bert = new Bert();
+let termLen;
+let termBin;
+const fakeWrite = () => undefined;
+process.stdout.write = fakeWrite;
+export class Port extends Duplex {
+ constructor() {
+ super({ objectMode: true });
+ }
+ _read() {
+ {
+ let term;
+ if (termLen === undefined && null !== (termBin = process.stdin.read(4))) {
+ termLen = bert.bytesToInt(termBin, 4, true);
+ }
+ if (termLen !== undefined &&
+ null !== (term = process.stdin.read(termLen))) {
+ termLen = undefined;
+ this.push(bert.decode(term));
+ }
+ }
+ }
+ _write(obj, _encoding, callback) {
+ let term;
+ try {
+ term = bert.encode(obj, true);
+ }
+ catch (error) {
+ console.error(error);
+ process.exit(1);
+ }
+ const len = Buffer.alloc(4);
+ len.writeUInt32BE(term.length, 0);
+ process.stdout.write = this._write;
+ process.stdout.write(len);
+ process.stdout.write(term, callback);
+ process.stdout.write = fakeWrite;
+ }
+ // When all the data is done passing, it stops.
+ _final() {
+ this.push(null);
+ }
+}