PureXML/Sources/Parsing/EUCTWDecoder.swift
Mihaela Mihaljevic 4130d905c8 refactor(parsing): drop slice copies; document the expansion bounds
The whole-buffer ISO-2022-JP, GB18030, and EUC-TW decoders index the input
ArraySlice directly instead of materializing an Array first (one avoidable
O(n) allocation per decode each); declaredEncoding's subsequence scan uses
elementsEqual instead of allocating an Array per candidate offset. Document
the two analysis-flagged bounds: entity expansion is doubly bounded (the
character budget caps output; the visiting set caps recursion depth at the
number of declared entities), and the RELAX NG derivative engine carries no
memoization by design (the simplifying constructors keep real grammars
small; adversarial nested-choice grammars can grow the residual, so treat
untrusted schemas accordingly). Behavior unchanged, all suites green.

Closes #118
2026-06-10 04:01:16 +02:00

59 lines
2.7 KiB
Swift

extension PureXML.Parsing.ByteDecoder {
/// The EUC-TW decoder (CNS 11643). ASCII passes through; a lead in `0xA1`-`0xFE`
/// opens a two-byte plane-1 character; `0x8E` opens a four-byte form whose
/// second byte selects the plane (`0xA1` = plane 1, `0xA2` = plane 2). Only the
/// real-world planes 1 and 2 are vendored; the rarely used planes 3-15 decode
/// to the replacement character.
enum EUCTW {
static func decode(_ bytes: ArraySlice<UInt8>) -> String {
var scalars = String.UnicodeScalarView()
var index = bytes.startIndex
while index < bytes.endIndex {
let byte = bytes[index]
if byte <= 0x7F {
scalars.append(Unicode.Scalar(byte))
index += 1
} else if byte == 0x8E {
guard index + 3 < bytes.endIndex else { scalars.append("\u{FFFD}")
index += 1
continue
}
scalars.append(fourByteScalar(bytes[index + 1], bytes[index + 2], bytes[index + 3]) ?? "\u{FFFD}")
index += 4
} else if (0xA1 ... 0xFE).contains(byte) {
guard index + 1 < bytes.endIndex, (0xA1 ... 0xFE).contains(bytes[index + 1]) else {
scalars.append("\u{FFFD}")
index += 1
continue
}
scalars.append(planeScalar(eucTWPlane1, byte, bytes[index + 1]) ?? "\u{FFFD}")
index += 2
} else {
scalars.append("\u{FFFD}")
index += 1
}
}
return String(scalars)
}
/// The scalar for a `(lead, trail)` pair within `table` (a CNS plane), or nil
/// for an out-of-range or unmapped pair.
static func planeScalar(_ table: [UInt32], _ lead: UInt8, _ trail: UInt8) -> Unicode.Scalar? {
guard (0xA1 ... 0xFE).contains(lead), (0xA1 ... 0xFE).contains(trail) else { return nil }
let pointer = (Int(lead) - 0xA1) * 94 + Int(trail) - 0xA1
guard pointer >= 0, pointer < table.count, table[pointer] != 0 else { return nil }
return Unicode.Scalar(table[pointer])
}
/// The scalar for a four-byte form: the plane selector then a `(lead, trail)`
/// pair. Planes beyond the vendored 1 and 2 return nil.
static func fourByteScalar(_ plane: UInt8, _ lead: UInt8, _ trail: UInt8) -> Unicode.Scalar? {
switch plane {
case 0xA1: planeScalar(eucTWPlane1, lead, trail)
case 0xA2: planeScalar(eucTWPlane2, lead, trail)
default: nil
}
}
}
}