Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
[implement] rudimentary base64
  • Loading branch information
jtenner committed Aug 22, 2019
commit edace6be34ab0eb30ef2dd3769d6844b8e82a597
68 changes: 68 additions & 0 deletions assembly/buffer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,72 @@ export namespace Buffer {
return changetype<string>(result);
}
}

export namespace BASE64 {

function charCodeFromByte(value: i32): u64 {
if (i32(value >= 0) & i32(value < 26)) return value + 0x41;
else if (i32(value >= 26) & i32(value < 51)) return value + 0x61;
else return select(0x2B, 0x2F, value == 62);
}

export function byteLength(input: string): i32 {
return -1;
}

export function stringLength(buffer: ArrayBuffer): i32 {
return <i32>Math.ceil(<f32>buffer.byteLength / 3) << 2;
}

export function encode(input: string): ArrayBuffer {
return new ArrayBuffer(0);
}

export function decode(buffer: ArrayBuffer): string {
return decodeUnsafe(changetype<usize>(buffer), buffer.byteLength);
}

export function decodeUnsafe(pointer: usize, length: i32): string {
if (length == 0) return "";
let iterations = <i32>Math.floor(length / 3);
let leftoverBytes = length % 3;
let stringLength = (iterations << 2) + select(4, 0, leftoverBytes);
let output = __alloc(stringLength << 1, idof<String>());

for (let i = 0; i < iterations; i++) {
let bytes = bswap<u32>(load<u32>(pointer + <usize>i * 3)) >>> 8;
let value1 = (bytes & 0b111111000000000000000000) >> 18; // first 6 bits
let value2 = (bytes & 0b111111000000000000) >> 12; // second 6 bits
let value3 = (bytes & 0b111111000000) >> 6 ; // third 6 bits
let value4 = (bytes & 0b111111) ; // fourth 6 bits

let chars: u64 = charCodeFromByte(value1)
| (charCodeFromByte(value2) << 16)
| (charCodeFromByte(value3) << 32)
| (charCodeFromByte(value4) << 48);
store<u64>(output + (<usize>i << alignof<u64>()), chars);
}

// A is 0x61
// = is 0x3D
if (leftoverBytes == 2) {
let bytes = <u32>bswap<u16>(load<u16>(pointer + <usize>(iterations * 3))) << 2;
let value1 = (bytes & 0b111111000000000000) >> 12; // first 6 bits
let value2 = (bytes & 0b111111000000) >> 6 ; // second 6 bits
let value3 = (bytes & 0b111111) >> 0 ; // third 6 bits
let chars: u64 = charCodeFromByte(value1)
| <u64>(charCodeFromByte(value2) << 16)
| <u64>(charCodeFromByte(value3) << 32)
| <u64>0x3D000000000000;
store<u64>(output + (<usize>iterations << alignof<u64>()), chars);
} else if (leftoverBytes == 1) {
let bytes = <i32>load<u8>(pointer + <usize>(iterations * 3)) >> 2;
let chars: u64 = charCodeFromByte(bytes & 0b111111)
| <u64>0x3D003D00410000;
store<u64>(output + (<usize>iterations << alignof<u64>()), chars);
}

return changetype<string>(output);
}
}
}
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { randomFillSync } = require("crypto");

for (let i = 0; i < 10; i++) {
let buffer = randomFillSync(Buffer.allocUnsafe(Math.floor(Math.random() * 100)));
let result = buffer.toString("base64");
console.log(Array.from(buffer), result);
}
34 changes: 33 additions & 1 deletion tests/buffer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ describe("buffer", () => {
// newBuff.swap64();
// }).toThrow();
});

test("#Hex.encode", () => {
let actual = "000102030405060708090a0b0c0d0e0f102030405060708090a0b0c0d0e0f0";
let exampleBuffer = create<Buffer>([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]);
Expand All @@ -584,4 +584,36 @@ describe("buffer", () => {
let decoded = Buffer.HEX.decode(exampleBuffer.buffer);
expect<string>(decoded).toStrictEqual(expected);
});

test("#Base64.decode", () => {
let decoded = Buffer.BASE64.decode(create<Buffer>([1, 2, 3]).buffer);
expect<string>(decoded).toBe("AQID");
decoded = Buffer.BASE64.decode(create<Buffer>([1, 2, 3, 4]).buffer);
expect<string>(decoded).toStrictEqual("AQIDBA==");
decoded = Buffer.BASE64.decode(create<Buffer>([1, 2, 3, 4, 5]).buffer);
expect<string>(decoded).toBe("AQIDBAU=", "target");

decoded = Buffer.BASE64.decode(create<Buffer>([
150, 144, 40, 36, 82, 6, 240, 81, 182, 94, 22,
137, 32, 212, 14, 6, 176, 169, 104, 91, 243, 241,
62, 242, 156, 72, 51, 139, 140, 227, 109, 204, 147,
130, 35, 104, 157, 139, 52, 201, 114, 154, 160, 230,
185, 245, 198, 192, 21, 153, 33, 120, 133, 40, 140,
215, 26, 233, 199, 245, 13, 211, 169, 183
]).buffer);
expect<string>(decoded).toBe("lpAoJFIG8FG2XhaJINQOBrCpaFvz8T7ynEgzi4zjbcyTgiNonYs0yXKaoOa59cbAFZkheIUojNca6cf1DdOptw==");

decoded = Buffer.BASE64.decode(create<Buffer>([
189, 36, 9, 30, 61, 86, 186, 198, 247, 150, 23, 224,
108, 178, 35, 223, 155, 242, 140, 171, 163, 130, 173, 126,
60, 70, 83, 211, 113, 6, 137, 140, 166, 98, 94, 94,
4, 110, 146, 2, 226, 51, 123, 226, 235, 4, 44, 31,
113, 69, 169, 100, 97, 170, 239, 185, 119, 106, 112, 107,
162, 44, 211, 166, 88, 225, 141, 38, 122, 82, 229, 153,
115, 23, 108, 119, 38, 17, 228, 204, 52, 14, 27, 104,
59, 64, 197, 109, 235, 206, 243, 196, 171, 179, 6, 62,
106
]).buffer);
expect<string>(decoded).toBe("vSQJHj1Wusb3lhfgbLIj35vyjKujgq1+PEZT03EGiYymYl5eBG6SAuIze+LrBCwfcUWpZGGq77l3anBroizTpljhjSZ6UuWZcxdsdyYR5Mw0DhtoO0DFbevO88SrswY+ag==");
});
});