Skip to content

Commit edace6b

Browse files
committed
[implement] rudimentary base64
1 parent e3e6b3a commit edace6b

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

assembly/buffer/index.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,4 +376,72 @@ export namespace Buffer {
376376
return changetype<string>(result);
377377
}
378378
}
379+
380+
export namespace BASE64 {
381+
382+
function charCodeFromByte(value: i32): u64 {
383+
if (i32(value >= 0) & i32(value < 26)) return value + 0x41;
384+
else if (i32(value >= 26) & i32(value < 51)) return value + 0x61;
385+
else return select(0x2B, 0x2F, value == 62);
386+
}
387+
388+
export function byteLength(input: string): i32 {
389+
return -1;
390+
}
391+
392+
export function stringLength(buffer: ArrayBuffer): i32 {
393+
return <i32>Math.ceil(<f32>buffer.byteLength / 3) << 2;
394+
}
395+
396+
export function encode(input: string): ArrayBuffer {
397+
return new ArrayBuffer(0);
398+
}
399+
400+
export function decode(buffer: ArrayBuffer): string {
401+
return decodeUnsafe(changetype<usize>(buffer), buffer.byteLength);
402+
}
403+
404+
export function decodeUnsafe(pointer: usize, length: i32): string {
405+
if (length == 0) return "";
406+
let iterations = <i32>Math.floor(length / 3);
407+
let leftoverBytes = length % 3;
408+
let stringLength = (iterations << 2) + select(4, 0, leftoverBytes);
409+
let output = __alloc(stringLength << 1, idof<String>());
410+
411+
for (let i = 0; i < iterations; i++) {
412+
let bytes = bswap<u32>(load<u32>(pointer + <usize>i * 3)) >>> 8;
413+
let value1 = (bytes & 0b111111000000000000000000) >> 18; // first 6 bits
414+
let value2 = (bytes & 0b111111000000000000) >> 12; // second 6 bits
415+
let value3 = (bytes & 0b111111000000) >> 6 ; // third 6 bits
416+
let value4 = (bytes & 0b111111) ; // fourth 6 bits
417+
418+
let chars: u64 = charCodeFromByte(value1)
419+
| (charCodeFromByte(value2) << 16)
420+
| (charCodeFromByte(value3) << 32)
421+
| (charCodeFromByte(value4) << 48);
422+
store<u64>(output + (<usize>i << alignof<u64>()), chars);
423+
}
424+
425+
// A is 0x61
426+
// = is 0x3D
427+
if (leftoverBytes == 2) {
428+
let bytes = <u32>bswap<u16>(load<u16>(pointer + <usize>(iterations * 3))) << 2;
429+
let value1 = (bytes & 0b111111000000000000) >> 12; // first 6 bits
430+
let value2 = (bytes & 0b111111000000) >> 6 ; // second 6 bits
431+
let value3 = (bytes & 0b111111) >> 0 ; // third 6 bits
432+
let chars: u64 = charCodeFromByte(value1)
433+
| <u64>(charCodeFromByte(value2) << 16)
434+
| <u64>(charCodeFromByte(value3) << 32)
435+
| <u64>0x3D000000000000;
436+
store<u64>(output + (<usize>iterations << alignof<u64>()), chars);
437+
} else if (leftoverBytes == 1) {
438+
let bytes = <i32>load<u8>(pointer + <usize>(iterations * 3)) >> 2;
439+
let chars: u64 = charCodeFromByte(bytes & 0b111111)
440+
| <u64>0x3D003D00410000;
441+
store<u64>(output + (<usize>iterations << alignof<u64>()), chars);
442+
}
443+
444+
return changetype<string>(output);
445+
}
446+
}
379447
}

index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { randomFillSync } = require("crypto");
2+
3+
for (let i = 0; i < 10; i++) {
4+
let buffer = randomFillSync(Buffer.allocUnsafe(Math.floor(Math.random() * 100)));
5+
let result = buffer.toString("base64");
6+
console.log(Array.from(buffer), result);
7+
}

tests/buffer.spec.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ describe("buffer", () => {
570570
// newBuff.swap64();
571571
// }).toThrow();
572572
});
573-
573+
574574
test("#Hex.encode", () => {
575575
let actual = "000102030405060708090a0b0c0d0e0f102030405060708090a0b0c0d0e0f0";
576576
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]);
@@ -584,4 +584,36 @@ describe("buffer", () => {
584584
let decoded = Buffer.HEX.decode(exampleBuffer.buffer);
585585
expect<string>(decoded).toStrictEqual(expected);
586586
});
587+
588+
test("#Base64.decode", () => {
589+
let decoded = Buffer.BASE64.decode(create<Buffer>([1, 2, 3]).buffer);
590+
expect<string>(decoded).toBe("AQID");
591+
decoded = Buffer.BASE64.decode(create<Buffer>([1, 2, 3, 4]).buffer);
592+
expect<string>(decoded).toStrictEqual("AQIDBA==");
593+
decoded = Buffer.BASE64.decode(create<Buffer>([1, 2, 3, 4, 5]).buffer);
594+
expect<string>(decoded).toBe("AQIDBAU=", "target");
595+
596+
decoded = Buffer.BASE64.decode(create<Buffer>([
597+
150, 144, 40, 36, 82, 6, 240, 81, 182, 94, 22,
598+
137, 32, 212, 14, 6, 176, 169, 104, 91, 243, 241,
599+
62, 242, 156, 72, 51, 139, 140, 227, 109, 204, 147,
600+
130, 35, 104, 157, 139, 52, 201, 114, 154, 160, 230,
601+
185, 245, 198, 192, 21, 153, 33, 120, 133, 40, 140,
602+
215, 26, 233, 199, 245, 13, 211, 169, 183
603+
]).buffer);
604+
expect<string>(decoded).toBe("lpAoJFIG8FG2XhaJINQOBrCpaFvz8T7ynEgzi4zjbcyTgiNonYs0yXKaoOa59cbAFZkheIUojNca6cf1DdOptw==");
605+
606+
decoded = Buffer.BASE64.decode(create<Buffer>([
607+
189, 36, 9, 30, 61, 86, 186, 198, 247, 150, 23, 224,
608+
108, 178, 35, 223, 155, 242, 140, 171, 163, 130, 173, 126,
609+
60, 70, 83, 211, 113, 6, 137, 140, 166, 98, 94, 94,
610+
4, 110, 146, 2, 226, 51, 123, 226, 235, 4, 44, 31,
611+
113, 69, 169, 100, 97, 170, 239, 185, 119, 106, 112, 107,
612+
162, 44, 211, 166, 88, 225, 141, 38, 122, 82, 229, 153,
613+
115, 23, 108, 119, 38, 17, 228, 204, 52, 14, 27, 104,
614+
59, 64, 197, 109, 235, 206, 243, 196, 171, 179, 6, 62,
615+
106
616+
]).buffer);
617+
expect<string>(decoded).toBe("vSQJHj1Wusb3lhfgbLIj35vyjKujgq1+PEZT03EGiYymYl5eBG6SAuIze+LrBCwfcUWpZGGq77l3anBroizTpljhjSZ6UuWZcxdsdyYR5Mw0DhtoO0DFbevO88SrswY+ag==");
618+
});
587619
});

0 commit comments

Comments
 (0)