| 35 | * Punycode is case-insensitive; decodePunycode handle labels individually without dot split |
| 36 | */ |
| 37 | export const decodePunycode = (input: string) => { |
| 38 | input = input.toLowerCase(); |
| 39 | input = input.startsWith("xn--") ? input.slice(4) : input; |
| 40 | if (!input || input.length > 251) error(ERR.INVALID); |
| 41 | const output: number[] = []; |
| 42 | const len = input.length; |
| 43 | let i = 0; |
| 44 | let n = initialN; |
| 45 | let bias = initialBias; |
| 46 | |
| 47 | const k = input.lastIndexOf(delimiter); |
| 48 | |
| 49 | let j = 0; |
| 50 | for (; j < k; ++j) { |
| 51 | const cp = input.codePointAt(j)!; |
| 52 | if (cp >= 0x80) error(ERR.INVALID); |
| 53 | output.push(cp); |
| 54 | } |
| 55 | |
| 56 | if (j > 0) j++; |
| 57 | |
| 58 | if (j >= len) error(ERR.INVALID); |
| 59 | |
| 60 | while (j < len) { |
| 61 | const oldi = i; |
| 62 | let w = 1; |
| 63 | |
| 64 | for (let k = base; ; k += base) { |
| 65 | if (j >= len) error(ERR.INVALID); |
| 66 | const cp = input.codePointAt(j++)!; |
| 67 | |
| 68 | let digit = -1; |
| 69 | // 0-9 / A-Z / a-z |
| 70 | if (cp >= 0x30 && cp < 0x3a) digit = 26 + (cp - 0x30); |
| 71 | else if (cp >= 0x41 && cp < 0x5b) digit = cp - 0x41; |
| 72 | else if (cp >= 0x61 && cp < 0x7b) digit = cp - 0x61; |
| 73 | else error(ERR.INVALID); |
| 74 | |
| 75 | i += digit * w; |
| 76 | if (i >= maxInt) error(ERR.OVERFLOW); |
| 77 | |
| 78 | const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias; |
| 79 | |
| 80 | if (digit < t) break; |
| 81 | |
| 82 | const baseMinusT = base - t; |
| 83 | w *= baseMinusT; |
| 84 | if (w >= maxInt) error(ERR.OVERFLOW); |
| 85 | } |
| 86 | |
| 87 | const out = output.length + 1; |
| 88 | bias = adapt(i - oldi, out, oldi === 0); |
| 89 | if (bias > 198) error(ERR.OVERFLOW); // 198 is the theoretical max for 251 bytes decoding to ~0x10ffff |
| 90 | n += floor(i / out); |
| 91 | if (n > 0x10ffff) error(ERR.OVERFLOW); |
| 92 | i %= out; |
| 93 | |
| 94 | output.splice(i++, 0, n); |