Created
July 26, 2020 13:45
-
-
Save peter-leonov/8ad54c27e1924a556d6c1c11345011e1 to your computer and use it in GitHub Desktop.
Simple not-byte-precise base64 encoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const CODE_A = "A".charCodeAt(0); | |
const CODE_a = "a".charCodeAt(0); | |
const CODE_0 = "0".charCodeAt(0); | |
const CODE_p = "+".charCodeAt(0); | |
const CODE_s = "/".charCodeAt(0); | |
// Dear v8, inline this please | |
const toA = (n) => | |
n === 63 | |
? CODE_s | |
: n === 62 | |
? CODE_p | |
: n >= 52 | |
? n - 52 + CODE_0 | |
: n >= 26 | |
? n - 26 + CODE_a | |
: n + CODE_A; | |
const SRC_BLOCK_SIZE = 3; // bytes | |
const DST_BLOCK_SIZE = 4; // bytes | |
function encode(bytes) { | |
const blocks = (bytes.length / SRC_BLOCK_SIZE) | 0; | |
const blockBytes = blocks * SRC_BLOCK_SIZE; | |
const left = bytes.length - blockBytes; | |
const resultSize = blocks * DST_BLOCK_SIZE + (left ? left + 1 : 0); | |
const res = new Uint8Array(resultSize); | |
let isrc = 0; | |
let idst = 0; | |
for ( | |
; | |
isrc < blockBytes; | |
isrc += SRC_BLOCK_SIZE, idst += DST_BLOCK_SIZE | |
) { | |
const s1 = bytes[isrc + 0]; | |
const s2 = bytes[isrc + 1]; | |
const s3 = bytes[isrc + 2]; | |
res[idst + 0] = toA(s1 >> 2); | |
res[idst + 1] = toA(((s1 & 0b00000011) << 4) | (s2 >> 4)); | |
res[idst + 2] = toA(((s2 & 0b00001111) << 2) | (s3 >> 6)); | |
res[idst + 3] = toA(s3 & 0b111111); | |
} | |
switch (left) { | |
case 1: | |
{ | |
const s1 = bytes[isrc]; | |
res[idst + 0] = toA(s1 >> 2); | |
res[idst + 1] = toA((s1 & 0b00000011) << 4); | |
} | |
break; | |
case 2: | |
{ | |
const s1 = bytes[isrc]; | |
const s2 = bytes[isrc + 1]; | |
res[idst + 0] = toA(s1 >> 2); | |
res[idst + 1] = toA(((s1 & 0b00000011) << 4) | (s2 >> 4)); | |
res[idst + 2] = toA((s2 & 0b00001111) << 2); | |
} | |
break; | |
} | |
return res; | |
} | |
const toBitString = (p) => (n) => n.toString(2).padStart(p, "0"); | |
// const bytes = new TextEncoder().encode("abcdefghi0"); | |
// console.log(Array.from(bytes).map(toBitString(8)).join("")); | |
// console.log(Array.from(map8to6(bytes)).map(toBitString(6)).join("")); | |
// console.log( | |
// Array.from(encode(bytes)) | |
// .map((c) => String.fromCharCode(c)) | |
// .join("") | |
// ); | |
// average full res photo | |
const size = 2_000_000; | |
const data = new Uint8Array(size); | |
let rng = 123; | |
for (let i = 0; i <= size; i++) { | |
rng = (rng * 110315 + 1752157) & 0x7fffffff; | |
data[i] = rng % 256; | |
} | |
console.time("warmup"); | |
for (let i = 0; i <= 100; i++) { | |
encode(data); | |
} | |
console.timeEnd("warmup"); | |
// gives 701.285ms on 2012 MacBook Air | |
console.time("1000x"); | |
for (let i = 0; i <= 1000; i++) { | |
encode(data); | |
} | |
console.timeEnd("1000x"); | |
// gives 6.700s on 2012 MacBook Air | |
console.log("done"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment