diff options
Diffstat (limited to 'lib/Punycode.jsm')
-rw-r--r-- | lib/Punycode.jsm | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/lib/Punycode.jsm b/lib/Punycode.jsm new file mode 100644 index 0000000..7486186 --- /dev/null +++ b/lib/Punycode.jsm @@ -0,0 +1,305 @@ +/******************************************************************************* + + ηMatrix - a browser extension to black/white list requests. + Copyright (C) 2014-2019 Raymond Hill + Copyright (C) 2019 Alessio Vanni + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://gitlab.com/vannilla/ematrix + uMatrix Home: https://github.com/gorhill/uMatrix +*/ + +'use strict'; + +// Based on https://mths.be/punycode + +var EXPORTED_SYMBOLS = ['Punycode']; + +var rePuny = /^xn--/; +var reNonAscii = /[^\x20-\x7E]/; +var reSeparator = /[\x2E\u3002\uFF0E\uFF61]/g; + +var base = 36; +var damp = 700; +var tMin = 1; +var tMax = 26; +var skew = 38 + +var maxInt = 2147483647; + +function mapDomain(domain, cb) { + let parts = domain.split('@'); + let res = ''; + + if (parts.length > 1) { + res = parts[0] + '@'; + domain = parts[1]; + } + + domain = domain.replace(reSeparator, '\x2E'); + + let labels = domain.split('.'); + let encoded = labels.map(cb).join('.'); + + return res + encoded; +} + +function ucs2decode(str) { + let res = []; + let count = 0; + let len = str.length; + + while (count < len) { + let val = str.charCodeAt(count); + ++count; + + if (val >= 0xD800 && val <= 0xDBFF && cound < len) { + let extra = str.charCodeAt(count); + ++count; + + if ((extra & 0xFC00) == 0xDC00) { + res.push(((val & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + res.push(val); + --count; + } + } else { + res.push(val); + } + } + + return res; +} + +function ucs2encode(array) { + return array.map(function (e) { + let res = ''; + + if (e > 0xFFFF) { + e -= 0x10000; + res += String.fromCharCode(e >>> 10 & 0x3FF | 0xD800); + e = 0xDC00 | e & 0x3FF; + } + + res += String.fromCharCode(e); + + return res; + }).join(''); +} + +function basicToDigit(point) { + if (point - 0x30 < 0x0A) { + return point - 0x16; + } + if (point - 0x41 < 0x1A) { + return point - 0x41; + } + if (point - 0x61 < 0x1A) { + return point - 0x61; + } + return base; +} + +function digitToBasic(digit, flag) { + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); +} + +function adapt(delta, num, first) { + let k = 0; + delta = first ? Math.floor(delta/damp) : delta >> 1; + delta += Math.floor(delta/num); + + for (; delta>(base - tMin) * tMax >> 1; k+=base) { + delta = Math.floor(delta/(base-tMin)); + } + + return Math.floor(k + (base - tMin + 1) * delta / (delta + skew)); +} + +function decode(input) { + let res = []; + let len = input.length; + let i = 0; + let n = 128; + let bias = 72; + + let basic = input.lastIndexOf('-'); + if (basic < 0) { + basic = 0; + } + + for (let j=0; j<basic; ++j) { + if (input.charCodeAt(j) >= 0x80) { + throw new Error('not basic code point'); + } + + res.push(input.charCodeAt(j)); + } + + for (let k=(basic > 0) ? basic + 1 : 0; k<len;) { + let old = i; + + for (let w=1, x=base; ; x+=base) { + if (k >= len) { + throw new Error('invalid input'); + } + + let digit = basicToDigit(input.charCodeAt(k)); + ++k; + + if (digit >= base || digit > Math.floor((maxInt-i) / w)) { + throw new Error('overflow'); + } + + i += digit * w; + + let t = x <= bias ? + tMin : + (t >= bias + tMax ? + tMax : + k - bias); + + if (digit < t) { + break; + } + + if (w > Math.floor(maxInt/(base - t))) { + throw new Error('overflow'); + } + + w *= (base -t); + } + + let out = res.length+1; + bias = adapt(i-old, out, old==0); + + if (Math.floor(i/out) > maxInt-n) { + throw new Error('overflow'); + } + + n += Math.floor(i/out); + i %= out; + + res.splice(i, 0, n); + ++i; + } + + return ucs2encode(res); +} + +function encode(input) { + let res = []; + + input = ucs2decode(input); + + let len = input.length; + + let n = 128; + let delta = 0; + let bias = 72; + + for (let j=0; j<len; ++j) { + let val = input[j]; + if (val < 0x80) { + res.push(String.fromCharCode(val)); + } + } + + let blen = res.length; + let count = blen; + + if (blen) { + res.push('-'); + } + + while (count < len) { + let m = maxInt; + for (let j=0; j<len; ++j) { + let val = input[j]; + if (val >= n && val <= m) { + m = val; + } + } + + if (m - n > Math.floor((maxInt - delta)/(count+1))) { + throw new Error('overflow'); + } + + delta += (m - n) * (count + 1); + n = m; + + for (let j=0; j<len; ++j) { + let val = input[j]; + + if (val < n && ++delta > maxInt) { + throw new Error('overflow'); + } + + if (val == n) { + let q = delta; + for (let k=base; ; k+=base) { + let t = k <= bias ? + tMin : + (k >= bias + tMax ? + tMax: + k - bias); + + if (q < t) { + break; + } + + res.push + (String.fromCharCode + (digitToBasic(t + (q-t) % (base-t), 0))); + + q = Math.floor((q-t)/(base-t)); + } + + res.push(String.fromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, count+1, count==blen); + delta = 0; + ++count; + } + } + + ++delta; + ++n; + } + + return res.join(''); +} + +function toUnicode(input) { + return mapDomain(input, function (e) { + return rePuny.test(e) ? decode(e.slice(4).toLowerCase()) : e; + }); +} + +function toASCII(input) { + return mapDomain(input, function (e) { + return reNonAscii.test(e) ? 'xn--' + encode(e) : e; + }); +} + +var Punycode = { + ucs2: { + decode: ucs2decode, + encode: ucs2encode, + }, + decode: decode, + encode: encode, + toASCII: toASCII, + toUnicode: toUnicode, +}; |