1#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
83
84use std::ffi::CStr;
85use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};
86use std::ptr;
87use std::sync::atomic::{AtomicBool, Ordering};
88
89use once_cell::sync::Lazy;
90use parking_lot::RwLock;
91
92use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlReallocImpl};
93use crate::abi::callbacks::{
94 xmlCharEncConvCtxtDtor, xmlCharEncConvFunc, xmlCharEncConvImpl, xmlCharEncodingInputFunc,
95 xmlCharEncodingOutputFunc,
96};
97use crate::abi::structs::{
98 _xmlBuffer, _xmlCharEncodingHandler, EncodingInputUnion, EncodingOutputUnion,
99};
100use crate::abi::types::{xmlChar, xmlCharEncoding};
101
102#[allow(dead_code)]
106const MAX_CHAR_BYTES: usize = 6;
107
108#[allow(dead_code)]
110const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
111
112const UTF16LE_BOM: [u8; 2] = [0xFF, 0xFE];
114
115const UTF16BE_BOM: [u8; 2] = [0xFE, 0xFF];
117
118#[derive(Clone, Copy)]
126struct HandlerPtr(*mut _xmlCharEncodingHandler);
127
128unsafe impl Send for HandlerPtr {}
129unsafe impl Sync for HandlerPtr {}
130
131static ENCODING_HANDLERS: Lazy<RwLock<Vec<HandlerPtr>>> = Lazy::new(|| RwLock::new(Vec::new()));
138
139static ENCODING_INITIALIZED: AtomicBool = AtomicBool::new(false);
141
142static ENCODING_INIT_MUTEX: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
144
145#[allow(dead_code)]
154pub(crate) fn detect_encoding_from_bom(data: &[u8]) -> xmlCharEncoding {
155 if data.len() >= 3 && data[0..3] == UTF8_BOM {
156 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
157 } else if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
158 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
159 } else if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
160 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
161 } else {
162 xmlCharEncoding::XML_CHAR_ENCODING_NONE
163 }
164}
165
166#[allow(dead_code)]
171pub(crate) fn detect_encoding_from_declaration(data: &[u8]) -> Option<Vec<u8>> {
172 let start = if data.len() >= 3 && data[0..3] == UTF8_BOM {
174 3
175 } else if data.len() >= 2 && (data[0..2] == UTF16LE_BOM || data[0..2] == UTF16BE_BOM) {
176 return None;
178 } else {
179 0
180 };
181
182 let remaining = &data[start..];
183
184 if remaining.len() < 5 || !remaining[0..5].eq_ignore_ascii_case(b"<?xml") {
186 return None;
187 }
188
189 let pi_end = remaining.windows(2).position(|w| w == b"?>")?;
191 let decl_content = &remaining[5..pi_end];
192
193 let decl_str = core::str::from_utf8(decl_content).ok()?;
195 let lower = decl_str.to_ascii_lowercase();
196
197 let enc_pos = lower.find("encoding")?;
199
200 let after_enc = &decl_content[enc_pos + 8..];
202 let after_enc_str = core::str::from_utf8(after_enc).ok()?;
203 let after_enc_trimmed = after_enc_str.trim_start();
204
205 if !after_enc_trimmed.starts_with('=') {
206 return None;
207 }
208
209 let after_eq = after_enc_trimmed[1..].trim_start();
210
211 let quote = after_eq.chars().next()?;
213 if quote != '"' && quote != '\'' {
214 return None;
215 }
216
217 let value_end = after_eq[1..].find(quote)?;
219 let encoding_value = &after_eq[1..=value_end];
220
221 Some(encoding_value.to_ascii_lowercase().as_bytes().to_vec())
222}
223
224pub(crate) fn encoding_from_name(name: &[u8]) -> xmlCharEncoding {
229 let s = core::str::from_utf8(name).unwrap_or("");
230 let s = s.trim().to_ascii_lowercase();
231
232 match s.as_str() {
233 "utf-8" | "utf8" => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
235
236 "utf-16" | "utf-16le" | "utf16le" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
238 "utf-16be" | "utf16be" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
239
240 "iso-8859-1" | "iso_8859-1" | "latin1" | "latin-1" | "l1" | "cp819" | "ibm819"
242 | "iso-ir-100" | "iso_8859-1:1987" => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
243 "iso-8859-2" | "iso_8859-2" | "latin2" | "latin-2" | "l2" => {
244 xmlCharEncoding::XML_CHAR_ENCODING_8859_2
245 }
246 "iso-8859-3" | "iso_8859-3" | "latin3" | "latin-3" | "l3" => {
247 xmlCharEncoding::XML_CHAR_ENCODING_8859_3
248 }
249 "iso-8859-4" | "iso_8859-4" | "latin4" | "latin-4" | "l4" => {
250 xmlCharEncoding::XML_CHAR_ENCODING_8859_4
251 }
252 "iso-8859-5" | "iso_8859-5" | "cyrillic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
253 "iso-8859-6" | "iso_8859-6" | "arabic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
254 "iso-8859-7" | "iso_8859-7" | "greek" => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
255 "iso-8859-8" | "iso_8859-8" | "hebrew" => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
256 "iso-8859-9" | "iso_8859-9" | "latin5" | "latin-5" | "l5" | "turkish" => {
257 xmlCharEncoding::XML_CHAR_ENCODING_8859_9
258 }
259
260 "ascii" | "us-ascii" | "us" | "ansi_x3.4-1968" | "ansi_x3.4-1986" | "iso-ir-6"
262 | "iso_646.irv:1991" | "cp367" | "ibm367" => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
263
264 "iso-2022-jp" | "iso2022-jp" => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
266 "shift_jis" | "shift-jis" | "sjis" | "cp932" => {
267 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS
268 }
269 "euc-jp" | "eucjp" => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
270
271 "ucs-4" | "ucs4" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
273 "ucs-4le" | "ucs4le" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
274 "ucs-4be" | "ucs4be" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
275 "ucs-2" | "ucs2" => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
276
277 "ebcdic" | "cp037" | "ibm037" => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
279
280 _ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
281 }
282}
283
284pub(crate) const fn encoding_name(enc: xmlCharEncoding) -> Option<&'static [u8]> {
288 match enc {
289 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => Some(b"UTF-8" as &[u8]),
290 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => Some(b"UTF-16LE" as &[u8]),
291 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => Some(b"UTF-16BE" as &[u8]),
292 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => Some(b"UCS-4LE" as &[u8]),
293 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => Some(b"UCS-4BE" as &[u8]),
294 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => Some(b"EBCDIC" as &[u8]),
295 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => Some(b"UCS-4-2143" as &[u8]),
296 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => Some(b"UCS-4-3412" as &[u8]),
297 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => Some(b"UCS-2" as &[u8]),
298 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => Some(b"ISO-8859-1" as &[u8]),
299 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => Some(b"ISO-8859-2" as &[u8]),
300 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => Some(b"ISO-8859-3" as &[u8]),
301 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => Some(b"ISO-8859-4" as &[u8]),
302 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => Some(b"ISO-8859-5" as &[u8]),
303 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => Some(b"ISO-8859-6" as &[u8]),
304 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => Some(b"ISO-8859-7" as &[u8]),
305 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => Some(b"ISO-8859-8" as &[u8]),
306 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => Some(b"ISO-8859-9" as &[u8]),
307 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => Some(b"ISO-2022-JP" as &[u8]),
308 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => Some(b"SHIFT_JIS" as &[u8]),
309 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => Some(b"EUC-JP" as &[u8]),
310 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => Some(b"US-ASCII" as &[u8]),
311 _ => None,
312 }
313}
314
315#[allow(dead_code)]
323pub(crate) const fn utf8_valid(data: &[u8]) -> bool {
324 core::str::from_utf8(data).is_ok()
325}
326
327#[allow(dead_code)]
339pub(crate) const fn is_valid_xml_char(cp: u32) -> bool {
340 matches!(
341 cp,
342 0x9 | 0xA | 0xD | 0x20..=0xD7FF | 0xE000..=0xFFFD | 0x10000..=0x10FFFF
343 )
344}
345
346#[inline]
352const fn read_utf16le_unit(data: &[u8]) -> Option<u16> {
353 if data.len() < 2 {
354 return None;
355 }
356 Some(u16::from_le_bytes([data[0], data[1]]))
357}
358
359#[inline]
361const fn read_utf16be_unit(data: &[u8]) -> Option<u16> {
362 if data.len() < 2 {
363 return None;
364 }
365 Some(u16::from_be_bytes([data[0], data[1]]))
366}
367
368const fn encode_codepoint_to_utf8(cp: u32, out: &mut [u8]) -> usize {
372 if cp < 0x80 {
373 if !out.is_empty() {
374 out[0] = cp as u8;
375 }
376 1
377 } else if cp < 0x800 {
378 if out.len() < 2 {
379 return 0;
380 }
381 out[0] = 0xC0 | ((cp >> 6) as u8);
382 out[1] = 0x80 | (cp as u8 & 0x3F);
383 2
384 } else if cp < 0x10000 {
385 if out.len() < 3 {
386 return 0;
387 }
388 out[0] = 0xE0 | ((cp >> 12) as u8);
389 out[1] = 0x80 | ((cp >> 6) as u8 & 0x3F);
390 out[2] = 0x80 | (cp as u8 & 0x3F);
391 3
392 } else if cp < 0x110000 {
393 if out.len() < 4 {
394 return 0;
395 }
396 out[0] = 0xF0 | ((cp >> 18) as u8);
397 out[1] = 0x80 | ((cp >> 12) as u8 & 0x3F);
398 out[2] = 0x80 | ((cp >> 6) as u8 & 0x3F);
399 out[3] = 0x80 | (cp as u8 & 0x3F);
400 4
401 } else {
402 0
403 }
404}
405
406pub(crate) fn utf16le_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
411 if data.is_empty() {
412 return Ok(Vec::new());
413 }
414
415 let offset = if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
417 2
418 } else {
419 0
420 };
421
422 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
423 let mut i = offset;
424
425 while i < data.len() {
426 let unit = read_utf16le_unit(&data[i..]).ok_or(())?;
427 i += 2;
428
429 if (0xD800..=0xDBFF).contains(&unit) {
430 let low = read_utf16le_unit(&data[i..]).ok_or(())?;
432 i += 2;
433
434 if !(0xDC00..=0xDFFF).contains(&low) {
435 return Err(());
436 }
437
438 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
439 let mut buf = [0u8; 4];
440 let n = encode_codepoint_to_utf8(cp, &mut buf);
441 if n == 0 {
442 return Err(());
443 }
444 result.extend_from_slice(&buf[..n]);
445 } else if (0xDC00..=0xDFFF).contains(&unit) {
446 return Err(());
448 } else {
449 let cp = unit as u32;
450 let mut buf = [0u8; 4];
451 let n = encode_codepoint_to_utf8(cp, &mut buf);
452 result.extend_from_slice(&buf[..n]);
453 }
454 }
455
456 Ok(result)
457}
458
459pub(crate) fn utf16be_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
463 if data.is_empty() {
464 return Ok(Vec::new());
465 }
466
467 let offset = if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
469 2
470 } else {
471 0
472 };
473
474 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
475 let mut i = offset;
476
477 while i < data.len() {
478 let unit = read_utf16be_unit(&data[i..]).ok_or(())?;
479 i += 2;
480
481 if (0xD800..=0xDBFF).contains(&unit) {
482 let low = read_utf16be_unit(&data[i..]).ok_or(())?;
484 i += 2;
485
486 if !(0xDC00..=0xDFFF).contains(&low) {
487 return Err(());
488 }
489
490 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
491 let mut buf = [0u8; 4];
492 let n = encode_codepoint_to_utf8(cp, &mut buf);
493 if n == 0 {
494 return Err(());
495 }
496 result.extend_from_slice(&buf[..n]);
497 } else if (0xDC00..=0xDFFF).contains(&unit) {
498 return Err(());
500 } else {
501 let cp = unit as u32;
502 let mut buf = [0u8; 4];
503 let n = encode_codepoint_to_utf8(cp, &mut buf);
504 result.extend_from_slice(&buf[..n]);
505 }
506 }
507
508 Ok(result)
509}
510
511fn encode_codepoint_to_utf16le(cp: u32, out: &mut [u8]) -> usize {
515 if cp < 0x10000 {
516 if out.len() < 2 {
517 return 0;
518 }
519 let u = cp as u16;
520 out[..2].copy_from_slice(&u.to_le_bytes());
521 2
522 } else if cp < 0x110000 {
523 if out.len() < 4 {
524 return 0;
525 }
526 let cp = cp - 0x10000;
527 let high = 0xD800 | ((cp >> 10) as u16);
528 let low = 0xDC00 | (cp as u16 & 0x3FF);
529 out[..2].copy_from_slice(&high.to_le_bytes());
530 out[2..4].copy_from_slice(&low.to_le_bytes());
531 4
532 } else {
533 0
534 }
535}
536
537pub(crate) fn utf8_to_utf16le(data: &[u8]) -> Result<Vec<u8>, ()> {
541 let s = core::str::from_utf8(data).map_err(|_| ())?;
542 let mut result = Vec::with_capacity(data.len() * 2);
543
544 for ch in s.chars() {
545 let cp = ch as u32;
546 let mut buf = [0u8; 4];
547 let n = encode_codepoint_to_utf16le(cp, &mut buf);
548 if n == 0 {
549 return Err(());
550 }
551 result.extend_from_slice(&buf[..n]);
552 }
553
554 Ok(result)
555}
556
557#[allow(dead_code)]
566pub(crate) fn latin1_to_utf8(data: &[u8]) -> Vec<u8> {
567 let mut result = Vec::with_capacity(data.len() * 2);
568
569 for &byte in data {
570 let cp = byte as u32;
571 let mut buf = [0u8; 2];
572 let n = encode_codepoint_to_utf8(cp, &mut buf);
573 result.extend_from_slice(&buf[..n]);
574 }
575
576 result
577}
578
579pub(crate) fn utf8_to_latin1(data: &[u8]) -> Result<Vec<u8>, ()> {
584 let s = core::str::from_utf8(data).map_err(|_| ())?;
585 let mut result = Vec::with_capacity(data.len());
586
587 for ch in s.chars() {
588 let cp = ch as u32;
589 if cp > 0xFF {
590 return Err(());
591 }
592 result.push(cp as u8);
593 }
594
595 Ok(result)
596}
597
598pub(crate) fn init_encodings() {
613 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
614 return;
615 }
616 let _guard = ENCODING_INIT_MUTEX.lock();
622 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
623 return;
624 }
625 register_builtin_handlers();
626 ENCODING_INITIALIZED.store(true, Ordering::SeqCst);
627}
628
629fn register_builtin_handlers() {
631 register_handler(
633 b"UTF-8\0",
634 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
635 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
636 Some(utf8_input_func as xmlCharEncodingInputFunc),
637 Some(utf8_output_func as xmlCharEncodingOutputFunc),
638 );
639
640 register_handler(
642 b"UTF-16LE\0",
643 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
644 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
645 Some(utf16le_input_func as xmlCharEncodingInputFunc),
646 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
647 );
648
649 register_handler(
651 b"UTF-16BE\0",
652 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
653 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
654 Some(utf16be_input_func as xmlCharEncodingInputFunc),
655 Some(utf16be_output_func as xmlCharEncodingOutputFunc),
656 );
657
658 register_handler(
660 b"ISO-8859-1\0",
661 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
662 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
663 Some(latin1_input_func as xmlCharEncodingInputFunc),
664 Some(latin1_output_func as xmlCharEncodingOutputFunc),
665 );
666
667 register_handler(
672 b"US-ASCII\0",
673 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
674 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
675 Some(ascii_input_func as xmlCharEncodingInputFunc),
676 Some(ascii_output_func as xmlCharEncodingOutputFunc),
677 );
678 register_handler(
679 b"ASCII\0",
680 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
681 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
682 Some(ascii_input_func as xmlCharEncodingInputFunc),
683 Some(ascii_output_func as xmlCharEncodingOutputFunc),
684 );
685
686 register_handler(
691 b"UTF-16\0",
692 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
693 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
694 Some(utf16le_input_func as xmlCharEncodingInputFunc),
695 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
696 );
697}
698
699fn register_handler(
701 name_bytes: &[u8],
702 _input_enc: xmlCharEncoding,
703 _output_enc: xmlCharEncoding,
704 input_func: Option<xmlCharEncodingInputFunc>,
705 output_func: Option<xmlCharEncodingOutputFunc>,
706) {
707 let name_raw =
708 unsafe { crate::abi::allocator::xmlMemStrdupImpl(name_bytes.as_ptr() as *const c_char) };
709 if name_raw.is_null() {
710 return;
711 }
712
713 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
714 as *mut _xmlCharEncodingHandler;
715
716 if handler.is_null() {
717 unsafe { xmlFreeImpl(name_raw) };
718 return;
719 }
720
721 unsafe {
722 ptr::write(
723 handler,
724 _xmlCharEncodingHandler {
725 name: name_raw as *mut c_char,
726 input: EncodingInputUnion {
727 legacyFunc: input_func,
728 },
729 output: EncodingOutputUnion {
730 legacyFunc: output_func,
731 },
732 inputCtxt: ptr::null_mut(),
733 outputCtxt: ptr::null_mut(),
734 ctxtDtor: None,
735 flags: 0,
736 },
737 );
738 }
739
740 add_encoding_handler(handler);
741}
742
743pub(crate) fn cleanup_encodings() {
747 let mut handlers = ENCODING_HANDLERS.write();
748 for &handler in handlers.iter() {
749 let ptr = handler.0;
750 if !ptr.is_null() {
751 unsafe {
752 if !(*ptr).name.is_null() {
753 xmlFreeImpl((*ptr).name as *mut c_void);
754 }
755 xmlFreeImpl(ptr as *mut c_void);
756 }
757 }
758 }
759 handlers.clear();
760 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
761}
762
763pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
769 if name.is_null() {
770 return ptr::null_mut();
771 }
772
773 init_encodings();
777
778 let name_str = unsafe {
779 match CStr::from_ptr(name as *const c_char).to_bytes() {
780 b"" => return ptr::null_mut(),
781 s => s,
782 }
783 };
784
785 let handlers = ENCODING_HANDLERS.read();
786 for &handler in handlers.iter() {
787 let ptr = handler.0;
788 if ptr.is_null() {
789 continue;
790 }
791 let h_name = unsafe {
792 if (*ptr).name.is_null() {
793 continue;
794 }
795 CStr::from_ptr((*ptr).name).to_bytes()
796 };
797
798 if name_str.eq_ignore_ascii_case(h_name) {
799 return ptr;
800 }
801 }
802
803 ptr::null_mut()
804}
805
806pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
810 if handler.is_null() {
811 return -1;
812 }
813
814 let mut handlers = ENCODING_HANDLERS.write();
815 handlers.push(HandlerPtr(handler));
816 0
817}
818
819#[allow(dead_code)]
827pub(crate) fn char_enc_in_func(
828 handler: *mut _xmlCharEncodingHandler,
829 out: &mut [u8],
830 in_data: &[u8],
831) -> c_int {
832 if handler.is_null() {
833 return -1;
834 }
835
836 let h = unsafe { &*handler };
837 let input_func = unsafe { h.input.legacyFunc };
838 let input_func = match input_func {
839 Some(f) => f,
840 None => return -1,
841 };
842
843 let mut outlen = out.len() as c_int;
844 let mut inlen = in_data.len() as c_int;
845
846 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
847}
848
849#[allow(dead_code)]
853pub(crate) fn char_enc_out_func(
854 handler: *mut _xmlCharEncodingHandler,
855 out: &mut [u8],
856 in_data: &[u8],
857) -> c_int {
858 if handler.is_null() {
859 return -1;
860 }
861
862 let h = unsafe { &*handler };
863 let output_func = unsafe { h.output.legacyFunc };
864 let output_func = match output_func {
865 Some(f) => f,
866 None => return -1,
867 };
868
869 let mut outlen = out.len() as c_int;
870 let mut inlen = in_data.len() as c_int;
871
872 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
873}
874
875pub(crate) fn char_enc_in(
882 handler: *mut _xmlCharEncodingHandler,
883 out: *mut _xmlBuffer,
884 in_: *mut _xmlBuffer,
885) -> c_int {
886 if handler.is_null() || out.is_null() || in_.is_null() {
887 return -1;
888 }
889
890 let h = unsafe { &*handler };
891 let input_func = unsafe { h.input.legacyFunc };
892 let input_func = match input_func {
893 Some(f) => f,
894 None => return -1,
895 };
896
897 let in_buf = unsafe { &*in_ };
898 let out_buf = unsafe { &mut *out };
899
900 if in_buf.content.is_null() || in_buf.use_ == 0 {
901 return 0;
902 }
903
904 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
905
906 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
908 let mut out_vec = vec![0u8; out_capacity];
909 let mut out_len = out_capacity as c_int;
910 let mut in_len = in_buf.use_ as c_int;
911
912 let ret = unsafe {
913 input_func(
914 out_vec.as_mut_ptr(),
915 &mut out_len,
916 in_data.as_ptr(),
917 &mut in_len,
918 )
919 };
920
921 if ret < 0 {
922 return -1;
923 }
924
925 let written = ret as usize;
926
927 append_to_xml_buffer(out_buf, &out_vec[..written]);
929
930 written as c_int
931}
932
933pub(crate) fn char_enc_out(
940 handler: *mut _xmlCharEncodingHandler,
941 out: *mut _xmlBuffer,
942 in_: *mut _xmlBuffer,
943) -> c_int {
944 if handler.is_null() || out.is_null() || in_.is_null() {
945 return -1;
946 }
947
948 let h = unsafe { &*handler };
949 let output_func = unsafe { h.output.legacyFunc };
950 let output_func = match output_func {
951 Some(f) => f,
952 None => return -1,
953 };
954
955 let in_buf = unsafe { &*in_ };
956 let out_buf = unsafe { &mut *out };
957
958 if in_buf.content.is_null() || in_buf.use_ == 0 {
959 return 0;
960 }
961
962 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
963
964 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
965 let mut out_vec = vec![0u8; out_capacity];
966 let mut out_len = out_capacity as c_int;
967 let mut in_len = in_buf.use_ as c_int;
968
969 let ret = unsafe {
970 output_func(
971 out_vec.as_mut_ptr(),
972 &mut out_len,
973 in_data.as_ptr(),
974 &mut in_len,
975 )
976 };
977
978 if ret < 0 {
979 return -1;
980 }
981
982 let written = ret as usize;
983
984 append_to_xml_buffer(out_buf, &out_vec[..written]);
986
987 written as c_int
988}
989
990fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
992 if data.is_empty() {
993 return;
994 }
995
996 let new_use = (buf.use_ as usize).saturating_add(data.len());
997 if new_use > buf.size as usize {
998 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
1000 let new_content =
1001 unsafe { xmlReallocImpl(buf.content as *mut c_void, new_size) as *mut xmlChar };
1002 if new_content.is_null() {
1003 return; }
1005 buf.content = new_content;
1006 buf.size = new_size as c_uint;
1007 }
1008
1009 unsafe {
1010 ptr::copy_nonoverlapping(
1011 data.as_ptr(),
1012 buf.content.add(buf.use_ as usize),
1013 data.len(),
1014 );
1015 }
1016 buf.use_ = new_use as c_uint;
1017}
1018
1019unsafe extern "C" fn utf8_input_func(
1029 out: *mut c_uchar,
1030 outlen: *mut c_int,
1031 in_: *const c_uchar,
1032 inlen: *mut c_int,
1033) -> c_int {
1034 let avail_out = *outlen as usize;
1035 let avail_in = *inlen as usize;
1036 let to_copy = avail_out.min(avail_in);
1037
1038 if to_copy > 0 {
1039 ptr::copy_nonoverlapping(in_, out, to_copy);
1040 }
1041
1042 *outlen = to_copy as c_int;
1043 *inlen = to_copy as c_int;
1044 to_copy as c_int
1045}
1046
1047unsafe extern "C" fn utf8_output_func(
1049 out: *mut c_uchar,
1050 outlen: *mut c_int,
1051 in_: *const c_uchar,
1052 inlen: *mut c_int,
1053) -> c_int {
1054 utf8_input_func(out, outlen, in_, inlen)
1055}
1056
1057unsafe extern "C" fn utf16le_input_func(
1061 out: *mut c_uchar,
1062 outlen: *mut c_int,
1063 in_: *const c_uchar,
1064 inlen: *mut c_int,
1065) -> c_int {
1066 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1067 return -1;
1068 }
1069
1070 let avail_in = *inlen as usize;
1071 let avail_out = *outlen as usize;
1072
1073 if avail_in == 0 || avail_out == 0 {
1074 *outlen = 0;
1075 *inlen = 0;
1076 return 0;
1077 }
1078
1079 let in_data = core::slice::from_raw_parts(in_, avail_in);
1080 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1081
1082 let result = match utf16le_to_utf8(in_data) {
1084 Ok(v) => v,
1085 Err(()) => return -1,
1086 };
1087
1088 let written = result.len().min(avail_out);
1089 if written > 0 {
1090 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1091 }
1092
1093 *outlen = written as c_int;
1094 *inlen = avail_in as c_int; written as c_int
1096}
1097
1098unsafe extern "C" fn utf16le_output_func(
1100 out: *mut c_uchar,
1101 outlen: *mut c_int,
1102 in_: *const c_uchar,
1103 inlen: *mut c_int,
1104) -> c_int {
1105 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1106 return -1;
1107 }
1108
1109 let avail_in = *inlen as usize;
1110 let avail_out = *outlen as usize;
1111
1112 if avail_in == 0 || avail_out == 0 {
1113 *outlen = 0;
1114 *inlen = 0;
1115 return 0;
1116 }
1117
1118 let in_data = core::slice::from_raw_parts(in_, avail_in);
1119 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1120
1121 let result = match utf8_to_utf16le(in_data) {
1122 Ok(v) => v,
1123 Err(()) => return -1,
1124 };
1125
1126 let written = result.len().min(avail_out);
1127 if written > 0 {
1128 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1129 }
1130
1131 *outlen = written as c_int;
1132 *inlen = avail_in as c_int;
1133 written as c_int
1134}
1135
1136unsafe extern "C" fn utf16be_input_func(
1140 out: *mut c_uchar,
1141 outlen: *mut c_int,
1142 in_: *const c_uchar,
1143 inlen: *mut c_int,
1144) -> c_int {
1145 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1146 return -1;
1147 }
1148
1149 let avail_in = *inlen as usize;
1150 let avail_out = *outlen as usize;
1151
1152 if avail_in == 0 || avail_out == 0 {
1153 *outlen = 0;
1154 *inlen = 0;
1155 return 0;
1156 }
1157
1158 let in_data = core::slice::from_raw_parts(in_, avail_in);
1159 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1160
1161 let result = match utf16be_to_utf8(in_data) {
1162 Ok(v) => v,
1163 Err(()) => return -1,
1164 };
1165
1166 let written = result.len().min(avail_out);
1167 if written > 0 {
1168 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1169 }
1170
1171 *outlen = written as c_int;
1172 *inlen = avail_in as c_int;
1173 written as c_int
1174}
1175
1176unsafe extern "C" fn utf16be_output_func(
1178 out: *mut c_uchar,
1179 outlen: *mut c_int,
1180 in_: *const c_uchar,
1181 inlen: *mut c_int,
1182) -> c_int {
1183 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1184 return -1;
1185 }
1186
1187 let avail_in = *inlen as usize;
1188 let avail_out = *outlen as usize;
1189
1190 if avail_in == 0 || avail_out == 0 {
1191 *outlen = 0;
1192 *inlen = 0;
1193 return 0;
1194 }
1195
1196 let in_data = core::slice::from_raw_parts(in_, avail_in);
1197
1198 let le_result = match utf8_to_utf16le(in_data) {
1200 Ok(v) => v,
1201 Err(()) => return -1,
1202 };
1203
1204 let mut result = le_result;
1206 for chunk in result.as_chunks_mut::<2>().0 {
1207 chunk.swap(0, 1);
1208 }
1209
1210 let written = result.len().min(avail_out);
1211 if written > 0 {
1212 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1213 }
1214
1215 *outlen = written as c_int;
1216 *inlen = avail_in as c_int;
1217 written as c_int
1218}
1219
1220unsafe extern "C" fn latin1_input_func(
1224 out: *mut c_uchar,
1225 outlen: *mut c_int,
1226 in_: *const c_uchar,
1227 inlen: *mut c_int,
1228) -> c_int {
1229 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1230 return -1;
1231 }
1232
1233 let avail_in = *inlen as usize;
1234 let avail_out = *outlen as usize;
1235
1236 if avail_in == 0 || avail_out == 0 {
1237 *outlen = 0;
1238 *inlen = 0;
1239 return 0;
1240 }
1241
1242 let in_data = core::slice::from_raw_parts(in_, avail_in);
1243 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1244
1245 let mut in_pos = 0;
1246 let mut out_pos = 0;
1247
1248 while in_pos < avail_in && out_pos < avail_out {
1249 let byte = in_data[in_pos];
1250 in_pos += 1;
1251
1252 if byte < 0x80 {
1253 if out_pos < avail_out {
1255 out_slice[out_pos] = byte;
1256 out_pos += 1;
1257 } else {
1258 break;
1259 }
1260 } else {
1261 if out_pos + 1 < avail_out {
1264 out_slice[out_pos] = 0xC2 | (byte >> 6);
1265 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1266 out_pos += 2;
1267 } else {
1268 break;
1269 }
1270 }
1271 }
1272
1273 *outlen = out_pos as c_int;
1274 *inlen = in_pos as c_int;
1275 out_pos as c_int
1276}
1277
1278unsafe extern "C" fn latin1_output_func(
1280 out: *mut c_uchar,
1281 outlen: *mut c_int,
1282 in_: *const c_uchar,
1283 inlen: *mut c_int,
1284) -> c_int {
1285 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1286 return -1;
1287 }
1288
1289 let avail_in = *inlen as usize;
1290 let avail_out = *outlen as usize;
1291
1292 if avail_in == 0 || avail_out == 0 {
1293 *outlen = 0;
1294 *inlen = 0;
1295 return 0;
1296 }
1297
1298 let in_data = core::slice::from_raw_parts(in_, avail_in);
1299 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1300
1301 let mut in_pos = 0;
1302 let mut out_pos = 0;
1303
1304 while in_pos < avail_in && out_pos < avail_out {
1305 let byte = in_data[in_pos];
1306 in_pos += 1;
1307
1308 if byte < 0x80 {
1309 out_slice[out_pos] = byte;
1311 out_pos += 1;
1312 } else if (0xC2..=0xC3).contains(&byte) {
1313 if in_pos < avail_in {
1315 let second = in_data[in_pos];
1316 in_pos += 1;
1317 if second & 0xC0 != 0x80 {
1318 return -1; }
1320 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1321 if cp > 0xFF {
1322 return -1; }
1324 out_slice[out_pos] = cp as u8;
1325 out_pos += 1;
1326 } else {
1327 return -1; }
1329 } else if (0x80..=0xBF).contains(&byte) {
1330 return -1;
1332 } else {
1333 return -1;
1336 }
1337 }
1338
1339 *outlen = out_pos as c_int;
1340 *inlen = in_pos as c_int;
1341 out_pos as c_int
1342}
1343
1344unsafe extern "C" fn ascii_input_func(
1348 out: *mut c_uchar,
1349 outlen: *mut c_int,
1350 in_: *const c_uchar,
1351 inlen: *mut c_int,
1352) -> c_int {
1353 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1354 return -1;
1355 }
1356
1357 let avail_in = *inlen as usize;
1358 let avail_out = *outlen as usize;
1359
1360 if avail_in == 0 || avail_out == 0 {
1361 *outlen = 0;
1362 *inlen = 0;
1363 return 0;
1364 }
1365
1366 let in_data = core::slice::from_raw_parts(in_, avail_in);
1367 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1368
1369 let mut pos = 0;
1370 while pos < avail_in && pos < avail_out {
1371 let byte = in_data[pos];
1372 if byte > 0x7F {
1373 return -1; }
1375 out_slice[pos] = byte;
1376 pos += 1;
1377 }
1378
1379 *outlen = pos as c_int;
1380 *inlen = pos as c_int;
1381 pos as c_int
1382}
1383
1384unsafe extern "C" fn ascii_output_func(
1386 out: *mut c_uchar,
1387 outlen: *mut c_int,
1388 in_: *const c_uchar,
1389 inlen: *mut c_int,
1390) -> c_int {
1391 ascii_input_func(out, outlen, in_, inlen)
1393}
1394
1395pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
1404 if name.is_null() {
1405 return ptr::null_mut();
1406 }
1407 find_encoding_handler(name as *const xmlChar)
1408}
1409
1410pub(crate) const fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
1414 match enc {
1418 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
1419 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE | xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => {
1420 c"UTF-16".as_ptr()
1421 }
1422 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE | xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => {
1423 c"UCS-4".as_ptr()
1424 }
1425 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"IBM037".as_ptr(),
1426 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
1427 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
1428 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
1429 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
1430 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
1431 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
1432 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
1433 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
1434 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
1435 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
1436 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
1437 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"Shift_JIS".as_ptr(),
1438 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
1439 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"US-ASCII".as_ptr(),
1441 _ => ptr::null(),
1442 }
1443}
1444
1445pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
1450 if name.is_null() {
1451 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
1452 }
1453 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
1454 encoding_from_name(bytes) as c_int
1455}
1456
1457static ENCODING_ALIASES: std::sync::OnceLock<
1465 parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>>,
1466> = std::sync::OnceLock::new();
1467
1468fn encoding_aliases() -> &'static parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>> {
1469 ENCODING_ALIASES.get_or_init(|| parking_lot::RwLock::new(std::collections::HashMap::new()))
1470}
1471
1472pub(crate) fn add_encoding_alias(name: *const c_char, alias: *const c_char) -> c_int {
1475 if name.is_null() || alias.is_null() {
1476 return -1;
1477 }
1478 let n = unsafe { CStr::from_ptr(name).to_bytes().to_vec() };
1479 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1480 encoding_aliases().write().insert(a, n);
1481 0
1482}
1483
1484pub(crate) fn del_encoding_alias(alias: *const c_char) -> c_int {
1487 if alias.is_null() {
1488 return -1;
1489 }
1490 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1491 if encoding_aliases().write().remove(&a).is_some() {
1492 0
1493 } else {
1494 -1
1495 }
1496}
1497
1498pub(crate) fn get_encoding_alias(alias: *const c_char) -> *const c_char {
1501 if alias.is_null() {
1502 return ptr::null();
1503 }
1504 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1505 let guard = encoding_aliases().read();
1506 match guard.get(&a) {
1507 Some(v) => {
1508 let leaked: &'static [u8] = Box::leak(v.clone().into_boxed_slice());
1511 leaked.as_ptr() as *const c_char
1512 }
1513 None => ptr::null(),
1514 }
1515}
1516
1517pub(crate) fn cleanup_encoding_aliases() {
1519 encoding_aliases().write().clear();
1520}
1521
1522pub(crate) fn xmlCharEncInFunc(
1526 handler: *mut _xmlCharEncodingHandler,
1527 out: *mut _xmlBuffer,
1528 in_: *mut _xmlBuffer,
1529) -> c_int {
1530 char_enc_in(handler, out, in_)
1531}
1532
1533pub(crate) fn xmlCharEncOutFunc(
1537 handler: *mut _xmlCharEncodingHandler,
1538 out: *mut _xmlBuffer,
1539 in_: *mut _xmlBuffer,
1540) -> c_int {
1541 char_enc_out(handler, out, in_)
1542}
1543
1544pub(crate) fn xmlNewCharEncodingHandler(
1550 name: *const c_char,
1551 input: xmlCharEncodingInputFunc,
1552 output: xmlCharEncodingOutputFunc,
1553) -> *mut _xmlCharEncodingHandler {
1554 if name.is_null() {
1555 return ptr::null_mut();
1556 }
1557
1558 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) };
1559 if name_raw.is_null() {
1560 return ptr::null_mut();
1561 }
1562
1563 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
1564 as *mut _xmlCharEncodingHandler;
1565
1566 if handler.is_null() {
1567 unsafe { xmlFreeImpl(name_raw) };
1568 return ptr::null_mut();
1569 }
1570
1571 unsafe {
1572 ptr::write(
1573 handler,
1574 _xmlCharEncodingHandler {
1575 name: name_raw as *mut c_char,
1576 input: EncodingInputUnion {
1577 legacyFunc: Some(input),
1578 },
1579 output: EncodingOutputUnion {
1580 legacyFunc: Some(output),
1581 },
1582 inputCtxt: ptr::null_mut(),
1583 outputCtxt: ptr::null_mut(),
1584 ctxtDtor: None,
1585 flags: 0,
1586 },
1587 );
1588 }
1589
1590 handler
1591}
1592
1593#[allow(dead_code)]
1597pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
1598 if handler.is_null() {
1599 return;
1600 }
1601
1602 {
1604 let mut handlers = ENCODING_HANDLERS.write();
1605 handlers.retain(|&h| h.0 != handler);
1606 }
1607
1608 unsafe {
1609 if !(*handler).name.is_null() {
1610 xmlFreeImpl((*handler).name as *mut c_void);
1611 }
1612 xmlFreeImpl(handler as *mut c_void);
1613 }
1614}
1615
1616pub(crate) fn xmlInitCharEncodingHandlers() {
1618 init_encodings();
1619}
1620
1621pub(crate) fn xmlCleanupCharEncodingHandlers() {
1623 cleanup_encodings();
1624}
1625
1626pub(crate) fn xmlLookupCharEncodingHandler(enc: c_int, out: *mut *mut c_void) -> c_int {
1648 if out.is_null() {
1649 return crate::abi::types::XML_ERR_ARGUMENT;
1650 }
1651 unsafe {
1652 *out = ptr::null_mut();
1653 }
1654 if enc <= 0 || enc >= 32 {
1655 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1656 }
1657 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int {
1659 return crate::abi::types::XML_ERR_OK;
1660 }
1661 let canonical: &[u8] = match enc {
1662 2 => b"UTF-16LE\0",
1664 3 => b"UTF-16BE\0",
1666 10 => b"ISO-8859-1\0",
1668 22 => b"US-ASCII\0",
1670 23 => b"UTF-16\0",
1672 _ => return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING,
1673 };
1674 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1675 if h.is_null() {
1676 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1677 }
1678 unsafe {
1679 *out = h as *mut c_void;
1680 }
1681 crate::abi::types::XML_ERR_OK
1682}
1683
1684pub(crate) fn xmlGetCharEncodingHandler(enc: c_int) -> *mut c_void {
1686 let mut ret: *mut c_void = ptr::null_mut();
1687 let _rc = xmlLookupCharEncodingHandler(enc, &mut ret);
1688 ret
1689}
1690
1691pub(crate) fn xmlCreateCharEncodingHandler(
1698 name: *const c_char,
1699 flags: c_int,
1700 impl_: Option<xmlCharEncConvImpl>,
1701 implCtxt: *mut c_void,
1702 out: *mut *mut c_void,
1703) -> c_int {
1704 if out.is_null() {
1705 return crate::abi::types::XML_ERR_ARGUMENT;
1706 }
1707 unsafe {
1708 *out = ptr::null_mut();
1709 }
1710 if name.is_null() || flags == 0 {
1711 return crate::abi::types::XML_ERR_ARGUMENT;
1712 }
1713 let norig = unsafe { CStr::from_ptr(name).to_bytes() };
1714
1715 let mut eff: &[u8] = norig;
1717 let alias = get_encoding_alias(name);
1718 if !alias.is_null() {
1719 eff = unsafe { CStr::from_ptr(alias).to_bytes() };
1720 }
1721
1722 let enc = encoding_from_name(eff);
1723
1724 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
1726 return crate::abi::types::XML_ERR_OK;
1727 }
1728
1729 let canonical: &[u8] = match enc {
1730 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => b"UTF-16LE\0",
1731 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => b"UTF-16BE\0",
1732 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => b"ISO-8859-1\0",
1733 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => b"US-ASCII\0",
1734 _ => {
1735 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1736 }
1737 };
1738 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1739 if h.is_null() {
1740 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1741 }
1742 unsafe {
1743 let src = &*h;
1744 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1745 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1746 if !has_in || !has_out {
1747 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1748 }
1749 let copy =
1754 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler;
1755 if copy.is_null() {
1756 return crate::abi::types::XML_ERR_NO_MEMORY;
1757 }
1758 let name_copy = crate::abi::allocator::xmlMemStrdupImpl(name) as *mut c_char;
1759 if name_copy.is_null() {
1760 xmlFreeImpl(copy as *mut c_void);
1761 return crate::abi::types::XML_ERR_NO_MEMORY;
1762 }
1763 ptr::write(
1764 copy,
1765 _xmlCharEncodingHandler {
1766 name: name_copy,
1767 input: EncodingInputUnion {
1768 legacyFunc: src.input.legacyFunc,
1769 },
1770 output: EncodingOutputUnion {
1771 legacyFunc: src.output.legacyFunc,
1772 },
1773 inputCtxt: src.inputCtxt,
1774 outputCtxt: src.outputCtxt,
1775 ctxtDtor: src.ctxtDtor,
1776 flags: src.flags,
1777 },
1778 );
1779 *out = copy as *mut c_void;
1780 }
1781 crate::abi::types::XML_ERR_OK
1782}
1783
1784fn find_extra_handler(
1790 norig: &[u8],
1791 name: &[u8],
1792 flags: c_int,
1793 impl_: Option<xmlCharEncConvImpl>,
1794 implCtxt: *mut c_void,
1795 out: *mut *mut c_void,
1796) -> c_int {
1797 if let Some(f) = impl_ {
1799 let mut n = norig.to_vec();
1800 n.push(0);
1801 let rc = unsafe {
1802 f(
1803 implCtxt,
1804 n.as_ptr() as *const c_char,
1805 flags,
1806 out as *mut *mut crate::abi::structs::_xmlCharEncodingHandler,
1807 )
1808 };
1809 return rc;
1810 }
1811 let mut n = name.to_vec();
1813 n.push(0);
1814 let h = find_encoding_handler(n.as_ptr() as *const xmlChar);
1815 if !h.is_null() {
1816 unsafe {
1817 let src = &*h;
1818 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1819 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1820 if has_in && has_out {
1821 *out = h as *mut c_void;
1822 return crate::abi::types::XML_ERR_OK;
1823 }
1824 }
1825 }
1826 crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING
1827}
1828
1829pub(crate) fn xmlOpenCharEncodingHandler(
1831 name: *const c_char,
1832 output: c_int,
1833 out: *mut *mut c_void,
1834) -> c_int {
1835 let flags: c_int = if output != 0 { 2 } else { 1 };
1837 xmlCreateCharEncodingHandler(name, flags, None, ptr::null_mut(), out)
1838}
1839
1840pub(crate) fn xmlCharEncNewCustomHandler(
1846 name: *const c_char,
1847 input: xmlCharEncConvFunc,
1848 output: xmlCharEncConvFunc,
1849 ctxtDtor: Option<xmlCharEncConvCtxtDtor>,
1850 inputCtxt: *mut c_void,
1851 outputCtxt: *mut c_void,
1852 out: *mut *mut c_void,
1853) -> c_int {
1854 if out.is_null() {
1855 return crate::abi::types::XML_ERR_ARGUMENT;
1856 }
1857 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
1858 as *mut _xmlCharEncodingHandler;
1859 if handler.is_null() {
1860 unsafe {
1861 if let Some(d) = ctxtDtor {
1862 if !inputCtxt.is_null() {
1863 d(inputCtxt);
1864 }
1865 if !outputCtxt.is_null() {
1866 d(outputCtxt);
1867 }
1868 }
1869 }
1870 return crate::abi::types::XML_ERR_NO_MEMORY;
1871 }
1872 let name_copy = if name.is_null() {
1873 ptr::null_mut()
1874 } else {
1875 let nc = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) } as *mut c_char;
1876 if nc.is_null() {
1877 unsafe { xmlFreeImpl(handler as *mut c_void) };
1878 unsafe {
1879 if let Some(d) = ctxtDtor {
1880 if !inputCtxt.is_null() {
1881 d(inputCtxt);
1882 }
1883 if !outputCtxt.is_null() {
1884 d(outputCtxt);
1885 }
1886 }
1887 }
1888 return crate::abi::types::XML_ERR_NO_MEMORY;
1889 }
1890 nc
1891 };
1892 unsafe {
1893 ptr::write(
1894 handler,
1895 _xmlCharEncodingHandler {
1896 name: name_copy,
1897 input: EncodingInputUnion { func: Some(input) },
1898 output: EncodingOutputUnion { func: Some(output) },
1899 inputCtxt,
1900 outputCtxt,
1901 ctxtDtor,
1902 flags: 0,
1903 },
1904 );
1905 *out = handler as *mut c_void;
1906 }
1907 crate::abi::types::XML_ERR_OK
1908}
1909
1910#[cfg(test)]
1915mod tests {
1916 use super::*;
1917
1918 #[test]
1921 fn test_detect_bom_utf8() {
1922 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
1923 assert_eq!(
1924 detect_encoding_from_bom(&data),
1925 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1926 );
1927 }
1928
1929 #[test]
1930 fn test_detect_bom_utf16le() {
1931 let data = [0xFF, 0xFE, 0x00, 0x01];
1932 assert_eq!(
1933 detect_encoding_from_bom(&data),
1934 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1935 );
1936 }
1937
1938 #[test]
1939 fn test_detect_bom_utf16be() {
1940 let data = [0xFE, 0xFF, 0x00, 0x01];
1941 assert_eq!(
1942 detect_encoding_from_bom(&data),
1943 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1944 );
1945 }
1946
1947 #[test]
1948 fn test_detect_bom_none() {
1949 let data = b"<xml>";
1950 assert_eq!(
1951 detect_encoding_from_bom(data),
1952 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1953 );
1954 }
1955
1956 #[test]
1957 fn test_detect_bom_empty() {
1958 assert_eq!(
1959 detect_encoding_from_bom(b""),
1960 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1961 );
1962 }
1963
1964 #[test]
1967 fn test_detect_encoding_declaration_utf8() {
1968 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
1969 let result = detect_encoding_from_declaration(data);
1970 assert_eq!(result, Some(b"utf-8".to_vec()));
1971 }
1972
1973 #[test]
1974 fn test_detect_encoding_declaration_iso() {
1975 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
1976 let result = detect_encoding_from_declaration(data);
1977 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
1978 }
1979
1980 #[test]
1981 fn test_detect_encoding_declaration_none() {
1982 let data = b"<?xml version=\"1.0\"?>";
1983 let result = detect_encoding_from_declaration(data);
1984 assert!(result.is_none());
1985 }
1986
1987 #[test]
1988 fn test_detect_encoding_declaration_no_xml() {
1989 let data = b"<root>";
1990 let result = detect_encoding_from_declaration(data);
1991 assert!(result.is_none());
1992 }
1993
1994 #[test]
1995 fn test_detect_encoding_declaration_with_bom() {
1996 let mut data = vec![0xEF, 0xBB, 0xBF];
1997 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1998 let result = detect_encoding_from_declaration(&data);
1999 assert_eq!(result, Some(b"utf-8".to_vec()));
2000 }
2001
2002 #[test]
2005 fn test_encoding_from_name_utf8() {
2006 assert_eq!(
2007 encoding_from_name(b"UTF-8"),
2008 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2009 );
2010 assert_eq!(
2011 encoding_from_name(b"utf8"),
2012 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2013 );
2014 }
2015
2016 #[test]
2017 fn test_encoding_from_name_utf16() {
2018 assert_eq!(
2019 encoding_from_name(b"UTF-16LE"),
2020 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2021 );
2022 assert_eq!(
2023 encoding_from_name(b"UTF-16BE"),
2024 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2025 );
2026 assert_eq!(
2027 encoding_from_name(b"utf-16"),
2028 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2029 );
2030 }
2031
2032 #[test]
2033 fn test_encoding_from_name_latin1() {
2034 assert_eq!(
2035 encoding_from_name(b"ISO-8859-1"),
2036 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2037 );
2038 assert_eq!(
2039 encoding_from_name(b"Latin1"),
2040 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2041 );
2042 }
2043
2044 #[test]
2045 fn test_encoding_from_name_ascii() {
2046 assert_eq!(
2047 encoding_from_name(b"ASCII"),
2048 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2049 );
2050 assert_eq!(
2051 encoding_from_name(b"US-ASCII"),
2052 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2053 );
2054 }
2055
2056 #[test]
2057 fn test_encoding_from_name_error() {
2058 assert_eq!(
2059 encoding_from_name(b"invalid-encoding"),
2060 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2061 );
2062 }
2063
2064 #[test]
2065 fn test_encoding_from_name_empty() {
2066 assert_eq!(
2067 encoding_from_name(b""),
2068 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2069 );
2070 }
2071
2072 #[test]
2075 fn test_encoding_name_utf8() {
2076 assert_eq!(
2077 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
2078 Some(b"UTF-8" as &[u8])
2079 );
2080 }
2081
2082 #[test]
2083 fn test_encoding_name_utf16le() {
2084 assert_eq!(
2085 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
2086 Some(b"UTF-16LE" as &[u8])
2087 );
2088 }
2089
2090 #[test]
2091 fn test_encoding_name_none() {
2092 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
2093 }
2094
2095 #[test]
2096 fn test_encoding_name_error() {
2097 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
2098 }
2099
2100 #[test]
2103 fn test_utf8_valid_ascii() {
2104 assert!(utf8_valid(b"hello world"));
2105 }
2106
2107 #[test]
2108 fn test_utf8_valid_multi_byte() {
2109 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
2110 }
2111
2112 #[test]
2113 fn test_utf8_valid_empty() {
2114 assert!(utf8_valid(b""));
2115 }
2116
2117 #[test]
2118 fn test_utf8_invalid() {
2119 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
2120 }
2121
2122 #[test]
2125 fn test_valid_xml_chars() {
2126 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));
2132 assert!(is_valid_xml_char(0xE000));
2133 assert!(is_valid_xml_char(0xFFFD));
2134 assert!(is_valid_xml_char(0x10000));
2135 assert!(is_valid_xml_char(0x10FFFF));
2136 }
2137
2138 #[test]
2139 fn test_invalid_xml_chars() {
2140 assert!(!is_valid_xml_char(0x00));
2141 assert!(!is_valid_xml_char(0x08));
2142 assert!(!is_valid_xml_char(0x0B));
2143 assert!(!is_valid_xml_char(0x0C));
2144 assert!(!is_valid_xml_char(0x0E));
2145 assert!(!is_valid_xml_char(0x1F));
2146 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
2149 assert!(!is_valid_xml_char(0xFFFF));
2150 assert!(!is_valid_xml_char(0x110000));
2151 }
2152
2153 #[test]
2156 fn test_utf16le_to_utf8_ascii() {
2157 let data = [b'A', 0x00, b'B', 0x00];
2159 let result = utf16le_to_utf8(&data).unwrap();
2160 assert_eq!(result, b"AB");
2161 }
2162
2163 #[test]
2164 fn test_utf16le_to_utf8_bom() {
2165 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
2167 let result = utf16le_to_utf8(&data).unwrap();
2168 assert_eq!(result, b"AB");
2169 }
2170
2171 #[test]
2172 fn test_utf16le_to_utf8_bmp() {
2173 let data = [0xE9, 0x00];
2175 let result = utf16le_to_utf8(&data).unwrap();
2176 assert_eq!(result, "é".as_bytes());
2177 }
2178
2179 #[test]
2180 fn test_utf16le_to_utf8_supplementary() {
2181 let data = [0x3D, 0xD8, 0x00, 0xDE];
2183 let result = utf16le_to_utf8(&data).unwrap();
2184 assert_eq!(result, "😀".as_bytes());
2185 }
2186
2187 #[test]
2188 fn test_utf16le_to_utf8_unpaired_surrogate() {
2189 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
2191 }
2192
2193 #[test]
2194 fn test_utf16le_to_utf8_truncated() {
2195 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
2197 }
2198
2199 #[test]
2200 fn test_utf16le_to_utf8_empty() {
2201 let result = utf16le_to_utf8(b"").unwrap();
2202 assert!(result.is_empty());
2203 }
2204
2205 #[test]
2208 fn test_utf16be_to_utf8_ascii() {
2209 let data = [0x00, b'A', 0x00, b'B'];
2210 let result = utf16be_to_utf8(&data).unwrap();
2211 assert_eq!(result, b"AB");
2212 }
2213
2214 #[test]
2215 fn test_utf16be_to_utf8_bom() {
2216 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
2218 let result = utf16be_to_utf8(&data).unwrap();
2219 assert_eq!(result, b"AB");
2220 }
2221
2222 #[test]
2223 fn test_utf16be_to_utf8_supplementary() {
2224 let data = [0xD8, 0x3D, 0xDE, 0x00];
2226 let result = utf16be_to_utf8(&data).unwrap();
2227 assert_eq!(result, "😀".as_bytes());
2228 }
2229
2230 #[test]
2231 fn test_utf16be_to_utf8_empty() {
2232 let result = utf16be_to_utf8(b"").unwrap();
2233 assert!(result.is_empty());
2234 }
2235
2236 #[test]
2239 fn test_utf8_to_utf16le_ascii() {
2240 let result = utf8_to_utf16le(b"AB").unwrap();
2241 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
2242 }
2243
2244 #[test]
2245 fn test_utf8_to_utf16le_bmp() {
2246 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
2247 assert_eq!(result, [0xE9, 0x00]);
2248 }
2249
2250 #[test]
2251 fn test_utf8_to_utf16le_supplementary() {
2252 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
2253 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
2254 }
2255
2256 #[test]
2257 fn test_utf8_to_utf16le_invalid_utf8() {
2258 assert!(utf8_to_utf16le(&[0xFF]).is_err());
2259 }
2260
2261 #[test]
2262 fn test_utf8_to_utf16le_empty() {
2263 let result = utf8_to_utf16le(b"").unwrap();
2264 assert!(result.is_empty());
2265 }
2266
2267 #[test]
2270 fn test_latin1_to_utf8_ascii() {
2271 let result = latin1_to_utf8(b"ABC");
2272 assert_eq!(result, b"ABC");
2273 }
2274
2275 #[test]
2276 fn test_latin1_to_utf8_accented() {
2277 let result = latin1_to_utf8(&[0xE9]);
2279 assert_eq!(result, "é".as_bytes());
2280 }
2281
2282 #[test]
2283 fn test_latin1_to_utf8_all_255() {
2284 let result = latin1_to_utf8(&[0xFF]);
2285 assert_eq!(result, [0xC3, 0xBF]);
2287 }
2288
2289 #[test]
2290 fn test_latin1_to_utf8_empty() {
2291 let result = latin1_to_utf8(b"");
2292 assert!(result.is_empty());
2293 }
2294
2295 #[test]
2296 fn test_latin1_to_utf8_mixed() {
2297 let result = latin1_to_utf8(b"caf\xE9");
2298 assert_eq!(result, "café".as_bytes());
2299 }
2300
2301 #[test]
2304 fn test_utf8_to_latin1_ascii() {
2305 let result = utf8_to_latin1(b"ABC").unwrap();
2306 assert_eq!(result, b"ABC");
2307 }
2308
2309 #[test]
2310 fn test_utf8_to_latin1_accented() {
2311 let result = utf8_to_latin1("é".as_bytes()).unwrap();
2312 assert_eq!(result, [0xE9]);
2313 }
2314
2315 #[test]
2316 fn test_utf8_to_latin1_out_of_range() {
2317 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
2319
2320 #[test]
2321 fn test_utf8_to_latin1_invalid_utf8() {
2322 assert!(utf8_to_latin1(&[0xFF]).is_err());
2323 }
2324
2325 #[test]
2326 fn test_utf8_to_latin1_empty() {
2327 let result = utf8_to_latin1(b"").unwrap();
2328 assert!(result.is_empty());
2329 }
2330
2331 #[test]
2334 fn test_init_and_find_encodings() {
2335 init_encodings();
2336
2337 let utf8_name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2338 assert!(!find_encoding_handler(utf8_name).is_null());
2339
2340 let utf16le_name: *const xmlChar = c"UTF-16LE".as_ptr() as *const xmlChar;
2341 assert!(!find_encoding_handler(utf16le_name).is_null());
2342
2343 let utf16be_name: *const xmlChar = c"UTF-16BE".as_ptr() as *const xmlChar;
2344 assert!(!find_encoding_handler(utf16be_name).is_null());
2345
2346 let latin1_name: *const xmlChar = c"ISO-8859-1".as_ptr() as *const xmlChar;
2347 assert!(!find_encoding_handler(latin1_name).is_null());
2348
2349 let ascii_name: *const xmlChar = c"ASCII".as_ptr() as *const xmlChar;
2350 assert!(!find_encoding_handler(ascii_name).is_null());
2351
2352 let lower_name: *const xmlChar = c"utf-8".as_ptr() as *const xmlChar;
2354 assert!(!find_encoding_handler(lower_name).is_null());
2355 }
2356
2357 #[test]
2358 fn test_find_encoding_handler_not_found() {
2359 let name: *const xmlChar = c"NONEXISTENT".as_ptr() as *const xmlChar;
2360 assert!(find_encoding_handler(name).is_null());
2361 }
2362
2363 #[test]
2364 fn test_find_encoding_handler_null() {
2365 assert!(find_encoding_handler(ptr::null()).is_null());
2366 }
2367
2368 #[test]
2369 fn test_add_encoding_handler() {
2370 let handler = unsafe {
2371 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
2372 };
2373 assert!(!handler.is_null());
2374
2375 let name = unsafe {
2376 crate::abi::allocator::xmlMemStrdupImpl(c"TEST-ENC".as_ptr() as *const c_char)
2377 };
2378 unsafe {
2379 ptr::write(
2380 handler,
2381 _xmlCharEncodingHandler {
2382 name: name as *mut c_char,
2383 input: EncodingInputUnion { legacyFunc: None },
2384 output: EncodingOutputUnion { legacyFunc: None },
2385 inputCtxt: ptr::null_mut(),
2386 outputCtxt: ptr::null_mut(),
2387 ctxtDtor: None,
2388 flags: 0,
2389 },
2390 );
2391 }
2392
2393 assert_eq!(add_encoding_handler(handler), 0);
2394
2395 let found = find_encoding_handler(c"TEST-ENC".as_ptr() as *const xmlChar);
2396 assert_eq!(found, handler);
2397
2398 {
2400 let mut handlers = ENCODING_HANDLERS.write();
2401 handlers.retain(|&h| h.0 != handler);
2402 }
2403
2404 unsafe {
2405 xmlFreeImpl(name as *mut c_void);
2406 xmlFreeImpl(handler as *mut c_void);
2407 }
2408 }
2409
2410 #[test]
2413 fn test_utf16le_roundtrip() {
2414 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
2415 let utf16 = utf8_to_utf16le(original).unwrap();
2416 let back = utf16le_to_utf8(&utf16).unwrap();
2417 assert_eq!(original.to_vec(), back);
2418 }
2419
2420 #[test]
2421 fn test_utf16be_roundtrip() {
2422 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
2423 let utf16le = utf8_to_utf16le(original).unwrap();
2424 let mut utf16be = utf16le.clone();
2426 for chunk in utf16be.as_chunks_mut::<2>().0 {
2427 chunk.swap(0, 1);
2428 }
2429 let back = utf16be_to_utf8(&utf16be).unwrap();
2430 assert_eq!(original.to_vec(), back);
2431 }
2432
2433 #[test]
2434 fn test_latin1_roundtrip() {
2435 let original: Vec<u8> = (0x00..=0xFF).collect();
2436 let utf8 = latin1_to_utf8(&original);
2437 let back = utf8_to_latin1(&utf8).unwrap();
2438 assert_eq!(original, back);
2439 }
2440
2441 #[test]
2444 fn test_utf8_handler_identity() {
2445 let input = b"Hello, UTF-8!";
2446 let mut output = [0u8; 64];
2447 let mut outlen = output.len() as c_int;
2448 let mut inlen = input.len() as c_int;
2449
2450 let ret = unsafe {
2451 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
2452 };
2453
2454 assert_eq!(ret, input.len() as c_int);
2455 assert_eq!(&output[..ret as usize], input);
2456 assert_eq!(inlen, input.len() as c_int);
2457 }
2458
2459 #[test]
2460 fn test_utf16le_handler_roundtrip() {
2461 init_encodings();
2462
2463 let original = b"Hello UTF-16LE!";
2464 let mut utf16_buf = [0u8; 128];
2465 let mut outlen = utf16_buf.len() as c_int;
2466 let mut inlen = original.len() as c_int;
2467
2468 let written = unsafe {
2469 utf16le_output_func(
2470 utf16_buf.as_mut_ptr(),
2471 &mut outlen,
2472 original.as_ptr(),
2473 &mut inlen,
2474 )
2475 };
2476 assert!(written > 0);
2477
2478 let mut decoded = [0u8; 128];
2480 let mut outlen2 = decoded.len() as c_int;
2481 let mut inlen2 = written;
2482
2483 let written2 = unsafe {
2484 utf16le_input_func(
2485 decoded.as_mut_ptr(),
2486 &mut outlen2,
2487 utf16_buf.as_ptr(),
2488 &mut inlen2,
2489 )
2490 };
2491 assert_eq!(written2 as usize, original.len());
2492 assert_eq!(&decoded[..written2 as usize], original);
2493 }
2494
2495 #[test]
2498 fn test_append_to_xml_buffer() {
2499 unsafe {
2500 let content = xmlMallocImpl(64) as *mut xmlChar;
2501 assert!(!content.is_null());
2502
2503 let mut buf = _xmlBuffer {
2504 content,
2505 use_: 0,
2506 size: 64,
2507 alloc: 0,
2508 contentIO: ptr::null_mut(),
2509 };
2510
2511 append_to_xml_buffer(&mut buf, b"Hello");
2512 assert_eq!(buf.use_, 5);
2513 let slice = core::slice::from_raw_parts(buf.content, 5);
2514 assert_eq!(slice, b"Hello");
2515
2516 append_to_xml_buffer(&mut buf, b" World");
2517 assert_eq!(buf.use_, 11);
2518 let slice = core::slice::from_raw_parts(buf.content, 11);
2519 assert_eq!(slice, b"Hello World");
2520
2521 xmlFreeImpl(buf.content as *mut c_void);
2522 }
2523 }
2524
2525 #[test]
2528 fn test_xml_parse_char_encoding() {
2529 let name = c"UTF-8".as_ptr() as *const c_char;
2530 assert_eq!(
2531 xmlParseCharEncoding(name),
2532 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
2533 );
2534
2535 let name = c"ISO-8859-1".as_ptr() as *const c_char;
2536 assert_eq!(
2537 xmlParseCharEncoding(name),
2538 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
2539 );
2540
2541 assert_eq!(
2542 xmlParseCharEncoding(ptr::null()),
2543 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
2544 );
2545 }
2546
2547 #[test]
2548 fn test_xml_new_and_del_encoding_handler() {
2549 let name = c"TestEnc".as_ptr() as *const c_char;
2550 let handler = xmlNewCharEncodingHandler(
2551 name,
2552 utf8_input_func as xmlCharEncodingInputFunc,
2553 utf8_output_func as xmlCharEncodingOutputFunc,
2554 );
2555 assert!(!handler.is_null());
2556
2557 unsafe {
2558 assert!(!(*handler).name.is_null());
2559 let cstr = CStr::from_ptr((*handler).name);
2560 assert_eq!(cstr.to_bytes(), b"TestEnc");
2561 }
2562
2563 xmlDelEncodingHandler(handler);
2564 }
2565
2566 #[test]
2567 fn test_xml_init_and_cleanup() {
2568 xmlInitCharEncodingHandlers();
2569
2570 let name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2571 assert!(!find_encoding_handler(name).is_null());
2572
2573 xmlCleanupCharEncodingHandlers();
2574 }
2576}