feat(parsing): Windows-1254 (Turkish) encoding (#97)

This commit is contained in:
Mihaela Mihaljevic 2026-06-09 07:02:13 +02:00
parent 7c26547915
commit a005def333
6 changed files with 26 additions and 2 deletions

View File

@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Single-byte legacy encodings (#97, partial): the byte decoder gains a table-based single-byte path and three encodings expressible exactly (so transcription-error-free): ISO-8859-15 (Latin-9, euro), ISO-8859-9 (Latin-5, Turkish), and ISO-8859-5 (Latin/Cyrillic). They are selected by the XML declaration's `encoding` name, for both the whole-buffer and streaming decoders. The remaining legacy encodings are data drop-ins into the same path.
- Single-byte legacy encodings (#97, partial): the byte decoder gains a table-based single-byte path and four encodings expressible exactly (so transcription-error-free): ISO-8859-15 (Latin-9, euro), ISO-8859-9 (Latin-5, Turkish), ISO-8859-5 (Latin/Cyrillic), and Windows-1254 (Turkish, composed from Windows-1252). They are selected by the XML declaration's `encoding` name, for both the whole-buffer and streaming decoders. The remaining legacy encodings are data drop-ins into the same path.
### Fixed

View File

@ -89,7 +89,7 @@ flowchart TB
PureXML is a working, dependency-free XML library today: parse, emit, validate,
query, and stream documents on macOS, Linux, Windows, and WASM. The test suite
currently runs **753 tests in 117 suites** (`swift test`).
currently runs **754 tests in 117 suites** (`swift test`).
### Shipped (libxml2-aligned surface)

View File

@ -31,6 +31,7 @@ extension PureXML.Parsing {
switch encoding {
case .latin1: { Unicode.Scalar($0) }
case .windows1252: windows1252Scalar
case .windows1254: SingleByte.windows1254
case .latinCyrillic: SingleByte.iso8859_5
case .latin5: SingleByte.iso8859_9
case .latin9: SingleByte.iso8859_15
@ -98,6 +99,7 @@ extension PureXML.Parsing {
"utf-8": .utf8, "utf8": .utf8, "us-ascii": .utf8, "ascii": .utf8,
"iso-8859-1": .latin1, "latin1": .latin1, "latin-1": .latin1, "l1": .latin1,
"windows-1252": .windows1252, "cp1252": .windows1252, "cp-1252": .windows1252,
"windows-1254": .windows1254, "cp1254": .windows1254,
"iso-8859-5": .latinCyrillic, "iso8859-5": .latinCyrillic, "cyrillic": .latinCyrillic,
"iso-8859-9": .latin5, "iso8859-9": .latin5, "latin5": .latin5, "latin-5": .latin5, "l5": .latin5,
"iso-8859-15": .latin9, "iso8859-15": .latin9, "latin9": .latin9, "latin-9": .latin9, "l9": .latin9,

View File

@ -10,6 +10,7 @@ public extension PureXML.Parsing {
case utf32LittleEndian
case latin1
case windows1252
case windows1254 // Turkish
case latinCyrillic // ISO-8859-5
case latin5 // ISO-8859-9 (Turkish)
case latin9 // ISO-8859-15

View File

@ -35,6 +35,20 @@ extension PureXML.Parsing.ByteDecoder {
}
}
/// Windows-1254 (Turkish): Windows-1252 with the same six Turkish letters
/// as ISO-8859-9 (the only differences fall in the Latin-1 upper half).
static func windows1254(_ byte: UInt8) -> Unicode.Scalar {
switch byte {
case 0xD0: "\u{011E}" // Ğ
case 0xDD: "\u{0130}" // İ
case 0xDE: "\u{015E}" // Ş
case 0xF0: "\u{011F}" // ğ
case 0xFD: "\u{0131}" // ı
case 0xFE: "\u{015F}" // ş
default: PureXML.Parsing.ByteDecoder.windows1252Scalar(byte)
}
}
/// ISO-8859-5 (Latin/Cyrillic): the Cyrillic alphabet laid out as a
/// contiguous block from `0xB0`, with a handful of named exceptions.
static func iso8859_5(_ byte: UInt8) -> Unicode.Scalar {

View File

@ -107,6 +107,13 @@ struct EncodingTests {
try #expect(decoded("ISO-8859-5", [0xF0]) == "\u{2116}") // №
}
@Test("Decodes Windows-1254: CP1252 punctuation plus the Turkish letters")
func test_windows1254() throws {
try #expect(decoded("windows-1254", [0x80]) == "\u{20AC}") // € (from CP1252 high range)
try #expect(decoded("windows-1254", [0xDE]) == "\u{015E}") // Ş
try #expect(decoded("windows-1254", [0xE9]) == "\u{E9}") // é, unchanged
}
private func byteSource(_ bytes: [UInt8]) -> () -> UInt8? {
var index = 0
return {