UNPKG

core-js

Version:
172 lines (142 loc) 4.97 kB
'use strict'; // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env` require('../modules/es.string.from-code-point'); var getBuiltIn = require('../internals/get-built-in'); var uncurryThis = require('../internals/function-uncurry-this'); var fromCharCode = String.fromCharCode; var fromCodePoint = getBuiltIn('String', 'fromCodePoint'); var $encodeURIComponent = encodeURIComponent; var $parseInt = parseInt; var charAt = uncurryThis(''.charAt); var push = uncurryThis([].push); var replace = uncurryThis(''.replace); var stringSlice = uncurryThis(''.slice); var exec = uncurryThis(/./.exec); var FALLBACK_REPLACER = '\uFFFD'; var VALID_HEX = /^[0-9a-f]+$/i; // a surrogate pair is matched first, so a one-unit match is always a lone surrogate var SURROGATE = /[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDFFF]/g; var parseHexOctet = function (string, start) { var substr = stringSlice(string, start, start + 2); if (!exec(VALID_HEX, substr)) return NaN; return $parseInt(substr, 16); }; var getLeadingOnes = function (octet) { var count = 0; for (var mask = 0x80; mask > 0 && (octet & mask) !== 0; mask >>= 1) { count++; } return count; }; var utf8Decode = function (octets) { var codePoint = null; var length = octets.length; switch (length) { case 1: codePoint = octets[0]; break; case 2: codePoint = (octets[0] & 0x1F) << 6 | (octets[1] & 0x3F); break; case 3: codePoint = (octets[0] & 0x0F) << 12 | (octets[1] & 0x3F) << 6 | (octets[2] & 0x3F); break; case 4: codePoint = (octets[0] & 0x07) << 18 | (octets[1] & 0x3F) << 12 | (octets[2] & 0x3F) << 6 | (octets[3] & 0x3F); break; } // reject surrogates, overlong encodings, and out-of-range codepoints if (codePoint === null || codePoint > 0x10FFFF || (codePoint >= 0xD800 && codePoint <= 0xDFFF) || codePoint < (length > 3 ? 0x10000 : length > 2 ? 0x800 : length > 1 ? 0x80 : 0) ) return null; return codePoint; }; var replaceLoneSurrogate = function (chunk) { return chunk.length === 2 ? chunk : FALLBACK_REPLACER; }; // https://url.spec.whatwg.org/#percent-decode /* eslint-disable max-depth -- ok */ var decode = function (input) { var length = input.length; var result = ''; var i = 0; while (i < length) { var decodedChar = charAt(input, i); if (decodedChar === '%') { if (charAt(input, i + 1) === '%' || i + 3 > length) { result += '%'; i++; continue; } var octet = parseHexOctet(input, i + 1); // eslint-disable-next-line no-self-compare -- NaN check if (octet !== octet) { result += decodedChar; i++; continue; } i += 2; var byteSequenceLength = getLeadingOnes(octet); if (byteSequenceLength === 0) { decodedChar = fromCharCode(octet); } else { if (byteSequenceLength === 1 || byteSequenceLength > 4) { result += FALLBACK_REPLACER; i++; continue; } var octets = [octet]; var sequenceIndex = 1; while (sequenceIndex < byteSequenceLength) { i++; if (i + 3 > length || charAt(input, i) !== '%') break; var nextByte = parseHexOctet(input, i + 1); // eslint-disable-next-line no-self-compare -- NaN check if (nextByte !== nextByte || nextByte > 191 || nextByte < 128) break; // https://encoding.spec.whatwg.org/#utf-8-decoder - position-specific byte ranges if (sequenceIndex === 1) { if (octet === 0xE0 && nextByte < 0xA0) break; if (octet === 0xED && nextByte > 0x9F) break; if (octet === 0xF0 && nextByte < 0x90) break; if (octet === 0xF4 && nextByte > 0x8F) break; } push(octets, nextByte); i += 2; sequenceIndex++; } if (octets.length !== byteSequenceLength) { result += FALLBACK_REPLACER; continue; } var codePoint = utf8Decode(octets); if (codePoint === null) { for (var replacement = 0; replacement < byteSequenceLength; replacement++) result += FALLBACK_REPLACER; i++; continue; } else { decodedChar = fromCodePoint(codePoint); } } } result += decodedChar; i++; } return result; }; /* eslint-enable max-depth -- ok */ // https://url.spec.whatwg.org/#string-percent-encode-after-encoding // a lone surrogate is the only input `encodeURIComponent` throws on, and the UTF-8 // encoder replaces it - so the throw selects the slow path instead of a per-call scan var encode = function (input) { try { return $encodeURIComponent(input); } catch (error) { return $encodeURIComponent(replace(input, SURROGATE, replaceLoneSurrogate)); } }; module.exports = { decode: decode, encode: encode };