diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs index bb8188c..fc32392 100644 --- a/crates/encoding/src/lib.rs +++ b/crates/encoding/src/lib.rs @@ -1,6 +1,7 @@ -//! WHATWG Encoding Standard — UTF-8 and UTF-16 codecs, pure Rust. +//! WHATWG Encoding Standard — UTF-8, UTF-16, and legacy single-byte codecs, pure Rust. pub mod error; +mod single_byte; mod utf16; mod utf8; @@ -17,6 +18,35 @@ pub enum Encoding { Utf8, Utf16Be, Utf16Le, + // Single-byte encodings + Ibm866, + Iso8859_2, + Iso8859_3, + Iso8859_4, + Iso8859_5, + Iso8859_6, + Iso8859_7, + Iso8859_8, + Iso8859_8I, + Iso8859_10, + Iso8859_13, + Iso8859_14, + Iso8859_15, + Iso8859_16, + Koi8R, + Koi8U, + Macintosh, + Windows874, + Windows1250, + Windows1251, + Windows1252, + Windows1253, + Windows1254, + Windows1255, + Windows1256, + Windows1257, + Windows1258, + XMacCyrillic, } impl Encoding { @@ -26,6 +56,34 @@ impl Encoding { Self::Utf8 => "UTF-8", Self::Utf16Be => "UTF-16BE", Self::Utf16Le => "UTF-16LE", + Self::Ibm866 => "IBM866", + Self::Iso8859_2 => "ISO-8859-2", + Self::Iso8859_3 => "ISO-8859-3", + Self::Iso8859_4 => "ISO-8859-4", + Self::Iso8859_5 => "ISO-8859-5", + Self::Iso8859_6 => "ISO-8859-6", + Self::Iso8859_7 => "ISO-8859-7", + Self::Iso8859_8 => "ISO-8859-8", + Self::Iso8859_8I => "ISO-8859-8-I", + Self::Iso8859_10 => "ISO-8859-10", + Self::Iso8859_13 => "ISO-8859-13", + Self::Iso8859_14 => "ISO-8859-14", + Self::Iso8859_15 => "ISO-8859-15", + Self::Iso8859_16 => "ISO-8859-16", + Self::Koi8R => "KOI8-R", + Self::Koi8U => "KOI8-U", + Self::Macintosh => "macintosh", + Self::Windows874 => "windows-874", + Self::Windows1250 => "windows-1250", + Self::Windows1251 => "windows-1251", + Self::Windows1252 => "windows-1252", + Self::Windows1253 => "windows-1253", + Self::Windows1254 => "windows-1254", + Self::Windows1255 => "windows-1255", + Self::Windows1256 => "windows-1256", + Self::Windows1257 => "windows-1257", + Self::Windows1258 => "windows-1258", + Self::XMacCyrillic => "x-mac-cyrillic", } } } @@ -55,6 +113,202 @@ const ENCODING_LABELS: &[(&str, Encoding)] = &[ ("unicodefeff", Encoding::Utf16Le), ("utf-16", Encoding::Utf16Le), ("utf-16le", Encoding::Utf16Le), + // IBM866 labels + ("866", Encoding::Ibm866), + ("cp866", Encoding::Ibm866), + ("csibm866", Encoding::Ibm866), + ("ibm866", Encoding::Ibm866), + // ISO-8859-2 labels + ("csisolatin2", Encoding::Iso8859_2), + ("iso-8859-2", Encoding::Iso8859_2), + ("iso-ir-101", Encoding::Iso8859_2), + ("iso8859-2", Encoding::Iso8859_2), + ("iso88592", Encoding::Iso8859_2), + ("iso_8859-2", Encoding::Iso8859_2), + ("iso_8859-2:1987", Encoding::Iso8859_2), + ("l2", Encoding::Iso8859_2), + ("latin2", Encoding::Iso8859_2), + // ISO-8859-3 labels + ("csisolatin3", Encoding::Iso8859_3), + ("iso-8859-3", Encoding::Iso8859_3), + ("iso-ir-109", Encoding::Iso8859_3), + ("iso8859-3", Encoding::Iso8859_3), + ("iso88593", Encoding::Iso8859_3), + ("iso_8859-3", Encoding::Iso8859_3), + ("iso_8859-3:1988", Encoding::Iso8859_3), + ("l3", Encoding::Iso8859_3), + ("latin3", Encoding::Iso8859_3), + // ISO-8859-4 labels + ("csisolatin4", Encoding::Iso8859_4), + ("iso-8859-4", Encoding::Iso8859_4), + ("iso-ir-110", Encoding::Iso8859_4), + ("iso8859-4", Encoding::Iso8859_4), + ("iso88594", Encoding::Iso8859_4), + ("iso_8859-4", Encoding::Iso8859_4), + ("iso_8859-4:1988", Encoding::Iso8859_4), + ("l4", Encoding::Iso8859_4), + ("latin4", Encoding::Iso8859_4), + // ISO-8859-5 labels + ("csisolatincyrillic", Encoding::Iso8859_5), + ("cyrillic", Encoding::Iso8859_5), + ("iso-8859-5", Encoding::Iso8859_5), + ("iso-ir-144", Encoding::Iso8859_5), + ("iso8859-5", Encoding::Iso8859_5), + ("iso88595", Encoding::Iso8859_5), + ("iso_8859-5", Encoding::Iso8859_5), + ("iso_8859-5:1988", Encoding::Iso8859_5), + // ISO-8859-6 labels + ("arabic", Encoding::Iso8859_6), + ("asmo-708", Encoding::Iso8859_6), + ("csiso88596e", Encoding::Iso8859_6), + ("csiso88596i", Encoding::Iso8859_6), + ("csisolatinarabic", Encoding::Iso8859_6), + ("ecma-114", Encoding::Iso8859_6), + ("iso-8859-6", Encoding::Iso8859_6), + ("iso-8859-6-e", Encoding::Iso8859_6), + ("iso-8859-6-i", Encoding::Iso8859_6), + ("iso-ir-127", Encoding::Iso8859_6), + ("iso8859-6", Encoding::Iso8859_6), + ("iso88596", Encoding::Iso8859_6), + ("iso_8859-6", Encoding::Iso8859_6), + ("iso_8859-6:1987", Encoding::Iso8859_6), + // ISO-8859-7 labels + ("csisolatingreek", Encoding::Iso8859_7), + ("ecma-118", Encoding::Iso8859_7), + ("elot_928", Encoding::Iso8859_7), + ("greek", Encoding::Iso8859_7), + ("greek8", Encoding::Iso8859_7), + ("iso-8859-7", Encoding::Iso8859_7), + ("iso-ir-126", Encoding::Iso8859_7), + ("iso8859-7", Encoding::Iso8859_7), + ("iso88597", Encoding::Iso8859_7), + ("iso_8859-7", Encoding::Iso8859_7), + ("iso_8859-7:1987", Encoding::Iso8859_7), + ("sun_eu_greek", Encoding::Iso8859_7), + // ISO-8859-8 labels + ("csiso88598e", Encoding::Iso8859_8), + ("csisolatinhebrew", Encoding::Iso8859_8), + ("hebrew", Encoding::Iso8859_8), + ("iso-8859-8", Encoding::Iso8859_8), + ("iso-8859-8-e", Encoding::Iso8859_8), + ("iso-ir-138", Encoding::Iso8859_8), + ("iso8859-8", Encoding::Iso8859_8), + ("iso88598", Encoding::Iso8859_8), + ("iso_8859-8", Encoding::Iso8859_8), + ("iso_8859-8:1988", Encoding::Iso8859_8), + ("visual", Encoding::Iso8859_8), + // ISO-8859-8-I labels + ("csiso88598i", Encoding::Iso8859_8I), + ("iso-8859-8-i", Encoding::Iso8859_8I), + ("logical", Encoding::Iso8859_8I), + // ISO-8859-10 labels + ("csisolatin6", Encoding::Iso8859_10), + ("iso-8859-10", Encoding::Iso8859_10), + ("iso-ir-157", Encoding::Iso8859_10), + ("iso8859-10", Encoding::Iso8859_10), + ("iso885910", Encoding::Iso8859_10), + ("l6", Encoding::Iso8859_10), + ("latin6", Encoding::Iso8859_10), + // ISO-8859-13 labels + ("iso-8859-13", Encoding::Iso8859_13), + ("iso8859-13", Encoding::Iso8859_13), + ("iso885913", Encoding::Iso8859_13), + // ISO-8859-14 labels + ("iso-8859-14", Encoding::Iso8859_14), + ("iso8859-14", Encoding::Iso8859_14), + ("iso885914", Encoding::Iso8859_14), + // ISO-8859-15 labels + ("csisolatin9", Encoding::Iso8859_15), + ("iso-8859-15", Encoding::Iso8859_15), + ("iso8859-15", Encoding::Iso8859_15), + ("iso885915", Encoding::Iso8859_15), + ("iso_8859-15", Encoding::Iso8859_15), + ("l9", Encoding::Iso8859_15), + // ISO-8859-16 labels + ("iso-8859-16", Encoding::Iso8859_16), + // KOI8-R labels + ("cskoi8r", Encoding::Koi8R), + ("koi", Encoding::Koi8R), + ("koi8", Encoding::Koi8R), + ("koi8-r", Encoding::Koi8R), + ("koi8_r", Encoding::Koi8R), + // KOI8-U labels + ("koi8-ru", Encoding::Koi8U), + ("koi8-u", Encoding::Koi8U), + // macintosh labels + ("csmacintosh", Encoding::Macintosh), + ("mac", Encoding::Macintosh), + ("macintosh", Encoding::Macintosh), + ("x-mac-roman", Encoding::Macintosh), + // windows-874 labels + ("dos-874", Encoding::Windows874), + ("iso-8859-11", Encoding::Windows874), + ("iso8859-11", Encoding::Windows874), + ("iso885911", Encoding::Windows874), + ("tis-620", Encoding::Windows874), + ("windows-874", Encoding::Windows874), + // windows-1250 labels + ("cp1250", Encoding::Windows1250), + ("windows-1250", Encoding::Windows1250), + ("x-cp1250", Encoding::Windows1250), + // windows-1251 labels + ("cp1251", Encoding::Windows1251), + ("windows-1251", Encoding::Windows1251), + ("x-cp1251", Encoding::Windows1251), + // windows-1252 labels (also serves as ISO-8859-1 and US-ASCII per WHATWG) + ("ansi_x3.4-1968", Encoding::Windows1252), + ("ascii", Encoding::Windows1252), + ("cp1252", Encoding::Windows1252), + ("cp819", Encoding::Windows1252), + ("csisolatin1", Encoding::Windows1252), + ("ibm819", Encoding::Windows1252), + ("iso-8859-1", Encoding::Windows1252), + ("iso-ir-100", Encoding::Windows1252), + ("iso8859-1", Encoding::Windows1252), + ("iso88591", Encoding::Windows1252), + ("iso_8859-1", Encoding::Windows1252), + ("iso_8859-1:1987", Encoding::Windows1252), + ("l1", Encoding::Windows1252), + ("latin1", Encoding::Windows1252), + ("us-ascii", Encoding::Windows1252), + ("windows-1252", Encoding::Windows1252), + ("x-cp1252", Encoding::Windows1252), + // windows-1253 labels + ("cp1253", Encoding::Windows1253), + ("windows-1253", Encoding::Windows1253), + ("x-cp1253", Encoding::Windows1253), + // windows-1254 labels + ("cp1254", Encoding::Windows1254), + ("csisolatin5", Encoding::Windows1254), + ("iso-8859-9", Encoding::Windows1254), + ("iso-ir-148", Encoding::Windows1254), + ("iso8859-9", Encoding::Windows1254), + ("iso88599", Encoding::Windows1254), + ("iso_8859-9", Encoding::Windows1254), + ("iso_8859-9:1989", Encoding::Windows1254), + ("l5", Encoding::Windows1254), + ("latin5", Encoding::Windows1254), + ("windows-1254", Encoding::Windows1254), + ("x-cp1254", Encoding::Windows1254), + // windows-1255 labels + ("cp1255", Encoding::Windows1255), + ("windows-1255", Encoding::Windows1255), + ("x-cp1255", Encoding::Windows1255), + // windows-1256 labels + ("cp1256", Encoding::Windows1256), + ("windows-1256", Encoding::Windows1256), + ("x-cp1256", Encoding::Windows1256), + // windows-1257 labels + ("cp1257", Encoding::Windows1257), + ("windows-1257", Encoding::Windows1257), + ("x-cp1257", Encoding::Windows1257), + // windows-1258 labels + ("cp1258", Encoding::Windows1258), + ("windows-1258", Encoding::Windows1258), + ("x-cp1258", Encoding::Windows1258), + // x-mac-cyrillic labels + ("x-mac-cyrillic", Encoding::XMacCyrillic), + ("x-mac-ukrainian", Encoding::XMacCyrillic), ]; /// Look up an encoding by its WHATWG label. @@ -102,6 +356,11 @@ pub fn decode(bytes: &[u8], encoding: Encoding) -> String { Encoding::Utf8 => utf8::decode_utf8(bytes, ErrorMode::Replacement).unwrap(), Encoding::Utf16Le => utf16::decode_utf16le(bytes, ErrorMode::Replacement).unwrap(), Encoding::Utf16Be => utf16::decode_utf16be(bytes, ErrorMode::Replacement).unwrap(), + enc => { + let table = single_byte::table_for(&enc).unwrap(); + single_byte::decode_single_byte(bytes, table, enc.name(), ErrorMode::Replacement) + .unwrap() + } } } @@ -113,21 +372,22 @@ pub fn decode_strict(bytes: &[u8], encoding: Encoding) -> Result { Encoding::Utf8 => utf8::decode_utf8(bytes, ErrorMode::Fatal), Encoding::Utf16Le => utf16::decode_utf16le(bytes, ErrorMode::Fatal), Encoding::Utf16Be => utf16::decode_utf16be(bytes, ErrorMode::Fatal), + enc => { + let table = single_byte::table_for(&enc).unwrap(); + single_byte::decode_single_byte(bytes, table, enc.name(), ErrorMode::Fatal) + } } } /// Encode a string to bytes using the given encoding. /// -/// Only UTF-8 encoding is supported for encode. Per WHATWG spec, UTF-16 +/// Only UTF-8 encoding is supported for encode. Per WHATWG spec, all other /// encodings are decode-only. pub fn encode(text: &str, encoding: Encoding) -> Result> { match encoding { Encoding::Utf8 => Ok(utf8::encode_utf8(text)), - Encoding::Utf16Be => Err(EncodingError::EncodeNotSupported { - encoding: "UTF-16BE", - }), - Encoding::Utf16Le => Err(EncodingError::EncodeNotSupported { - encoding: "UTF-16LE", + other => Err(EncodingError::EncodeNotSupported { + encoding: other.name(), }), } } @@ -177,6 +437,10 @@ mod tests { assert_eq!(Encoding::Utf8.name(), "UTF-8"); assert_eq!(Encoding::Utf16Be.name(), "UTF-16BE"); assert_eq!(Encoding::Utf16Le.name(), "UTF-16LE"); + assert_eq!(Encoding::Windows1252.name(), "windows-1252"); + assert_eq!(Encoding::Iso8859_2.name(), "ISO-8859-2"); + assert_eq!(Encoding::Koi8R.name(), "KOI8-R"); + assert_eq!(Encoding::Macintosh.name(), "macintosh"); } // -- Label lookup -- @@ -203,19 +467,54 @@ mod tests { assert_eq!(lookup("iso-10646-ucs-2"), Some(Encoding::Utf16Le)); } + #[test] + fn lookup_windows_1252_labels() { + // windows-1252 is THE most important single-byte encoding + assert_eq!(lookup("windows-1252"), Some(Encoding::Windows1252)); + assert_eq!(lookup("cp1252"), Some(Encoding::Windows1252)); + assert_eq!(lookup("x-cp1252"), Some(Encoding::Windows1252)); + // ISO-8859-1 maps to windows-1252 per WHATWG + assert_eq!(lookup("iso-8859-1"), Some(Encoding::Windows1252)); + assert_eq!(lookup("latin1"), Some(Encoding::Windows1252)); + assert_eq!(lookup("l1"), Some(Encoding::Windows1252)); + // US-ASCII maps to windows-1252 per WHATWG + assert_eq!(lookup("us-ascii"), Some(Encoding::Windows1252)); + assert_eq!(lookup("ascii"), Some(Encoding::Windows1252)); + } + + #[test] + fn lookup_legacy_labels() { + assert_eq!(lookup("iso-8859-2"), Some(Encoding::Iso8859_2)); + assert_eq!(lookup("latin2"), Some(Encoding::Iso8859_2)); + assert_eq!(lookup("iso-8859-5"), Some(Encoding::Iso8859_5)); + assert_eq!(lookup("cyrillic"), Some(Encoding::Iso8859_5)); + assert_eq!(lookup("iso-8859-7"), Some(Encoding::Iso8859_7)); + assert_eq!(lookup("greek"), Some(Encoding::Iso8859_7)); + assert_eq!(lookup("iso-8859-15"), Some(Encoding::Iso8859_15)); + assert_eq!(lookup("koi8-r"), Some(Encoding::Koi8R)); + assert_eq!(lookup("koi8-u"), Some(Encoding::Koi8U)); + assert_eq!(lookup("macintosh"), Some(Encoding::Macintosh)); + assert_eq!(lookup("ibm866"), Some(Encoding::Ibm866)); + assert_eq!(lookup("windows-1251"), Some(Encoding::Windows1251)); + assert_eq!(lookup("windows-874"), Some(Encoding::Windows874)); + assert_eq!(lookup("iso-8859-9"), Some(Encoding::Windows1254)); + assert_eq!(lookup("x-mac-cyrillic"), Some(Encoding::XMacCyrillic)); + } + #[test] fn lookup_with_whitespace() { assert_eq!(lookup(" utf-8 "), Some(Encoding::Utf8)); assert_eq!(lookup("\tutf-8\n"), Some(Encoding::Utf8)); assert_eq!(lookup("\r\nutf-16le\r\n"), Some(Encoding::Utf16Le)); + assert_eq!(lookup(" windows-1252 "), Some(Encoding::Windows1252)); } #[test] fn lookup_unknown() { - assert_eq!(lookup("latin1"), None); assert_eq!(lookup(""), None); assert_eq!(lookup(" "), None); assert_eq!(lookup("utf-99"), None); + assert_eq!(lookup("bogus-encoding"), None); } // -- BOM sniffing -- @@ -285,6 +584,41 @@ mod tests { assert_eq!(decode(&[0x00, 0x41], Encoding::Utf16Be), "A"); } + #[test] + fn decode_windows_1252_euro() { + assert_eq!(decode(&[0x80], Encoding::Windows1252), "\u{20AC}"); + } + + #[test] + fn decode_windows_1252_cafe() { + // "Café" in windows-1252 + assert_eq!( + decode(&[0x43, 0x61, 0x66, 0xE9], Encoding::Windows1252), + "Caf\u{00E9}" + ); + } + + #[test] + fn decode_iso_8859_2() { + // 0xA1 → Ą + assert_eq!(decode(&[0xA1], Encoding::Iso8859_2), "\u{0104}"); + } + + #[test] + fn decode_koi8r_cyrillic() { + // 0xE1 → А (U+0410) + assert_eq!(decode(&[0xE1], Encoding::Koi8R), "\u{0410}"); + } + + #[test] + fn decode_windows_1251_cyrillic() { + // 0xC0 → А (U+0410), 0xE0 → а (U+0430) + assert_eq!( + decode(&[0xC0, 0xE0], Encoding::Windows1251), + "\u{0410}\u{0430}" + ); + } + // -- Top-level decode_strict -- #[test] @@ -297,6 +631,20 @@ mod tests { assert!(decode_strict(&[0xFF], Encoding::Utf8).is_err()); } + #[test] + fn decode_strict_single_byte_unmapped() { + // ISO-8859-3 byte 0xA5 is unmapped + assert!(decode_strict(&[0xA5], Encoding::Iso8859_3).is_err()); + } + + #[test] + fn decode_strict_single_byte_valid() { + assert_eq!( + decode_strict(&[0x80], Encoding::Windows1252).unwrap(), + "\u{20AC}" + ); + } + // -- Top-level encode -- #[test] @@ -305,18 +653,18 @@ mod tests { } #[test] - fn encode_utf16_not_supported() { + fn encode_non_utf8_not_supported() { assert!(matches!( encode("Hello", Encoding::Utf16Le), - Err(EncodingError::EncodeNotSupported { - encoding: "UTF-16LE" - }) + Err(EncodingError::EncodeNotSupported { .. }) )); assert!(matches!( encode("Hello", Encoding::Utf16Be), - Err(EncodingError::EncodeNotSupported { - encoding: "UTF-16BE" - }) + Err(EncodingError::EncodeNotSupported { .. }) + )); + assert!(matches!( + encode("Hello", Encoding::Windows1252), + Err(EncodingError::EncodeNotSupported { .. }) )); } diff --git a/crates/encoding/src/single_byte.rs b/crates/encoding/src/single_byte.rs new file mode 100644 index 0000000..839e166 --- /dev/null +++ b/crates/encoding/src/single_byte.rs @@ -0,0 +1,858 @@ +//! Single-byte encoding decoders per WHATWG Encoding Standard §14.1. +//! +//! Each single-byte encoding maps bytes 0x00–0x7F to ASCII and +//! bytes 0x80–0xFF via a 128-entry lookup table to Unicode codepoints. + +use crate::error::{EncodingError, Result}; +use crate::utf8::ErrorMode; + +/// Decode bytes using a single-byte encoding lookup table. +/// +/// Bytes 0x00–0x7F are ASCII. Bytes 0x80–0xFF are looked up in `table`. +/// Table entries of `0xFFFD` indicate unmapped bytes. +pub(crate) fn decode_single_byte( + bytes: &[u8], + table: &[u16; 128], + encoding_name: &'static str, + mode: ErrorMode, +) -> Result { + let mut output = String::with_capacity(bytes.len()); + for (i, &byte) in bytes.iter().enumerate() { + if byte < 0x80 { + output.push(byte as char); + } else { + let cp = table[(byte - 0x80) as usize]; + if cp == 0xFFFD { + if mode == ErrorMode::Fatal { + return Err(EncodingError::InvalidSequence { + encoding: encoding_name, + position: i, + }); + } + output.push('\u{FFFD}'); + } else { + output.push(char::from_u32(cp as u32).unwrap_or('\u{FFFD}')); + } + } + } + Ok(output) +} + +// --------------------------------------------------------------------------- +// WHATWG single-byte encoding index tables +// Each table maps bytes 0x80–0xFF (index 0–127) to Unicode codepoints. +// 0xFFFD = unmapped byte position. +// --------------------------------------------------------------------------- + +/// windows-1252 (WHATWG index) +/// Also serves as the encoding for labels: iso-8859-1, us-ascii, latin1, etc. +pub(crate) const WINDOWS_1252: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, + 0x0152, 0x008D, 0x017D, 0x008F, // 0x90–0x9F + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, + 0x0153, 0x009D, 0x017E, 0x0178, // 0xA0–0xAF + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0x00BF, // 0xC0–0xCF + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, + 0x00CC, 0x00CD, 0x00CE, 0x00CF, // 0xD0–0xDF + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, + 0x00DC, 0x00DD, 0x00DE, 0x00DF, // 0xE0–0xEF + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, + 0x00EC, 0x00ED, 0x00EE, 0x00EF, // 0xF0–0xFF + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, + 0x00FC, 0x00FD, 0x00FE, 0x00FF, +]; + +/// ISO-8859-2 (Latin-2, Central European) +pub(crate) const ISO_8859_2: [u16; 128] = [ + // 0x80–0x8F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, // 0x90–0x9F: C1 controls + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, + 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, + 0x0179, 0x00AD, 0x017D, 0x017B, // 0xB0–0xBF + 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, + 0x017A, 0x02DD, 0x017E, 0x017C, // 0xC0–0xCF + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, + 0x011A, 0x00CD, 0x00CE, 0x010E, // 0xD0–0xDF + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, + 0x00DC, 0x00DD, 0x0162, 0x00DF, // 0xE0–0xEF + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, + 0x011B, 0x00ED, 0x00EE, 0x010F, // 0xF0–0xFF + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, + 0x00FC, 0x00FD, 0x0163, 0x02D9, +]; + +/// ISO-8859-3 (Latin-3, South European) +pub(crate) const ISO_8859_3: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xFFFD, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, + 0x0134, 0x00AD, 0xFFFD, 0x017B, // 0xB0–0xBF + 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, + 0x0135, 0x00BD, 0xFFFD, 0x017C, // 0xC0–0xCF + 0x00C0, 0x00C1, 0x00C2, 0xFFFD, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, + 0x00CC, 0x00CD, 0x00CE, 0x00CF, // 0xD0–0xDF + 0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, + 0x00DC, 0x016C, 0x015C, 0x00DF, // 0xE0–0xEF + 0x00E0, 0x00E1, 0x00E2, 0xFFFD, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, + 0x00EC, 0x00ED, 0x00EE, 0x00EF, // 0xF0–0xFF + 0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, + 0x00FC, 0x016D, 0x015D, 0x02D9, +]; + +/// ISO-8859-4 (Latin-4, North European) +pub(crate) const ISO_8859_4: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, + 0x0166, 0x00AD, 0x017D, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, + 0x0167, 0x014A, 0x017E, 0x014B, // 0xC0–0xCF + 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, + 0x0116, 0x00CD, 0x00CE, 0x012A, // 0xD0–0xDF + 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, + 0x00DC, 0x0168, 0x016A, 0x00DF, // 0xE0–0xEF + 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, + 0x0117, 0x00ED, 0x00EE, 0x012B, // 0xF0–0xFF + 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, + 0x00FC, 0x0169, 0x016B, 0x02D9, +]; + +/// ISO-8859-5 (Cyrillic) +pub(crate) const ISO_8859_5: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, + 0x040C, 0x00AD, 0x040E, 0x040F, // 0xB0–0xBF + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, + 0x041C, 0x041D, 0x041E, 0x041F, // 0xC0–0xCF + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, + 0x042C, 0x042D, 0x042E, 0x042F, // 0xD0–0xDF + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, + 0x043C, 0x043D, 0x043E, 0x043F, // 0xE0–0xEF + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, + 0x044C, 0x044D, 0x044E, 0x044F, // 0xF0–0xFF + 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, + 0x045C, 0x00A7, 0x045E, 0x045F, +]; + +/// ISO-8859-6 (Arabic) +pub(crate) const ISO_8859_6: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0xFFFD, 0xFFFD, 0xFFFD, 0x00A4, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0x060C, 0x00AD, 0xFFFD, 0xFFFD, // 0xB0–0xBF + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x061B, + 0xFFFD, 0xFFFD, 0xFFFD, 0x061F, // 0xC0–0xCF + 0xFFFD, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, + 0x062C, 0x062D, 0x062E, 0x062F, // 0xD0–0xDF + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0xE0–0xEF + 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, + 0x064C, 0x064D, 0x064E, 0x064F, // 0xF0–0xFF + 0x0650, 0x0651, 0x0652, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, +]; + +/// ISO-8859-7 (Greek) +pub(crate) const ISO_8859_7: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x2018, 0x2019, 0x00A3, 0x20AC, 0x20AF, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x037A, 0x00AB, + 0x00AC, 0x00AD, 0xFFFD, 0x2015, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, + 0x038C, 0x00BD, 0x038E, 0x038F, // 0xC0–0xCF + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, + 0x039C, 0x039D, 0x039E, 0x039F, // 0xD0–0xDF + 0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, + 0x03AC, 0x03AD, 0x03AE, 0x03AF, // 0xE0–0xEF + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, + 0x03BC, 0x03BD, 0x03BE, 0x03BF, // 0xF0–0xFF + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, + 0x03CC, 0x03CD, 0x03CE, 0xFFFD, +]; + +/// ISO-8859-8 (Hebrew) — also used for ISO-8859-8-I (logical order) +pub(crate) const ISO_8859_8: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0xFFFD, // 0xC0–0xCF + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0xD0–0xDF + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0x2017, // 0xE0–0xEF + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, + 0x05DC, 0x05DD, 0x05DE, 0x05DF, // 0xF0–0xFF + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0xFFFD, + 0xFFFD, 0x200E, 0x200F, 0xFFFD, +]; + +/// ISO-8859-10 (Latin-6, Nordic) +pub(crate) const ISO_8859_10: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, 0x013B, 0x0110, 0x0160, 0x0166, + 0x017D, 0x00AD, 0x016A, 0x014A, // 0xB0–0xBF + 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, 0x013C, 0x0111, 0x0161, 0x0167, + 0x017E, 0x2015, 0x016B, 0x014B, // 0xC0–0xCF + 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, + 0x0116, 0x00CD, 0x00CE, 0x00CF, // 0xD0–0xDF + 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, 0x00D8, 0x0172, 0x00DA, 0x00DB, + 0x00DC, 0x00DD, 0x00DE, 0x00DF, // 0xE0–0xEF + 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, + 0x0117, 0x00ED, 0x00EE, 0x00EF, // 0xF0–0xFF + 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, 0x00F8, 0x0173, 0x00FA, 0x00FB, + 0x00FC, 0x00FD, 0x00FE, 0x0138, +]; + +/// ISO-8859-13 (Latin-7, Baltic Rim) +pub(crate) const ISO_8859_13: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00C6, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0x00E6, // 0xC0–0xCF + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, + 0x0122, 0x0136, 0x012A, 0x013B, // 0xD0–0xDF + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, + 0x00DC, 0x017B, 0x017D, 0x00DF, // 0xE0–0xEF + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, + 0x0123, 0x0137, 0x012B, 0x013C, // 0xF0–0xFF + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, + 0x00FC, 0x017C, 0x017E, 0x2019, +]; + +/// ISO-8859-14 (Latin-8, Celtic) +pub(crate) const ISO_8859_14: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, 0x1E80, 0x00A9, 0x1E82, 0x1E0B, + 0x1EF2, 0x00AD, 0x00AE, 0x0178, // 0xB0–0xBF + 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, 0x1E81, 0x1E57, 0x1E83, 0x1E60, + 0x1EF3, 0x1E84, 0x1E85, 0x1E61, // 0xC0–0xCF + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, + 0x00CC, 0x00CD, 0x00CE, 0x00CF, // 0xD0–0xDF + 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, 0x00D8, 0x00D9, 0x00DA, 0x00DB, + 0x00DC, 0x00DD, 0x0176, 0x00DF, // 0xE0–0xEF + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, + 0x00EC, 0x00ED, 0x00EE, 0x00EF, // 0xF0–0xFF + 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, 0x00F8, 0x00F9, 0x00FA, 0x00FB, + 0x00FC, 0x00FD, 0x0177, 0x00FF, +]; + +/// ISO-8859-15 (Latin-9, updated Western European) +pub(crate) const ISO_8859_15: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + // 0xA0–0xAF: Differs from 8859-1 at A4, A6, A8, B4, B8, BC, BD, BE + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, + 0x0152, 0x0153, 0x0178, 0x00BF, // 0xC0–0xFF: same as ISO-8859-1 + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, + 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, + 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, + 0x00FC, 0x00FD, 0x00FE, 0x00FF, +]; + +/// ISO-8859-16 (Latin-10, South-Eastern European) +pub(crate) const ISO_8859_16: [u16; 128] = [ + // 0x80–0x9F: C1 controls + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, + 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, // 0xA0–0xAF + 0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x0218, 0x00AB, + 0x0179, 0x00AD, 0x017A, 0x017B, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7, 0x017E, 0x010D, 0x0219, 0x00BB, + 0x0152, 0x0153, 0x0178, 0x017C, // 0xC0–0xCF + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, + 0x00CC, 0x00CD, 0x00CE, 0x00CF, // 0xD0–0xDF + 0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A, 0x0170, 0x00D9, 0x00DA, 0x00DB, + 0x00DC, 0x0118, 0x021A, 0x00DF, // 0xE0–0xEF + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, + 0x00EC, 0x00ED, 0x00EE, 0x00EF, // 0xF0–0xFF + 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, 0x0171, 0x00F9, 0x00FA, 0x00FB, + 0x00FC, 0x0119, 0x021B, 0x00FF, +]; + +/// KOI8-R (Russian) +pub(crate) const KOI8_R: [u16; 128] = [ + // 0x80–0x8F: box drawing + 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, + 0x2584, 0x2588, 0x258C, 0x2590, // 0x90–0x9F: more box drawing + block elements + 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0x00A0, 0x2321, + 0x00B0, 0x00B2, 0x00B7, 0x00F7, // 0xA0–0xAF + 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, + 0x255B, 0x255C, 0x255D, 0x255E, // 0xB0–0xBF + 0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, + 0x256A, 0x256B, 0x256C, 0x00A9, // 0xC0–0xCF: Cyrillic lowercase + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, + 0x043B, 0x043C, 0x043D, 0x043E, // 0xD0–0xDF + 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044B, 0x0437, 0x0448, + 0x044D, 0x0449, 0x0447, 0x044A, // 0xE0–0xEF: Cyrillic uppercase + 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, + 0x041B, 0x041C, 0x041D, 0x041E, // 0xF0–0xFF + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, + 0x042D, 0x0429, 0x0427, 0x042A, +]; + +/// KOI8-U (Ukrainian) +pub(crate) const KOI8_U: [u16; 128] = [ + // 0x80–0x8F: box drawing (same as KOI8-R) + 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, + 0x2584, 0x2588, 0x258C, 0x2590, // 0x90–0x9F + 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0x00A0, 0x2321, + 0x00B0, 0x00B2, 0x00B7, 0x00F7, + // 0xA0–0xAF: differs from KOI8-R at A3, A4, A6, A7 + 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, 0x2557, 0x2558, 0x2559, 0x255A, + 0x255B, 0x0491, 0x255D, 0x255E, // 0xB0–0xBF + 0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, 0x2566, 0x2567, 0x2568, 0x2569, + 0x256A, 0x0490, 0x256C, 0x00A9, // 0xC0–0xFF: Cyrillic (same as KOI8-R) + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, + 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, + 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, 0x042E, 0x0410, 0x0411, 0x0426, + 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, + 0x042D, 0x0429, 0x0427, 0x042A, +]; + +/// macintosh (Mac OS Roman) +pub(crate) const MACINTOSH: [u16; 128] = [ + // 0x80–0x8F + 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, 0x00E0, 0x00E2, 0x00E4, 0x00E3, + 0x00E5, 0x00E7, 0x00E9, 0x00E8, // 0x90–0x9F + 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, 0x00F2, 0x00F4, 0x00F6, 0x00F5, + 0x00FA, 0x00F9, 0x00FB, 0x00FC, // 0xA0–0xAF + 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, 0x00AE, 0x00A9, 0x2122, 0x00B4, + 0x00A8, 0x2260, 0x00C6, 0x00D8, // 0xB0–0xBF + 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, 0x220F, 0x03C0, 0x222B, 0x00AA, + 0x00BA, 0x2126, 0x00E6, 0x00F8, // 0xC0–0xCF + 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, 0x00BB, 0x2026, 0x00A0, 0x00C0, + 0x00C3, 0x00D5, 0x0152, 0x0153, // 0xD0–0xDF + 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, 0x00FF, 0x0178, 0x2044, 0x20AC, + 0x2039, 0x203A, 0xFB01, 0xFB02, // 0xE0–0xEF + 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, 0x00CB, 0x00C8, 0x00CD, 0x00CE, + 0x00CF, 0x00CC, 0x00D3, 0x00D4, // 0xF0–0xFF + 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, 0x00AF, 0x02D8, 0x02D9, 0x02DA, + 0x00B8, 0x02DD, 0x02DB, 0x02C7, +]; + +/// IBM866 (DOS Cyrillic) +pub(crate) const IBM866: [u16; 128] = [ + // 0x80–0x8F: Cyrillic uppercase А–П + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, + 0x041C, 0x041D, 0x041E, 0x041F, // 0x90–0x9F: Cyrillic uppercase Р–Я + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, + 0x042C, 0x042D, 0x042E, 0x042F, // 0xA0–0xAF: Cyrillic lowercase а–п + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, + 0x043C, 0x043D, 0x043E, 0x043F, // 0xB0–0xBF: box drawing light + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, + 0x255D, 0x255C, 0x255B, 0x2510, // 0xC0–0xCF: box drawing + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, + 0x2560, 0x2550, 0x256C, 0x2567, // 0xD0–0xDF: more box drawing + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, + 0x2584, 0x258C, 0x2590, 0x2580, // 0xE0–0xEF: Cyrillic lowercase р–я + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, + 0x044C, 0x044D, 0x044E, 0x044F, // 0xF0–0xFF + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, + 0x2116, 0x00A4, 0x25A0, 0x00A0, +]; + +/// windows-874 (Thai) +pub(crate) const WINDOWS_874: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x2026, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0x90–0x9F + 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0xA0–0xAF + 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, + 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, // 0xB0–0xBF + 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, + 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, // 0xC0–0xCF + 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, + 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, // 0xD0–0xDF + 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0x0E3F, // 0xE0–0xEF + 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, + 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, // 0xF0–0xFF + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, +]; + +/// windows-1250 (Central European) +pub(crate) const WINDOWS_1250: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0xFFFD, 0x201A, 0xFFFD, 0x201E, 0x2026, 0x2020, 0x2021, 0xFFFD, 0x2030, 0x0160, 0x2039, + 0x015A, 0x0164, 0x017D, 0x0179, // 0x90–0x9F + 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0x0161, 0x203A, + 0x015B, 0x0165, 0x017E, 0x017A, // 0xA0–0xAF + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x017B, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, + 0x013D, 0x02DD, 0x013E, 0x017C, // 0xC0–0xCF + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, + 0x011A, 0x00CD, 0x00CE, 0x010E, // 0xD0–0xDF + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, + 0x00DC, 0x00DD, 0x0162, 0x00DF, // 0xE0–0xEF + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, + 0x011B, 0x00ED, 0x00EE, 0x010F, // 0xF0–0xFF + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, + 0x00FC, 0x00FD, 0x0163, 0x02D9, +]; + +/// windows-1251 (Cyrillic) +pub(crate) const WINDOWS_1251: [u16; 128] = [ + // 0x80–0x8F + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x0409, 0x2039, + 0x040A, 0x040C, 0x040B, 0x040F, // 0x90–0x9F + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0x0459, 0x203A, + 0x045A, 0x045C, 0x045B, 0x045F, // 0xA0–0xAF + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x0407, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, + 0x0458, 0x0405, 0x0455, 0x0457, // 0xC0–0xCF: А–П + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, + 0x041C, 0x041D, 0x041E, 0x041F, // 0xD0–0xDF: Р–Я + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, + 0x042C, 0x042D, 0x042E, 0x042F, // 0xE0–0xEF: а–п + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, + 0x043C, 0x043D, 0x043E, 0x043F, // 0xF0–0xFF: р–я + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, + 0x044C, 0x044D, 0x044E, 0x044F, +]; + +/// windows-1253 (Greek) +pub(crate) const WINDOWS_1253: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0xFFFD, 0x2030, 0xFFFD, 0x2039, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0x90–0x9F + 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0xFFFD, 0x203A, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0xA0–0xAF + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0xFFFD, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x2015, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, + 0x038C, 0x00BD, 0x038E, 0x038F, // 0xC0–0xCF + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, + 0x039C, 0x039D, 0x039E, 0x039F, // 0xD0–0xDF + 0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, + 0x03AC, 0x03AD, 0x03AE, 0x03AF, // 0xE0–0xEF + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, + 0x03BC, 0x03BD, 0x03BE, 0x03BF, // 0xF0–0xFF + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, + 0x03CC, 0x03CD, 0x03CE, 0xFFFD, +]; + +/// windows-1254 (Turkish) +pub(crate) const WINDOWS_1254: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, + 0x0152, 0xFFFD, 0xFFFD, 0xFFFD, // 0x90–0x9F + 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, + 0x0153, 0xFFFD, 0xFFFD, 0x0178, // 0xA0–0xAF + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0x00BF, // 0xC0–0xCF + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, + 0x00CC, 0x00CD, 0x00CE, 0x00CF, // 0xD0–0xDF: differs from 1252 at D0, DD, DE + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, + 0x00DC, 0x0130, 0x015E, 0x00DF, // 0xE0–0xEF + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, + 0x00EC, 0x00ED, 0x00EE, 0x00EF, // 0xF0–0xFF: differs from 1252 at F0, FD, FE + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, + 0x00FC, 0x0131, 0x015F, 0x00FF, +]; + +/// windows-1255 (Hebrew) +pub(crate) const WINDOWS_1255: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0xFFFD, 0x2039, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0x90–0x9F + 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0xFFFD, 0x203A, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0xA0–0xAF + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AA, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0x00BF, // 0xC0–0xCF + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB, + 0x05BC, 0x05BD, 0x05BE, 0x05BF, // 0xD0–0xDF + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0xFFFD, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, // 0xE0–0xEF + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, + 0x05DC, 0x05DD, 0x05DE, 0x05DF, // 0xF0–0xFF + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0xFFFD, + 0xFFFD, 0x200E, 0x200F, 0xFFFD, +]; + +/// windows-1256 (Arabic) +pub(crate) const WINDOWS_1256: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0679, 0x2039, + 0x0152, 0x0686, 0x0698, 0x0688, // 0x90–0x9F + 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691, 0x203A, + 0x0153, 0x200C, 0x200D, 0x06BA, // 0xA0–0xAF + 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x06BE, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x061B, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0x061F, // 0xC0–0xCF + 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, + 0x062C, 0x062D, 0x062E, 0x062F, // 0xD0–0xDF + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, 0x0637, 0x0638, 0x0639, 0x063A, + 0x0640, 0x0641, 0x0642, 0x0643, // 0xE0–0xEF + 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, + 0x0649, 0x064A, 0x00EE, 0x00EF, // 0xF0–0xFF + 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, 0x0651, 0x00F9, 0x0652, 0x00FB, + 0x00FC, 0x200E, 0x200F, 0x06D2, +]; + +/// windows-1257 (Baltic) +pub(crate) const WINDOWS_1257: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0xFFFD, 0x201A, 0xFFFD, 0x201E, 0x2026, 0x2020, 0x2021, 0xFFFD, 0x2030, 0xFFFD, 0x2039, + 0xFFFD, 0x00A8, 0x02C7, 0x00B8, // 0x90–0x9F + 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0xFFFD, 0x203A, + 0xFFFD, 0x00AF, 0x02DB, 0xFFFD, // 0xA0–0xAF + 0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0xFFFD, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00C6, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0x00E6, // 0xC0–0xCF + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, + 0x0122, 0x0136, 0x012A, 0x013B, // 0xD0–0xDF + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, + 0x00DC, 0x017B, 0x017D, 0x00DF, // 0xE0–0xEF + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, + 0x0123, 0x0137, 0x012B, 0x013C, // 0xF0–0xFF + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, + 0x00FC, 0x017C, 0x017E, 0x02D9, +]; + +/// windows-1258 (Vietnamese) +pub(crate) const WINDOWS_1258: [u16; 128] = [ + // 0x80–0x8F + 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0xFFFD, 0x2039, + 0x0152, 0xFFFD, 0xFFFD, 0xFFFD, // 0x90–0x9F + 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0xFFFD, 0x203A, + 0x0153, 0xFFFD, 0xFFFD, 0x0178, // 0xA0–0xAF + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, + 0x00AC, 0x00AD, 0x00AE, 0x00AF, // 0xB0–0xBF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, + 0x00BC, 0x00BD, 0x00BE, 0x00BF, // 0xC0–0xCF + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, + 0x0300, 0x00CD, 0x00CE, 0x00CF, // 0xD0–0xDF + 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, + 0x00DC, 0x01AF, 0x0303, 0x00DF, // 0xE0–0xEF + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, + 0x0301, 0x00ED, 0x00EE, 0x00EF, // 0xF0–0xFF + 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, + 0x00FC, 0x01B0, 0x20AB, 0x00FF, +]; + +/// x-mac-cyrillic +pub(crate) const X_MAC_CYRILLIC: [u16; 128] = [ + // 0x80–0x8F + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, + 0x041C, 0x041D, 0x041E, 0x041F, // 0x90–0x9F + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, + 0x042C, 0x042D, 0x042E, 0x042F, // 0xA0–0xAF + 0x2020, 0x00B0, 0x0490, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x0406, 0x00AE, 0x00A9, 0x2122, 0x0402, + 0x0452, 0x2260, 0x0403, 0x0453, // 0xB0–0xBF + 0x221E, 0x00B1, 0x2264, 0x2265, 0x0456, 0x00B5, 0x0491, 0x0408, 0x0404, 0x0454, 0x0407, 0x0457, + 0x0409, 0x0459, 0x040A, 0x045A, // 0xC0–0xCF + 0x0458, 0x0405, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, 0x00BB, 0x2026, 0x00A0, 0x040B, + 0x045B, 0x040C, 0x045C, 0x0455, // 0xD0–0xDF + 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x201E, 0x040E, 0x045E, 0x040F, 0x045F, + 0x2116, 0x0401, 0x0451, 0x044F, // 0xE0–0xEF + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, + 0x043C, 0x043D, 0x043E, 0x043F, // 0xF0–0xFF + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, + 0x044C, 0x044D, 0x044E, 0x20AC, +]; + +/// Return the lookup table for a single-byte encoding variant. +pub(crate) fn table_for(encoding: &crate::Encoding) -> Option<&'static [u16; 128]> { + use crate::Encoding::*; + match encoding { + Windows1252 => Some(&WINDOWS_1252), + Iso8859_2 => Some(&ISO_8859_2), + Iso8859_3 => Some(&ISO_8859_3), + Iso8859_4 => Some(&ISO_8859_4), + Iso8859_5 => Some(&ISO_8859_5), + Iso8859_6 => Some(&ISO_8859_6), + Iso8859_7 => Some(&ISO_8859_7), + Iso8859_8 | Iso8859_8I => Some(&ISO_8859_8), + Iso8859_10 => Some(&ISO_8859_10), + Iso8859_13 => Some(&ISO_8859_13), + Iso8859_14 => Some(&ISO_8859_14), + Iso8859_15 => Some(&ISO_8859_15), + Iso8859_16 => Some(&ISO_8859_16), + Koi8R => Some(&KOI8_R), + Koi8U => Some(&KOI8_U), + Macintosh => Some(&MACINTOSH), + Ibm866 => Some(&IBM866), + Windows874 => Some(&WINDOWS_874), + Windows1250 => Some(&WINDOWS_1250), + Windows1251 => Some(&WINDOWS_1251), + Windows1253 => Some(&WINDOWS_1253), + Windows1254 => Some(&WINDOWS_1254), + Windows1255 => Some(&WINDOWS_1255), + Windows1256 => Some(&WINDOWS_1256), + Windows1257 => Some(&WINDOWS_1257), + Windows1258 => Some(&WINDOWS_1258), + XMacCyrillic => Some(&X_MAC_CYRILLIC), + _ => None, + } +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + use crate::error::EncodingError; + + fn decode_replace(bytes: &[u8], table: &[u16; 128]) -> String { + decode_single_byte(bytes, table, "test", ErrorMode::Replacement).unwrap() + } + + // -- Basic ASCII passthrough -- + + #[test] + fn ascii_passthrough() { + assert_eq!( + decode_replace(b"Hello, world!", &WINDOWS_1252), + "Hello, world!" + ); + } + + #[test] + fn empty_input() { + assert_eq!(decode_replace(b"", &WINDOWS_1252), ""); + } + + #[test] + fn null_byte() { + assert_eq!(decode_replace(&[0x00], &WINDOWS_1252), "\0"); + } + + // -- Windows-1252 -- + + #[test] + fn windows_1252_euro() { + // 0x80 → U+20AC (€) + assert_eq!(decode_replace(&[0x80], &WINDOWS_1252), "\u{20AC}"); + } + + #[test] + fn windows_1252_smart_quotes() { + // 0x93 → U+201C (") and 0x94 → U+201D (") + assert_eq!( + decode_replace(&[0x93, 0x94], &WINDOWS_1252), + "\u{201C}\u{201D}" + ); + } + + #[test] + fn windows_1252_trademark() { + // 0x99 → U+2122 (™) + assert_eq!(decode_replace(&[0x99], &WINDOWS_1252), "\u{2122}"); + } + + #[test] + fn windows_1252_high_latin() { + // 0xE9 → U+00E9 (é) + assert_eq!(decode_replace(&[0xE9], &WINDOWS_1252), "\u{00E9}"); + } + + #[test] + fn windows_1252_mixed() { + // "Caf" + 0xE9 → "Café" + assert_eq!( + decode_replace(&[0x43, 0x61, 0x66, 0xE9], &WINDOWS_1252), + "Caf\u{00E9}" + ); + } + + // -- ISO-8859-2 (Central European) -- + + #[test] + fn iso_8859_2_polish() { + // 0xA1 → U+0104 (Ą), 0xB1 → U+0105 (ą) + assert_eq!(decode_replace(&[0xA1], &ISO_8859_2), "\u{0104}"); + assert_eq!(decode_replace(&[0xB1], &ISO_8859_2), "\u{0105}"); + } + + #[test] + fn iso_8859_2_czech() { + // 0xC8 → U+010C (Č), 0xE8 → U+010D (č) + assert_eq!(decode_replace(&[0xC8], &ISO_8859_2), "\u{010C}"); + assert_eq!(decode_replace(&[0xE8], &ISO_8859_2), "\u{010D}"); + } + + // -- ISO-8859-5 (Cyrillic) -- + + #[test] + fn iso_8859_5_cyrillic() { + // 0xB0 → U+0410 (А), 0xD0 → U+0430 (а) + assert_eq!(decode_replace(&[0xB0], &ISO_8859_5), "\u{0410}"); + assert_eq!(decode_replace(&[0xD0], &ISO_8859_5), "\u{0430}"); + } + + // -- ISO-8859-7 (Greek) -- + + #[test] + fn iso_8859_7_greek() { + // 0xC1 → U+0391 (Α), 0xE1 → U+03B1 (α) + assert_eq!(decode_replace(&[0xC1], &ISO_8859_7), "\u{0391}"); + assert_eq!(decode_replace(&[0xE1], &ISO_8859_7), "\u{03B1}"); + } + + // -- ISO-8859-15 (Latin-9) -- + + #[test] + fn iso_8859_15_euro() { + // 0xA4 → U+20AC (€) — differs from ISO-8859-1 + assert_eq!(decode_replace(&[0xA4], &ISO_8859_15), "\u{20AC}"); + } + + #[test] + fn iso_8859_15_oe_ligature() { + // 0xBC → U+0152 (Œ), 0xBD → U+0153 (œ) + assert_eq!(decode_replace(&[0xBC], &ISO_8859_15), "\u{0152}"); + assert_eq!(decode_replace(&[0xBD], &ISO_8859_15), "\u{0153}"); + } + + // -- KOI8-R (Russian) -- + + #[test] + fn koi8_r_cyrillic() { + // 0xC1 → U+0430 (а), 0xE1 → U+0410 (А) + assert_eq!(decode_replace(&[0xC1], &KOI8_R), "\u{0430}"); + assert_eq!(decode_replace(&[0xE1], &KOI8_R), "\u{0410}"); + } + + #[test] + fn koi8_r_copyright() { + // 0xBF → U+00A9 (©) + assert_eq!(decode_replace(&[0xBF], &KOI8_R), "\u{00A9}"); + } + + // -- Windows-1251 (Cyrillic) -- + + #[test] + fn windows_1251_cyrillic() { + // 0xC0 → U+0410 (А), 0xE0 → U+0430 (а) + assert_eq!(decode_replace(&[0xC0], &WINDOWS_1251), "\u{0410}"); + assert_eq!(decode_replace(&[0xE0], &WINDOWS_1251), "\u{0430}"); + } + + #[test] + fn windows_1251_euro() { + // 0x88 → U+20AC (€) + assert_eq!(decode_replace(&[0x88], &WINDOWS_1251), "\u{20AC}"); + } + + // -- macintosh -- + + #[test] + fn macintosh_special() { + // 0x80 → U+00C4 (Ä), 0xCA → U+00A0 (NBSP) + assert_eq!(decode_replace(&[0x80], &MACINTOSH), "\u{00C4}"); + assert_eq!(decode_replace(&[0xCA], &MACINTOSH), "\u{00A0}"); + } + + // -- IBM866 -- + + #[test] + fn ibm866_cyrillic() { + // 0x80 → U+0410 (А), 0xA0 → U+0430 (а) + assert_eq!(decode_replace(&[0x80], &IBM866), "\u{0410}"); + assert_eq!(decode_replace(&[0xA0], &IBM866), "\u{0430}"); + } + + // -- Unmapped bytes -- + + #[test] + fn unmapped_replacement() { + // ISO-8859-3 has unmapped bytes, e.g. 0xA5 + assert_eq!(decode_replace(&[0xA5], &ISO_8859_3), "\u{FFFD}"); + } + + #[test] + fn unmapped_fatal() { + let err = decode_single_byte(&[0x41, 0xA5], &ISO_8859_3, "ISO-8859-3", ErrorMode::Fatal) + .unwrap_err(); + assert!(matches!( + err, + EncodingError::InvalidSequence { + encoding: "ISO-8859-3", + position: 1 + } + )); + } + + // -- Table sizes -- + + #[test] + fn all_tables_128_entries() { + assert_eq!(WINDOWS_1252.len(), 128); + assert_eq!(ISO_8859_2.len(), 128); + assert_eq!(ISO_8859_3.len(), 128); + assert_eq!(ISO_8859_4.len(), 128); + assert_eq!(ISO_8859_5.len(), 128); + assert_eq!(ISO_8859_6.len(), 128); + assert_eq!(ISO_8859_7.len(), 128); + assert_eq!(ISO_8859_8.len(), 128); + assert_eq!(ISO_8859_10.len(), 128); + assert_eq!(ISO_8859_13.len(), 128); + assert_eq!(ISO_8859_14.len(), 128); + assert_eq!(ISO_8859_15.len(), 128); + assert_eq!(ISO_8859_16.len(), 128); + assert_eq!(KOI8_R.len(), 128); + assert_eq!(KOI8_U.len(), 128); + assert_eq!(MACINTOSH.len(), 128); + assert_eq!(IBM866.len(), 128); + assert_eq!(WINDOWS_874.len(), 128); + assert_eq!(WINDOWS_1250.len(), 128); + assert_eq!(WINDOWS_1251.len(), 128); + assert_eq!(WINDOWS_1253.len(), 128); + assert_eq!(WINDOWS_1254.len(), 128); + assert_eq!(WINDOWS_1255.len(), 128); + assert_eq!(WINDOWS_1256.len(), 128); + assert_eq!(WINDOWS_1257.len(), 128); + assert_eq!(WINDOWS_1258.len(), 128); + assert_eq!(X_MAC_CYRILLIC.len(), 128); + } +}