UNPKG

@yume-chan/adb

Version:

TypeScript implementation of Android Debug Bridge (ADB) protocol.

359 lines (291 loc) 10.9 kB
const [charToIndex, indexToChar, paddingChar] = /* #__PURE__ */ (() => { // Array is faster than object literal or `Map` const charToIndex: number[] = []; const indexToChar: number[] = []; const paddingChar = "=".charCodeAt(0); function addRange(start: string, end: string) { 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: number, ): [outputLength: number, paddingLength: number] { const remainder = inputLength % 3; const paddingLength = remainder !== 0 ? 3 - remainder : 0; return [((inputLength + paddingLength) / 3) * 4, paddingLength]; } /** * Encode the given input buffer into base64. * * @param input The input buffer * @returns The encoded output buffer */ export function encodeBase64(input: Uint8Array): Uint8Array; /** * Encode the given input into base64 and write it to the output buffer. * * The output buffer must be at least as long as the value returned by `calculateBase64EncodedLength`. * It can points to the same buffer as the input, as long as `output.offset <= input.offset - input.length / 3`, * or `output.offset >= input.offset - 1` * * @param input The input buffer * @param output The output buffer * @returns The number of bytes written to the output buffer */ export function encodeBase64(input: Uint8Array, output: Uint8Array): number; export function encodeBase64( input: Uint8Array, output?: Uint8Array, ): Uint8Array | number { 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: Uint8Array, output: Uint8Array, paddingLength: number, ) { 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: Uint8Array, output: Uint8Array, paddingLength: number, ) { 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: string): Uint8Array { let padding: number; 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; }