public extension PureXML.Parsing { /// A character encoding PureXML can decode from raw bytes. Covers the Unicode /// transformation formats, the ISO-8859 single-byte family, the Windows code /// pages (1250-1258), KOI8-R/U, and the CJK multi-byte encodings (Shift-JIS, /// EUC-JP/KR/TW, GBK, GB18030, Big5, ISO-2022-JP), for libxml2 encoding parity. enum InputEncoding: String, Equatable, Sendable { case utf8 case utf16BigEndian case utf16LittleEndian case utf32BigEndian case utf32LittleEndian case latin1 case windows1252 case windows1254 // Turkish case latin2 // ISO-8859-2 case latin3 // ISO-8859-3 case latin4 // ISO-8859-4 case greek // ISO-8859-7 case latin7 // ISO-8859-13 case arabic // ISO-8859-6 case hebrew // ISO-8859-8 case latin6 // ISO-8859-10 case latin8 // ISO-8859-14 case latin10 // ISO-8859-16 case thai // ISO-8859-11 case windows1250 case windows1251 case windows1253 case windows1257 case windows1255 case windows1256 case windows1258 case koi8r case koi8u case shiftJIS case eucJP case eucKR case gbk case gb18030 case big5 case iso2022jp case eucTW case latinCyrillic // ISO-8859-5 case latin5 // ISO-8859-9 (Turkish) case latin9 // ISO-8859-15 /// Detects the encoding from the leading bytes, following the XML sniff /// order: a byte-order mark first, then the ` InputEncoding { detectWithBOM(bytes).encoding } /// Detects the encoding and the length of any byte-order mark to strip. static func detectWithBOM(_ bytes: [UInt8]) -> (encoding: InputEncoding, bomLength: Int) { if bytes.starts(with: [0xEF, 0xBB, 0xBF]) { return (.utf8, 3) } // UTF-32 marks are four bytes and must be checked before UTF-16, since // the UTF-32LE mark begins with the UTF-16LE mark. if bytes.starts(with: [0x00, 0x00, 0xFE, 0xFF]) { return (.utf32BigEndian, 4) } if bytes.starts(with: [0xFF, 0xFE, 0x00, 0x00]) { return (.utf32LittleEndian, 4) } if bytes.starts(with: [0xFE, 0xFF]) { return (.utf16BigEndian, 2) } if bytes.starts(with: [0xFF, 0xFE]) { return (.utf16LittleEndian, 2) } // No BOM: sniff the "