soiz1's picture
Upload 225 files
7aec436 verified
// This implements a custom base85 encoding for improved efficiency compared to base64.
// The character set used is 0x2a - 0x7e of ASCII. Little endian.
// 0x3c (<) is replaced with 0x28 (opening parenthesis) and 0x3e (>) is replaced with 0x29 (closing parenthesis),
// which makes the encoded data safe to include in any HTML context without escapes.
const getBase85EncodeCharacter = (n) => {
n += 0x2a;
if (n === 0x3c) return 0x28;
if (n === 0x3e) return 0x29;
return n;
};
/**
* @param {Uint8Array} uint8 The data to encode. No assumptions made about backing buffer.
* @returns {string} Base 85 encoding
*/
export const encode = (uint8) => {
const originalLength = uint8.length;
// Data length needs to be a multiple of 4 so we can use getUint32.
// If it's not, we'll have to make a copy and pad with zeros.
let dataView;
if (originalLength % 4 !== 0) {
const newUint8 = new Uint8Array(Math.ceil(originalLength / 4) * 4);
for (let i = 0; i < originalLength; i++) {
newUint8[i] = uint8[i];
}
dataView = new DataView(newUint8.buffer);
} else {
dataView = new DataView(uint8.buffer, uint8.byteOffset, uint8.byteLength);
}
// Pre-allocating buffer and using TextDecoder at the end is faster than string concatenation
// Each set of 4 bytes is represented by 5 characters. Pad with zeros if needed.
const result = new Uint8Array(Math.ceil(originalLength / 4) * 5);
let resultIndex = 0;
for (let i = 0; i < dataView.byteLength; i += 4) {
let n = dataView.getUint32(i, true);
result[resultIndex++] = getBase85EncodeCharacter(n % 85);
n = Math.floor(n / 85);
result[resultIndex++] = getBase85EncodeCharacter(n % 85);
n = Math.floor(n / 85);
result[resultIndex++] = getBase85EncodeCharacter(n % 85);
n = Math.floor(n / 85);
result[resultIndex++] = getBase85EncodeCharacter(n % 85);
n = Math.floor(n / 85);
result[resultIndex++] = getBase85EncodeCharacter(n % 85);
}
return new TextDecoder().decode(result);
};
// Keep the base85 decode function up-to-date in packager.js
const getBase85DecodeValue = (code) => {
if (code === 0x28) code = 0x3c;
if (code === 0x29) code = 0x3e;
return code - 0x2a;
};
/**
* @param {string} str Base 85 data
* @param {ArrayBuffer} outBuffer Assumed to have a byteLength that is a multiple of 4
* @param {number} outOffset Assumed to have be a multiple of 4
*/
export const decode = (str, outBuffer, outOffset) => {
const view = new DataView(outBuffer, outOffset, Math.floor(str.length / 5 * 4));
for (let i = 0, j = 0; i < str.length; i += 5, j += 4) {
view.setUint32(j, (
getBase85DecodeValue(str.charCodeAt(i + 4)) * 85 * 85 * 85 * 85 +
getBase85DecodeValue(str.charCodeAt(i + 3)) * 85 * 85 * 85 +
getBase85DecodeValue(str.charCodeAt(i + 2)) * 85 * 85 +
getBase85DecodeValue(str.charCodeAt(i + 1)) * 85 +
getBase85DecodeValue(str.charCodeAt(i))
), true);
}
};