Files
meteor/packages/base64/base64.js
James Burgess 480c5d5a56 Avoid use of forEach in Base64 encode method (#10629)
For strings we convert to Uint8Array, which does not support forEach on
any version of IE, and is not polyfilled by default.
2019-07-15 12:38:10 -04:00

155 lines
3.2 KiB
JavaScript

// Base 64 encoding
const BASE_64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const BASE_64_VALS = Object.create(null);
const getChar = val => BASE_64_CHARS.charAt(val);
const getVal = ch => ch === '=' ? -1 : BASE_64_VALS[ch];
for (let i = 0; i < BASE_64_CHARS.length; i++) {
BASE_64_VALS[getChar(i)] = i;
};
const encode = array => {
if (typeof array === "string") {
const str = array;
array = newBinary(str.length);
for (let i = 0; i < str.length; i++) {
const ch = str.charCodeAt(i);
if (ch > 0xFF) {
throw new Error(
"Not ascii. Base64.encode can only take ascii strings.");
}
array[i] = ch;
}
}
const answer = [];
let a = null;
let b = null;
let c = null;
let d = null;
for (let i = 0; i < array.length; i++) {
switch (i % 3) {
case 0:
a = (array[i] >> 2) & 0x3F;
b = (array[i] & 0x03) << 4;
break;
case 1:
b = b | (array[i] >> 4) & 0xF;
c = (array[i] & 0xF) << 2;
break;
case 2:
c = c | (array[i] >> 6) & 0x03;
d = array[i] & 0x3F;
answer.push(getChar(a));
answer.push(getChar(b));
answer.push(getChar(c));
answer.push(getChar(d));
a = null;
b = null;
c = null;
d = null;
break;
}
}
if (a != null) {
answer.push(getChar(a));
answer.push(getChar(b));
if (c == null) {
answer.push('=');
} else {
answer.push(getChar(c));
}
if (d == null) {
answer.push('=');
}
}
return answer.join("");
};
// XXX This is a weird place for this to live, but it's used both by
// this package and 'ejson', and we can't put it in 'ejson' without
// introducing a circular dependency. It should probably be in its own
// package or as a helper in a package that both 'base64' and 'ejson'
// use.
const newBinary = len => {
if (typeof Uint8Array === 'undefined' || typeof ArrayBuffer === 'undefined') {
const ret = [];
for (let i = 0; i < len; i++) {
ret.push(0);
}
ret.$Uint8ArrayPolyfill = true;
return ret;
}
return new Uint8Array(new ArrayBuffer(len));
};
const decode = str => {
let len = Math.floor((str.length * 3) / 4);
if (str.charAt(str.length - 1) == '=') {
len--;
if (str.charAt(str.length - 2) == '=') {
len--;
}
}
const arr = newBinary(len);
let one = null;
let two = null;
let three = null;
let j = 0;
for (let i = 0; i < str.length; i++) {
const c = str.charAt(i);
const v = getVal(c);
switch (i % 4) {
case 0:
if (v < 0) {
throw new Error('invalid base64 string');
}
one = v << 2;
break;
case 1:
if (v < 0) {
throw new Error('invalid base64 string');
}
one = one | (v >> 4);
arr[j++] = one;
two = (v & 0x0F) << 4;
break;
case 2:
if (v >= 0) {
two = two | (v >> 2);
arr[j++] = two;
three = (v & 0x03) << 6;
}
break;
case 3:
if (v >= 0) {
arr[j++] = three | v;
}
break;
}
}
return arr;
};
export const Base64 = { encode, decode, newBinary };