PureXML/Sources/Parsing/EUCJPDecoder.swift
Mihaela Mihaljevic 380160cc2b refactor(validation): decompose XSD instance validation into composable rules
The two monolithic XSD content/identity rules only satisfied the OpenAPIKit
validation idiom at the wrong granularity: no per-constraint removability,
no isolable failing/near-miss tests. Split them into 32 named rules, each
finding tagged at its origin and cached per XSDContext so the walk still
runs once per document (the #101 decision against duplicating XSD's
recursive type resolution still holds). Wired the streaming path's ID/IDREF
and identity checks through the same framework instead of calling the
validators imperatively, and added the unwrapAndLookup combinator the rule
already named as required.

Also includes the mandated repo-wide swiftformat/swiftlint pass.
2026-07-12 14:12:17 +02:00

58 lines
2.6 KiB
Swift

extension PureXML.Parsing.ByteDecoder {
/// The JIS X 0212 index, assembled from the vendored parts.
static let jis0212: [UInt16] = jis0212Part1 + jis0212Part2
/// The EUC-JP decoder (the WHATWG `EUC-JP` algorithm): ASCII directly, `0x8E`
/// then a byte for half-width katakana, `0x8F` then two bytes through JIS X
/// 0212, and any other lead byte then a byte through JIS X 0208.
enum EUCJP {
static func decode(_ bytes: ArraySlice<UInt8>) -> String {
var scalars = String.UnicodeScalarView()
var lead: UInt8 = 0
var useJis0212 = false
for byte in bytes {
if lead == 0x8E, (0xA1 ... 0xDF).contains(byte) {
lead = 0
scalars.append(Unicode.Scalar(0xFF61 + UInt32(byte) - 0xA1) ?? "\u{FFFD}")
} else if lead == 0x8F, (0xA1 ... 0xFE).contains(byte) {
useJis0212 = true
lead = byte
} else if lead != 0 {
let saved = lead
lead = 0
let jis0212 = useJis0212
useJis0212 = false
if let scalar = twoByteScalar(lead: saved, trail: byte, useJis0212: jis0212) {
scalars.append(scalar)
} else {
scalars.append("\u{FFFD}")
if byte <= 0x7F {
scalars.append(Unicode.Scalar(byte))
}
}
} else if byte <= 0x7F {
scalars.append(Unicode.Scalar(byte))
} else if byte == 0x8E || byte == 0x8F || (0xA1 ... 0xFE).contains(byte) {
lead = byte
} else {
scalars.append("\u{FFFD}")
}
}
if lead != 0 {
scalars.append("\u{FFFD}")
}
return String(scalars)
}
/// The scalar for a `(lead, trail)` pair, resolved through JIS X 0212 when
/// `useJis0212`, otherwise JIS X 0208. Nil for an invalid or unmapped pair.
static func twoByteScalar(lead: UInt8, trail: UInt8, useJis0212: Bool) -> Unicode.Scalar? {
guard (0xA1 ... 0xFE).contains(lead), (0xA1 ... 0xFE).contains(trail) else { return nil }
let pointer = (Int(lead) - 0xA1) * 94 + Int(trail) - 0xA1
let table = useJis0212 ? jis0212 : jis0208
guard pointer >= 0, pointer < table.count, table[pointer] != 0xFFFF else { return nil }
return Unicode.Scalar(UInt32(table[pointer]))
}
}
}