1#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
22
23use std::ffi::CStr;
24use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};
25use std::ptr;
26use std::sync::atomic::{AtomicBool, Ordering};
27
28use once_cell::sync::Lazy;
29use parking_lot::RwLock;
30
31use crate::abi::allocator::{xmlFree, xmlMalloc, xmlRealloc};
32use crate::abi::callbacks::{xmlCharEncodingInputFunc, xmlCharEncodingOutputFunc};
33use crate::abi::constants::XML_MAX_ENCODING_NAME_LEN;
34use crate::abi::structs::{
35 _xmlBuffer, _xmlCharEncodingHandler, EncodingInputUnion, EncodingOutputUnion,
36};
37use crate::abi::types::{xmlChar, xmlCharEncoding, xmlCharPtr};
38
39const MAX_CHAR_BYTES: usize = 6;
43
44const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
46
47const UTF16LE_BOM: [u8; 2] = [0xFF, 0xFE];
49
50const UTF16BE_BOM: [u8; 2] = [0xFE, 0xFF];
52
53#[derive(Clone, Copy)]
61struct HandlerPtr(*mut _xmlCharEncodingHandler);
62
63unsafe impl Send for HandlerPtr {}
64unsafe impl Sync for HandlerPtr {}
65
66static ENCODING_HANDLERS: Lazy<RwLock<Vec<HandlerPtr>>> = Lazy::new(|| RwLock::new(Vec::new()));
68
69static ENCODING_INITIALIZED: AtomicBool = AtomicBool::new(false);
71
72pub(crate) fn detect_encoding_from_bom(data: &[u8]) -> xmlCharEncoding {
81 if data.len() >= 3 && data[0..3] == UTF8_BOM {
82 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
83 } else if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
84 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
85 } else if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
86 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
87 } else {
88 xmlCharEncoding::XML_CHAR_ENCODING_NONE
89 }
90}
91
92pub(crate) fn detect_encoding_from_declaration(data: &[u8]) -> Option<Vec<u8>> {
97 let start = if data.len() >= 3 && data[0..3] == UTF8_BOM {
99 3
100 } else if data.len() >= 2 && (data[0..2] == UTF16LE_BOM || data[0..2] == UTF16BE_BOM) {
101 return None;
103 } else {
104 0
105 };
106
107 let remaining = &data[start..];
108
109 if remaining.len() < 5 || !remaining[0..5].eq_ignore_ascii_case(b"<?xml") {
111 return None;
112 }
113
114 let pi_end = remaining.windows(2).position(|w| w == b"?>")?;
116 let decl_content = &remaining[5..pi_end];
117
118 let decl_str = core::str::from_utf8(decl_content).ok()?;
120 let lower = decl_str.to_ascii_lowercase();
121
122 let enc_pos = lower.find("encoding")?;
124
125 let after_enc = &decl_content[enc_pos + 8..];
127 let after_enc_str = core::str::from_utf8(after_enc).ok()?;
128 let after_enc_trimmed = after_enc_str.trim_start();
129
130 if !after_enc_trimmed.starts_with('=') {
131 return None;
132 }
133
134 let after_eq = after_enc_trimmed[1..].trim_start();
135
136 let quote = after_eq.chars().next()?;
138 if quote != '"' && quote != '\'' {
139 return None;
140 }
141
142 let value_end = after_eq[1..].find(quote)?;
144 let encoding_value = &after_eq[1..=value_end];
145
146 Some(encoding_value.to_ascii_lowercase().as_bytes().to_vec())
147}
148
149pub(crate) fn encoding_from_name(name: &[u8]) -> xmlCharEncoding {
154 let s = core::str::from_utf8(name).unwrap_or("");
155 let s = s.trim().to_ascii_lowercase();
156
157 match s.as_str() {
158 "utf-8" | "utf8" => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
160
161 "utf-16" | "utf-16le" | "utf16le" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
163 "utf-16be" | "utf16be" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
164
165 "iso-8859-1" | "iso_8859-1" | "latin1" | "latin-1" | "l1" | "cp819" | "ibm819"
167 | "iso-ir-100" | "iso_8859-1:1987" => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
168 "iso-8859-2" | "iso_8859-2" | "latin2" | "latin-2" | "l2" => {
169 xmlCharEncoding::XML_CHAR_ENCODING_8859_2
170 }
171 "iso-8859-3" | "iso_8859-3" | "latin3" | "latin-3" | "l3" => {
172 xmlCharEncoding::XML_CHAR_ENCODING_8859_3
173 }
174 "iso-8859-4" | "iso_8859-4" | "latin4" | "latin-4" | "l4" => {
175 xmlCharEncoding::XML_CHAR_ENCODING_8859_4
176 }
177 "iso-8859-5" | "iso_8859-5" | "cyrillic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
178 "iso-8859-6" | "iso_8859-6" | "arabic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
179 "iso-8859-7" | "iso_8859-7" | "greek" => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
180 "iso-8859-8" | "iso_8859-8" | "hebrew" => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
181 "iso-8859-9" | "iso_8859-9" | "latin5" | "latin-5" | "l5" | "turkish" => {
182 xmlCharEncoding::XML_CHAR_ENCODING_8859_9
183 }
184
185 "ascii" | "us-ascii" | "us" | "ansi_x3.4-1968" | "ansi_x3.4-1986" | "iso-ir-6"
187 | "iso_646.irv:1991" | "cp367" | "ibm367" => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
188
189 "iso-2022-jp" | "iso2022-jp" => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
191 "shift_jis" | "shift-jis" | "sjis" | "cp932" => {
192 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS
193 }
194 "euc-jp" | "eucjp" => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
195
196 "ucs-4" | "ucs4" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
198 "ucs-4le" | "ucs4le" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
199 "ucs-4be" | "ucs4be" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
200 "ucs-2" | "ucs2" => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
201
202 "ebcdic" | "cp037" | "ibm037" => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
204
205 _ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
206 }
207}
208
209pub(crate) fn encoding_name(enc: xmlCharEncoding) -> Option<&'static [u8]> {
213 match enc {
214 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => Some(b"UTF-8" as &[u8]),
215 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => Some(b"UTF-16LE" as &[u8]),
216 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => Some(b"UTF-16BE" as &[u8]),
217 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => Some(b"UCS-4LE" as &[u8]),
218 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => Some(b"UCS-4BE" as &[u8]),
219 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => Some(b"EBCDIC" as &[u8]),
220 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => Some(b"UCS-4-2143" as &[u8]),
221 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => Some(b"UCS-4-3412" as &[u8]),
222 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => Some(b"UCS-2" as &[u8]),
223 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => Some(b"ISO-8859-1" as &[u8]),
224 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => Some(b"ISO-8859-2" as &[u8]),
225 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => Some(b"ISO-8859-3" as &[u8]),
226 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => Some(b"ISO-8859-4" as &[u8]),
227 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => Some(b"ISO-8859-5" as &[u8]),
228 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => Some(b"ISO-8859-6" as &[u8]),
229 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => Some(b"ISO-8859-7" as &[u8]),
230 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => Some(b"ISO-8859-8" as &[u8]),
231 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => Some(b"ISO-8859-9" as &[u8]),
232 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => Some(b"ISO-2022-JP" as &[u8]),
233 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => Some(b"SHIFT_JIS" as &[u8]),
234 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => Some(b"EUC-JP" as &[u8]),
235 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => Some(b"ASCII" as &[u8]),
236 _ => None,
237 }
238}
239
240pub(crate) fn utf8_valid(data: &[u8]) -> bool {
248 core::str::from_utf8(data).is_ok()
249}
250
251pub(crate) fn is_valid_xml_char(cp: u32) -> bool {
263 match cp {
264 0x9 | 0xA | 0xD => true,
265 0x20..=0xD7FF => true,
266 0xE000..=0xFFFD => true,
267 0x10000..=0x10FFFF => true,
268 _ => false,
269 }
270}
271
272#[inline]
278fn read_utf16le_unit(data: &[u8]) -> Option<u16> {
279 if data.len() < 2 {
280 return None;
281 }
282 Some(u16::from_le_bytes([data[0], data[1]]))
283}
284
285#[inline]
287fn read_utf16be_unit(data: &[u8]) -> Option<u16> {
288 if data.len() < 2 {
289 return None;
290 }
291 Some(u16::from_be_bytes([data[0], data[1]]))
292}
293
294fn encode_codepoint_to_utf8(cp: u32, out: &mut [u8]) -> usize {
298 if cp < 0x80 {
299 if !out.is_empty() {
300 out[0] = cp as u8;
301 }
302 1
303 } else if cp < 0x800 {
304 if out.len() < 2 {
305 return 0;
306 }
307 out[0] = 0xC0 | ((cp >> 6) as u8);
308 out[1] = 0x80 | (cp as u8 & 0x3F);
309 2
310 } else if cp < 0x10000 {
311 if out.len() < 3 {
312 return 0;
313 }
314 out[0] = 0xE0 | ((cp >> 12) as u8);
315 out[1] = 0x80 | ((cp >> 6) as u8 & 0x3F);
316 out[2] = 0x80 | (cp as u8 & 0x3F);
317 3
318 } else if cp < 0x110000 {
319 if out.len() < 4 {
320 return 0;
321 }
322 out[0] = 0xF0 | ((cp >> 18) as u8);
323 out[1] = 0x80 | ((cp >> 12) as u8 & 0x3F);
324 out[2] = 0x80 | ((cp >> 6) as u8 & 0x3F);
325 out[3] = 0x80 | (cp as u8 & 0x3F);
326 4
327 } else {
328 0
329 }
330}
331
332pub(crate) fn utf16le_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
337 if data.is_empty() {
338 return Ok(Vec::new());
339 }
340
341 let offset = if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
343 2
344 } else {
345 0
346 };
347
348 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
349 let mut i = offset;
350
351 while i < data.len() {
352 let unit = read_utf16le_unit(&data[i..]).ok_or(())?;
353 i += 2;
354
355 if unit >= 0xD800 && unit <= 0xDBFF {
356 let low = read_utf16le_unit(&data[i..]).ok_or(())?;
358 i += 2;
359
360 if !(0xDC00..=0xDFFF).contains(&low) {
361 return Err(());
362 }
363
364 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
365 let mut buf = [0u8; 4];
366 let n = encode_codepoint_to_utf8(cp, &mut buf);
367 if n == 0 {
368 return Err(());
369 }
370 result.extend_from_slice(&buf[..n]);
371 } else if unit >= 0xDC00 && unit <= 0xDFFF {
372 return Err(());
374 } else {
375 let cp = unit as u32;
376 let mut buf = [0u8; 4];
377 let n = encode_codepoint_to_utf8(cp, &mut buf);
378 result.extend_from_slice(&buf[..n]);
379 }
380 }
381
382 Ok(result)
383}
384
385pub(crate) fn utf16be_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
389 if data.is_empty() {
390 return Ok(Vec::new());
391 }
392
393 let offset = if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
395 2
396 } else {
397 0
398 };
399
400 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
401 let mut i = offset;
402
403 while i < data.len() {
404 let unit = read_utf16be_unit(&data[i..]).ok_or(())?;
405 i += 2;
406
407 if unit >= 0xD800 && unit <= 0xDBFF {
408 let low = read_utf16be_unit(&data[i..]).ok_or(())?;
410 i += 2;
411
412 if !(0xDC00..=0xDFFF).contains(&low) {
413 return Err(());
414 }
415
416 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
417 let mut buf = [0u8; 4];
418 let n = encode_codepoint_to_utf8(cp, &mut buf);
419 if n == 0 {
420 return Err(());
421 }
422 result.extend_from_slice(&buf[..n]);
423 } else if unit >= 0xDC00 && unit <= 0xDFFF {
424 return Err(());
426 } else {
427 let cp = unit as u32;
428 let mut buf = [0u8; 4];
429 let n = encode_codepoint_to_utf8(cp, &mut buf);
430 result.extend_from_slice(&buf[..n]);
431 }
432 }
433
434 Ok(result)
435}
436
437fn encode_codepoint_to_utf16le(cp: u32, out: &mut [u8]) -> usize {
441 if cp < 0x10000 {
442 if out.len() < 2 {
443 return 0;
444 }
445 let u = cp as u16;
446 out[..2].copy_from_slice(&u.to_le_bytes());
447 2
448 } else if cp < 0x110000 {
449 if out.len() < 4 {
450 return 0;
451 }
452 let cp = cp - 0x10000;
453 let high = 0xD800 | ((cp >> 10) as u16);
454 let low = 0xDC00 | (cp as u16 & 0x3FF);
455 out[..2].copy_from_slice(&high.to_le_bytes());
456 out[2..4].copy_from_slice(&low.to_le_bytes());
457 4
458 } else {
459 0
460 }
461}
462
463pub(crate) fn utf8_to_utf16le(data: &[u8]) -> Result<Vec<u8>, ()> {
467 let s = core::str::from_utf8(data).map_err(|_| ())?;
468 let mut result = Vec::with_capacity(data.len() * 2);
469
470 for ch in s.chars() {
471 let cp = ch as u32;
472 let mut buf = [0u8; 4];
473 let n = encode_codepoint_to_utf16le(cp, &mut buf);
474 if n == 0 {
475 return Err(());
476 }
477 result.extend_from_slice(&buf[..n]);
478 }
479
480 Ok(result)
481}
482
483pub(crate) fn latin1_to_utf8(data: &[u8]) -> Vec<u8> {
492 let mut result = Vec::with_capacity(data.len() * 2);
493
494 for &byte in data {
495 let cp = byte as u32;
496 let mut buf = [0u8; 2];
497 let n = encode_codepoint_to_utf8(cp, &mut buf);
498 result.extend_from_slice(&buf[..n]);
499 }
500
501 result
502}
503
504pub(crate) fn utf8_to_latin1(data: &[u8]) -> Result<Vec<u8>, ()> {
509 let s = core::str::from_utf8(data).map_err(|_| ())?;
510 let mut result = Vec::with_capacity(data.len());
511
512 for ch in s.chars() {
513 let cp = ch as u32;
514 if cp > 0xFF {
515 return Err(());
516 }
517 result.push(cp as u8);
518 }
519
520 Ok(result)
521}
522
523pub(crate) fn init_encodings() {
538 if ENCODING_INITIALIZED.swap(true, Ordering::SeqCst) {
539 return;
540 }
541
542 register_builtin_handlers();
543}
544
545fn register_builtin_handlers() {
547 register_handler(
549 b"UTF-8\0",
550 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
551 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
552 Some(utf8_input_func as xmlCharEncodingInputFunc),
553 Some(utf8_output_func as xmlCharEncodingOutputFunc),
554 );
555
556 register_handler(
558 b"UTF-16LE\0",
559 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
560 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
561 Some(utf16le_input_func as xmlCharEncodingInputFunc),
562 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
563 );
564
565 register_handler(
567 b"UTF-16BE\0",
568 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
569 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
570 Some(utf16be_input_func as xmlCharEncodingInputFunc),
571 Some(utf16be_output_func as xmlCharEncodingOutputFunc),
572 );
573
574 register_handler(
576 b"ISO-8859-1\0",
577 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
578 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
579 Some(latin1_input_func as xmlCharEncodingInputFunc),
580 Some(latin1_output_func as xmlCharEncodingOutputFunc),
581 );
582
583 register_handler(
585 b"ASCII\0",
586 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
587 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
588 Some(ascii_input_func as xmlCharEncodingInputFunc),
589 Some(ascii_output_func as xmlCharEncodingOutputFunc),
590 );
591}
592
593fn register_handler(
595 name_bytes: &[u8],
596 input_enc: xmlCharEncoding,
597 output_enc: xmlCharEncoding,
598 input_func: Option<xmlCharEncodingInputFunc>,
599 output_func: Option<xmlCharEncodingOutputFunc>,
600) {
601 let name_raw =
602 unsafe { crate::abi::allocator::xmlMemStrdup(name_bytes.as_ptr() as *const c_char) };
603 if name_raw.is_null() {
604 return;
605 }
606
607 let handler =
608 unsafe { xmlMalloc(size_of::<_xmlCharEncodingHandler>()) } as *mut _xmlCharEncodingHandler;
609
610 if handler.is_null() {
611 unsafe { xmlFree(name_raw) };
612 return;
613 }
614
615 unsafe {
616 ptr::write(
617 handler,
618 _xmlCharEncodingHandler {
619 name: name_raw as *mut c_char,
620 input: EncodingInputUnion {
621 legacyFunc: input_func,
622 },
623 output: EncodingOutputUnion {
624 legacyFunc: output_func,
625 },
626 inputCtxt: ptr::null_mut(),
627 outputCtxt: ptr::null_mut(),
628 ctxtDtor: None,
629 flags: 0,
630 },
631 );
632 }
633
634 add_encoding_handler(handler);
635}
636
637pub(crate) fn cleanup_encodings() {
641 let mut handlers = ENCODING_HANDLERS.write();
642 for &handler in handlers.iter() {
643 let ptr = handler.0;
644 if !ptr.is_null() {
645 unsafe {
646 if !(*ptr).name.is_null() {
647 xmlFree((*ptr).name as *mut c_void);
648 }
649 xmlFree(ptr as *mut c_void);
650 }
651 }
652 }
653 handlers.clear();
654 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
655}
656
657pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
663 if name.is_null() {
664 return ptr::null_mut();
665 }
666
667 let name_str = unsafe {
668 match CStr::from_ptr(name as *const c_char).to_bytes() {
669 b"" => return ptr::null_mut(),
670 s => s,
671 }
672 };
673
674 let handlers = ENCODING_HANDLERS.read();
675 for &handler in handlers.iter() {
676 let ptr = handler.0;
677 if ptr.is_null() {
678 continue;
679 }
680 let h_name = unsafe {
681 if (*ptr).name.is_null() {
682 continue;
683 }
684 CStr::from_ptr((*ptr).name).to_bytes()
685 };
686
687 if name_str.eq_ignore_ascii_case(h_name) {
688 return ptr;
689 }
690 }
691
692 ptr::null_mut()
693}
694
695pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
699 if handler.is_null() {
700 return -1;
701 }
702
703 let mut handlers = ENCODING_HANDLERS.write();
704 handlers.push(HandlerPtr(handler));
705 0
706}
707
708pub(crate) fn char_enc_in_func(
716 handler: *mut _xmlCharEncodingHandler,
717 out: &mut [u8],
718 in_data: &[u8],
719) -> c_int {
720 if handler.is_null() {
721 return -1;
722 }
723
724 let h = unsafe { &*handler };
725 let input_func = unsafe { h.input.legacyFunc };
726 let input_func = match input_func {
727 Some(f) => f,
728 None => return -1,
729 };
730
731 let mut outlen = out.len() as c_int;
732 let mut inlen = in_data.len() as c_int;
733
734 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
735}
736
737pub(crate) fn char_enc_out_func(
741 handler: *mut _xmlCharEncodingHandler,
742 out: &mut [u8],
743 in_data: &[u8],
744) -> c_int {
745 if handler.is_null() {
746 return -1;
747 }
748
749 let h = unsafe { &*handler };
750 let output_func = unsafe { h.output.legacyFunc };
751 let output_func = match output_func {
752 Some(f) => f,
753 None => return -1,
754 };
755
756 let mut outlen = out.len() as c_int;
757 let mut inlen = in_data.len() as c_int;
758
759 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
760}
761
762pub(crate) fn char_enc_in(
769 handler: *mut _xmlCharEncodingHandler,
770 out: *mut _xmlBuffer,
771 in_: *mut _xmlBuffer,
772) -> c_int {
773 if handler.is_null() || out.is_null() || in_.is_null() {
774 return -1;
775 }
776
777 let h = unsafe { &*handler };
778 let input_func = unsafe { h.input.legacyFunc };
779 let input_func = match input_func {
780 Some(f) => f,
781 None => return -1,
782 };
783
784 let in_buf = unsafe { &*in_ };
785 let out_buf = unsafe { &mut *out };
786
787 if in_buf.content.is_null() || in_buf.use_ == 0 {
788 return 0;
789 }
790
791 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
792
793 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
795 let mut out_vec = vec![0u8; out_capacity];
796 let mut out_len = out_capacity as c_int;
797 let mut in_len = in_buf.use_ as c_int;
798
799 let ret = unsafe {
800 input_func(
801 out_vec.as_mut_ptr(),
802 &mut out_len,
803 in_data.as_ptr(),
804 &mut in_len,
805 )
806 };
807
808 if ret < 0 {
809 return -1;
810 }
811
812 let written = ret as usize;
813
814 append_to_xml_buffer(out_buf, &out_vec[..written]);
816
817 written as c_int
818}
819
820pub(crate) fn char_enc_out(
827 handler: *mut _xmlCharEncodingHandler,
828 out: *mut _xmlBuffer,
829 in_: *mut _xmlBuffer,
830) -> c_int {
831 if handler.is_null() || out.is_null() || in_.is_null() {
832 return -1;
833 }
834
835 let h = unsafe { &*handler };
836 let output_func = unsafe { h.output.legacyFunc };
837 let output_func = match output_func {
838 Some(f) => f,
839 None => return -1,
840 };
841
842 let in_buf = unsafe { &*in_ };
843 let out_buf = unsafe { &mut *out };
844
845 if in_buf.content.is_null() || in_buf.use_ == 0 {
846 return 0;
847 }
848
849 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
850
851 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
852 let mut out_vec = vec![0u8; out_capacity];
853 let mut out_len = out_capacity as c_int;
854 let mut in_len = in_buf.use_ as c_int;
855
856 let ret = unsafe {
857 output_func(
858 out_vec.as_mut_ptr(),
859 &mut out_len,
860 in_data.as_ptr(),
861 &mut in_len,
862 )
863 };
864
865 if ret < 0 {
866 return -1;
867 }
868
869 let written = ret as usize;
870
871 append_to_xml_buffer(out_buf, &out_vec[..written]);
873
874 written as c_int
875}
876
877fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
879 if data.is_empty() {
880 return;
881 }
882
883 let new_use = (buf.use_ as usize).saturating_add(data.len());
884 if new_use > buf.size as usize {
885 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
887 let new_content =
888 unsafe { xmlRealloc(buf.content as *mut c_void, new_size) as *mut xmlChar };
889 if new_content.is_null() {
890 return; }
892 buf.content = new_content;
893 buf.size = new_size as c_uint;
894 }
895
896 unsafe {
897 ptr::copy_nonoverlapping(
898 data.as_ptr(),
899 buf.content.add(buf.use_ as usize),
900 data.len(),
901 );
902 }
903 buf.use_ = new_use as c_uint;
904}
905
906unsafe extern "C" fn utf8_input_func(
916 out: *mut c_uchar,
917 outlen: *mut c_int,
918 in_: *const c_uchar,
919 inlen: *mut c_int,
920) -> c_int {
921 let avail_out = *outlen as usize;
922 let avail_in = *inlen as usize;
923 let to_copy = avail_out.min(avail_in);
924
925 if to_copy > 0 {
926 ptr::copy_nonoverlapping(in_, out, to_copy);
927 }
928
929 *outlen = to_copy as c_int;
930 *inlen = to_copy as c_int;
931 to_copy as c_int
932}
933
934unsafe extern "C" fn utf8_output_func(
936 out: *mut c_uchar,
937 outlen: *mut c_int,
938 in_: *const c_uchar,
939 inlen: *mut c_int,
940) -> c_int {
941 utf8_input_func(out, outlen, in_, inlen)
942}
943
944unsafe extern "C" fn utf16le_input_func(
948 out: *mut c_uchar,
949 outlen: *mut c_int,
950 in_: *const c_uchar,
951 inlen: *mut c_int,
952) -> c_int {
953 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
954 return -1;
955 }
956
957 let avail_in = *inlen as usize;
958 let avail_out = *outlen as usize;
959
960 if avail_in == 0 || avail_out == 0 {
961 *outlen = 0;
962 *inlen = 0;
963 return 0;
964 }
965
966 let in_data = core::slice::from_raw_parts(in_, avail_in);
967 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
968
969 let result = match utf16le_to_utf8(in_data) {
971 Ok(v) => v,
972 Err(()) => return -1,
973 };
974
975 let written = result.len().min(avail_out);
976 if written > 0 {
977 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
978 }
979
980 *outlen = written as c_int;
981 *inlen = avail_in as c_int; written as c_int
983}
984
985unsafe extern "C" fn utf16le_output_func(
987 out: *mut c_uchar,
988 outlen: *mut c_int,
989 in_: *const c_uchar,
990 inlen: *mut c_int,
991) -> c_int {
992 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
993 return -1;
994 }
995
996 let avail_in = *inlen as usize;
997 let avail_out = *outlen as usize;
998
999 if avail_in == 0 || avail_out == 0 {
1000 *outlen = 0;
1001 *inlen = 0;
1002 return 0;
1003 }
1004
1005 let in_data = core::slice::from_raw_parts(in_, avail_in);
1006 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1007
1008 let result = match utf8_to_utf16le(in_data) {
1009 Ok(v) => v,
1010 Err(()) => return -1,
1011 };
1012
1013 let written = result.len().min(avail_out);
1014 if written > 0 {
1015 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1016 }
1017
1018 *outlen = written as c_int;
1019 *inlen = avail_in as c_int;
1020 written as c_int
1021}
1022
1023unsafe extern "C" fn utf16be_input_func(
1027 out: *mut c_uchar,
1028 outlen: *mut c_int,
1029 in_: *const c_uchar,
1030 inlen: *mut c_int,
1031) -> c_int {
1032 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1033 return -1;
1034 }
1035
1036 let avail_in = *inlen as usize;
1037 let avail_out = *outlen as usize;
1038
1039 if avail_in == 0 || avail_out == 0 {
1040 *outlen = 0;
1041 *inlen = 0;
1042 return 0;
1043 }
1044
1045 let in_data = core::slice::from_raw_parts(in_, avail_in);
1046 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1047
1048 let result = match utf16be_to_utf8(in_data) {
1049 Ok(v) => v,
1050 Err(()) => return -1,
1051 };
1052
1053 let written = result.len().min(avail_out);
1054 if written > 0 {
1055 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1056 }
1057
1058 *outlen = written as c_int;
1059 *inlen = avail_in as c_int;
1060 written as c_int
1061}
1062
1063unsafe extern "C" fn utf16be_output_func(
1065 out: *mut c_uchar,
1066 outlen: *mut c_int,
1067 in_: *const c_uchar,
1068 inlen: *mut c_int,
1069) -> c_int {
1070 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1071 return -1;
1072 }
1073
1074 let avail_in = *inlen as usize;
1075 let avail_out = *outlen as usize;
1076
1077 if avail_in == 0 || avail_out == 0 {
1078 *outlen = 0;
1079 *inlen = 0;
1080 return 0;
1081 }
1082
1083 let in_data = core::slice::from_raw_parts(in_, avail_in);
1084
1085 let le_result = match utf8_to_utf16le(in_data) {
1087 Ok(v) => v,
1088 Err(()) => return -1,
1089 };
1090
1091 let mut result = le_result;
1093 for chunk in result.chunks_exact_mut(2) {
1094 chunk.swap(0, 1);
1095 }
1096
1097 let written = result.len().min(avail_out);
1098 if written > 0 {
1099 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1100 }
1101
1102 *outlen = written as c_int;
1103 *inlen = avail_in as c_int;
1104 written as c_int
1105}
1106
1107unsafe extern "C" fn latin1_input_func(
1111 out: *mut c_uchar,
1112 outlen: *mut c_int,
1113 in_: *const c_uchar,
1114 inlen: *mut c_int,
1115) -> c_int {
1116 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1117 return -1;
1118 }
1119
1120 let avail_in = *inlen as usize;
1121 let avail_out = *outlen as usize;
1122
1123 if avail_in == 0 || avail_out == 0 {
1124 *outlen = 0;
1125 *inlen = 0;
1126 return 0;
1127 }
1128
1129 let in_data = core::slice::from_raw_parts(in_, avail_in);
1130 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1131
1132 let mut in_pos = 0;
1133 let mut out_pos = 0;
1134
1135 while in_pos < avail_in && out_pos < avail_out {
1136 let byte = in_data[in_pos];
1137 in_pos += 1;
1138
1139 if byte < 0x80 {
1140 if out_pos < avail_out {
1142 out_slice[out_pos] = byte;
1143 out_pos += 1;
1144 } else {
1145 break;
1146 }
1147 } else {
1148 if out_pos + 1 < avail_out {
1151 out_slice[out_pos] = 0xC2 | (byte >> 6);
1152 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1153 out_pos += 2;
1154 } else {
1155 break;
1156 }
1157 }
1158 }
1159
1160 *outlen = out_pos as c_int;
1161 *inlen = in_pos as c_int;
1162 out_pos as c_int
1163}
1164
1165unsafe extern "C" fn latin1_output_func(
1167 out: *mut c_uchar,
1168 outlen: *mut c_int,
1169 in_: *const c_uchar,
1170 inlen: *mut c_int,
1171) -> c_int {
1172 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1173 return -1;
1174 }
1175
1176 let avail_in = *inlen as usize;
1177 let avail_out = *outlen as usize;
1178
1179 if avail_in == 0 || avail_out == 0 {
1180 *outlen = 0;
1181 *inlen = 0;
1182 return 0;
1183 }
1184
1185 let in_data = core::slice::from_raw_parts(in_, avail_in);
1186 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1187
1188 let mut in_pos = 0;
1189 let mut out_pos = 0;
1190
1191 while in_pos < avail_in && out_pos < avail_out {
1192 let byte = in_data[in_pos];
1193 in_pos += 1;
1194
1195 if byte < 0x80 {
1196 out_slice[out_pos] = byte;
1198 out_pos += 1;
1199 } else if byte >= 0xC2 && byte <= 0xC3 {
1200 if in_pos < avail_in {
1202 let second = in_data[in_pos];
1203 in_pos += 1;
1204 if second & 0xC0 != 0x80 {
1205 return -1; }
1207 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1208 if cp > 0xFF {
1209 return -1; }
1211 out_slice[out_pos] = cp as u8;
1212 out_pos += 1;
1213 } else {
1214 return -1; }
1216 } else if byte >= 0x80 && byte <= 0xBF {
1217 return -1;
1219 } else {
1220 return -1;
1223 }
1224 }
1225
1226 *outlen = out_pos as c_int;
1227 *inlen = in_pos as c_int;
1228 out_pos as c_int
1229}
1230
1231unsafe extern "C" fn ascii_input_func(
1235 out: *mut c_uchar,
1236 outlen: *mut c_int,
1237 in_: *const c_uchar,
1238 inlen: *mut c_int,
1239) -> c_int {
1240 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1241 return -1;
1242 }
1243
1244 let avail_in = *inlen as usize;
1245 let avail_out = *outlen as usize;
1246
1247 if avail_in == 0 || avail_out == 0 {
1248 *outlen = 0;
1249 *inlen = 0;
1250 return 0;
1251 }
1252
1253 let in_data = core::slice::from_raw_parts(in_, avail_in);
1254 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1255
1256 let mut pos = 0;
1257 while pos < avail_in && pos < avail_out {
1258 let byte = in_data[pos];
1259 if byte > 0x7F {
1260 return -1; }
1262 out_slice[pos] = byte;
1263 pos += 1;
1264 }
1265
1266 *outlen = pos as c_int;
1267 *inlen = pos as c_int;
1268 pos as c_int
1269}
1270
1271unsafe extern "C" fn ascii_output_func(
1273 out: *mut c_uchar,
1274 outlen: *mut c_int,
1275 in_: *const c_uchar,
1276 inlen: *mut c_int,
1277) -> c_int {
1278 ascii_input_func(out, outlen, in_, inlen)
1280}
1281
1282pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
1291 if name.is_null() {
1292 return ptr::null_mut();
1293 }
1294 find_encoding_handler(name as *const xmlChar)
1295}
1296
1297pub(crate) fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
1301 match enc {
1303 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
1304 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => c"UTF-16LE".as_ptr(),
1305 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => c"UTF-16BE".as_ptr(),
1306 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => c"UCS-4LE".as_ptr(),
1307 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => c"UCS-4BE".as_ptr(),
1308 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"EBCDIC".as_ptr(),
1309 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => c"UCS-4-2143".as_ptr(),
1310 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => c"UCS-4-3412".as_ptr(),
1311 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
1312 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
1313 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
1314 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
1315 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
1316 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
1317 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
1318 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
1319 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
1320 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
1321 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
1322 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"SHIFT_JIS".as_ptr(),
1323 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
1324 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"ASCII".as_ptr(),
1325 _ => ptr::null(),
1326 }
1327}
1328
1329pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
1334 if name.is_null() {
1335 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
1336 }
1337 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
1338 encoding_from_name(bytes) as c_int
1339}
1340
1341static ENCODING_ALIASES: std::sync::OnceLock<
1349 parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>>,
1350> = std::sync::OnceLock::new();
1351
1352fn encoding_aliases() -> &'static parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>> {
1353 ENCODING_ALIASES.get_or_init(|| {
1354 let m = parking_lot::RwLock::new(std::collections::HashMap::new());
1355 m
1356 })
1357}
1358
1359pub(crate) fn add_encoding_alias(name: *const c_char, alias: *const c_char) -> c_int {
1362 if name.is_null() || alias.is_null() {
1363 return -1;
1364 }
1365 let n = unsafe { CStr::from_ptr(name).to_bytes().to_vec() };
1366 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1367 encoding_aliases().write().insert(a, n);
1368 0
1369}
1370
1371pub(crate) fn del_encoding_alias(alias: *const c_char) -> c_int {
1374 if alias.is_null() {
1375 return -1;
1376 }
1377 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1378 if encoding_aliases().write().remove(&a).is_some() {
1379 0
1380 } else {
1381 -1
1382 }
1383}
1384
1385pub(crate) fn get_encoding_alias(alias: *const c_char) -> *const c_char {
1388 if alias.is_null() {
1389 return ptr::null();
1390 }
1391 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1392 let guard = encoding_aliases().read();
1393 match guard.get(&a) {
1394 Some(v) => {
1395 let leaked: &'static [u8] = Box::leak(v.clone().into_boxed_slice());
1398 leaked.as_ptr() as *const c_char
1399 }
1400 None => ptr::null(),
1401 }
1402}
1403
1404pub(crate) fn cleanup_encoding_aliases() {
1406 encoding_aliases().write().clear();
1407}
1408
1409pub(crate) fn xmlCharEncInFunc(
1413 handler: *mut _xmlCharEncodingHandler,
1414 out: *mut _xmlBuffer,
1415 in_: *mut _xmlBuffer,
1416) -> c_int {
1417 char_enc_in(handler, out, in_)
1418}
1419
1420pub(crate) fn xmlCharEncOutFunc(
1424 handler: *mut _xmlCharEncodingHandler,
1425 out: *mut _xmlBuffer,
1426 in_: *mut _xmlBuffer,
1427) -> c_int {
1428 char_enc_out(handler, out, in_)
1429}
1430
1431pub(crate) fn xmlNewCharEncodingHandler(
1437 name: *const c_char,
1438 input: xmlCharEncodingInputFunc,
1439 output: xmlCharEncodingOutputFunc,
1440) -> *mut _xmlCharEncodingHandler {
1441 if name.is_null() {
1442 return ptr::null_mut();
1443 }
1444
1445 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdup(name) };
1446 if name_raw.is_null() {
1447 return ptr::null_mut();
1448 }
1449
1450 let handler =
1451 unsafe { xmlMalloc(size_of::<_xmlCharEncodingHandler>()) } as *mut _xmlCharEncodingHandler;
1452
1453 if handler.is_null() {
1454 unsafe { xmlFree(name_raw) };
1455 return ptr::null_mut();
1456 }
1457
1458 unsafe {
1459 ptr::write(
1460 handler,
1461 _xmlCharEncodingHandler {
1462 name: name_raw as *mut c_char,
1463 input: EncodingInputUnion {
1464 legacyFunc: Some(input),
1465 },
1466 output: EncodingOutputUnion {
1467 legacyFunc: Some(output),
1468 },
1469 inputCtxt: ptr::null_mut(),
1470 outputCtxt: ptr::null_mut(),
1471 ctxtDtor: None,
1472 flags: 0,
1473 },
1474 );
1475 }
1476
1477 handler
1478}
1479
1480pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
1484 if handler.is_null() {
1485 return;
1486 }
1487
1488 {
1490 let mut handlers = ENCODING_HANDLERS.write();
1491 handlers.retain(|&h| h.0 != handler);
1492 }
1493
1494 unsafe {
1495 if !(*handler).name.is_null() {
1496 xmlFree((*handler).name as *mut c_void);
1497 }
1498 xmlFree(handler as *mut c_void);
1499 }
1500}
1501
1502pub(crate) fn xmlInitCharEncodingHandlers() {
1504 init_encodings();
1505}
1506
1507pub(crate) fn xmlCleanupCharEncodingHandlers() {
1509 cleanup_encodings();
1510}
1511
1512#[cfg(test)]
1517mod tests {
1518 use super::*;
1519
1520 #[test]
1523 fn test_detect_bom_utf8() {
1524 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
1525 assert_eq!(
1526 detect_encoding_from_bom(&data),
1527 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1528 );
1529 }
1530
1531 #[test]
1532 fn test_detect_bom_utf16le() {
1533 let data = [0xFF, 0xFE, 0x00, 0x01];
1534 assert_eq!(
1535 detect_encoding_from_bom(&data),
1536 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1537 );
1538 }
1539
1540 #[test]
1541 fn test_detect_bom_utf16be() {
1542 let data = [0xFE, 0xFF, 0x00, 0x01];
1543 assert_eq!(
1544 detect_encoding_from_bom(&data),
1545 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1546 );
1547 }
1548
1549 #[test]
1550 fn test_detect_bom_none() {
1551 let data = b"<xml>";
1552 assert_eq!(
1553 detect_encoding_from_bom(data),
1554 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1555 );
1556 }
1557
1558 #[test]
1559 fn test_detect_bom_empty() {
1560 assert_eq!(
1561 detect_encoding_from_bom(b""),
1562 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1563 );
1564 }
1565
1566 #[test]
1569 fn test_detect_encoding_declaration_utf8() {
1570 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
1571 let result = detect_encoding_from_declaration(data);
1572 assert_eq!(result, Some(b"utf-8".to_vec()));
1573 }
1574
1575 #[test]
1576 fn test_detect_encoding_declaration_iso() {
1577 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
1578 let result = detect_encoding_from_declaration(data);
1579 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
1580 }
1581
1582 #[test]
1583 fn test_detect_encoding_declaration_none() {
1584 let data = b"<?xml version=\"1.0\"?>";
1585 let result = detect_encoding_from_declaration(data);
1586 assert!(result.is_none());
1587 }
1588
1589 #[test]
1590 fn test_detect_encoding_declaration_no_xml() {
1591 let data = b"<root>";
1592 let result = detect_encoding_from_declaration(data);
1593 assert!(result.is_none());
1594 }
1595
1596 #[test]
1597 fn test_detect_encoding_declaration_with_bom() {
1598 let mut data = vec![0xEF, 0xBB, 0xBF];
1599 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1600 let result = detect_encoding_from_declaration(&data);
1601 assert_eq!(result, Some(b"utf-8".to_vec()));
1602 }
1603
1604 #[test]
1607 fn test_encoding_from_name_utf8() {
1608 assert_eq!(
1609 encoding_from_name(b"UTF-8"),
1610 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1611 );
1612 assert_eq!(
1613 encoding_from_name(b"utf8"),
1614 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1615 );
1616 }
1617
1618 #[test]
1619 fn test_encoding_from_name_utf16() {
1620 assert_eq!(
1621 encoding_from_name(b"UTF-16LE"),
1622 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1623 );
1624 assert_eq!(
1625 encoding_from_name(b"UTF-16BE"),
1626 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1627 );
1628 assert_eq!(
1629 encoding_from_name(b"utf-16"),
1630 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1631 );
1632 }
1633
1634 #[test]
1635 fn test_encoding_from_name_latin1() {
1636 assert_eq!(
1637 encoding_from_name(b"ISO-8859-1"),
1638 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
1639 );
1640 assert_eq!(
1641 encoding_from_name(b"Latin1"),
1642 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
1643 );
1644 }
1645
1646 #[test]
1647 fn test_encoding_from_name_ascii() {
1648 assert_eq!(
1649 encoding_from_name(b"ASCII"),
1650 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
1651 );
1652 assert_eq!(
1653 encoding_from_name(b"US-ASCII"),
1654 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
1655 );
1656 }
1657
1658 #[test]
1659 fn test_encoding_from_name_error() {
1660 assert_eq!(
1661 encoding_from_name(b"invalid-encoding"),
1662 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
1663 );
1664 }
1665
1666 #[test]
1667 fn test_encoding_from_name_empty() {
1668 assert_eq!(
1669 encoding_from_name(b""),
1670 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
1671 );
1672 }
1673
1674 #[test]
1677 fn test_encoding_name_utf8() {
1678 assert_eq!(
1679 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
1680 Some(b"UTF-8" as &[u8])
1681 );
1682 }
1683
1684 #[test]
1685 fn test_encoding_name_utf16le() {
1686 assert_eq!(
1687 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
1688 Some(b"UTF-16LE" as &[u8])
1689 );
1690 }
1691
1692 #[test]
1693 fn test_encoding_name_none() {
1694 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
1695 }
1696
1697 #[test]
1698 fn test_encoding_name_error() {
1699 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
1700 }
1701
1702 #[test]
1705 fn test_utf8_valid_ascii() {
1706 assert!(utf8_valid(b"hello world"));
1707 }
1708
1709 #[test]
1710 fn test_utf8_valid_multi_byte() {
1711 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
1712 }
1713
1714 #[test]
1715 fn test_utf8_valid_empty() {
1716 assert!(utf8_valid(b""));
1717 }
1718
1719 #[test]
1720 fn test_utf8_invalid() {
1721 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
1722 }
1723
1724 #[test]
1727 fn test_valid_xml_chars() {
1728 assert!(is_valid_xml_char(0x9)); assert!(is_valid_xml_char(0xA)); assert!(is_valid_xml_char(0xD)); assert!(is_valid_xml_char(0x20)); assert!(is_valid_xml_char(0x41)); assert!(is_valid_xml_char(0xD7FF));
1734 assert!(is_valid_xml_char(0xE000));
1735 assert!(is_valid_xml_char(0xFFFD));
1736 assert!(is_valid_xml_char(0x10000));
1737 assert!(is_valid_xml_char(0x10FFFF));
1738 }
1739
1740 #[test]
1741 fn test_invalid_xml_chars() {
1742 assert!(!is_valid_xml_char(0x00));
1743 assert!(!is_valid_xml_char(0x08));
1744 assert!(!is_valid_xml_char(0x0B));
1745 assert!(!is_valid_xml_char(0x0C));
1746 assert!(!is_valid_xml_char(0x0E));
1747 assert!(!is_valid_xml_char(0x1F));
1748 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
1751 assert!(!is_valid_xml_char(0xFFFF));
1752 assert!(!is_valid_xml_char(0x110000));
1753 }
1754
1755 #[test]
1758 fn test_utf16le_to_utf8_ascii() {
1759 let data = [b'A', 0x00, b'B', 0x00];
1761 let result = utf16le_to_utf8(&data).unwrap();
1762 assert_eq!(result, b"AB");
1763 }
1764
1765 #[test]
1766 fn test_utf16le_to_utf8_bom() {
1767 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
1769 let result = utf16le_to_utf8(&data).unwrap();
1770 assert_eq!(result, b"AB");
1771 }
1772
1773 #[test]
1774 fn test_utf16le_to_utf8_bmp() {
1775 let data = [0xE9, 0x00];
1777 let result = utf16le_to_utf8(&data).unwrap();
1778 assert_eq!(result, "é".as_bytes());
1779 }
1780
1781 #[test]
1782 fn test_utf16le_to_utf8_supplementary() {
1783 let data = [0x3D, 0xD8, 0x00, 0xDE];
1785 let result = utf16le_to_utf8(&data).unwrap();
1786 assert_eq!(result, "😀".as_bytes());
1787 }
1788
1789 #[test]
1790 fn test_utf16le_to_utf8_unpaired_surrogate() {
1791 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
1793 }
1794
1795 #[test]
1796 fn test_utf16le_to_utf8_truncated() {
1797 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
1799 }
1800
1801 #[test]
1802 fn test_utf16le_to_utf8_empty() {
1803 let result = utf16le_to_utf8(b"").unwrap();
1804 assert!(result.is_empty());
1805 }
1806
1807 #[test]
1810 fn test_utf16be_to_utf8_ascii() {
1811 let data = [0x00, b'A', 0x00, b'B'];
1812 let result = utf16be_to_utf8(&data).unwrap();
1813 assert_eq!(result, b"AB");
1814 }
1815
1816 #[test]
1817 fn test_utf16be_to_utf8_bom() {
1818 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
1820 let result = utf16be_to_utf8(&data).unwrap();
1821 assert_eq!(result, b"AB");
1822 }
1823
1824 #[test]
1825 fn test_utf16be_to_utf8_supplementary() {
1826 let data = [0xD8, 0x3D, 0xDE, 0x00];
1828 let result = utf16be_to_utf8(&data).unwrap();
1829 assert_eq!(result, "😀".as_bytes());
1830 }
1831
1832 #[test]
1833 fn test_utf16be_to_utf8_empty() {
1834 let result = utf16be_to_utf8(b"").unwrap();
1835 assert!(result.is_empty());
1836 }
1837
1838 #[test]
1841 fn test_utf8_to_utf16le_ascii() {
1842 let result = utf8_to_utf16le(b"AB").unwrap();
1843 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
1844 }
1845
1846 #[test]
1847 fn test_utf8_to_utf16le_bmp() {
1848 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
1849 assert_eq!(result, [0xE9, 0x00]);
1850 }
1851
1852 #[test]
1853 fn test_utf8_to_utf16le_supplementary() {
1854 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
1855 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
1856 }
1857
1858 #[test]
1859 fn test_utf8_to_utf16le_invalid_utf8() {
1860 assert!(utf8_to_utf16le(&[0xFF]).is_err());
1861 }
1862
1863 #[test]
1864 fn test_utf8_to_utf16le_empty() {
1865 let result = utf8_to_utf16le(b"").unwrap();
1866 assert!(result.is_empty());
1867 }
1868
1869 #[test]
1872 fn test_latin1_to_utf8_ascii() {
1873 let result = latin1_to_utf8(b"ABC");
1874 assert_eq!(result, b"ABC");
1875 }
1876
1877 #[test]
1878 fn test_latin1_to_utf8_accented() {
1879 let result = latin1_to_utf8(&[0xE9]);
1881 assert_eq!(result, "é".as_bytes());
1882 }
1883
1884 #[test]
1885 fn test_latin1_to_utf8_all_255() {
1886 let result = latin1_to_utf8(&[0xFF]);
1887 assert_eq!(result, [0xC3, 0xBF]);
1889 }
1890
1891 #[test]
1892 fn test_latin1_to_utf8_empty() {
1893 let result = latin1_to_utf8(b"");
1894 assert!(result.is_empty());
1895 }
1896
1897 #[test]
1898 fn test_latin1_to_utf8_mixed() {
1899 let result = latin1_to_utf8(b"caf\xE9");
1900 assert_eq!(result, "café".as_bytes());
1901 }
1902
1903 #[test]
1906 fn test_utf8_to_latin1_ascii() {
1907 let result = utf8_to_latin1(b"ABC").unwrap();
1908 assert_eq!(result, b"ABC");
1909 }
1910
1911 #[test]
1912 fn test_utf8_to_latin1_accented() {
1913 let result = utf8_to_latin1("é".as_bytes()).unwrap();
1914 assert_eq!(result, [0xE9]);
1915 }
1916
1917 #[test]
1918 fn test_utf8_to_latin1_out_of_range() {
1919 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
1921
1922 #[test]
1923 fn test_utf8_to_latin1_invalid_utf8() {
1924 assert!(utf8_to_latin1(&[0xFF]).is_err());
1925 }
1926
1927 #[test]
1928 fn test_utf8_to_latin1_empty() {
1929 let result = utf8_to_latin1(b"").unwrap();
1930 assert!(result.is_empty());
1931 }
1932
1933 #[test]
1936 fn test_init_and_find_encodings() {
1937 init_encodings();
1938
1939 let utf8_name: *const xmlChar = b"UTF-8\0".as_ptr();
1940 assert!(!find_encoding_handler(utf8_name).is_null());
1941
1942 let utf16le_name: *const xmlChar = b"UTF-16LE\0".as_ptr();
1943 assert!(!find_encoding_handler(utf16le_name).is_null());
1944
1945 let utf16be_name: *const xmlChar = b"UTF-16BE\0".as_ptr();
1946 assert!(!find_encoding_handler(utf16be_name).is_null());
1947
1948 let latin1_name: *const xmlChar = b"ISO-8859-1\0".as_ptr();
1949 assert!(!find_encoding_handler(latin1_name).is_null());
1950
1951 let ascii_name: *const xmlChar = b"ASCII\0".as_ptr();
1952 assert!(!find_encoding_handler(ascii_name).is_null());
1953
1954 let lower_name: *const xmlChar = b"utf-8\0".as_ptr();
1956 assert!(!find_encoding_handler(lower_name).is_null());
1957 }
1958
1959 #[test]
1960 fn test_find_encoding_handler_not_found() {
1961 let name: *const xmlChar = b"NONEXISTENT\0".as_ptr();
1962 assert!(find_encoding_handler(name).is_null());
1963 }
1964
1965 #[test]
1966 fn test_find_encoding_handler_null() {
1967 assert!(find_encoding_handler(ptr::null()).is_null());
1968 }
1969
1970 #[test]
1971 fn test_add_encoding_handler() {
1972 let handler = unsafe {
1973 xmlMalloc(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
1974 };
1975 assert!(!handler.is_null());
1976
1977 let name =
1978 unsafe { crate::abi::allocator::xmlMemStrdup(b"TEST-ENC\0".as_ptr() as *const c_char) };
1979 unsafe {
1980 ptr::write(
1981 handler,
1982 _xmlCharEncodingHandler {
1983 name: name as *mut c_char,
1984 input: EncodingInputUnion { legacyFunc: None },
1985 output: EncodingOutputUnion { legacyFunc: None },
1986 inputCtxt: ptr::null_mut(),
1987 outputCtxt: ptr::null_mut(),
1988 ctxtDtor: None,
1989 flags: 0,
1990 },
1991 );
1992 }
1993
1994 assert_eq!(add_encoding_handler(handler), 0);
1995
1996 let found = find_encoding_handler(b"TEST-ENC\0".as_ptr() as *const xmlChar);
1997 assert_eq!(found, handler);
1998
1999 {
2001 let mut handlers = ENCODING_HANDLERS.write();
2002 handlers.retain(|&h| h.0 != handler);
2003 }
2004
2005 unsafe {
2006 xmlFree(name as *mut c_void);
2007 xmlFree(handler as *mut c_void);
2008 }
2009 }
2010
2011 #[test]
2014 fn test_utf16le_roundtrip() {
2015 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
2016 let utf16 = utf8_to_utf16le(original).unwrap();
2017 let back = utf16le_to_utf8(&utf16).unwrap();
2018 assert_eq!(original.to_vec(), back);
2019 }
2020
2021 #[test]
2022 fn test_utf16be_roundtrip() {
2023 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
2024 let utf16le = utf8_to_utf16le(original).unwrap();
2025 let mut utf16be = utf16le.clone();
2027 for chunk in utf16be.chunks_exact_mut(2) {
2028 chunk.swap(0, 1);
2029 }
2030 let back = utf16be_to_utf8(&utf16be).unwrap();
2031 assert_eq!(original.to_vec(), back);
2032 }
2033
2034 #[test]
2035 fn test_latin1_roundtrip() {
2036 let original: Vec<u8> = (0x00..=0xFF).collect();
2037 let utf8 = latin1_to_utf8(&original);
2038 let back = utf8_to_latin1(&utf8).unwrap();
2039 assert_eq!(original, back);
2040 }
2041
2042 #[test]
2045 fn test_utf8_handler_identity() {
2046 let input = b"Hello, UTF-8!";
2047 let mut output = [0u8; 64];
2048 let mut outlen = output.len() as c_int;
2049 let mut inlen = input.len() as c_int;
2050
2051 let ret = unsafe {
2052 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
2053 };
2054
2055 assert_eq!(ret, input.len() as c_int);
2056 assert_eq!(&output[..ret as usize], input);
2057 assert_eq!(inlen, input.len() as c_int);
2058 }
2059
2060 #[test]
2061 fn test_utf16le_handler_roundtrip() {
2062 init_encodings();
2063
2064 let original = b"Hello UTF-16LE!";
2065 let mut utf16_buf = [0u8; 128];
2066 let mut outlen = utf16_buf.len() as c_int;
2067 let mut inlen = original.len() as c_int;
2068
2069 let written = unsafe {
2070 utf16le_output_func(
2071 utf16_buf.as_mut_ptr(),
2072 &mut outlen,
2073 original.as_ptr(),
2074 &mut inlen,
2075 )
2076 };
2077 assert!(written > 0);
2078
2079 let mut decoded = [0u8; 128];
2081 let mut outlen2 = decoded.len() as c_int;
2082 let mut inlen2 = written;
2083
2084 let written2 = unsafe {
2085 utf16le_input_func(
2086 decoded.as_mut_ptr(),
2087 &mut outlen2,
2088 utf16_buf.as_ptr(),
2089 &mut inlen2,
2090 )
2091 };
2092 assert_eq!(written2 as usize, original.len());
2093 assert_eq!(&decoded[..written2 as usize], original);
2094 }
2095
2096 #[test]
2099 fn test_append_to_xml_buffer() {
2100 unsafe {
2101 let content = xmlMalloc(64) as *mut xmlChar;
2102 assert!(!content.is_null());
2103
2104 let mut buf = _xmlBuffer {
2105 content,
2106 use_: 0,
2107 size: 64,
2108 alloc: 0,
2109 contentIO: ptr::null_mut(),
2110 };
2111
2112 append_to_xml_buffer(&mut buf, b"Hello");
2113 assert_eq!(buf.use_, 5);
2114 let slice = core::slice::from_raw_parts(buf.content, 5);
2115 assert_eq!(slice, b"Hello");
2116
2117 append_to_xml_buffer(&mut buf, b" World");
2118 assert_eq!(buf.use_, 11);
2119 let slice = core::slice::from_raw_parts(buf.content, 11);
2120 assert_eq!(slice, b"Hello World");
2121
2122 xmlFree(buf.content as *mut c_void);
2123 }
2124 }
2125
2126 #[test]
2129 fn test_xml_parse_char_encoding() {
2130 let name = b"UTF-8\0".as_ptr() as *const c_char;
2131 assert_eq!(
2132 xmlParseCharEncoding(name),
2133 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
2134 );
2135
2136 let name = b"ISO-8859-1\0".as_ptr() as *const c_char;
2137 assert_eq!(
2138 xmlParseCharEncoding(name),
2139 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
2140 );
2141
2142 assert_eq!(
2143 xmlParseCharEncoding(ptr::null()),
2144 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
2145 );
2146 }
2147
2148 #[test]
2149 fn test_xml_new_and_del_encoding_handler() {
2150 let name = b"TestEnc\0".as_ptr() as *const c_char;
2151 let handler = xmlNewCharEncodingHandler(
2152 name,
2153 utf8_input_func as xmlCharEncodingInputFunc,
2154 utf8_output_func as xmlCharEncodingOutputFunc,
2155 );
2156 assert!(!handler.is_null());
2157
2158 unsafe {
2159 assert!(!(*handler).name.is_null());
2160 let cstr = CStr::from_ptr((*handler).name);
2161 assert_eq!(cstr.to_bytes(), b"TestEnc");
2162 }
2163
2164 xmlDelEncodingHandler(handler);
2165 }
2166
2167 #[test]
2168 fn test_xml_init_and_cleanup() {
2169 xmlInitCharEncodingHandlers();
2170
2171 let name: *const xmlChar = b"UTF-8\0".as_ptr();
2172 assert!(!find_encoding_handler(name).is_null());
2173
2174 xmlCleanupCharEncodingHandlers();
2175 }
2177}