diff --git a/assembly/buffer/index.ts b/assembly/buffer/index.ts index 0159575..6201e2d 100644 --- a/assembly/buffer/index.ts +++ b/assembly/buffer/index.ts @@ -1,6 +1,10 @@ import { BLOCK_MAXSIZE, BLOCK, BLOCK_OVERHEAD } from "rt/common"; import { E_INVALIDLENGTH, E_INDEXOUTOFRANGE } from "util/error"; import { Uint8Array } from "typedarray"; +const BUFFER_INSPECT_HEADER_START = "(this.dataStart + offset, bswap(reinterpret(value))); return offset + 8; } + + /** + * Calculate an inspectable string to print to the console. + * + * @example + * let result = Buffer.from([1, 2, 3, 4, 5]).inspect(); + * // '' + */ + inspect(): string { + let byteLength = this.byteLength; + if (INSPECT_MAX_BYTES == 0 || byteLength == 0) return ""; + + // Calculate if an elipsis will be in the string + let elipsisEnd = byteLength > INSPECT_MAX_BYTES; + let maxBytes = elipsisEnd ? INSPECT_MAX_BYTES : byteLength; + + // find the start of the buffer + let dataStart = this.dataStart; + + /** + * Formula for stringLength is calculated by adding the constant character count + * to the number of visible bytes multiplied by 3, and adding 3 more for an ellipsis + * if the total number of visible bytes is less than the actual byteLength. + * + * @example + * let stringLength = (3 * maxBytes) + len("") + (elipsisEnd ? 3 : 0); + * + * // This can be reduced to... + * let stringLength = (3 * maxBytes) + 8 + (elipsisEnd ? 3 : 0); + * + * // finally, `a * 3 + 3` is the same thing as `(a + 1) * 3` + * // we can cast `elipsisEnd` to an integer `1 | 0` + * let stringLength = 3 * (maxBytes + i32(elipsisEnd)) + 8; + */ + let stringLength = 3 * (maxBytes + i32(elipsisEnd)) + 8; + + // create the result + let result = __alloc(stringLength << 1, idof()); + + // copy the 16 "(BUFFER_INSPECT_HEADER_START), + BUFFER_INSPECT_HEADER_BYTE_LEN, + ); + + // Start writing at index 8 + let writeOffset = result + BUFFER_INSPECT_HEADER_BYTE_LEN; + for (let i = 0; i < maxBytes; i++, writeOffset += 6) { + let byte = load(dataStart + i); + + store(writeOffset, Buffer.HEX.charsFromByte(byte)); + if (i == maxBytes - 1) { + if (elipsisEnd) { + // make this a single 64 bit store + store(writeOffset, 0x003e_002e_002e_002e, 4); // "...>" + } else { + store(writeOffset, 62, 4); // ">" + } + } else { + store(writeOffset, 32, 4); // " " + } + } + + return changetype(result); + } } export namespace Buffer { diff --git a/assembly/node.d.ts b/assembly/node.d.ts index 9e3f070..c2df1f7 100644 --- a/assembly/node.d.ts +++ b/assembly/node.d.ts @@ -11,6 +11,8 @@ declare class Buffer extends Uint8Array { readUInt8(offset?: i32): u8; /** Writes an inputted value to the buffer, at the desired offset. */ writeInt8(value:i8, offset?:i32): i32; + /** Reads a signed integer at the designated offset. */ + readInt8(offset?: i32): i8; /** Writes an inputted u8 value to the buffer, at the desired offset. */ writeUInt8(value:u8, offset?:i32): i32; /** Reads a signed 16-bit integer, stored in Little Endian format at the designated offset. */ @@ -77,6 +79,24 @@ declare class Buffer extends Uint8Array { writeDoubleLE(value: f64, offset?: i32): i32; /** Writes an inputted 64-bit double at the designated offset, stored in Big Endian format */ writeDoubleBE(value: f64, offset?: i32): i32; + /** Inspect a buffer. */ + inspect(): string; +} + +declare module "buffer" { + /** + * The maximum number of bytes to inspect on a buffer. + * + * @example + * import { INSPECT_MAX_BYTES } from "buffer"; + * // @ts-ignore: This is treated like a global + * INSPECT_MAX_BYTES = 10; + */ + export var INSPECT_MAX_BYTES: i32; + + // To export the buffer, we must obtain the `typeof Buffer` + const BuffType: typeof Buffer; + export { BuffType as Buffer }; } declare namespace Buffer { diff --git a/tests/buffer.spec.ts b/tests/buffer.spec.ts index 38a7a20..217d420 100644 --- a/tests/buffer.spec.ts +++ b/tests/buffer.spec.ts @@ -10,6 +10,7 @@ * }); */ import { BLOCK_MAXSIZE } from "rt/common"; +import { INSPECT_MAX_BYTES } from "buffer"; // Helper function to quickly create a Buffer from an array. //@ts-ignore @@ -140,6 +141,20 @@ describe("buffer", () => { // }).toThrow(); }); + + test("#inspect", () => { + let buff = create([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]); + let result = buff.inspect(); + expect(result).toBe(""); + INSPECT_MAX_BYTES = 5; + result = buff.inspect(); + expect(result).toBe(""); + + buff = new Buffer(0); + result = buff.inspect() + expect(result).toBe(""); + }); + test("#readUInt16LE", () => { let buff = create([0x0,0x05,0x0]); expect(buff.readUInt16LE()).toBe(1280); @@ -531,7 +546,7 @@ describe("buffer", () => { expected = create([5, 6, 7]); expect(actual).toStrictEqual(expected); }); - + test("#Hex.encode", () => { let actual = "000102030405060708090a0b0c0d0e0f102030405060708090a0b0c0d0e0f0"; let exampleBuffer = create([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0]);