Skip to content

Instantly share code, notes, and snippets.

@paulc
Created June 20, 2021 11:41
Show Gist options
  • Save paulc/784b965284d0740d3adebdc5305cf4d1 to your computer and use it in GitHub Desktop.
Save paulc/784b965284d0740d3adebdc5305cf4d1 to your computer and use it in GitHub Desktop.
Base64 module for QuickJS
import * as os from "os";
import * as std from "std";
export const Base64 = {
_key : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
decode: function(input) {
input = input.replace(/[^A-Za-z0-9\+\/\=]/g,"");
const padding = input.includes("=") ? input.length - input.indexOf("=") : 0
const buflen = (input.length / 4) * 3 - padding;
const buf = new ArrayBuffer(buflen)
const uarray = new Uint8Array(buf);
let i = 0, j = 0, enc1, enc2, enc3, enc4;
while (i < input.length) {
enc1 = this._key.indexOf(input.charAt(i++));
enc2 = this._key.indexOf(input.charAt(i++));
enc3 = this._key.indexOf(input.charAt(i++));
enc4 = this._key.indexOf(input.charAt(i++));
uarray[j++] = (enc1 << 2) | (enc2 >> 4);
if (enc3 != 64) uarray[j++] = ((enc2 & 15) << 4) | (enc3 >> 2);
if (enc4 != 64) uarray[j++] = ((enc3 & 3) << 6) | enc4;
}
return buf;
},
decodeStr: function(input) {
return String.fromCharCode.apply(null, new Uint16Array(this.decode(input)));
},
encode: function(arrayBuffer) {
let base64 = '';
const bytes = new Uint8Array(arrayBuffer);
const byteLength = bytes.byteLength;
const byteRemainder = byteLength % 3;
const mainLength = byteLength - byteRemainder;
let a,b,c,d,chunk;
// Main loop deals with bytes in chunks of 3
for (let i = 0; i < mainLength; i += 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
d = chunk & 63; // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += this._key[a] + this._key[b] + this._key[c] + this._key[d];
}
// Deal with the remaining bytes and padding
if (byteRemainder === 1) {
chunk = bytes[mainLength];
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4; // 3 = 2^2 - 1
base64 += `${this._key[a]}${this._key[b]}==`;
} else if (byteRemainder === 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2; // 15 = 2^4 - 1
base64 += `${this._key[a]}${this._key[b]}${this._key[c]}=`;
}
return base64;
},
readFileToBuffer: function(f) {
const bufLength = 1024;
let buf = new ArrayBuffer(bufLength);
let outbuf;
let out = []
while (true) {
let n = std.in.read(buf,0,buf.byteLength);
if (n == bufLength) {
out.push(buf);
buf = new ArrayBuffer(bufLength);
} else {
outbuf = new ArrayBuffer((out.length*bufLength) + n)
const outbuf_v = new Uint8Array(outbuf);
let i = 0;
for (i=0;i<out.length;i++) {
outbuf_v.set(new Uint8Array(out[i]),i*bufLength,bufLength);
}
outbuf_v.set(new Uint8Array(buf,0,n),i*bufLength,n);
break;
}
}
return outbuf;
},
stringFromArray: function(a) {
return String.fromCharCode.apply(null,a);
},
}
if (scriptArgs[1] == "-d") {
const dec = Base64.decode(std.in.readAsString());
std.out.write(dec,0,dec.byteLength);
} else if (scriptArgs[1] == "-ds") {
const dec = Base64.decode(scriptArgs[2]);
std.out.write(dec,0,dec.byteLength);
} else {
print(Base64.encode(Base64.readFileToBuffer(std.in)));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment