1use crate::platform::{PlatformId, define_u16_ids};
2use std::borrow::Cow;
3
4#[cfg(feature = "non-standard-encodings")]
5use encoding_rs::{BIG5, EUC_KR, Encoding as EncodingRs, GB18030, SHIFT_JIS};
6
7#[derive(Debug, Copy, Hash)]
8#[derive_const(Clone, PartialEq, Eq)]
9#[repr(u16)]
10pub enum EncodingId {
11 Unicode(UnicodeEncodingId) = 0,
12 Macintosh(MacintoshEncodingId) = 1,
13 Iso(IsoEncodingId) = 2,
14 Windows(WindowsEncodingId) = 3,
15 Custom(u8) = 4,
16}
17
18define_u16_ids! {
19 pub enum UnicodeEncodingId {
20 Unicode1_0 = 0, Unicode1_1 = 1, IsoIec10646 = 2, Unicode2_0BmpOnly = 3, Unicode2_0Full = 4,
21 UnicodeVariations = 5, UnicodeFull = 6,
22 }
23 pub enum MacintoshEncodingId {
24 Roman = 0, Japanese = 1, ChineseTraditional = 2, Korean = 3, Arabic = 4, Hebrew = 5,
25 Greek = 6, Russian = 7, RSymbol = 8, Devanagari = 9, Gurmukhi = 10, Gujarati = 11,
26 Odia = 12, Bangla = 13, Tamil = 14, Telugu = 15, Kannada = 16, Malayalam = 17,
27 Sinhalese = 18, Burmese = 19, Khmer = 20, Thai = 21, Laotian = 22, Georgian = 23,
28 Armenian = 24, ChineseSimplified = 25, Tibetan = 26, Mongolian = 27, Geez = 28,
29 Slavic = 29, Vietnamese = 30, Sindhi = 31, Uninterpreted = 32,
30 }
31 pub enum IsoEncodingId {
32 SevenBitAscii = 0, Iso10646 = 1, Iso8859_1 = 2,
33 }
34 pub enum WindowsEncodingId {
35 Symbol = 0, UnicodeBmp = 1, ShiftJis = 2, Prc = 3, Big5 = 4, Wansung = 5, Johab = 6,
36 Reserved7 = 7, Reserved8 = 8, Reserved9 = 9, UnicodeFull = 10,
37 }
38}
39
40#[derive(Debug, thiserror::Error)]
41#[derive_const(Clone, PartialEq, Eq)]
42pub enum EncodingError {
43 #[error("unknown platform id")]
44 UnknownPlatform,
45 #[error("unknown encoding id")]
46 UnknownEncoding,
47 #[error("string data could not be decoded")]
48 MalformedString,
49 #[error("this encoding is not implemented")]
50 Unimplemented,
51}
52
53impl EncodingId {
54 pub const fn new(platform_id: u16, encoding_id: u16) -> Result<Self, EncodingError> {
55 let platform = PlatformId::new(platform_id).ok_or(EncodingError::UnknownPlatform)?;
56 platform.encoding(encoding_id).ok_or(EncodingError::UnknownEncoding)
57 }
58
59 pub const fn platform_id(&self) -> u16 {
60 match self {
61 Self::Unicode(_) => 0,
62 Self::Macintosh(_) => 1,
63 Self::Iso(_) => 2,
64 Self::Windows(_) => 3,
65 Self::Custom(_) => 4,
66 }
67 }
68 pub const fn encoding_id(&self) -> u16 {
69 match self {
70 Self::Unicode(x) => x.get(),
71 Self::Macintosh(x) => x.get(),
72 Self::Iso(x) => x.get(),
73 Self::Windows(x) => x.get(),
74 Self::Custom(x) => *x as u16,
75 }
76 }
77 pub fn name(&self) -> Cow<'static, str> {
78 Cow::Borrowed(match self {
79 Self::Unicode(x) => x.name(),
80 Self::Macintosh(x) => x.name(),
81 Self::Iso(x) => x.name(),
82 Self::Windows(x) => x.name(),
83 Self::Custom(charset) => {
84 return Cow::Owned(format!("Charset {}", charset));
85 },
86 })
87 }
88
89 pub fn decode_utf16be(&self, bytes: &[u8]) -> Result<String, EncodingError> {
90 use EncodingError::*;
91
92 fn decode_utf16be(bytes: &[u8]) -> Result<String, EncodingError> {
93 String::from_utf16be(bytes).map_err(|_| MalformedString)
94 }
95
96 #[cfg(feature = "non-standard-encodings")]
97 fn decode(bytes: &[u8], encoding: &'static EncodingRs) -> Result<String, EncodingError> {
98 let decoded = encoding.decode_without_bom_handling_and_without_replacement(bytes);
99 decoded.ok_or(MalformedString).map(|x| x.into_owned())
100 }
101
102 match *self {
103 Self::Unicode(_) => decode_utf16be(bytes),
105
106 Self::Macintosh(encoding) => {
107 use MacintoshEncodingId::*;
108
109 match encoding {
110 Roman => Ok(decode_macos_roman(bytes)), _ => Err(Unimplemented),
112 }
113 },
114
115 Self::Iso(_) => Err(Unimplemented),
116
117 Self::Windows(encoding) => {
118 use WindowsEncodingId::*;
119
120 match encoding {
121 Symbol | UnicodeBmp | UnicodeFull => decode_utf16be(bytes),
122
123 #[cfg(feature = "non-standard-encodings")]
124 ShiftJis => decode(bytes, SHIFT_JIS), #[cfg(feature = "non-standard-encodings")]
126 Prc => decode(bytes, GB18030), #[cfg(feature = "non-standard-encodings")]
128 Big5 => decode(bytes, BIG5), #[cfg(feature = "non-standard-encodings")]
130 Wansung => decode(bytes, EUC_KR), _ => Err(Unimplemented),
135 }
136 },
137
138 Self::Custom(_) => Err(Unimplemented),
139 }
140 }
141}
142
143const impl From<UnicodeEncodingId> for EncodingId {
144 fn from(value: UnicodeEncodingId) -> Self {
145 Self::Unicode(value)
146 }
147}
148const impl From<MacintoshEncodingId> for EncodingId {
149 fn from(value: MacintoshEncodingId) -> Self {
150 Self::Macintosh(value)
151 }
152}
153const impl From<IsoEncodingId> for EncodingId {
154 fn from(value: IsoEncodingId) -> Self {
155 Self::Iso(value)
156 }
157}
158const impl From<WindowsEncodingId> for EncodingId {
159 fn from(value: WindowsEncodingId) -> Self {
160 Self::Windows(value)
161 }
162}
163
164fn decode_macos_roman(bytes: &[u8]) -> String {
166 bytes.iter().copied().map(map_macos_roman).collect()
167}
168fn map_macos_roman(byte: u8) -> char {
169 if byte < 0x80 {
170 byte as char
171 } else {
172 unsafe {
173 char::from_u32_unchecked(*MACOS_ROMAN.get_unchecked((byte - 0x80) as usize) as u32)
174 }
175 }
176}
177
178const MACOS_ROMAN: [u16; 128] = [
179 196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233, 232, 234, 235, 237,
180 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249, 251, 252, 8224, 176, 162, 163, 167,
181 8226, 182, 223, 174, 169, 8482, 180, 168, 8800, 198, 216, 8734, 177, 8804, 8805, 165, 181,
182 8706, 8721, 8719, 960, 8747, 170, 186, 937, 230, 248, 191, 161, 172, 8730, 402, 8776, 8710,
183 171, 187, 8230, 160, 192, 195, 213, 338, 339, 8211, 8212, 8220, 8221, 8216, 8217, 247, 9674,
184 255, 376, 8260, 8364, 8249, 8250, 64257, 64258, 8225, 183, 8218, 8222, 8240, 194, 202, 193,
185 203, 200, 205, 206, 207, 204, 211, 212, 63743, 210, 218, 219, 217, 305, 710, 732, 175, 728,
186 729, 730, 184, 733, 731, 711,
187];