mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
For strings we convert to Uint8Array, which does not support forEach on any version of IE, and is not polyfilled by default.
155 lines
3.2 KiB
JavaScript
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 };
|