@yume-chan/adb
Version:
TypeScript implementation of Android Debug Bridge (ADB) protocol.
261 lines • 9.8 kB
JavaScript
const [charToIndex, indexToChar, paddingChar] = /* #__PURE__ */ (() => {
// Array is faster than object literal or `Map`
const charToIndex = [];
const indexToChar = [];
const paddingChar = "=".charCodeAt(0);
function addRange(start, end) {
const charCodeStart = start.charCodeAt(0);
const charCodeEnd = end.charCodeAt(0);
for (let charCode = charCodeStart; charCode <= charCodeEnd; charCode += 1) {
charToIndex[charCode] = indexToChar.length;
indexToChar.push(charCode);
}
}
addRange("A", "Z");
addRange("a", "z");
addRange("0", "9");
addRange("+", "+");
addRange("/", "/");
return [charToIndex, indexToChar, paddingChar];
})();
/**
* Calculate the required length of the output buffer for the given input length.
*
* @param inputLength Length of the input in bytes
* @returns Length of the output in bytes
*/
export function calculateBase64EncodedLength(inputLength) {
const remainder = inputLength % 3;
const paddingLength = remainder !== 0 ? 3 - remainder : 0;
return [((inputLength + paddingLength) / 3) * 4, paddingLength];
}
export function encodeBase64(input, output) {
const [outputLength, paddingLength] = calculateBase64EncodedLength(input.length);
if (!output) {
output = new Uint8Array(outputLength);
encodeForward(input, output, paddingLength);
return output;
}
else {
if (output.length < outputLength) {
throw new TypeError("output buffer is too small");
}
output = output.subarray(0, outputLength);
// When input and output are on same ArrayBuffer,
// we check if it's possible to encode in-place.
if (input.buffer !== output.buffer) {
encodeForward(input, output, paddingLength);
}
else if (output.byteOffset + output.length - (paddingLength + 1) <=
input.byteOffset + input.length) {
// Output ends before input ends.
// Can encode forwards, because writing output won't catch up with reading input.
// The output end is subtracted by `(paddingLength + 1)` because
// depending on padding length, it's possible to write 1-3 extra bytes after input ends.
//
// The following diagrams show how the last read from input and the last write to output
// are not conflicting.
//
// spell: disable
//
// `paddingLength === 2` can write 3 extra bytes:
//
// input: | aaaaaabb | | | |
// output: | aaaaaa | bb0000 | = | = |
//
// `paddingLength === 1` can write 2 extra bytes:
//
// input: | aaaaaabb | bbbbcccc | | |
// output: | aaaaaa | bbbbbb | cccc00 | = |
//
// `paddingLength === 0` can write 1 extra byte:
//
// input: | aaaaaabb | bbbbcccc | ccdddddd | |
// output: | aaaaaa | bbbbbb | cccccc | dddddd |
//
// spell: enable
encodeForward(input, output, paddingLength);
}
else if (output.byteOffset >= input.byteOffset - 1) {
// Output starts after input starts
// So in backwards, writing output won't catch up with reading input.
// The input start is subtracted by `1`, Because as the first 3 bytes becomes 4 bytes,
// it's possible to write 1 extra byte before input starts.
// spell: disable-next-line
// input: | | aaaaaabb | bbbbcccc | ccdddddd |
// output: | aaaaaa | bbbbbb | cccccc | dddddd |
// Must encode backwards.
encodeBackward(input, output, paddingLength);
}
else {
// Input is in the middle of output,
// It's not possible to read either the first or the last three bytes
// before they are overwritten by the output.
throw new TypeError("input and output cannot overlap");
}
return outputLength;
}
}
function encodeForward(input, output, paddingLength) {
let inputIndex = 0;
let outputIndex = 0;
while (inputIndex < input.length - 2) {
/* cspell: disable-next-line */
// aaaaaabb
const x = input[inputIndex];
inputIndex += 1;
/* cspell: disable-next-line */
// bbbbcccc
const y = input[inputIndex];
inputIndex += 1;
/* cspell: disable-next-line */
// ccdddddd
const z = input[inputIndex];
inputIndex += 1;
output[outputIndex] = indexToChar[x >> 2];
outputIndex += 1;
output[outputIndex] = indexToChar[((x & 0b11) << 4) | (y >> 4)];
outputIndex += 1;
output[outputIndex] = indexToChar[((y & 0b1111) << 2) | (z >> 6)];
outputIndex += 1;
output[outputIndex] = indexToChar[z & 0b111111];
outputIndex += 1;
}
if (paddingLength === 2) {
/* cspell: disable-next-line */
// aaaaaabb
const x = input[inputIndex];
inputIndex += 1;
output[outputIndex] = indexToChar[x >> 2];
outputIndex += 1;
output[outputIndex] = indexToChar[(x & 0b11) << 4];
outputIndex += 1;
output[outputIndex] = paddingChar;
outputIndex += 1;
output[outputIndex] = paddingChar;
}
else if (paddingLength === 1) {
/* cspell: disable-next-line */
// aaaaaabb
const x = input[inputIndex];
inputIndex += 1;
/* cspell: disable-next-line */
// bbbbcccc
const y = input[inputIndex];
inputIndex += 1;
output[outputIndex] = indexToChar[x >> 2];
outputIndex += 1;
output[outputIndex] = indexToChar[((x & 0b11) << 4) | (y >> 4)];
outputIndex += 1;
output[outputIndex] = indexToChar[(y & 0b1111) << 2];
outputIndex += 1;
output[outputIndex] = paddingChar;
}
}
function encodeBackward(input, output, paddingLength) {
let inputIndex = input.length - 1;
let outputIndex = output.length - 1;
if (paddingLength === 2) {
/* cspell: disable-next-line */
// aaaaaabb
const x = input[inputIndex];
inputIndex -= 1;
output[outputIndex] = paddingChar;
outputIndex -= 1;
output[outputIndex] = paddingChar;
outputIndex -= 1;
output[outputIndex] = indexToChar[(x & 0b11) << 4];
outputIndex -= 1;
output[outputIndex] = indexToChar[x >> 2];
outputIndex -= 1;
}
else if (paddingLength === 1) {
/* cspell: disable-next-line */
// bbbbcccc
const y = input[inputIndex];
inputIndex -= 1;
/* cspell: disable-next-line */
// aaaaaabb
const x = input[inputIndex];
inputIndex -= 1;
output[outputIndex] = paddingChar;
outputIndex -= 1;
output[outputIndex] = indexToChar[(y & 0b1111) << 2];
outputIndex -= 1;
output[outputIndex] = indexToChar[((x & 0b11) << 4) | (y >> 4)];
outputIndex -= 1;
output[outputIndex] = indexToChar[x >> 2];
outputIndex -= 1;
}
while (inputIndex >= 0) {
/* cspell: disable-next-line */
// ccdddddd
const z = input[inputIndex];
inputIndex -= 1;
/* cspell: disable-next-line */
// bbbbcccc
const y = input[inputIndex];
inputIndex -= 1;
/* cspell: disable-next-line */
// aaaaaabb
const x = input[inputIndex];
inputIndex -= 1;
output[outputIndex] = indexToChar[z & 0b111111];
outputIndex -= 1;
output[outputIndex] = indexToChar[((y & 0b1111) << 2) | (z >> 6)];
outputIndex -= 1;
output[outputIndex] = indexToChar[((x & 0b11) << 4) | (y >> 4)];
outputIndex -= 1;
output[outputIndex] = indexToChar[x >> 2];
outputIndex -= 1;
}
}
export function decodeBase64(input) {
let padding;
if (input[input.length - 2] === "=") {
padding = 2;
}
else if (input[input.length - 1] === "=") {
padding = 1;
}
else {
padding = 0;
}
const result = new Uint8Array((input.length / 4) * 3 - padding);
let sIndex = 0;
let dIndex = 0;
while (sIndex < input.length - (padding !== 0 ? 4 : 0)) {
const a = charToIndex[input.charCodeAt(sIndex)];
sIndex += 1;
const b = charToIndex[input.charCodeAt(sIndex)];
sIndex += 1;
const c = charToIndex[input.charCodeAt(sIndex)];
sIndex += 1;
const d = charToIndex[input.charCodeAt(sIndex)];
sIndex += 1;
result[dIndex] = (a << 2) | ((b & 0b11_0000) >> 4);
dIndex += 1;
result[dIndex] = ((b & 0b1111) << 4) | ((c & 0b11_1100) >> 2);
dIndex += 1;
result[dIndex] = ((c & 0b11) << 6) | d;
dIndex += 1;
}
if (padding === 1) {
const a = charToIndex[input.charCodeAt(sIndex)];
sIndex += 1;
const b = charToIndex[input.charCodeAt(sIndex)];
sIndex += 1;
const c = charToIndex[input.charCodeAt(sIndex)];
result[dIndex] = (a << 2) | ((b & 0b11_0000) >> 4);
dIndex += 1;
result[dIndex] = ((b & 0b1111) << 4) | ((c & 0b11_1100) >> 2);
}
else if (padding === 2) {
const a = charToIndex[input.charCodeAt(sIndex)];
sIndex += 1;
const b = charToIndex[input.charCodeAt(sIndex)];
result[dIndex] = (a << 2) | ((b & 0b11_0000) >> 4);
}
return result;
}
//# sourceMappingURL=base64.js.map