From 47c9b7b5512a161b08fbd7b8a6bbfce253bc57c3 Mon Sep 17 00:00:00 2001 From: Joshua Tenner Date: Thu, 12 Mar 2020 18:00:12 -0400 Subject: [PATCH 1/5] start implementation --- assembly/buffer/index.ts | 29 +++++++++++++++++++++++++++++ assembly/node.d.ts | 10 ++++++++++ tests/buffer.spec.ts | 14 ++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/assembly/buffer/index.ts b/assembly/buffer/index.ts index 1c5a832..37f64fc 100644 --- a/assembly/buffer/index.ts +++ b/assembly/buffer/index.ts @@ -287,6 +287,35 @@ export class Buffer extends Uint8Array { } export namespace Buffer { + export namespace ASCII { + export function encode(str: string): ArrayBuffer { + let length = str.length; + let output = __alloc(length, idof()); + for (let i = 0; i < length; i++) { + let char = load(changetype(str) + i << 1); + store(output + i, (0b01111111 & char)); + } + return changetype(output); + } + + export function decode(buffer: ArrayBuffer): String { + return decodeUnsafe(changetype(buffer), buffer.byteLength); + } + + const ASCII_DECODE_LOOKUP = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + + @unsafe export function decodeUnsafe(pointer: usize, length: i32): String { + let result = __alloc(length << 1, idof()); + + for (let i = 0; i < length; i++) { + let byte = load(pointer + i); + store(result + (i << 1), load(changetype(ASCII_DECODE_LOOKUP) + ((byte & 0b01111111) << 1))); + } + + return changetype(result); + } + } + export namespace HEX { /** Calculates the byte length of the specified string when encoded as HEX. */ export function byteLength(str: string): i32 { diff --git a/assembly/node.d.ts b/assembly/node.d.ts index 419384a..a276e60 100644 --- a/assembly/node.d.ts +++ b/assembly/node.d.ts @@ -86,6 +86,7 @@ declare class Buffer extends Uint8Array { } declare namespace Buffer { + /** The HEX encoding and decoding namespace. */ export namespace HEX { /** Creates an ArrayBuffer from a given string that is encoded in the hex format. */ export function encode(str: string): ArrayBuffer; @@ -94,4 +95,13 @@ declare namespace Buffer { /** Decodes a chunk of memory to a utf16le encoded string in hex format. */ export function decodeUnsafe(ptr: usize, byteLength: i32): string; } + /** The ASCII encoding and decoding namespace. */ + export namespace ASCII { + /** Creates an ArrayBuffer from a given string that is encoded in the ASCII format. */ + export function encode(str: string): ArrayBuffer; + /** Creates a string from a given ArrayBuffer that is decoded into ASCII format. */ + export function decode(buffer: ArrayBuffer): string; + /** Decodes a chunk of memory to a utf16le encoded string in ASCII format. */ + export function decodeUnsafe(ptr: usize, byteLength: i32): string; + } } diff --git a/tests/buffer.spec.ts b/tests/buffer.spec.ts index b7681a4..dcd4177 100644 --- a/tests/buffer.spec.ts +++ b/tests/buffer.spec.ts @@ -550,4 +550,18 @@ describe("buffer", () => { let decoded = Buffer.HEX.decode(exampleBuffer.buffer); expect(decoded).toStrictEqual(expected); }); + + test("#ASCII.encode", () => { + let actual = "D34dB3eF"; + let exampleBuffer = create([0x44, 0x33, 0x34, 0x64, 0x42, 0x33, 0x65, 0x46]); + let encoded = Buffer.ASCII.encode(actual); + expect(encoded).toStrictEqual(exampleBuffer.buffer); + }); + + test("#ASCII.decode", () => { + let expected = create([0x44, 0x33, 0x34, 0x64, 0x42, 0x33, 0x65, 0x46]); + let example = "D34dB3eF"; + let encoded = Buffer.ASCII.decode(expected.buffer); + expect(encoded).toStrictEqual(example); + }); }); From 6729e19e38ab0bab98b10ee49d84354138625297 Mon Sep 17 00:00:00 2001 From: Joshua Tenner Date: Thu, 12 Mar 2020 21:16:25 -0400 Subject: [PATCH 2/5] fix operator grouping, now to optimize --- assembly/buffer/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly/buffer/index.ts b/assembly/buffer/index.ts index 37f64fc..ddc2f71 100644 --- a/assembly/buffer/index.ts +++ b/assembly/buffer/index.ts @@ -292,7 +292,7 @@ export namespace Buffer { let length = str.length; let output = __alloc(length, idof()); for (let i = 0; i < length; i++) { - let char = load(changetype(str) + i << 1); + let char = load(changetype(str) + (i << 1)); store(output + i, (0b01111111 & char)); } return changetype(output); From 0905b356c871b924da6429b8eb17c3a7723a8629 Mon Sep 17 00:00:00 2001 From: Joshua Tenner Date: Thu, 12 Mar 2020 21:22:56 -0400 Subject: [PATCH 3/5] remove lookup table, switch to simple bitwise --- assembly/buffer/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/assembly/buffer/index.ts b/assembly/buffer/index.ts index ddc2f71..80f28fb 100644 --- a/assembly/buffer/index.ts +++ b/assembly/buffer/index.ts @@ -302,14 +302,12 @@ export namespace Buffer { return decodeUnsafe(changetype(buffer), buffer.byteLength); } - const ASCII_DECODE_LOOKUP = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; - @unsafe export function decodeUnsafe(pointer: usize, length: i32): String { let result = __alloc(length << 1, idof()); for (let i = 0; i < length; i++) { let byte = load(pointer + i); - store(result + (i << 1), load(changetype(ASCII_DECODE_LOOKUP) + ((byte & 0b01111111) << 1))); + store(result + (i << 1), (byte & 0b01111111)); } return changetype(result); From 5ca0e6a59dbf34008e4f7e4a863ffb18383e27e6 Mon Sep 17 00:00:00 2001 From: Joshua Tenner Date: Fri, 13 Mar 2020 00:03:28 -0400 Subject: [PATCH 4/5] mask with shorter literal, remove explicit cast --- assembly/buffer/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assembly/buffer/index.ts b/assembly/buffer/index.ts index 80f28fb..76eab60 100644 --- a/assembly/buffer/index.ts +++ b/assembly/buffer/index.ts @@ -293,7 +293,7 @@ export namespace Buffer { let output = __alloc(length, idof()); for (let i = 0; i < length; i++) { let char = load(changetype(str) + (i << 1)); - store(output + i, (0b01111111 & char)); + store(output + i, char & 0x7F); } return changetype(output); } @@ -307,7 +307,7 @@ export namespace Buffer { for (let i = 0; i < length; i++) { let byte = load(pointer + i); - store(result + (i << 1), (byte & 0b01111111)); + store(result + (i << 1), byte & 0x7F); } return changetype(result); From 0bb5ea0b799b762f5ad74b2dcff778c7c35ae12d Mon Sep 17 00:00:00 2001 From: Joshua Tenner Date: Fri, 13 Mar 2020 00:04:39 -0400 Subject: [PATCH 5/5] add decorator --- assembly/buffer/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assembly/buffer/index.ts b/assembly/buffer/index.ts index 76eab60..627a273 100644 --- a/assembly/buffer/index.ts +++ b/assembly/buffer/index.ts @@ -302,6 +302,7 @@ export namespace Buffer { return decodeUnsafe(changetype(buffer), buffer.byteLength); } + // @ts-ignore: decorator @unsafe export function decodeUnsafe(pointer: usize, length: i32): String { let result = __alloc(length << 1, idof()); @@ -385,6 +386,7 @@ export namespace Buffer { } /** Decodes a chunk of memory to a utf16le encoded string in hex format. */ + // @ts-ignore: decorator @unsafe export function decodeUnsafe(ptr: usize, length: i32): string { let stringByteLength = length << 2; // length * (2 bytes per char) * (2 chars per input byte) let result = __alloc(stringByteLength, idof()); @@ -401,6 +403,7 @@ export namespace Buffer { } /** Calculates the two char combination from the byte. */ + // @ts-ignore: decorator @inline function charsFromByte(byte: u32): u32 { let hi = (byte >>> 4) & 0xF; let lo = byte & 0xF;