| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962 |
- import { E_STRING } from './_utils.js'
- import { asciiPrefix, decodeAscii, decodeLatin1, decodeUCS2, encodeAscii } from './latin1.js'
- import { getTable } from './multi-byte.table.js'
- export const E_STRICT = 'Input is not well-formed for this encoding'
- /* Decoders */
- // If the decoder is not cleared properly, state can be preserved between non-streaming calls!
- // See comment about fatal stream
- // All except iso-2022-jp are ASCII supersets
- // When adding something that is not an ASCII superset, ajust the ASCII fast path
- const mappers = {
- // https://encoding.spec.whatwg.org/#euc-kr-decoder
- 'euc-kr': (err) => {
- const euc = getTable('euc-kr')
- let lead = 0
- let oi = 0
- let o16
- const decodeLead = (b) => {
- if (b < 0x41 || b > 0xfe) {
- lead = 0
- o16[oi++] = err()
- if (b < 128) o16[oi++] = b
- } else {
- const p = euc[(lead - 0x81) * 190 + b - 0x41]
- lead = 0
- if (p) {
- o16[oi++] = p
- } else {
- o16[oi++] = err()
- if (b < 128) o16[oi++] = b
- }
- }
- }
- const decode = (arr, start, end, stream) => {
- let i = start
- o16 = new Uint16Array(end - start + (lead ? 1 : 0)) // there are pairs but they consume more than one byte
- oi = 0
- // Fast path
- if (!lead) {
- for (const last1 = end - 1; i < last1; ) {
- const l = arr[i]
- if (l < 128) {
- o16[oi++] = l
- i++
- } else {
- if (l === 0x80 || l === 0xff) break
- const b = arr[i + 1]
- if (b < 0x41 || b === 0xff) break
- const p = euc[(l - 0x81) * 190 + b - 0x41]
- if (!p) break
- o16[oi++] = p
- i += 2
- }
- }
- }
- if (lead && i < end) decodeLead(arr[i++])
- while (i < end) {
- const b = arr[i++]
- if (b < 128) {
- o16[oi++] = b
- } else if (b === 0x80 || b === 0xff) {
- o16[oi++] = err()
- } else {
- lead = b
- if (i < end) decodeLead(arr[i++])
- }
- }
- if (lead && !stream) {
- lead = 0
- o16[oi++] = err()
- }
- const res = decodeUCS2(o16, oi)
- o16 = null
- return res
- }
- return { decode, isAscii: () => lead === 0 }
- },
- // https://encoding.spec.whatwg.org/#euc-jp-decoder
- 'euc-jp': (err) => {
- const jis0208 = getTable('jis0208')
- const jis0212 = getTable('jis0212')
- let j12 = false
- let lead = 0
- let oi = 0
- let o16
- const decodeLead = (b) => {
- if (lead === 0x8e && b >= 0xa1 && b <= 0xdf) {
- lead = 0
- o16[oi++] = 0xfe_c0 + b
- } else if (lead === 0x8f && b >= 0xa1 && b <= 0xfe) {
- j12 = true
- lead = b
- } else {
- let cp
- if (lead >= 0xa1 && lead <= 0xfe && b >= 0xa1 && b <= 0xfe) {
- cp = (j12 ? jis0212 : jis0208)[(lead - 0xa1) * 94 + b - 0xa1]
- }
- lead = 0
- j12 = false
- if (cp) {
- o16[oi++] = cp
- } else {
- o16[oi++] = err()
- if (b < 128) o16[oi++] = b
- }
- }
- }
- const decode = (arr, start, end, stream) => {
- let i = start
- o16 = new Uint16Array(end - start + (lead ? 1 : 0))
- oi = 0
- // Fast path, non-j12
- // lead = 0 means j12 = 0
- if (!lead) {
- for (const last1 = end - 1; i < last1; ) {
- const l = arr[i]
- if (l < 128) {
- o16[oi++] = l
- i++
- } else {
- const b = arr[i + 1]
- if (l === 0x8e && b >= 0xa1 && b <= 0xdf) {
- o16[oi++] = 0xfe_c0 + b
- i += 2
- } else {
- if (l < 0xa1 || l === 0xff || b < 0xa1 || b === 0xff) break
- const cp = jis0208[(l - 0xa1) * 94 + b - 0xa1]
- if (!cp) break
- o16[oi++] = cp
- i += 2
- }
- }
- }
- }
- if (lead && i < end) decodeLead(arr[i++])
- if (lead && i < end) decodeLead(arr[i++]) // could be two leads, but no more
- while (i < end) {
- const b = arr[i++]
- if (b < 128) {
- o16[oi++] = b
- } else if ((b < 0xa1 && b !== 0x8e && b !== 0x8f) || b === 0xff) {
- o16[oi++] = err()
- } else {
- lead = b
- if (i < end) decodeLead(arr[i++])
- if (lead && i < end) decodeLead(arr[i++]) // could be two leads
- }
- }
- if (lead && !stream) {
- lead = 0
- j12 = false // can be true only when lead is non-zero
- o16[oi++] = err()
- }
- const res = decodeUCS2(o16, oi)
- o16 = null
- return res
- }
- return { decode, isAscii: () => lead === 0 } // j12 can be true only when lead is non-zero
- },
- // https://encoding.spec.whatwg.org/#iso-2022-jp-decoder
- 'iso-2022-jp': (err) => {
- const jis0208 = getTable('jis0208')
- let dState = 1
- let oState = 1
- let lead = 0 // 0 or 0x21-0x7e
- let out = false
- const bytes = (pushback, b) => {
- if (dState < 5 && b === 0x1b) {
- dState = 6 // escape start
- return
- }
- switch (dState) {
- case 1:
- case 2:
- // ASCII, Roman (common)
- out = false
- if (dState === 2) {
- if (b === 0x5c) return 0xa5
- if (b === 0x7e) return 0x20_3e
- }
- if (b <= 0x7f && b !== 0x0e && b !== 0x0f) return b
- return err()
- case 3:
- // Katakana
- out = false
- if (b >= 0x21 && b <= 0x5f) return 0xff_40 + b
- return err()
- case 4:
- // Leading byte
- out = false
- if (b < 0x21 || b > 0x7e) return err()
- lead = b
- dState = 5
- return
- case 5:
- // Trailing byte
- out = false
- if (b === 0x1b) {
- dState = 6 // escape start
- return err()
- }
- dState = 4
- if (b >= 0x21 && b <= 0x7e) {
- const cp = jis0208[(lead - 0x21) * 94 + b - 0x21]
- if (cp) return cp
- }
- return err()
- case 6:
- // Escape start
- if (b === 0x24 || b === 0x28) {
- lead = b
- dState = 7
- return
- }
- out = false
- dState = oState
- pushback.push(b)
- return err()
- case 7: {
- // Escape
- const l = lead
- lead = 0
- let s
- if (l === 0x28) {
- // eslint-disable-next-line unicorn/prefer-switch
- if (b === 0x42) {
- s = 1
- } else if (b === 0x4a) {
- s = 2
- } else if (b === 0x49) {
- s = 3
- }
- } else if (l === 0x24 && (b === 0x40 || b === 0x42)) {
- s = 4
- }
- if (s) {
- dState = oState = s
- const output = out
- out = true
- return output ? err() : undefined
- }
- out = false
- dState = oState
- pushback.push(b, l)
- return err()
- }
- }
- }
- const eof = (pushback) => {
- if (dState < 5) return null
- out = false
- switch (dState) {
- case 5:
- dState = 4
- return err()
- case 6:
- dState = oState
- return err()
- case 7: {
- dState = oState
- pushback.push(lead)
- lead = 0
- return err()
- }
- }
- }
- const decode = (arr, start, end, stream) => {
- const o16 = new Uint16Array(end - start + 2) // err in eof + lead from state
- let oi = 0
- let i = start
- const pushback = [] // local and auto-cleared
- // First, dump everything until EOF
- // Same as the full loop, but without EOF handling
- while (i < end || pushback.length > 0) {
- const c = bytes(pushback, pushback.length > 0 ? pushback.pop() : arr[i++])
- if (c !== undefined) o16[oi++] = c // 16-bit
- }
- // Then, dump EOF. This needs the same loop as the characters can be pushed back
- if (!stream) {
- while (i <= end || pushback.length > 0) {
- if (i < end || pushback.length > 0) {
- const c = bytes(pushback, pushback.length > 0 ? pushback.pop() : arr[i++])
- if (c !== undefined) o16[oi++] = c // 16-bit
- } else {
- const c = eof(pushback)
- if (c === null) break // clean exit
- o16[oi++] = c
- }
- }
- }
- // Chrome and WebKit fail on this, we don't: completely destroy the old decoder state when finished streaming
- // > If this’s do not flush is false, then set this’s decoder to a new instance of this’s encoding’s decoder,
- // > Set this’s do not flush to options["stream"]
- if (!stream) {
- dState = oState = 1
- lead = 0
- out = false
- }
- return decodeUCS2(o16, oi)
- }
- return { decode, isAscii: () => false }
- },
- // https://encoding.spec.whatwg.org/#shift_jis-decoder
- shift_jis: (err) => {
- const jis0208 = getTable('jis0208')
- let lead = 0
- let oi = 0
- let o16
- const decodeLead = (b) => {
- const l = lead
- lead = 0
- if (b >= 0x40 && b <= 0xfc && b !== 0x7f) {
- const p = (l - (l < 0xa0 ? 0x81 : 0xc1)) * 188 + b - (b < 0x7f ? 0x40 : 0x41)
- if (p >= 8836 && p <= 10_715) {
- o16[oi++] = 0xe0_00 - 8836 + p
- return
- }
- const cp = jis0208[p]
- if (cp) {
- o16[oi++] = cp
- return
- }
- }
- o16[oi++] = err()
- if (b < 128) o16[oi++] = b
- }
- const decode = (arr, start, end, stream) => {
- o16 = new Uint16Array(end - start + (lead ? 1 : 0))
- oi = 0
- let i = start
- // Fast path
- if (!lead) {
- for (const last1 = end - 1; i < last1; ) {
- const l = arr[i]
- if (l <= 0x80) {
- o16[oi++] = l
- i++
- } else if (l >= 0xa1 && l <= 0xdf) {
- o16[oi++] = 0xfe_c0 + l
- i++
- } else {
- if (l === 0xa0 || l > 0xfc) break
- const b = arr[i + 1]
- if (b < 0x40 || b > 0xfc || b === 0x7f) break
- const p = (l - (l < 0xa0 ? 0x81 : 0xc1)) * 188 + b - (b < 0x7f ? 0x40 : 0x41)
- if (p >= 8836 && p <= 10_715) {
- o16[oi++] = 0xe0_00 - 8836 + p
- i += 2
- } else {
- const cp = jis0208[p]
- if (!cp) break
- o16[oi++] = cp
- i += 2
- }
- }
- }
- }
- if (lead && i < end) decodeLead(arr[i++])
- while (i < end) {
- const b = arr[i++]
- if (b <= 0x80) {
- o16[oi++] = b // 0x80 is allowed
- } else if (b >= 0xa1 && b <= 0xdf) {
- o16[oi++] = 0xfe_c0 + b
- } else if (b === 0xa0 || b > 0xfc) {
- o16[oi++] = err()
- } else {
- lead = b
- if (i < end) decodeLead(arr[i++])
- }
- }
- if (lead && !stream) {
- lead = 0
- o16[oi++] = err()
- }
- const res = decodeUCS2(o16, oi)
- o16 = null
- return res
- }
- return { decode, isAscii: () => lead === 0 }
- },
- // https://encoding.spec.whatwg.org/#gbk-decoder
- gbk: (err) => mappers.gb18030(err), // 10.1.1. GBK’s decoder is gb18030’s decoder
- // https://encoding.spec.whatwg.org/#gb18030-decoder
- gb18030: (err) => {
- const gb18030 = getTable('gb18030')
- const gb18030r = getTable('gb18030-ranges')
- let g1 = 0, g2 = 0, g3 = 0 // prettier-ignore
- const index = (p) => {
- if ((p > 39_419 && p < 189_000) || p > 1_237_575) return
- if (p === 7457) return 0xe7_c7
- let a = 0, b = 0 // prettier-ignore
- for (const [c, d] of gb18030r) {
- if (c > p) break
- a = c
- b = d
- }
- return b + p - a
- }
- // g1 is 0 or 0x81-0xfe
- // g2 is 0 or 0x30-0x39
- // g3 is 0 or 0x81-0xfe
- const decode = (arr, start, end, stream) => {
- const o16 = new Uint16Array(end - start + (g1 ? 3 : 0)) // even with pushback it's at most 1 char per byte
- let oi = 0
- let i = start
- const pushback = [] // local and auto-cleared
- // Fast path for 2-byte only
- // pushback is always empty ad start, and g1 = 0 means g2 = g3 = 0
- if (g1 === 0) {
- for (const last1 = end - 1; i < last1; ) {
- const b = arr[i]
- if (b < 128) {
- o16[oi++] = b
- i++
- } else if (b === 0x80) {
- o16[oi++] = 0x20_ac
- i++
- } else {
- if (b === 0xff) break
- const n = arr[i + 1]
- let cp
- if (n < 0x7f) {
- if (n < 0x40) break
- cp = gb18030[(b - 0x81) * 190 + n - 0x40]
- } else {
- if (n === 0xff || n === 0x7f) break
- cp = gb18030[(b - 0x81) * 190 + n - 0x41]
- }
- if (!cp) break
- o16[oi++] = cp // 16-bit
- i += 2
- }
- }
- }
- // First, dump everything until EOF
- // Same as the full loop, but without EOF handling
- while (i < end || pushback.length > 0) {
- const b = pushback.length > 0 ? pushback.pop() : arr[i++]
- if (g1) {
- // g2 can be set only when g1 is set, g3 can be set only when g2 is set
- // hence, 3 checks for g3 is faster than 3 checks for g1
- if (g2) {
- if (g3) {
- if (b <= 0x39 && b >= 0x30) {
- const p = index(
- (g1 - 0x81) * 12_600 + (g2 - 0x30) * 1260 + (g3 - 0x81) * 10 + b - 0x30
- )
- g1 = g2 = g3 = 0
- if (p === undefined) {
- o16[oi++] = err()
- } else if (p <= 0xff_ff) {
- o16[oi++] = p // Can validly return replacement
- } else {
- const d = p - 0x1_00_00
- o16[oi++] = 0xd8_00 | (d >> 10)
- o16[oi++] = 0xdc_00 | (d & 0x3_ff)
- }
- } else {
- pushback.push(b, g3, g2)
- g1 = g2 = g3 = 0
- o16[oi++] = err()
- }
- } else if (b >= 0x81 && b <= 0xfe) {
- g3 = b
- } else {
- pushback.push(b, g2)
- g1 = g2 = 0
- o16[oi++] = err()
- }
- } else if (b <= 0x39 && b >= 0x30) {
- g2 = b
- } else {
- let cp
- if (b >= 0x40 && b <= 0xfe && b !== 0x7f) {
- cp = gb18030[(g1 - 0x81) * 190 + b - (b < 0x7f ? 0x40 : 0x41)]
- }
- g1 = 0
- if (cp) {
- o16[oi++] = cp // 16-bit
- } else {
- o16[oi++] = err()
- if (b < 128) o16[oi++] = b // can be processed immediately
- }
- }
- } else if (b < 128) {
- o16[oi++] = b
- } else if (b === 0x80) {
- o16[oi++] = 0x20_ac
- } else if (b === 0xff) {
- o16[oi++] = err()
- } else {
- g1 = b
- }
- }
- // if g1 = 0 then g2 = g3 = 0
- if (g1 && !stream) {
- g1 = g2 = g3 = 0
- o16[oi++] = err()
- }
- return decodeUCS2(o16, oi)
- }
- return { decode, isAscii: () => g1 === 0 } // if g1 = 0 then g2 = g3 = 0
- },
- // https://encoding.spec.whatwg.org/#big5
- big5: (err) => {
- // The only decoder which returns multiple codepoints per byte, also has non-charcode codepoints
- // We store that as strings
- const big5 = getTable('big5')
- let lead = 0
- let oi = 0
- let o16
- const decodeLead = (b) => {
- if (b < 0x40 || (b > 0x7e && b < 0xa1) || b === 0xff) {
- lead = 0
- o16[oi++] = err()
- if (b < 128) o16[oi++] = b
- } else {
- const p = big5[(lead - 0x81) * 157 + b - (b < 0x7f ? 0x40 : 0x62)]
- lead = 0
- if (p > 0x1_00_00) {
- o16[oi++] = p >> 16
- o16[oi++] = p & 0xff_ff
- } else if (p) {
- o16[oi++] = p
- } else {
- o16[oi++] = err()
- if (b < 128) o16[oi++] = b
- }
- }
- }
- // eslint-disable-next-line sonarjs/no-identical-functions
- const decode = (arr, start, end, stream) => {
- let i = start
- o16 = new Uint16Array(end - start + (lead ? 1 : 0)) // there are pairs but they consume more than one byte
- oi = 0
- // Fast path
- if (!lead) {
- for (const last1 = end - 1; i < last1; ) {
- const l = arr[i]
- if (l < 128) {
- o16[oi++] = l
- i++
- } else {
- if (l === 0x80 || l === 0xff) break
- const b = arr[i + 1]
- if (b < 0x40 || (b > 0x7e && b < 0xa1) || b === 0xff) break
- const p = big5[(l - 0x81) * 157 + b - (b < 0x7f ? 0x40 : 0x62)]
- if (p > 0x1_00_00) {
- o16[oi++] = p >> 16
- o16[oi++] = p & 0xff_ff
- } else {
- if (!p) break
- o16[oi++] = p
- }
- i += 2
- }
- }
- }
- if (lead && i < end) decodeLead(arr[i++])
- while (i < end) {
- const b = arr[i++]
- if (b < 128) {
- o16[oi++] = b
- } else if (b === 0x80 || b === 0xff) {
- o16[oi++] = err()
- } else {
- lead = b
- if (i < end) decodeLead(arr[i++])
- }
- }
- if (lead && !stream) {
- lead = 0
- o16[oi++] = err()
- }
- const res = decodeUCS2(o16, oi)
- o16 = null
- return res
- }
- return { decode, isAscii: () => lead === 0 }
- },
- }
- export const isAsciiSuperset = (enc) => enc !== 'iso-2022-jp' // all others are ASCII supersets and can use fast path
- export function multibyteDecoder(enc, loose = false) {
- if (typeof loose !== 'boolean') throw new TypeError('loose option should be boolean')
- if (!Object.hasOwn(mappers, enc)) throw new RangeError('Unsupported encoding')
- // Input is assumed to be typechecked already
- let mapper
- const asciiSuperset = isAsciiSuperset(enc)
- let streaming // because onErr is cached in mapper
- const onErr = loose
- ? () => 0xff_fd
- : () => {
- // The correct way per spec seems to be not destoying the decoder state in stream mode, even when fatal
- // Decoders big5, euc-jp, euc-kr, shift_jis, gb18030 / gbk - all clear state before throwing unless EOF, so not affected
- // iso-2022-jp is the only tricky one one where this !stream check matters in non-stream mode
- if (!streaming) mapper = null // destroy state, effectively the same as 'do not flush' = false, but early
- throw new TypeError(E_STRICT)
- }
- return (arr, stream = false) => {
- let res = ''
- if (asciiSuperset && (!mapper || mapper.isAscii?.())) {
- const prefixLen = asciiPrefix(arr)
- if (prefixLen === arr.length) return decodeAscii(arr) // ascii
- res = decodeLatin1(arr, 0, prefixLen) // TODO: check if decodeAscii with subarray is faster for small prefixes too
- }
- streaming = stream // affects onErr
- if (!mapper) mapper = mappers[enc](onErr)
- return res + mapper.decode(arr, res.length, arr.length, stream)
- }
- }
- /* Encoders */
- const maps = new Map()
- const e7 = [[148, 236], [149, 237], [150, 243]] // prettier-ignore
- const e8 = [[30, 89], [38, 97], [43, 102], [44, 103], [50, 109], [67, 126], [84, 144], [100, 160]] // prettier-ignore
- const preencoders = {
- __proto__: null,
- big5: (p) => ((((p / 157) | 0) + 0x81) << 8) | ((p % 157 < 0x3f ? 0x40 : 0x62) + (p % 157)),
- shift_jis: (p) => {
- const l = (p / 188) | 0
- const t = p % 188
- return ((l + (l < 0x1f ? 0x81 : 0xc1)) << 8) | ((t < 0x3f ? 0x40 : 0x41) + t)
- },
- 'iso-2022-jp': (p) => ((((p / 94) | 0) + 0x21) << 8) | ((p % 94) + 0x21),
- 'euc-jp': (p) => ((((p / 94) | 0) + 0xa1) << 8) | ((p % 94) + 0xa1),
- 'euc-kr': (p) => ((((p / 190) | 0) + 0x81) << 8) | ((p % 190) + 0x41),
- gb18030: (p) => ((((p / 190) | 0) + 0x81) << 8) | ((p % 190 < 0x3f ? 0x40 : 0x41) + (p % 190)),
- }
- preencoders.gbk = preencoders.gb18030
- // We accept that encoders use non-trivial amount of mem, for perf
- // most are are 128 KiB mem, big5 is 380 KiB, lazy-loaded at first use
- function getMap(id, size, ascii) {
- const cached = maps.get(id)
- if (cached) return cached
- let tname = id
- const sjis = id === 'shift_jis'
- const iso2022jp = id === 'iso-2022-jp'
- if (iso2022jp) tname = 'jis0208'
- if (id === 'gbk') tname = 'gb18030'
- if (id === 'euc-jp' || sjis) tname = 'jis0208'
- const table = getTable(tname)
- const map = new Uint16Array(size)
- const enc = preencoders[id] || ((p) => p + 1)
- for (let i = 0; i < table.length; i++) {
- const c = table[i]
- if (!c) continue
- if (id === 'big5') {
- if (i < 5024) continue // this also skips multi-codepoint strings
- // In big5, all return first entries except for these
- if (
- map[c] &&
- c !== 0x25_50 &&
- c !== 0x25_5e &&
- c !== 0x25_61 &&
- c !== 0x25_6a &&
- c !== 0x53_41 &&
- c !== 0x53_45
- ) {
- continue
- }
- } else {
- if (sjis && i >= 8272 && i <= 8835) continue
- if (map[c]) continue
- }
- if (c > 0xff_ff) {
- // always a single codepoint here
- const s = String.fromCharCode(c >> 16, c & 0xff_ff)
- map[s.codePointAt(0)] = enc(i)
- } else {
- map[c] = enc(i)
- }
- }
- if (ascii) for (let i = 0; i < 0x80; i++) map[i] = i
- if (sjis || id === 'euc-jp') {
- if (sjis) map[0x80] = 0x80
- const d = sjis ? 0xfe_c0 : 0x70_c0
- for (let i = 0xff_61; i <= 0xff_9f; i++) map[i] = i - d
- map[0x22_12] = map[0xff_0d]
- map[0xa5] = 0x5c
- map[0x20_3e] = 0x7e
- } else if (tname === 'gb18030') {
- if (id === 'gbk') map[0x20_ac] = 0x80
- for (let i = 0xe7_8d; i <= 0xe7_93; i++) map[i] = i - 0x40_b4
- for (const [a, b] of e7) map[0xe7_00 | a] = 0xa6_00 | b
- for (const [a, b] of e8) map[0xe8_00 | a] = 0xfe_00 | b
- }
- maps.set(id, map)
- return map
- }
- const NON_LATIN = /[^\x00-\xFF]/ // eslint-disable-line no-control-regex
- let gb18030r, katakana
- export function multibyteEncoder(enc, onError) {
- if (!Object.hasOwn(mappers, enc)) throw new RangeError('Unsupported encoding')
- const size = enc === 'big5' ? 0x2_f8_a7 : 0x1_00_00 // for big5, max codepoint in table + 1
- const iso2022jp = enc === 'iso-2022-jp'
- const gb18030 = enc === 'gb18030'
- const ascii = isAsciiSuperset(enc)
- const width = iso2022jp ? 5 : gb18030 ? 4 : 2
- const tailsize = iso2022jp ? 3 : 0
- const map = getMap(enc, size, ascii)
- if (gb18030 && !gb18030r) gb18030r = getTable('gb18030-ranges')
- if (iso2022jp && !katakana) katakana = getTable('iso-2022-jp-katakana')
- return (str) => {
- if (typeof str !== 'string') throw new TypeError(E_STRING)
- if (ascii && !NON_LATIN.test(str)) {
- try {
- return encodeAscii(str, E_STRICT)
- } catch {}
- }
- const length = str.length
- const u8 = new Uint8Array(length * width + tailsize)
- let i = 0
- if (ascii) {
- while (i < length) {
- const x = str.charCodeAt(i)
- if (x >= 128) break
- u8[i++] = x
- }
- }
- // eslint-disable-next-line unicorn/consistent-function-scoping
- const err = (code) => {
- if (onError) return onError(code, u8, i)
- throw new TypeError(E_STRICT)
- }
- if (!map || map.length < size) /* c8 ignore next */ throw new Error('Unreachable') // Important for perf
- if (iso2022jp) {
- let state = 0 // 0 = ASCII, 1 = Roman, 2 = jis0208
- const restore = () => {
- state = 0
- u8[i++] = 0x1b
- u8[i++] = 0x28
- u8[i++] = 0x42
- }
- for (let j = 0; j < length; j++) {
- let x = str.charCodeAt(j)
- if (x >= 0xd8_00 && x < 0xe0_00) {
- if (state === 2) restore()
- if (x >= 0xdc_00 || j + 1 === length) {
- i += err(x) // lone
- } else {
- const x1 = str.charCodeAt(j + 1)
- if (x1 < 0xdc_00 || x1 >= 0xe0_00) {
- i += err(x) // lone
- } else {
- j++ // consume x1
- i += err(0x1_00_00 + ((x1 & 0x3_ff) | ((x & 0x3_ff) << 10)))
- }
- }
- } else if (x < 0x80) {
- if (state === 2 || (state === 1 && (x === 0x5c || x === 0x7e))) restore()
- if (x === 0xe || x === 0xf || x === 0x1b) {
- i += err(0xff_fd) // 12.2.2. step 3: This returns U+FFFD rather than codePoint to prevent attacks
- } else {
- u8[i++] = x
- }
- } else if (x === 0xa5 || x === 0x20_3e) {
- if (state !== 1) {
- state = 1
- u8[i++] = 0x1b
- u8[i++] = 0x28
- u8[i++] = 0x4a
- }
- u8[i++] = x === 0xa5 ? 0x5c : 0x7e
- } else {
- if (x === 0x22_12) x = 0xff_0d
- if (x >= 0xff_61 && x <= 0xff_9f) x = katakana[x - 0xff_61]
- const e = map[x]
- if (e) {
- if (state !== 2) {
- state = 2
- u8[i++] = 0x1b
- u8[i++] = 0x24
- u8[i++] = 0x42
- }
- u8[i++] = e >> 8
- u8[i++] = e & 0xff
- } else {
- if (state === 2) restore()
- i += err(x)
- }
- }
- }
- if (state) restore()
- } else if (gb18030) {
- // Deduping this branch hurts other encoders perf
- const encode = (cp) => {
- let a = 0, b = 0 // prettier-ignore
- for (const [c, d] of gb18030r) {
- if (d > cp) break
- a = c
- b = d
- }
- let rp = cp === 0xe7_c7 ? 7457 : a + cp - b
- u8[i++] = 0x81 + ((rp / 12_600) | 0)
- rp %= 12_600
- u8[i++] = 0x30 + ((rp / 1260) | 0)
- rp %= 1260
- u8[i++] = 0x81 + ((rp / 10) | 0)
- u8[i++] = 0x30 + (rp % 10)
- }
- for (let j = i; j < length; j++) {
- const x = str.charCodeAt(j)
- if (x >= 0xd8_00 && x < 0xe0_00) {
- if (x >= 0xdc_00 || j + 1 === length) {
- i += err(x) // lone
- } else {
- const x1 = str.charCodeAt(j + 1)
- if (x1 < 0xdc_00 || x1 >= 0xe0_00) {
- i += err(x) // lone
- } else {
- j++ // consume x1
- encode(0x1_00_00 + ((x1 & 0x3_ff) | ((x & 0x3_ff) << 10)))
- }
- }
- } else {
- const e = map[x]
- if (e & 0xff_00) {
- u8[i++] = e >> 8
- u8[i++] = e & 0xff
- } else if (e || x === 0) {
- u8[i++] = e
- } else if (x === 0xe5_e5) {
- i += err(x)
- } else {
- encode(x)
- }
- }
- }
- } else {
- const long =
- enc === 'big5'
- ? (x) => {
- const e = map[x]
- if (e & 0xff_00) {
- u8[i++] = e >> 8
- u8[i++] = e & 0xff
- } else if (e || x === 0) {
- u8[i++] = e
- } else {
- i += err(x)
- }
- }
- : (x) => {
- i += err(x)
- }
- for (let j = i; j < length; j++) {
- const x = str.charCodeAt(j)
- if (x >= 0xd8_00 && x < 0xe0_00) {
- if (x >= 0xdc_00 || j + 1 === length) {
- i += err(x) // lone
- } else {
- const x1 = str.charCodeAt(j + 1)
- if (x1 < 0xdc_00 || x1 >= 0xe0_00) {
- i += err(x) // lone
- } else {
- j++ // consume x1
- long(0x1_00_00 + ((x1 & 0x3_ff) | ((x & 0x3_ff) << 10)))
- }
- }
- } else {
- const e = map[x]
- if (e & 0xff_00) {
- u8[i++] = e >> 8
- u8[i++] = e & 0xff
- } else if (e || x === 0) {
- u8[i++] = e
- } else {
- i += err(x)
- }
- }
- }
- }
- return i === u8.length ? u8 : u8.subarray(0, i)
- }
- }
|