1use crate::encoding_tables::*;
5use crate::error::{XmlError, XML_ERR_UNSUPPORTED_ENCODING};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9#[repr(i32)]
10pub enum XmlCharEncoding {
11 Error = -1,
12 None = 0,
13 Utf8 = 1,
14 Utf16Le = 2,
15 Utf16Be = 3,
16 Ucs4Le = 4,
17 Ucs4Be = 5,
18 Ebcdic = 6,
19 Ucs4_2143 = 7,
20 Ucs4_3412 = 8,
21 Ucs2 = 9,
22 Iso8859_1 = 10,
23 Iso8859_2 = 11,
24 Iso8859_3 = 12,
25 Iso8859_4 = 13,
26 Iso8859_5 = 14,
27 Iso8859_6 = 15,
28 Iso8859_7 = 16,
29 Iso8859_8 = 17,
30 Iso8859_9 = 18,
31 Iso2022Jp = 19,
32 ShiftJis = 20,
33 EucJp = 21,
34 Ascii = 22,
35 Utf16 = 23,
36 Html = 24,
37 Iso8859_10 = 25,
38 Iso8859_11 = 26,
39 Iso8859_13 = 27,
40 Iso8859_14 = 28,
41 Iso8859_15 = 29,
42 Iso8859_16 = 30,
43 Windows1252 = 31,
44 Windows1250 = 32,
45 Windows1251 = 33,
46 Windows1253 = 34,
47 Windows1254 = 35,
48 Windows1255 = 36,
49 Windows1256 = 37,
50 Windows1257 = 38,
51 Windows1258 = 39,
52 Koi8R = 40,
53 Koi8U = 41,
54 Ibm866 = 42,
55 Macintosh = 43,
56}
57
58const NAME_MAP: &[(&str, XmlCharEncoding)] = &[
59 ("ascii", XmlCharEncoding::Ascii),
60 ("windows-1250", XmlCharEncoding::Windows1250),
61 ("cp1250", XmlCharEncoding::Windows1250),
62 ("windows1250", XmlCharEncoding::Windows1250),
63 ("windows-1251", XmlCharEncoding::Windows1251),
64 ("cp1251", XmlCharEncoding::Windows1251),
65 ("windows1251", XmlCharEncoding::Windows1251),
66 ("windows-1253", XmlCharEncoding::Windows1253),
67 ("cp1253", XmlCharEncoding::Windows1253),
68 ("windows1253", XmlCharEncoding::Windows1253),
69 ("windows-1254", XmlCharEncoding::Windows1254),
70 ("cp1254", XmlCharEncoding::Windows1254),
71 ("windows1254", XmlCharEncoding::Windows1254),
72 ("windows-1255", XmlCharEncoding::Windows1255),
73 ("cp1255", XmlCharEncoding::Windows1255),
74 ("windows1255", XmlCharEncoding::Windows1255),
75 ("windows-1256", XmlCharEncoding::Windows1256),
76 ("cp1256", XmlCharEncoding::Windows1256),
77 ("windows1256", XmlCharEncoding::Windows1256),
78 ("windows-1257", XmlCharEncoding::Windows1257),
79 ("cp1257", XmlCharEncoding::Windows1257),
80 ("windows1257", XmlCharEncoding::Windows1257),
81 ("windows-1258", XmlCharEncoding::Windows1258),
82 ("cp1258", XmlCharEncoding::Windows1258),
83 ("windows1258", XmlCharEncoding::Windows1258),
84 ("koi8-r", XmlCharEncoding::Koi8R),
85 ("koi8r", XmlCharEncoding::Koi8R),
86 ("cskoi8r", XmlCharEncoding::Koi8R),
87 ("koi8-u", XmlCharEncoding::Koi8U),
88 ("koi8u", XmlCharEncoding::Koi8U),
89 ("ibm866", XmlCharEncoding::Ibm866),
90 ("cp866", XmlCharEncoding::Ibm866),
91 ("866", XmlCharEncoding::Ibm866),
92 ("macintosh", XmlCharEncoding::Macintosh),
93 ("mac", XmlCharEncoding::Macintosh),
94 ("csmacintosh", XmlCharEncoding::Macintosh),
95
96 ("csisolatin1", XmlCharEncoding::Iso8859_1),
97 ("iso-8859-1", XmlCharEncoding::Iso8859_1),
98 ("iso-8859-2", XmlCharEncoding::Iso8859_2),
99 ("iso-8859-3", XmlCharEncoding::Iso8859_3),
100 ("iso-8859-4", XmlCharEncoding::Iso8859_4),
101 ("iso-8859-5", XmlCharEncoding::Iso8859_5),
102 ("iso-8859-6", XmlCharEncoding::Iso8859_6),
103 ("iso-8859-7", XmlCharEncoding::Iso8859_7),
104 ("iso-8859-8", XmlCharEncoding::Iso8859_8),
105 ("iso-8859-9", XmlCharEncoding::Iso8859_9),
106 ("iso-8859-10", XmlCharEncoding::Iso8859_10),
107 ("iso-8859-11", XmlCharEncoding::Iso8859_11),
108 ("iso-8859-13", XmlCharEncoding::Iso8859_13),
109 ("iso-8859-14", XmlCharEncoding::Iso8859_14),
110 ("iso-8859-15", XmlCharEncoding::Iso8859_15),
111 ("iso-8859-16", XmlCharEncoding::Iso8859_16),
112 ("iso8859-1", XmlCharEncoding::Iso8859_1),
113 ("iso_8859-1", XmlCharEncoding::Iso8859_1),
114 ("latin1", XmlCharEncoding::Iso8859_1),
115 ("us-ascii", XmlCharEncoding::Ascii),
116 ("utf-16", XmlCharEncoding::Utf16),
117 ("utf-16be", XmlCharEncoding::Utf16Be),
118 ("utf-16le", XmlCharEncoding::Utf16Le),
119 ("utf-8", XmlCharEncoding::Utf8),
120 ("utf16", XmlCharEncoding::Utf16),
121 ("utf8", XmlCharEncoding::Utf8),
122 ("unicode", XmlCharEncoding::Utf16),
123 ("ucs-2", XmlCharEncoding::Ucs2),
124 ("ucs-4", XmlCharEncoding::Ucs4Le),
125 ("ucs2", XmlCharEncoding::Ucs2),
126 ("ucs4", XmlCharEncoding::Ucs4Le),
127 ("windows-1252", XmlCharEncoding::Windows1252),
128 ("x-cp1252", XmlCharEncoding::Windows1252),
129 ("ibm037", XmlCharEncoding::Ebcdic),
130 ("ebcdic", XmlCharEncoding::Ebcdic),
131 ("iso-2022-jp", XmlCharEncoding::Iso2022Jp),
132 ("shift_jis", XmlCharEncoding::ShiftJis),
133 ("shift-jis", XmlCharEncoding::ShiftJis),
134 ("sjis", XmlCharEncoding::ShiftJis),
135 ("euc-jp", XmlCharEncoding::EucJp),
136];
137
138#[doc(alias = "xmlParseCharEncoding")]
140pub fn xml_parse_char_encoding(name: &str) -> XmlCharEncoding {
141 let lower = name.trim().to_ascii_lowercase();
142 for (n, enc) in NAME_MAP {
143 if *n == lower {
144 return if *enc == XmlCharEncoding::Utf16 {
145 XmlCharEncoding::Utf16Le
146 } else {
147 *enc
148 };
149 }
150 }
151 XmlCharEncoding::Error
152}
153
154#[doc(alias = "xmlGetCharEncodingName")]
156pub fn xml_get_char_encoding_name(enc: XmlCharEncoding) -> Option<&'static str> {
157 Some(match enc {
158 XmlCharEncoding::Utf8 => "UTF-8",
159 XmlCharEncoding::Utf16Le | XmlCharEncoding::Utf16Be | XmlCharEncoding::Utf16 => "UTF-16",
160 XmlCharEncoding::Ucs4Le | XmlCharEncoding::Ucs4Be => "UCS-4",
161 XmlCharEncoding::Iso8859_1 => "ISO-8859-1",
162 XmlCharEncoding::Windows1250 => "windows-1250",
163 XmlCharEncoding::Windows1251 => "windows-1251",
164 XmlCharEncoding::Windows1253 => "windows-1253",
165 XmlCharEncoding::Windows1254 => "windows-1254",
166 XmlCharEncoding::Windows1255 => "windows-1255",
167 XmlCharEncoding::Windows1256 => "windows-1256",
168 XmlCharEncoding::Windows1257 => "windows-1257",
169 XmlCharEncoding::Windows1258 => "windows-1258",
170 XmlCharEncoding::Koi8R => "KOI8-R",
171 XmlCharEncoding::Koi8U => "KOI8-U",
172 XmlCharEncoding::Ibm866 => "IBM866",
173 XmlCharEncoding::Macintosh => "macintosh",
174 XmlCharEncoding::Iso8859_2 => "ISO-8859-2",
175 XmlCharEncoding::Iso8859_3 => "ISO-8859-3",
176 XmlCharEncoding::Iso8859_4 => "ISO-8859-4",
177 XmlCharEncoding::Iso8859_5 => "ISO-8859-5",
178 XmlCharEncoding::Iso8859_6 => "ISO-8859-6",
179 XmlCharEncoding::Iso8859_7 => "ISO-8859-7",
180 XmlCharEncoding::Iso8859_8 => "ISO-8859-8",
181 XmlCharEncoding::Iso8859_9 => "ISO-8859-9",
182 XmlCharEncoding::Iso8859_10 => "ISO-8859-10",
183 XmlCharEncoding::Iso8859_11 => "ISO-8859-11",
184 XmlCharEncoding::Iso8859_13 => "ISO-8859-13",
185 XmlCharEncoding::Iso8859_14 => "ISO-8859-14",
186 XmlCharEncoding::Iso8859_15 => "ISO-8859-15",
187 XmlCharEncoding::Iso8859_16 => "ISO-8859-16",
188 XmlCharEncoding::Ascii => "US-ASCII",
189 XmlCharEncoding::Windows1252 => "windows-1252",
190 XmlCharEncoding::Ebcdic => "IBM037",
191 XmlCharEncoding::Ucs2 => "UCS-2",
192 XmlCharEncoding::Iso2022Jp => "ISO-2022-JP",
193 XmlCharEncoding::ShiftJis => "Shift_JIS",
194 XmlCharEncoding::EucJp => "EUC-JP",
195 XmlCharEncoding::Html => "HTML",
196 XmlCharEncoding::None | XmlCharEncoding::Error | XmlCharEncoding::Ucs4_2143 | XmlCharEncoding::Ucs4_3412 => {
197 return None
198 }
199 })
200}
201
202#[doc(alias = "xmlDetectCharEncoding")]
204pub fn xml_detect_char_encoding(input: &[u8]) -> XmlCharEncoding {
205 if input.len() >= 4 {
206 let b = &input[..4];
207 if b == [0x00, 0x00, 0x00, 0x3C] {
208 return XmlCharEncoding::Ucs4Be;
209 }
210 if b == [0x3C, 0x00, 0x00, 0x00] {
211 return XmlCharEncoding::Ucs4Le;
212 }
213 if b == [0x00, 0x00, 0x3C, 0x00] {
214 return XmlCharEncoding::Ucs4_2143;
215 }
216 if b == [0x00, 0x3C, 0x00, 0x00] {
217 return XmlCharEncoding::Ucs4_3412;
218 }
219 if b == [0x4C, 0x6F, 0xA7, 0x94] {
220 return XmlCharEncoding::Ebcdic;
221 }
222 if b == [0x3C, 0x3F, 0x78, 0x6D] {
223 return XmlCharEncoding::Utf8;
224 }
225 if b == [0x3C, 0x00, 0x3F, 0x00] {
226 return XmlCharEncoding::Utf16Le;
227 }
228 if b == [0x00, 0x3C, 0x00, 0x3F] {
229 return XmlCharEncoding::Utf16Be;
230 }
231 if b == [0x00, 0x00, 0xFE, 0xFF] {
232 return XmlCharEncoding::Ucs4Be;
233 }
234 if b == [0xFF, 0xFE, 0x00, 0x00] {
235 return XmlCharEncoding::Ucs4Le;
236 }
237 }
238 if input.len() >= 3 && input[..3] == [0xEF, 0xBB, 0xBF] {
239 return XmlCharEncoding::Utf8;
240 }
241 if input.len() >= 2 {
242 if input[0] == 0xFE && input[1] == 0xFF {
243 return XmlCharEncoding::Utf16Be;
244 }
245 if input[0] == 0xFF && input[1] == 0xFE {
246 return XmlCharEncoding::Utf16Le;
247 }
248 }
249 XmlCharEncoding::None
250}
251
252fn eightbit_to_utf8(input: &[u8], table: &[u16; 128]) -> Vec<u8> {
253 let mut out = Vec::with_capacity(input.len() * 2);
254 for &b in input {
255 let cp = if b < 0x80 {
256 b as u32
257 } else {
258 table[(b - 0x80) as usize] as u32
259 };
260 if let Some(c) = char::from_u32(cp) {
261 let mut buf = [0u8; 4];
262 out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
263 }
264 }
265 out
266}
267
268fn latin1_to_utf8(input: &[u8]) -> Vec<u8> {
269 let mut out = Vec::with_capacity(input.len() * 2);
270 for &b in input {
271 let c = b as char;
272 let mut buf = [0u8; 4];
273 out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
274 }
275 out
276}
277
278fn utf16_to_utf8(input: &[u8], be: bool) -> Result<Vec<u8>, XmlError> {
279 let mut i = 0;
280 if input.len() >= 2 {
281 let bom = if be {
282 input[0] == 0xFE && input[1] == 0xFF
283 } else {
284 input[0] == 0xFF && input[1] == 0xFE
285 };
286 if bom {
287 i = 2;
288 }
289 }
290 let mut units = Vec::new();
291 while i + 1 < input.len() {
292 let u = if be {
293 u16::from_be_bytes([input[i], input[i + 1]])
294 } else {
295 u16::from_le_bytes([input[i], input[i + 1]])
296 };
297 units.push(u);
298 i += 2;
299 }
300 let s = String::from_utf16(&units).map_err(|_| {
301 XmlError::new(XML_ERR_UNSUPPORTED_ENCODING, "Invalid UTF-16", 0, 0)
302 })?;
303 Ok(s.into_bytes())
304}
305
306fn ucs4_to_utf8(input: &[u8], order: [usize; 4]) -> Result<Vec<u8>, XmlError> {
307 let mut i = 0;
308 if input.len() >= 4 {
309 let w = u32::from_be_bytes([input[0], input[1], input[2], input[3]]);
310 if w == 0xFEFF || w == 0xFFFE0000 {
311 i = 4;
312 }
313 }
314 let mut out = String::new();
315 while i + 3 < input.len() {
316 let b = [input[i], input[i + 1], input[i + 2], input[i + 3]];
317 let cp = u32::from_be_bytes([b[order[0]], b[order[1]], b[order[2]], b[order[3]]]);
318 match char::from_u32(cp) {
319 Some(c) if c != '\u{feff}' || !out.is_empty() => out.push(c),
320 Some(_) => {}
321 None => {
322 return Err(XmlError::new(
323 XML_ERR_UNSUPPORTED_ENCODING,
324 "Invalid UCS-4",
325 0,
326 0,
327 ));
328 }
329 }
330 i += 4;
331 }
332 Ok(out.into_bytes())
333}
334
335static CP037: [u16; 256] = [
337 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
338 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f,
339 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07,
340 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a,
341 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c,
342 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0xac,
343 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,
344 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22,
345 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1,
346 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4,
347 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae,
348 0x5e, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, 0xbd, 0xbe, 0x5b, 0x5d, 0xaf, 0xa8, 0xb4, 0xd7,
349 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5,
350 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff,
351 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5,
352 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f,
353];
354
355fn table_for(enc: XmlCharEncoding) -> Option<&'static [u16; 128]> {
356 Some(match enc {
357 XmlCharEncoding::Iso8859_2 => &XML_UNICODE_ISO8859_2,
358 XmlCharEncoding::Iso8859_3 => &XML_UNICODE_ISO8859_3,
359 XmlCharEncoding::Iso8859_4 => &XML_UNICODE_ISO8859_4,
360 XmlCharEncoding::Iso8859_5 => &XML_UNICODE_ISO8859_5,
361 XmlCharEncoding::Iso8859_6 => &XML_UNICODE_ISO8859_6,
362 XmlCharEncoding::Iso8859_7 => &XML_UNICODE_ISO8859_7,
363 XmlCharEncoding::Iso8859_8 => &XML_UNICODE_ISO8859_8,
364 XmlCharEncoding::Iso8859_9 => &XML_UNICODE_ISO8859_9,
365 XmlCharEncoding::Iso8859_10 => &XML_UNICODE_ISO8859_10,
366 XmlCharEncoding::Iso8859_11 => &XML_UNICODE_ISO8859_11,
367 XmlCharEncoding::Iso8859_13 => &XML_UNICODE_ISO8859_13,
368 XmlCharEncoding::Iso8859_14 => &XML_UNICODE_ISO8859_14,
369 XmlCharEncoding::Iso8859_15 => &XML_UNICODE_ISO8859_15,
370 XmlCharEncoding::Iso8859_16 => &XML_UNICODE_ISO8859_16,
371 XmlCharEncoding::Windows1252 => &XML_UNICODE_windows_1252,
372 XmlCharEncoding::Windows1250 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1250,
373 XmlCharEncoding::Windows1251 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1251,
374 XmlCharEncoding::Windows1253 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1253,
375 XmlCharEncoding::Windows1254 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1254,
376 XmlCharEncoding::Windows1255 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1255,
377 XmlCharEncoding::Windows1256 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1256,
378 XmlCharEncoding::Windows1257 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1257,
379 XmlCharEncoding::Windows1258 => &crate::encoding_tables_more::XML_UNICODE_WINDOWS1258,
380 XmlCharEncoding::Koi8R => &crate::encoding_tables_more::XML_UNICODE_KOI8R,
381 XmlCharEncoding::Koi8U => &crate::encoding_tables_more::XML_UNICODE_KOI8U,
382 XmlCharEncoding::Ibm866 => &crate::encoding_tables_more::XML_UNICODE_IBM866,
383 XmlCharEncoding::Macintosh => &crate::encoding_tables_more::XML_UNICODE_MACINTOSH,
384
385 _ => return None,
386 })
387}
388
389fn sniff_encoding_decl(bytes: &[u8]) -> Option<XmlCharEncoding> {
390 let n = bytes.len().min(1024);
393 let head = &bytes[..n];
394 if head.len() < 8 {
395 return None;
396 }
397 let idx = head
398 .windows(8)
399 .position(|w| w.eq_ignore_ascii_case(b"encoding"))?;
400 let mut i = idx + 8;
401 while i < n && matches!(head[i], b' ' | b'\t' | b'\r' | b'\n' | b'=') {
402 i += 1;
403 }
404 if i >= n || (head[i] != b'"' && head[i] != b'\'') {
405 return None;
406 }
407 let q = head[i];
408 i += 1;
409 let start = i;
410 while i < n && head[i] != q {
411 i += 1;
412 }
413 if i >= n {
414 return None;
415 }
416 let name = std::str::from_utf8(&head[start..i]).ok()?;
417 xml_parse_char_encoding(name).into_option()
418}
419
420impl XmlCharEncoding {
421 fn into_option(self) -> Option<XmlCharEncoding> {
422 match self {
423 XmlCharEncoding::Error | XmlCharEncoding::None => None,
424 e => Some(e),
425 }
426 }
427}
428
429pub fn xml_convert_to_utf8(
431 input: &[u8],
432 hint: Option<&str>,
433) -> Result<(Vec<u8>, Option<String>), XmlError> {
434 let (c, n) = xml_convert_to_utf8_cow(input, hint)?;
435 Ok((c.into_owned(), n.map(str::to_string)))
436}
437
438pub(crate) fn xml_convert_to_utf8_cow<'a>(
442 input: &'a [u8],
443 hint: Option<&str>,
444) -> Result<(std::borrow::Cow<'a, [u8]>, Option<&'static str>), XmlError> {
445 if input.len() >= 2 && input[0] == 0x1f && input[1] == 0x8b {
446 return Err(XmlError::new(
447 XML_ERR_UNSUPPORTED_ENCODING,
448 "gzip input requires the unzip feature / XML_PARSE_UNZIP",
449 0,
450 0,
451 ));
452 }
453 let mut enc = hint
454 .and_then(|h| xml_parse_char_encoding(h).into_option())
455 .unwrap_or_else(|| xml_detect_char_encoding(input));
456 if enc == XmlCharEncoding::None || enc == XmlCharEncoding::Utf8 {
457 if let Some(d) = sniff_encoding_decl(input) {
458 if d != XmlCharEncoding::Utf8 && d != XmlCharEncoding::Ascii {
459 enc = d;
460 }
461 }
462 }
463 if enc == XmlCharEncoding::None || enc == XmlCharEncoding::Utf8 || enc == XmlCharEncoding::Ascii
464 {
465 let body = if input.starts_with(&[0xEF, 0xBB, 0xBF]) { &input[3..] } else { input };
467 return Ok((std::borrow::Cow::Borrowed(body), xml_get_char_encoding_name(enc)));
468 }
469 let converted = match enc {
470 XmlCharEncoding::Iso8859_1 => latin1_to_utf8(input),
471 XmlCharEncoding::Utf16Le | XmlCharEncoding::Utf16 => utf16_to_utf8(input, false)?,
472 XmlCharEncoding::Utf16Be => utf16_to_utf8(input, true)?,
473 XmlCharEncoding::Ucs4Be => ucs4_to_utf8(input, [0, 1, 2, 3])?,
474 XmlCharEncoding::Ucs4Le => ucs4_to_utf8(input, [3, 2, 1, 0])?,
475 XmlCharEncoding::Ucs4_2143 => ucs4_to_utf8(input, [1, 0, 3, 2])?,
476 XmlCharEncoding::Ucs4_3412 => ucs4_to_utf8(input, [2, 3, 0, 1])?,
477 XmlCharEncoding::Ucs2 => utf16_to_utf8(input, true)?,
478 XmlCharEncoding::Ebcdic => {
479 let mut out = Vec::new();
480 for &b in input {
481 let c = char::from_u32(CP037[b as usize] as u32).unwrap_or('\u{fffd}');
482 let mut buf = [0u8; 4];
483 out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
484 }
485 out
486 }
487 other => {
488 if let Some(t) = table_for(other) {
489 eightbit_to_utf8(input, t)
490 } else {
491 return Err(XmlError::new(
492 XML_ERR_UNSUPPORTED_ENCODING,
493 format!(
494 "Unsupported encoding {}",
495 xml_get_char_encoding_name(other).unwrap_or("?")
496 ),
497 0,
498 0,
499 ));
500 }
501 }
502 };
503 Ok((
504 std::borrow::Cow::Owned(converted),
505 xml_get_char_encoding_name(enc),
506 ))
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512
513 #[test]
514 fn latin1_converts() {
515 let (u, _) = xml_convert_to_utf8(&[0xE9], Some("ISO-8859-1")).unwrap();
516 assert_eq!(u, "é".as_bytes());
517 }
518
519 #[test]
520 fn utf16_le_bom() {
521 let mut b = vec![0xFF, 0xFE];
523 for c in "<a/>".encode_utf16() {
524 b.extend_from_slice(&c.to_le_bytes());
525 }
526 let (u, _) = xml_convert_to_utf8(&b, None).unwrap();
527 assert_eq!(std::str::from_utf8(&u).unwrap().trim_start_matches('\u{feff}'), "<a/>");
528 }
529
530 #[test]
531 fn iso8859_2_table() {
532 let (u, _) = xml_convert_to_utf8(&[0xA1], Some("ISO-8859-2")).unwrap();
534 assert_eq!(u, "Ą".as_bytes());
535 }
536
537 #[test]
538 fn parse_encoding_names() {
539 assert_eq!(xml_parse_char_encoding("utf-8"), XmlCharEncoding::Utf8);
540 assert_eq!(xml_parse_char_encoding("latin1"), XmlCharEncoding::Iso8859_1);
541 assert_eq!(xml_parse_char_encoding("windows-1252"), XmlCharEncoding::Windows1252);
542 }
543}
544