extension PureXML.Parsing.StreamingDecoder { /// Streams one character from a multi-byte CJK encoding. mutating func nextCJK(_ encoding: PureXML.Parsing.InputEncoding) -> Character? { switch encoding { case .shiftJIS: nextShiftJIS() case .eucJP: nextEUCJP() case .eucKR: nextEUCKR() case .gbk: nextGBK() case .gb18030: nextGB18030() case .big5: nextBig5() case .iso2022jp: nextISO2022JP() case .eucTW: nextEUCTW() default: nextUTF8() } } /// Streams one GBK character: ASCII, `0x80` (euro), or a lead+trail. private mutating func nextGBK() -> Character? { guard let lead = nextByte() else { finished = true return nil } if lead <= 0x7F { return Character(Unicode.Scalar(lead)) } if lead == 0x80 { return "\u{20AC}" } guard (0x81 ... 0xFE).contains(lead) else { return Self.replacement } guard let trail = nextByte() else { finished = true return Self.replacement } return PureXML.Parsing.ByteDecoder.GBK.twoByteScalar(lead: lead, trail: trail).map(Character.init) ?? Self.replacement } /// Streams one GB18030 character: ASCII, `0x80` (euro), a four-byte /// sequence when the lead is followed by a digit, or a two-byte GBK pair. private mutating func nextGB18030() -> Character? { guard let lead = nextByte() else { finished = true return nil } if lead <= 0x7F { return Character(Unicode.Scalar(lead)) } if lead == 0x80 { return "\u{20AC}" } guard (0x81 ... 0xFE).contains(lead) else { return Self.replacement } guard let second = nextByte() else { finished = true return Self.replacement } if (0x30 ... 0x39).contains(second) { guard let third = nextByte(), let fourth = nextByte() else { finished = true return Self.replacement } guard (0x81 ... 0xFE).contains(third), (0x30 ... 0x39).contains(fourth) else { return Self.replacement } let pointer = PureXML.Parsing.ByteDecoder.GB18030.fourBytePointer(lead, second, third, fourth) return PureXML.Parsing.ByteDecoder.GB18030.rangeScalar(pointer).map(Character.init) ?? Self.replacement } return PureXML.Parsing.ByteDecoder.GBK.twoByteScalar(lead: lead, trail: second).map(Character.init) ?? Self.replacement } /// Streams one Big5 character: ASCII, or a lead+trail through the index /// (a two-scalar combining sequence stays one grapheme, one `Character`). private mutating func nextBig5() -> Character? { guard let lead = nextByte() else { finished = true return nil } if lead <= 0x7F { return Character(Unicode.Scalar(lead)) } guard (0x81 ... 0xFE).contains(lead) else { return Self.replacement } guard let trail = nextByte() else { finished = true return Self.replacement } guard let sequence = PureXML.Parsing.ByteDecoder.Big5.mapping(lead: lead, trail: trail) else { return Self.replacement } return Character(String(String.UnicodeScalarView(sequence))) } /// Streams one EUC-KR character: ASCII, or a lead+trail through CP949. private mutating func nextEUCKR() -> Character? { guard let lead = nextByte() else { finished = true return nil } if lead <= 0x7F { return Character(Unicode.Scalar(lead)) } guard (0x81 ... 0xFE).contains(lead) else { return Self.replacement } guard let trail = nextByte() else { finished = true return Self.replacement } return PureXML.Parsing.ByteDecoder.EUCKR.twoByteScalar(lead: lead, trail: trail).map(Character.init) ?? Self.replacement } /// Streams one EUC-JP character: ASCII, `0x8E`+byte (half-width katakana), /// `0x8F`+two bytes (JIS X 0212), or a lead+byte through JIS X 0208. private mutating func nextEUCJP() -> Character? { guard let lead = nextByte() else { finished = true return nil } if lead <= 0x7F { return Character(Unicode.Scalar(lead)) } if lead == 0x8E { guard let byte = nextByte() else { finished = true return Self.replacement } guard (0xA1 ... 0xDF).contains(byte) else { return Self.replacement } return Character(Unicode.Scalar(0xFF61 + UInt32(byte) - 0xA1) ?? "\u{FFFD}") } if lead == 0x8F { guard let mid = nextByte(), let trail = nextByte() else { finished = true return Self.replacement } return PureXML.Parsing.ByteDecoder.EUCJP.twoByteScalar(lead: mid, trail: trail, useJis0212: true).map(Character.init) ?? Self.replacement } guard let trail = nextByte() else { finished = true return Self.replacement } return PureXML.Parsing.ByteDecoder.EUCJP.twoByteScalar(lead: lead, trail: trail, useJis0212: false).map(Character.init) ?? Self.replacement } /// Streams one Shift-JIS character: a single byte (ASCII or half-width /// katakana) or a lead+trail two-byte sequence resolved through JIS X 0208. private mutating func nextShiftJIS() -> Character? { guard let lead = nextByte() else { finished = true return nil } if let single = PureXML.Parsing.ByteDecoder.ShiftJIS.singleByteScalar(lead) { return Character(single) } guard PureXML.Parsing.ByteDecoder.ShiftJIS.isLead(lead) else { return Self.replacement } guard let trail = nextByte() else { finished = true return Self.replacement } guard let scalar = PureXML.Parsing.ByteDecoder.ShiftJIS.twoByteScalar(lead: lead, trail: trail) else { return Self.replacement } return Character(scalar) } }