lots of small cleanup

This commit is contained in:
Moon Man 2024-01-29 08:59:30 -05:00
parent ec035e9a5d
commit 362fda5b4b
5 changed files with 203 additions and 208 deletions

171
dist/bert.d.ts vendored
View File

@ -1,3 +1,7 @@
/**
* TODO: bigint, large decimals. For right now, only use numbers that fit in
* JavaScript number.
*/
/// <reference types="node" /> /// <reference types="node" />
export declare const Types: { export declare const Types: {
readonly BERT_START: 131; readonly BERT_START: 131;
@ -22,6 +26,23 @@ export declare const Lang: {
readonly ELIXIR: 0; readonly ELIXIR: 0;
readonly ERLANG: 1; readonly ERLANG: 1;
}; };
interface Partial<Type> {
value: Type;
rest: Buffer;
}
export declare class Atom {
readonly value: string;
private constructor();
private static atoms;
static toAtom: (value: string) => Atom;
}
/**
* WIP WIP WIP
*/
export declare class Tuple {
readonly values: any[];
constructor(values: any[]);
}
export declare class Bert { export declare class Bert {
#private; #private;
private allBinariesAsString; private allBinariesAsString;
@ -147,150 +168,21 @@ export declare class Bert {
encode_tuple: (obj: Buffer, buffer: Buffer) => Buffer; encode_tuple: (obj: Buffer, buffer: Buffer) => Buffer;
encode_array: (obj: any[], buffer: Buffer) => Buffer; encode_array: (obj: any[], buffer: Buffer) => Buffer;
encode_map: (obj: Record<string, any>, buffer: Buffer) => Buffer; encode_map: (obj: Record<string, any>, buffer: Buffer) => Buffer;
decode_atom: (buffer: Buffer, count: 1 | 2 | 4) => { decode_atom: (buffer: Buffer, count: 1 | 2 | 4) => Partial<any>;
value: any; decode_binary: (buffer: Buffer) => Partial<string | Buffer>;
rest: Buffer; decode_integer: (buffer: Buffer, count: 1 | 2 | 4, unsigned?: boolean) => Partial<number>;
};
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) => { decode_big: (buffer: Buffer, count: 1 | 2 | 4) => {
value: number; value: number;
rest: Buffer; rest: Buffer;
}; };
decode_float: (buffer: Buffer) => { decode_float: (buffer: Buffer) => Partial<number>;
value: number; decode_new_float: (buffer: Buffer) => Partial<number>;
rest: Buffer; decode_string: (buffer: Buffer) => Partial<string>;
}; decode_list: (buffer: Buffer) => Partial<any[]>;
decode_new_float: (buffer: Buffer) => { decode_map: (buffer: Buffer) => Partial<Record<string, any>>;
value: number; decode_tuple: (buffer: Buffer, count: 1 | 2 | 4) => Partial<any>;
rest: Buffer; decode_nil: (buffer: Buffer) => Partial<[]>;
};
decode_string: (buffer: Buffer) => {
value: string;
rest: Buffer;
};
decode_list: (buffer: Buffer) => {
value: any[];
rest: Buffer;
};
decode_map: (buffer: Buffer) => {
value: Record<string, any>;
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>[]): 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<S extends any>(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<U>(callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any): U[];
filter<S_1 extends any>(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<U_1>(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<U_2>(callbackfn: (previousValue: U_2, currentValue: any, currentIndex: number, array: any[]) => U_2, initialValue: U_2): U_2;
find<S_2 extends any>(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<number>;
values(): IterableIterator<any>;
includes(searchElement: any, fromIndex?: number | undefined): boolean;
flatMap<U_3, This = undefined>(callback: (this: This, value: any, index: number, array: any[]) => U_3 | readonly U_3[], thisArg?: This | undefined): U_3[];
flat<A, D extends number = 1>(this: A, depth?: D | undefined): FlatArray<A, D>[];
at(index: number): any;
findLast<S_3 extends any>(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<any>;
[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; bytesToInt: (buffer: Buffer, length: 1 | 2 | 4, unsigned: boolean) => number;
binary_to_list: (str: string) => string[];
} }
/** /**
* Convert object to atom. * Convert object to atom.
@ -404,3 +296,4 @@ export declare const toTuple: (arr: any[]) => {
readonly [Symbol.unscopables]?: boolean | undefined; readonly [Symbol.unscopables]?: boolean | undefined;
}; };
}; };
export {};

93
dist/bert.js vendored
View File

@ -1,3 +1,7 @@
/**
* TODO: bigint, large decimals. For right now, only use numbers that fit in
* JavaScript number.
*/
export const Types = { export const Types = {
BERT_START: 131, BERT_START: 131,
SMALL_ATOM: 119, SMALL_ATOM: 119,
@ -21,7 +25,52 @@ export const Lang = {
ELIXIR: 0, ELIXIR: 0,
ERLANG: 1, ERLANG: 1,
}; };
/**
* WIP WIP WIP
*
* @param key
* @param value
* @returns
*/
const reviver = (key, value) => {
if (typeof value === "object" && value.type === "atom") {
return Atom.toAtom(value.value);
}
else if (typeof value === "object" && value.type === "tuple") {
return new Tuple(value.values);
}
else
return value;
};
const log = (msg) => process.stderr.write(`${msg}\r\n`); const log = (msg) => process.stderr.write(`${msg}\r\n`);
/**
* Return a subarray of the supplied buffer, minus len bytes from the front.
*
* @param buffer
* @param len Number of bytes to chop off front
* @returns Subarray of buffer
*/
const chopl = (buffer, len = 1) => buffer.subarray(len);
export class Atom {
value;
constructor(value) {
this.value = value;
}
static atoms = {};
static toAtom = (value) => {
return Atom.atoms[value] || (Atom.atoms[value] = new Atom(value));
};
}
/**
* WIP WIP WIP
*/
export class Tuple {
values;
constructor(values) {
// Yes right now this is only one level deep.
this.values = [...values];
}
}
export class Bert { export class Bert {
allBinariesAsString; allBinariesAsString;
mapKeyAsAtom; mapKeyAsAtom;
@ -56,7 +105,7 @@ export class Bert {
}; };
#decode = (buffer) => { #decode = (buffer) => {
const t = buffer[0]; const t = buffer[0];
buffer = buffer.subarray(1); buffer = chopl(buffer);
switch (t) { switch (t) {
case Types.SMALL_ATOM: case Types.SMALL_ATOM:
return this.decode_atom(buffer, 1); return this.decode_atom(buffer, 1);
@ -96,7 +145,7 @@ export class Bert {
if (buffer[0] !== Types.BERT_START) { if (buffer[0] !== Types.BERT_START) {
throw new Error("Invalid BERT start magic"); throw new Error("Invalid BERT start magic");
} }
const obj = this.#decode(buffer.subarray(1)); const obj = this.#decode(chopl(buffer));
if (obj.rest.length !== 0) { if (obj.rest.length !== 0) {
throw new Error(`Invalid BERT, remainder was: ${obj.rest.length}`); throw new Error(`Invalid BERT, remainder was: ${obj.rest.length}`);
} }
@ -253,7 +302,7 @@ export class Bert {
}; };
decode_atom = (buffer, count) => { decode_atom = (buffer, count) => {
const size = this.bytesToInt(buffer, count, true); const size = this.bytesToInt(buffer, count, true);
buffer = buffer.subarray(count); buffer = chopl(buffer, count);
let value = buffer.toString("utf8", 0, size); let value = buffer.toString("utf8", 0, size);
if (value === "true") { if (value === "true") {
value = true; value = true;
@ -276,33 +325,33 @@ export class Bert {
} }
return { return {
value, value,
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_binary = (buffer) => { decode_binary = (buffer) => {
const size = this.bytesToInt(buffer, 4, true); const size = this.bytesToInt(buffer, 4, true);
buffer = buffer.subarray(4); buffer = chopl(buffer, 4);
const bin = Buffer.alloc(size); const bin = Buffer.alloc(size);
buffer.copy(bin, 0, 0, size); buffer.copy(bin, 0, 0, size);
return { return {
value: this.convention === Lang.ELIXIR && this.allBinariesAsString value: this.convention === Lang.ELIXIR && this.allBinariesAsString
? bin.toString("utf-8") ? bin.toString("utf-8")
: bin, : bin,
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_integer = (buffer, count, unsigned = false) => { decode_integer = (buffer, count, unsigned = false) => {
return { return {
value: this.bytesToInt(buffer, count, unsigned), value: this.bytesToInt(buffer, count, unsigned),
rest: buffer.subarray(count), rest: chopl(buffer, count),
}; };
}; };
decode_big = (buffer, count) => { decode_big = (buffer, count) => {
const size = this.bytesToInt(buffer, count, false); const size = this.bytesToInt(buffer, count, true);
buffer = buffer.subarray(count); buffer = chopl(buffer, count);
let num = 0; let num = 0;
const isNegative = buffer[0] === 1; const isNegative = buffer[0] === 1;
buffer = buffer.subarray(1); buffer = chopl(buffer);
for (let i = size - 1; i >= 0; --i) { for (let i = size - 1; i >= 0; --i) {
const n = buffer[i]; const n = buffer[i];
if (num === 0) { if (num === 0) {
@ -317,35 +366,35 @@ export class Bert {
} }
return { return {
value: num, value: num,
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_float = (buffer) => { decode_float = (buffer) => {
const size = 31; const size = 31;
return { return {
value: parseFloat(buffer.toString("utf8", 0, size)), value: parseFloat(buffer.toString("utf8", 0, size)),
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_new_float = (buffer) => { decode_new_float = (buffer) => {
return { return {
value: buffer.readDoubleBE(0), value: buffer.readDoubleBE(0),
rest: buffer.subarray(8), rest: chopl(buffer, 8),
}; };
}; };
decode_string = (buffer) => { decode_string = (buffer) => {
const sizeLen = this.convention === Lang.ELIXIR ? 4 : 2; const sizeLen = this.convention === Lang.ELIXIR ? 4 : 2;
const size = this.bytesToInt(buffer, sizeLen, true); const size = this.bytesToInt(buffer, sizeLen, true);
buffer = buffer.subarray(sizeLen); buffer = chopl(buffer, sizeLen);
return { return {
value: buffer.toString("utf8", 0, size), value: buffer.toString("utf8", 0, size),
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_list = (buffer) => { decode_list = (buffer) => {
const arr = []; const arr = [];
const size = this.bytesToInt(buffer, 4, true); const size = this.bytesToInt(buffer, 4, true);
buffer = buffer.subarray(4); buffer = chopl(buffer, 4);
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
const el = this.#decode(buffer); const el = this.#decode(buffer);
arr.push(el.value); arr.push(el.value);
@ -355,7 +404,7 @@ export class Bert {
if (lastChar !== Types.NIL) { if (lastChar !== Types.NIL) {
throw new Error("List does not end with NIL"); throw new Error("List does not end with NIL");
} }
buffer = buffer.subarray(1); buffer = chopl(buffer);
return { return {
value: arr, value: arr,
rest: buffer, rest: buffer,
@ -364,7 +413,7 @@ export class Bert {
decode_map = (buffer) => { decode_map = (buffer) => {
const map = {}; const map = {};
const size = this.bytesToInt(buffer, 4, true); const size = this.bytesToInt(buffer, 4, true);
buffer = buffer.subarray(4); buffer = chopl(buffer, 4);
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
let el = this.#decode(buffer); let el = this.#decode(buffer);
const key = el.value; const key = el.value;
@ -381,7 +430,7 @@ export class Bert {
decode_tuple = (buffer, count) => { decode_tuple = (buffer, count) => {
const arr = []; const arr = [];
const size = this.bytesToInt(buffer, count, true); const size = this.bytesToInt(buffer, count, true);
buffer = buffer.subarray(count); buffer = chopl(buffer, count);
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
const el = this.#decode(buffer); const el = this.#decode(buffer);
arr.push(el.value); arr.push(el.value);
@ -409,12 +458,6 @@ export class Bert {
return unsigned ? buffer.readUInt32BE(0) : buffer.readInt32BE(0); 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. * Convert object to atom.

7
dist/main.js vendored
View File

@ -1,5 +1,6 @@
import { Bert } from "./bert.js"; import { Bert } from "./bert.js";
import { Duplex } from "node:stream"; import { Duplex } from "node:stream";
const log = (msg) => process.stderr.write(`${msg}\r\n`);
export class Port extends Duplex { export class Port extends Duplex {
bert; bert;
originalStdout; originalStdout;
@ -17,7 +18,7 @@ export class Port extends Duplex {
const lenBytes = process.stdin.read(4); const lenBytes = process.stdin.read(4);
if (lenBytes) { if (lenBytes) {
const termLen = this.bert.bytesToInt(lenBytes, 4, true); const termLen = this.bert.bytesToInt(lenBytes, 4, true);
process.stderr.write(`Got incoming term length: ${termLen} (bytes: <<${lenBytes.toString('hex').match(/../g).map((hex) => parseInt(`0x${hex}`).toString()).join(', ')}>>)\n`); log(`Got incoming term length: ${termLen} (bytes: <<${lenBytes.toString('hex').match(/../g).map((hex) => parseInt(`0x${hex}`).toString()).join(', ')}>>).`);
const termBytes = process.stdin.read(termLen); const termBytes = process.stdin.read(termLen);
if (termBytes) { if (termBytes) {
const decoded = this.bert.decode(termBytes); const decoded = this.bert.decode(termBytes);
@ -25,7 +26,7 @@ export class Port extends Duplex {
return decoded; return decoded;
} }
else { else {
process.stderr.write(`Term read got erroneous null.\n`); log("Term read got erroneous null.");
return null; return null;
} }
} }
@ -45,7 +46,7 @@ export class Port extends Duplex {
return true; return true;
} }
catch (error) { catch (error) {
process.stderr.write(`Error writing: ${error}\n`); log(`Error writing: ${error}`);
return false; return false;
} }
} }

View File

@ -1,3 +1,8 @@
/**
* TODO: bigint, large decimals. For right now, only use numbers that fit in
* JavaScript number.
*/
export const Types = { export const Types = {
BERT_START: 131, BERT_START: 131,
SMALL_ATOM: 119, SMALL_ATOM: 119,
@ -23,8 +28,63 @@ export const Lang = {
ERLANG: 1, ERLANG: 1,
} as const; } as const;
interface Partial<Type> {
value: Type;
rest: Buffer;
}
/**
* WIP WIP WIP
*
* @param key
* @param value
* @returns
*/
const reviver = (key: string, value: any) => {
if (typeof value === "object" && value.type === "atom") {
return Atom.toAtom(value.value);
}
else if (typeof value === "object" && value.type === "tuple") {
return new Tuple(value.values);
}
else return value;
};
const log = (msg: string) => process.stderr.write(`${msg}\r\n`); const log = (msg: string) => process.stderr.write(`${msg}\r\n`);
/**
* Return a subarray of the supplied buffer, minus len bytes from the front.
*
* @param buffer
* @param len Number of bytes to chop off front
* @returns Subarray of buffer
*/
const chopl = (buffer: Buffer, len = 1) => buffer.subarray(len);
export class Atom {
public readonly value;
private constructor(value: string) {
this.value = value;
}
private static atoms: Record<string, Atom> = {};
public static toAtom = (value: string) => {
return Atom.atoms[value] || (Atom.atoms[value] = new Atom(value));
};
}
/**
* WIP WIP WIP
*/
export class Tuple {
public readonly values;
constructor(values: any[]) {
// Yes right now this is only one level deep.
this.values = [...values];
}
}
export class Bert { export class Bert {
private allBinariesAsString; private allBinariesAsString;
private mapKeyAsAtom; private mapKeyAsAtom;
@ -74,7 +134,7 @@ export class Bert {
#decode = (buffer: Buffer): any => { #decode = (buffer: Buffer): any => {
const t = buffer[0]; const t = buffer[0];
buffer = buffer.subarray(1); buffer = chopl(buffer);
switch (t) { switch (t) {
case Types.SMALL_ATOM: case Types.SMALL_ATOM:
@ -117,7 +177,7 @@ export class Bert {
throw new Error("Invalid BERT start magic"); throw new Error("Invalid BERT start magic");
} }
const obj = this.#decode(buffer.subarray(1)); const obj = this.#decode(chopl(buffer));
if (obj.rest.length !== 0) { if (obj.rest.length !== 0) {
throw new Error(`Invalid BERT, remainder was: ${obj.rest.length}`); throw new Error(`Invalid BERT, remainder was: ${obj.rest.length}`);
@ -191,7 +251,8 @@ export class Bert {
buffer.writeUInt8(offset - 1, 1); buffer.writeUInt8(offset - 1, 1);
numBuffer.copy(buffer, 2, 0, offset); numBuffer.copy(buffer, 2, 0, offset);
return buffer.subarray(0, 2 + offset); return buffer.subarray(0, 2 + offset);
} else { }
else {
buffer[0] = Types.LARGE_BIG; buffer[0] = Types.LARGE_BIG;
buffer.writeUInt32BE(offset - 1, 1); buffer.writeUInt32BE(offset - 1, 1);
numBuffer.copy(buffer, 5, 0, offset); numBuffer.copy(buffer, 5, 0, offset);
@ -297,9 +358,9 @@ export class Bert {
return buffer; return buffer;
}; };
decode_atom = (buffer: Buffer, count: 1 | 2 | 4) => { decode_atom = (buffer: Buffer, count: 1 | 2 | 4): Partial<any> => {
const size = this.bytesToInt(buffer, count, true); const size = this.bytesToInt(buffer, count, true);
buffer = buffer.subarray(count); buffer = chopl(buffer, count);
let value: any = buffer.toString("utf8", 0, size); let value: any = buffer.toString("utf8", 0, size);
if (value === "true") { if (value === "true") {
value = true; value = true;
@ -327,13 +388,13 @@ export class Bert {
return { return {
value, value,
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_binary = (buffer: Buffer) => { decode_binary = (buffer: Buffer): Partial<string | Buffer> => {
const size = this.bytesToInt(buffer, 4, true); const size = this.bytesToInt(buffer, 4, true);
buffer = buffer.subarray(4); buffer = chopl(buffer, 4);
const bin = Buffer.alloc(size); const bin = Buffer.alloc(size);
buffer.copy(bin, 0, 0, size); buffer.copy(bin, 0, 0, size);
return { return {
@ -341,31 +402,32 @@ export class Bert {
this.convention === Lang.ELIXIR && this.allBinariesAsString this.convention === Lang.ELIXIR && this.allBinariesAsString
? bin.toString("utf-8") ? bin.toString("utf-8")
: bin, : bin,
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_integer = (buffer: Buffer, count: 1 | 2 | 4, unsigned = false) => { decode_integer = (buffer: Buffer, count: 1 | 2 | 4, unsigned = false): Partial<number> => {
return { return {
value: this.bytesToInt(buffer, count, unsigned), value: this.bytesToInt(buffer, count, unsigned),
rest: buffer.subarray(count), rest: chopl(buffer, count),
}; };
}; };
decode_big = (buffer: Buffer, count: 1 | 2 | 4) => { decode_big = (buffer: Buffer, count: 1 | 2 | 4) => {
const size = this.bytesToInt(buffer, count, false); const size = this.bytesToInt(buffer, count, true);
buffer = buffer.subarray(count); buffer = chopl(buffer, count);
let num = 0; let num = 0;
const isNegative = buffer[0] === 1; const isNegative = buffer[0] === 1;
buffer = buffer.subarray(1); buffer = chopl(buffer);
for (let i = size - 1; i >= 0; --i) { for (let i = size - 1; i >= 0; --i) {
const n = buffer[i]; const n = buffer[i];
if (num === 0) { if (num === 0) {
num = n; num = n;
} else { }
else {
num = num * 256 + n; num = num * 256 + n;
} }
} }
@ -375,41 +437,41 @@ export class Bert {
return { return {
value: num, value: num,
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_float = (buffer: Buffer) => { decode_float = (buffer: Buffer): Partial<number> => {
const size = 31; const size = 31;
return { return {
value: parseFloat(buffer.toString("utf8", 0, size)), value: parseFloat(buffer.toString("utf8", 0, size)),
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_new_float = (buffer: Buffer) => { decode_new_float = (buffer: Buffer): Partial<number> => {
return { return {
value: buffer.readDoubleBE(0), value: buffer.readDoubleBE(0),
rest: buffer.subarray(8), rest: chopl(buffer, 8),
}; };
}; };
decode_string = (buffer: Buffer) => { decode_string = (buffer: Buffer): Partial<string> => {
const sizeLen = this.convention === Lang.ELIXIR ? 4 : 2; const sizeLen = this.convention === Lang.ELIXIR ? 4 : 2;
const size = this.bytesToInt(buffer, sizeLen, true); const size = this.bytesToInt(buffer, sizeLen, true);
buffer = buffer.subarray(sizeLen); buffer = chopl(buffer, sizeLen);
return { return {
value: buffer.toString("utf8", 0, size), value: buffer.toString("utf8", 0, size),
rest: buffer.subarray(size), rest: chopl(buffer, size),
}; };
}; };
decode_list = (buffer: Buffer) => { decode_list = (buffer: Buffer): Partial<any[]> => {
const arr = []; const arr = [];
const size = this.bytesToInt(buffer, 4, true); const size = this.bytesToInt(buffer, 4, true);
buffer = buffer.subarray(4); buffer = chopl(buffer, 4);
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
const el = this.#decode(buffer); const el = this.#decode(buffer);
@ -423,7 +485,7 @@ export class Bert {
throw new Error("List does not end with NIL"); throw new Error("List does not end with NIL");
} }
buffer = buffer.subarray(1); buffer = chopl(buffer);
return { return {
value: arr, value: arr,
@ -431,11 +493,11 @@ export class Bert {
}; };
}; };
decode_map = (buffer: Buffer) => { decode_map = (buffer: Buffer): Partial<Record<string, any>> => {
const map: Record<string, any> = {}; const map: Record<string, any> = {};
const size = this.bytesToInt(buffer, 4, true); const size = this.bytesToInt(buffer, 4, true);
buffer = buffer.subarray(4); buffer = chopl(buffer, 4);
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
let el = this.#decode(buffer); let el = this.#decode(buffer);
@ -451,22 +513,23 @@ export class Bert {
}; };
}; };
decode_tuple = (buffer: Buffer, count: 1 | 2 | 4) => { decode_tuple = (buffer: Buffer, count: 1 | 2 | 4): Partial<any> => {
const arr = []; const arr = [];
const size = this.bytesToInt(buffer, count, true); const size = this.bytesToInt(buffer, count, true);
buffer = buffer.subarray(count); buffer = chopl(buffer, count);
for (let i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
const el = this.#decode(buffer); const el = this.#decode(buffer);
arr.push(el.value); arr.push(el.value);
buffer = el.rest; buffer = el.rest;
} }
return { return {
value: this.toTuple(arr), value: this.toTuple(arr),
rest: buffer, rest: buffer,
}; };
}; };
decode_nil = (buffer: Buffer) => { decode_nil = (buffer: Buffer): Partial<[]> => {
// nil is an empty list // nil is an empty list
return { return {
value: [], value: [],
@ -484,13 +547,6 @@ export class Bert {
return unsigned ? buffer.readUInt32BE(0) : buffer.readInt32BE(0); return unsigned ? buffer.readUInt32BE(0) : buffer.readInt32BE(0);
} }
}; };
binary_to_list = (str: string) => {
const ret = [];
for (let i = 0; i < str.length; ++i) ret.push(str[i]);
return ret;
};
} }
/** /**

View File

@ -3,6 +3,8 @@ import { Duplex } from "node:stream";
type WriteCallback = (error: Error | null | undefined) => void; type WriteCallback = (error: Error | null | undefined) => void;
const log = (msg: string) => process.stderr.write(`${msg}\r\n`);
export class Port extends Duplex { export class Port extends Duplex {
public readonly bert: Bert; public readonly bert: Bert;
private originalStdout; private originalStdout;
@ -22,7 +24,7 @@ export class Port extends Duplex {
const lenBytes = process.stdin.read(4); const lenBytes = process.stdin.read(4);
if (lenBytes) { if (lenBytes) {
const termLen = this.bert.bytesToInt(lenBytes, 4, true); const termLen = this.bert.bytesToInt(lenBytes, 4, true);
process.stderr.write(`Got incoming term length: ${termLen} (bytes: <<${lenBytes.toString('hex').match(/../g).map((hex: string) => parseInt(`0x${hex}`).toString()).join(', ')}>>)\n`); log(`Got incoming term length: ${termLen} (bytes: <<${lenBytes.toString('hex').match(/../g).map((hex: string) => parseInt(`0x${hex}`).toString()).join(', ')}>>).`);
const termBytes = process.stdin.read(termLen); const termBytes = process.stdin.read(termLen);
if (termBytes) { if (termBytes) {
@ -31,7 +33,7 @@ export class Port extends Duplex {
return decoded; return decoded;
} }
else { else {
process.stderr.write(`Term read got erroneous null.\n`); log("Term read got erroneous null.");
return null; return null;
} }
} }
@ -53,7 +55,7 @@ export class Port extends Duplex {
return true; return true;
} }
catch (error) { catch (error) {
process.stderr.write(`Error writing: ${error}\n`); log(`Error writing: ${error}`);
return false; return false;
} }
} }