| |
| |
| |
| |
| |
| |
| |
| |
| |
| const TO_BASE64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split(''); |
| |
| |
| |
| |
| const IGNORE_BASE64URL = ' \t\n\r='.split(''); |
| |
| |
| |
| |
| const FROM_BASE64URL = (() => { |
| const charMap = new Array(128); |
| for (let i = 0; i < charMap.length; i += 1) { |
| charMap[i] = -1; |
| } |
| for (let i = 0; i < IGNORE_BASE64URL.length; i += 1) { |
| charMap[IGNORE_BASE64URL[i].charCodeAt(0)] = -2; |
| } |
| for (let i = 0; i < TO_BASE64URL.length; i += 1) { |
| charMap[TO_BASE64URL[i].charCodeAt(0)] = i; |
| } |
| return charMap; |
| })(); |
| |
| |
| |
| |
| |
| |
| |
| export function byteToBase64URL(byte, state, emit) { |
| if (byte !== null) { |
| state.queue = (state.queue << 8) | byte; |
| state.queuedBits += 8; |
| while (state.queuedBits >= 6) { |
| const pos = (state.queue >> (state.queuedBits - 6)) & 63; |
| emit(TO_BASE64URL[pos]); |
| state.queuedBits -= 6; |
| } |
| } |
| else if (state.queuedBits > 0) { |
| state.queue = state.queue << (6 - state.queuedBits); |
| state.queuedBits = 6; |
| while (state.queuedBits >= 6) { |
| const pos = (state.queue >> (state.queuedBits - 6)) & 63; |
| emit(TO_BASE64URL[pos]); |
| state.queuedBits -= 6; |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| export function byteFromBase64URL(charCode, state, emit) { |
| const bits = FROM_BASE64URL[charCode]; |
| if (bits > -1) { |
| |
| state.queue = (state.queue << 6) | bits; |
| state.queuedBits += 6; |
| while (state.queuedBits >= 8) { |
| emit((state.queue >> (state.queuedBits - 8)) & 0xff); |
| state.queuedBits -= 8; |
| } |
| } |
| else if (bits === -2) { |
| |
| return; |
| } |
| else { |
| throw new Error(`Invalid Base64-URL character "${String.fromCharCode(charCode)}"`); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| export function stringToBase64URL(str) { |
| const base64 = []; |
| const emitter = (char) => { |
| base64.push(char); |
| }; |
| const state = { queue: 0, queuedBits: 0 }; |
| stringToUTF8(str, (byte) => { |
| byteToBase64URL(byte, state, emitter); |
| }); |
| byteToBase64URL(null, state, emitter); |
| return base64.join(''); |
| } |
| |
| |
| |
| |
| |
| |
| export function stringFromBase64URL(str) { |
| const conv = []; |
| const utf8Emit = (codepoint) => { |
| conv.push(String.fromCodePoint(codepoint)); |
| }; |
| const utf8State = { |
| utf8seq: 0, |
| codepoint: 0, |
| }; |
| const b64State = { queue: 0, queuedBits: 0 }; |
| const byteEmit = (byte) => { |
| stringFromUTF8(byte, utf8State, utf8Emit); |
| }; |
| for (let i = 0; i < str.length; i += 1) { |
| byteFromBase64URL(str.charCodeAt(i), b64State, byteEmit); |
| } |
| return conv.join(''); |
| } |
| |
| |
| |
| |
| |
| |
| export function codepointToUTF8(codepoint, emit) { |
| if (codepoint <= 0x7f) { |
| emit(codepoint); |
| return; |
| } |
| else if (codepoint <= 0x7ff) { |
| emit(0xc0 | (codepoint >> 6)); |
| emit(0x80 | (codepoint & 0x3f)); |
| return; |
| } |
| else if (codepoint <= 0xffff) { |
| emit(0xe0 | (codepoint >> 12)); |
| emit(0x80 | ((codepoint >> 6) & 0x3f)); |
| emit(0x80 | (codepoint & 0x3f)); |
| return; |
| } |
| else if (codepoint <= 0x10ffff) { |
| emit(0xf0 | (codepoint >> 18)); |
| emit(0x80 | ((codepoint >> 12) & 0x3f)); |
| emit(0x80 | ((codepoint >> 6) & 0x3f)); |
| emit(0x80 | (codepoint & 0x3f)); |
| return; |
| } |
| throw new Error(`Unrecognized Unicode codepoint: ${codepoint.toString(16)}`); |
| } |
| |
| |
| |
| |
| |
| |
| export function stringToUTF8(str, emit) { |
| for (let i = 0; i < str.length; i += 1) { |
| let codepoint = str.charCodeAt(i); |
| if (codepoint > 0xd7ff && codepoint <= 0xdbff) { |
| |
| |
| |
| const highSurrogate = ((codepoint - 0xd800) * 0x400) & 0xffff; |
| const lowSurrogate = (str.charCodeAt(i + 1) - 0xdc00) & 0xffff; |
| codepoint = (lowSurrogate | highSurrogate) + 0x10000; |
| i += 1; |
| } |
| codepointToUTF8(codepoint, emit); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| export function stringFromUTF8(byte, state, emit) { |
| if (state.utf8seq === 0) { |
| if (byte <= 0x7f) { |
| emit(byte); |
| return; |
| } |
| |
| for (let leadingBit = 1; leadingBit < 6; leadingBit += 1) { |
| if (((byte >> (7 - leadingBit)) & 1) === 0) { |
| state.utf8seq = leadingBit; |
| break; |
| } |
| } |
| if (state.utf8seq === 2) { |
| state.codepoint = byte & 31; |
| } |
| else if (state.utf8seq === 3) { |
| state.codepoint = byte & 15; |
| } |
| else if (state.utf8seq === 4) { |
| state.codepoint = byte & 7; |
| } |
| else { |
| throw new Error('Invalid UTF-8 sequence'); |
| } |
| state.utf8seq -= 1; |
| } |
| else if (state.utf8seq > 0) { |
| if (byte <= 0x7f) { |
| throw new Error('Invalid UTF-8 sequence'); |
| } |
| state.codepoint = (state.codepoint << 6) | (byte & 63); |
| state.utf8seq -= 1; |
| if (state.utf8seq === 0) { |
| emit(state.codepoint); |
| } |
| } |
| } |
| |
| |
| |
| export function base64UrlToUint8Array(str) { |
| const result = []; |
| const state = { queue: 0, queuedBits: 0 }; |
| const onByte = (byte) => { |
| result.push(byte); |
| }; |
| for (let i = 0; i < str.length; i += 1) { |
| byteFromBase64URL(str.charCodeAt(i), state, onByte); |
| } |
| return new Uint8Array(result); |
| } |
| export function stringToUint8Array(str) { |
| const result = []; |
| stringToUTF8(str, (byte) => result.push(byte)); |
| return new Uint8Array(result); |
| } |
| export function bytesToBase64URL(bytes) { |
| const result = []; |
| const state = { queue: 0, queuedBits: 0 }; |
| const onChar = (char) => { |
| result.push(char); |
| }; |
| bytes.forEach((byte) => byteToBase64URL(byte, state, onChar)); |
| |
| byteToBase64URL(null, state, onChar); |
| return result.join(''); |
| } |
| |