1#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
90
91use std::ffi::CStr;
92use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};
93use std::ptr;
94use std::sync::atomic::{AtomicBool, Ordering};
95
96use once_cell::sync::Lazy;
97use parking_lot::RwLock;
98
99use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlReallocImpl};
100use crate::abi::callbacks::{
101 xmlCharEncConvCtxtDtor, xmlCharEncConvFunc, xmlCharEncConvImpl, xmlCharEncodingInputFunc,
102 xmlCharEncodingOutputFunc,
103};
104use crate::abi::structs::{
105 _xmlBuffer, _xmlCharEncodingHandler, EncodingInputUnion, EncodingOutputUnion,
106};
107use crate::abi::types::{xmlChar, xmlCharEncoding};
108
109#[allow(dead_code)]
113const MAX_CHAR_BYTES: usize = 6;
114
115#[allow(dead_code)]
117const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
118
119const UTF16LE_BOM: [u8; 2] = [0xFF, 0xFE];
121
122const UTF16BE_BOM: [u8; 2] = [0xFE, 0xFF];
124
125#[derive(Clone, Copy)]
133struct HandlerPtr(*mut _xmlCharEncodingHandler);
134
135unsafe impl Send for HandlerPtr {}
136unsafe impl Sync for HandlerPtr {}
137
138static ENCODING_HANDLERS: Lazy<RwLock<Vec<HandlerPtr>>> = Lazy::new(|| RwLock::new(Vec::new()));
145
146static ENCODING_INITIALIZED: AtomicBool = AtomicBool::new(false);
148
149static ENCODING_INIT_MUTEX: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
151
152#[allow(dead_code)]
161pub(crate) fn detect_encoding_from_bom(data: &[u8]) -> xmlCharEncoding {
162 if data.len() >= 3 && data[0..3] == UTF8_BOM {
163 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
164 } else if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
165 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
166 } else if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
167 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
168 } else {
169 xmlCharEncoding::XML_CHAR_ENCODING_NONE
170 }
171}
172
173#[allow(dead_code)]
178pub(crate) fn detect_encoding_from_declaration(data: &[u8]) -> Option<Vec<u8>> {
179 let start = if data.len() >= 3 && data[0..3] == UTF8_BOM {
181 3
182 } else if data.len() >= 2 && (data[0..2] == UTF16LE_BOM || data[0..2] == UTF16BE_BOM) {
183 return None;
185 } else {
186 0
187 };
188
189 let remaining = &data[start..];
190
191 if remaining.len() < 5 || !remaining[0..5].eq_ignore_ascii_case(b"<?xml") {
193 return None;
194 }
195
196 let pi_end = remaining.windows(2).position(|w| w == b"?>")?;
198 let decl_content = &remaining[5..pi_end];
199
200 let decl_str = core::str::from_utf8(decl_content).ok()?;
202 let lower = decl_str.to_ascii_lowercase();
203
204 let enc_pos = lower.find("encoding")?;
206
207 let after_enc = &decl_content[enc_pos + 8..];
209 let after_enc_str = core::str::from_utf8(after_enc).ok()?;
210 let after_enc_trimmed = after_enc_str.trim_start();
211
212 if !after_enc_trimmed.starts_with('=') {
213 return None;
214 }
215
216 let after_eq = after_enc_trimmed[1..].trim_start();
217
218 let quote = after_eq.chars().next()?;
220 if quote != '"' && quote != '\'' {
221 return None;
222 }
223
224 let value_end = after_eq[1..].find(quote)?;
226 let encoding_value = &after_eq[1..=value_end];
227
228 Some(encoding_value.to_ascii_lowercase().as_bytes().to_vec())
229}
230
231pub(crate) fn encoding_from_name(name: &[u8]) -> xmlCharEncoding {
236 let s = core::str::from_utf8(name).unwrap_or("");
237 let s = s.trim().to_ascii_lowercase();
238
239 match s.as_str() {
240 "utf-8" | "utf8" => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
242
243 "utf-16" | "utf-16le" | "utf16le" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
245 "utf-16be" | "utf16be" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
246
247 "iso-8859-1" | "iso_8859-1" | "latin1" | "latin-1" | "l1" | "cp819" | "ibm819"
249 | "iso-ir-100" | "iso_8859-1:1987" => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
250 "iso-8859-2" | "iso_8859-2" | "latin2" | "latin-2" | "l2" => {
251 xmlCharEncoding::XML_CHAR_ENCODING_8859_2
252 }
253 "iso-8859-3" | "iso_8859-3" | "latin3" | "latin-3" | "l3" => {
254 xmlCharEncoding::XML_CHAR_ENCODING_8859_3
255 }
256 "iso-8859-4" | "iso_8859-4" | "latin4" | "latin-4" | "l4" => {
257 xmlCharEncoding::XML_CHAR_ENCODING_8859_4
258 }
259 "iso-8859-5" | "iso_8859-5" | "cyrillic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
260 "iso-8859-6" | "iso_8859-6" | "arabic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
261 "iso-8859-7" | "iso_8859-7" | "greek" => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
262 "iso-8859-8" | "iso_8859-8" | "hebrew" => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
263 "iso-8859-9" | "iso_8859-9" | "latin5" | "latin-5" | "l5" | "turkish" => {
264 xmlCharEncoding::XML_CHAR_ENCODING_8859_9
265 }
266
267 "ascii" | "us-ascii" | "us" | "ansi_x3.4-1968" | "ansi_x3.4-1986" | "iso-ir-6"
269 | "iso_646.irv:1991" | "cp367" | "ibm367" => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
270
271 "iso-2022-jp" | "iso2022-jp" => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
273 "shift_jis" | "shift-jis" | "sjis" | "cp932" => {
274 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS
275 }
276 "euc-jp" | "eucjp" => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
277
278 "ucs-4" | "ucs4" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
280 "ucs-4le" | "ucs4le" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
281 "ucs-4be" | "ucs4be" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
282 "ucs-2" | "ucs2" => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
283
284 "ebcdic" | "cp037" | "ibm037" => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
286
287 _ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
288 }
289}
290
291pub(crate) const fn encoding_name(enc: xmlCharEncoding) -> Option<&'static [u8]> {
295 match enc {
296 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => Some(b"UTF-8" as &[u8]),
297 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => Some(b"UTF-16LE" as &[u8]),
298 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => Some(b"UTF-16BE" as &[u8]),
299 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => Some(b"UCS-4LE" as &[u8]),
300 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => Some(b"UCS-4BE" as &[u8]),
301 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => Some(b"EBCDIC" as &[u8]),
302 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => Some(b"UCS-4-2143" as &[u8]),
303 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => Some(b"UCS-4-3412" as &[u8]),
304 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => Some(b"UCS-2" as &[u8]),
305 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => Some(b"ISO-8859-1" as &[u8]),
306 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => Some(b"ISO-8859-2" as &[u8]),
307 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => Some(b"ISO-8859-3" as &[u8]),
308 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => Some(b"ISO-8859-4" as &[u8]),
309 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => Some(b"ISO-8859-5" as &[u8]),
310 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => Some(b"ISO-8859-6" as &[u8]),
311 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => Some(b"ISO-8859-7" as &[u8]),
312 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => Some(b"ISO-8859-8" as &[u8]),
313 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => Some(b"ISO-8859-9" as &[u8]),
314 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => Some(b"ISO-2022-JP" as &[u8]),
315 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => Some(b"SHIFT_JIS" as &[u8]),
316 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => Some(b"EUC-JP" as &[u8]),
317 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => Some(b"US-ASCII" as &[u8]),
318 _ => None,
319 }
320}
321
322#[allow(dead_code)]
330pub(crate) const fn utf8_valid(data: &[u8]) -> bool {
331 core::str::from_utf8(data).is_ok()
332}
333
334#[allow(dead_code)]
346pub(crate) const fn is_valid_xml_char(cp: u32) -> bool {
347 matches!(
348 cp,
349 0x9 | 0xA | 0xD | 0x20..=0xD7FF | 0xE000..=0xFFFD | 0x10000..=0x10FFFF
350 )
351}
352
353#[inline]
359const fn read_utf16le_unit(data: &[u8]) -> Option<u16> {
360 if data.len() < 2 {
361 return None;
362 }
363 Some(u16::from_le_bytes([data[0], data[1]]))
364}
365
366#[inline]
368const fn read_utf16be_unit(data: &[u8]) -> Option<u16> {
369 if data.len() < 2 {
370 return None;
371 }
372 Some(u16::from_be_bytes([data[0], data[1]]))
373}
374
375const fn encode_codepoint_to_utf8(cp: u32, out: &mut [u8]) -> usize {
379 if cp < 0x80 {
380 if !out.is_empty() {
381 out[0] = cp as u8;
382 }
383 1
384 } else if cp < 0x800 {
385 if out.len() < 2 {
386 return 0;
387 }
388 out[0] = 0xC0 | ((cp >> 6) as u8);
389 out[1] = 0x80 | (cp as u8 & 0x3F);
390 2
391 } else if cp < 0x10000 {
392 if out.len() < 3 {
393 return 0;
394 }
395 out[0] = 0xE0 | ((cp >> 12) as u8);
396 out[1] = 0x80 | ((cp >> 6) as u8 & 0x3F);
397 out[2] = 0x80 | (cp as u8 & 0x3F);
398 3
399 } else if cp < 0x110000 {
400 if out.len() < 4 {
401 return 0;
402 }
403 out[0] = 0xF0 | ((cp >> 18) as u8);
404 out[1] = 0x80 | ((cp >> 12) as u8 & 0x3F);
405 out[2] = 0x80 | ((cp >> 6) as u8 & 0x3F);
406 out[3] = 0x80 | (cp as u8 & 0x3F);
407 4
408 } else {
409 0
410 }
411}
412
413pub(crate) fn utf16le_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
418 if data.is_empty() {
419 return Ok(Vec::new());
420 }
421
422 let offset = if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
424 2
425 } else {
426 0
427 };
428
429 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
430 let mut i = offset;
431
432 while i < data.len() {
433 let unit = read_utf16le_unit(&data[i..]).ok_or(())?;
434 i += 2;
435
436 if (0xD800..=0xDBFF).contains(&unit) {
437 let low = read_utf16le_unit(&data[i..]).ok_or(())?;
439 i += 2;
440
441 if !(0xDC00..=0xDFFF).contains(&low) {
442 return Err(());
443 }
444
445 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
446 let mut buf = [0u8; 4];
447 let n = encode_codepoint_to_utf8(cp, &mut buf);
448 if n == 0 {
449 return Err(());
450 }
451 result.extend_from_slice(&buf[..n]);
452 } else if (0xDC00..=0xDFFF).contains(&unit) {
453 return Err(());
455 } else {
456 let cp = unit as u32;
457 let mut buf = [0u8; 4];
458 let n = encode_codepoint_to_utf8(cp, &mut buf);
459 result.extend_from_slice(&buf[..n]);
460 }
461 }
462
463 Ok(result)
464}
465
466pub(crate) fn utf16be_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
470 if data.is_empty() {
471 return Ok(Vec::new());
472 }
473
474 let offset = if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
476 2
477 } else {
478 0
479 };
480
481 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
482 let mut i = offset;
483
484 while i < data.len() {
485 let unit = read_utf16be_unit(&data[i..]).ok_or(())?;
486 i += 2;
487
488 if (0xD800..=0xDBFF).contains(&unit) {
489 let low = read_utf16be_unit(&data[i..]).ok_or(())?;
491 i += 2;
492
493 if !(0xDC00..=0xDFFF).contains(&low) {
494 return Err(());
495 }
496
497 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
498 let mut buf = [0u8; 4];
499 let n = encode_codepoint_to_utf8(cp, &mut buf);
500 if n == 0 {
501 return Err(());
502 }
503 result.extend_from_slice(&buf[..n]);
504 } else if (0xDC00..=0xDFFF).contains(&unit) {
505 return Err(());
507 } else {
508 let cp = unit as u32;
509 let mut buf = [0u8; 4];
510 let n = encode_codepoint_to_utf8(cp, &mut buf);
511 result.extend_from_slice(&buf[..n]);
512 }
513 }
514
515 Ok(result)
516}
517
518fn encode_codepoint_to_utf16le(cp: u32, out: &mut [u8]) -> usize {
522 if cp < 0x10000 {
523 if out.len() < 2 {
524 return 0;
525 }
526 let u = cp as u16;
527 out[..2].copy_from_slice(&u.to_le_bytes());
528 2
529 } else if cp < 0x110000 {
530 if out.len() < 4 {
531 return 0;
532 }
533 let cp = cp - 0x10000;
534 let high = 0xD800 | ((cp >> 10) as u16);
535 let low = 0xDC00 | (cp as u16 & 0x3FF);
536 out[..2].copy_from_slice(&high.to_le_bytes());
537 out[2..4].copy_from_slice(&low.to_le_bytes());
538 4
539 } else {
540 0
541 }
542}
543
544pub(crate) fn utf8_to_utf16le(data: &[u8]) -> Result<Vec<u8>, ()> {
548 let s = core::str::from_utf8(data).map_err(|_| ())?;
549 let mut result = Vec::with_capacity(data.len() * 2);
550
551 for ch in s.chars() {
552 let cp = ch as u32;
553 let mut buf = [0u8; 4];
554 let n = encode_codepoint_to_utf16le(cp, &mut buf);
555 if n == 0 {
556 return Err(());
557 }
558 result.extend_from_slice(&buf[..n]);
559 }
560
561 Ok(result)
562}
563
564#[allow(dead_code)]
573pub(crate) fn latin1_to_utf8(data: &[u8]) -> Vec<u8> {
574 let mut result = Vec::with_capacity(data.len() * 2);
575
576 for &byte in data {
577 let cp = byte as u32;
578 let mut buf = [0u8; 2];
579 let n = encode_codepoint_to_utf8(cp, &mut buf);
580 result.extend_from_slice(&buf[..n]);
581 }
582
583 result
584}
585
586pub(crate) fn utf8_to_latin1(data: &[u8]) -> Result<Vec<u8>, ()> {
591 let s = core::str::from_utf8(data).map_err(|_| ())?;
592 let mut result = Vec::with_capacity(data.len());
593
594 for ch in s.chars() {
595 let cp = ch as u32;
596 if cp > 0xFF {
597 return Err(());
598 }
599 result.push(cp as u8);
600 }
601
602 Ok(result)
603}
604
605pub(crate) fn init_encodings() {
620 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
621 return;
622 }
623 let _guard = ENCODING_INIT_MUTEX.lock();
629 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
630 return;
631 }
632 register_builtin_handlers();
633 ENCODING_INITIALIZED.store(true, Ordering::SeqCst);
634}
635
636fn register_builtin_handlers() {
638 register_handler(
640 b"UTF-8\0",
641 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
642 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
643 Some(utf8_input_func as xmlCharEncodingInputFunc),
644 Some(utf8_output_func as xmlCharEncodingOutputFunc),
645 );
646
647 register_handler(
649 b"UTF-16LE\0",
650 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
651 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
652 Some(utf16le_input_func as xmlCharEncodingInputFunc),
653 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
654 );
655
656 register_handler(
658 b"UTF-16BE\0",
659 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
660 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
661 Some(utf16be_input_func as xmlCharEncodingInputFunc),
662 Some(utf16be_output_func as xmlCharEncodingOutputFunc),
663 );
664
665 register_handler(
667 b"ISO-8859-1\0",
668 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
669 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
670 Some(latin1_input_func as xmlCharEncodingInputFunc),
671 Some(latin1_output_func as xmlCharEncodingOutputFunc),
672 );
673
674 register_handler(
679 b"US-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 register_handler(
686 b"ASCII\0",
687 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
688 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
689 Some(ascii_input_func as xmlCharEncodingInputFunc),
690 Some(ascii_output_func as xmlCharEncodingOutputFunc),
691 );
692
693 register_handler(
698 b"UTF-16\0",
699 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
700 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
701 Some(utf16le_input_func as xmlCharEncodingInputFunc),
702 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
703 );
704}
705
706fn register_handler(
708 name_bytes: &[u8],
709 _input_enc: xmlCharEncoding,
710 _output_enc: xmlCharEncoding,
711 input_func: Option<xmlCharEncodingInputFunc>,
712 output_func: Option<xmlCharEncodingOutputFunc>,
713) {
714 let name_raw =
715 unsafe { crate::abi::allocator::xmlMemStrdupImpl(name_bytes.as_ptr() as *const c_char) };
716 if name_raw.is_null() {
717 return;
718 }
719
720 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
721 as *mut _xmlCharEncodingHandler;
722
723 if handler.is_null() {
724 unsafe { xmlFreeImpl(name_raw) };
725 return;
726 }
727
728 unsafe {
729 ptr::write(
730 handler,
731 _xmlCharEncodingHandler {
732 name: name_raw as *mut c_char,
733 input: EncodingInputUnion {
734 legacyFunc: input_func,
735 },
736 output: EncodingOutputUnion {
737 legacyFunc: output_func,
738 },
739 inputCtxt: ptr::null_mut(),
740 outputCtxt: ptr::null_mut(),
741 ctxtDtor: None,
742 flags: 0,
743 },
744 );
745 }
746
747 add_encoding_handler(handler);
748}
749
750pub(crate) fn cleanup_encodings() {
754 let mut handlers = ENCODING_HANDLERS.write();
755 for &handler in handlers.iter() {
756 let ptr = handler.0;
757 if !ptr.is_null() {
758 unsafe {
759 if !(*ptr).name.is_null() {
760 xmlFreeImpl((*ptr).name as *mut c_void);
761 }
762 xmlFreeImpl(ptr as *mut c_void);
763 }
764 }
765 }
766 handlers.clear();
767 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
768}
769
770pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
776 if name.is_null() {
777 return ptr::null_mut();
778 }
779
780 init_encodings();
784
785 let name_str = unsafe {
786 match CStr::from_ptr(name as *const c_char).to_bytes() {
787 b"" => return ptr::null_mut(),
788 s => s,
789 }
790 };
791
792 let handlers = ENCODING_HANDLERS.read();
793 for &handler in handlers.iter() {
794 let ptr = handler.0;
795 if ptr.is_null() {
796 continue;
797 }
798 let h_name = unsafe {
799 if (*ptr).name.is_null() {
800 continue;
801 }
802 CStr::from_ptr((*ptr).name).to_bytes()
803 };
804
805 if name_str.eq_ignore_ascii_case(h_name) {
806 return ptr;
807 }
808 }
809
810 ptr::null_mut()
811}
812
813pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
817 if handler.is_null() {
818 return -1;
819 }
820
821 let mut handlers = ENCODING_HANDLERS.write();
822 handlers.push(HandlerPtr(handler));
823 0
824}
825
826#[allow(dead_code)]
834pub(crate) fn char_enc_in_func(
835 handler: *mut _xmlCharEncodingHandler,
836 out: &mut [u8],
837 in_data: &[u8],
838) -> c_int {
839 if handler.is_null() {
840 return -1;
841 }
842
843 let h = unsafe { &*handler };
844 let input_func = unsafe { h.input.legacyFunc };
845 let input_func = match input_func {
846 Some(f) => f,
847 None => return -1,
848 };
849
850 let mut outlen = out.len() as c_int;
851 let mut inlen = in_data.len() as c_int;
852
853 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
854}
855
856#[allow(dead_code)]
860pub(crate) fn char_enc_out_func(
861 handler: *mut _xmlCharEncodingHandler,
862 out: &mut [u8],
863 in_data: &[u8],
864) -> c_int {
865 if handler.is_null() {
866 return -1;
867 }
868
869 let h = unsafe { &*handler };
870 let output_func = unsafe { h.output.legacyFunc };
871 let output_func = match output_func {
872 Some(f) => f,
873 None => return -1,
874 };
875
876 let mut outlen = out.len() as c_int;
877 let mut inlen = in_data.len() as c_int;
878
879 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
880}
881
882pub(crate) fn char_enc_in(
889 handler: *mut _xmlCharEncodingHandler,
890 out: *mut _xmlBuffer,
891 in_: *mut _xmlBuffer,
892) -> c_int {
893 if handler.is_null() || out.is_null() || in_.is_null() {
894 return -1;
895 }
896
897 let h = unsafe { &*handler };
898 let input_func = unsafe { h.input.legacyFunc };
899 let input_func = match input_func {
900 Some(f) => f,
901 None => return -1,
902 };
903
904 let in_buf = unsafe { &*in_ };
905 let out_buf = unsafe { &mut *out };
906
907 if in_buf.content.is_null() || in_buf.use_ == 0 {
908 return 0;
909 }
910
911 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
912
913 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
915 let mut out_vec = vec![0u8; out_capacity];
916 let mut out_len = out_capacity as c_int;
917 let mut in_len = in_buf.use_ as c_int;
918
919 let ret = unsafe {
920 input_func(
921 out_vec.as_mut_ptr(),
922 &mut out_len,
923 in_data.as_ptr(),
924 &mut in_len,
925 )
926 };
927
928 if ret < 0 {
929 return -1;
930 }
931
932 let written = ret as usize;
933
934 append_to_xml_buffer(out_buf, &out_vec[..written]);
936
937 written as c_int
938}
939
940pub(crate) fn char_enc_out(
947 handler: *mut _xmlCharEncodingHandler,
948 out: *mut _xmlBuffer,
949 in_: *mut _xmlBuffer,
950) -> c_int {
951 if handler.is_null() || out.is_null() || in_.is_null() {
952 return -1;
953 }
954
955 let h = unsafe { &*handler };
956 let output_func = unsafe { h.output.legacyFunc };
957 let output_func = match output_func {
958 Some(f) => f,
959 None => return -1,
960 };
961
962 let in_buf = unsafe { &*in_ };
963 let out_buf = unsafe { &mut *out };
964
965 if in_buf.content.is_null() || in_buf.use_ == 0 {
966 return 0;
967 }
968
969 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
970
971 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
972 let mut out_vec = vec![0u8; out_capacity];
973 let mut out_len = out_capacity as c_int;
974 let mut in_len = in_buf.use_ as c_int;
975
976 let ret = unsafe {
977 output_func(
978 out_vec.as_mut_ptr(),
979 &mut out_len,
980 in_data.as_ptr(),
981 &mut in_len,
982 )
983 };
984
985 if ret < 0 {
986 return -1;
987 }
988
989 let written = ret as usize;
990
991 append_to_xml_buffer(out_buf, &out_vec[..written]);
993
994 written as c_int
995}
996
997fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
999 if data.is_empty() {
1000 return;
1001 }
1002
1003 let new_use = (buf.use_ as usize).saturating_add(data.len());
1004 if new_use > buf.size as usize {
1005 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
1007 let new_content =
1008 unsafe { xmlReallocImpl(buf.content as *mut c_void, new_size) as *mut xmlChar };
1009 if new_content.is_null() {
1010 return; }
1012 buf.content = new_content;
1013 buf.size = new_size as c_uint;
1014 }
1015
1016 unsafe {
1017 ptr::copy_nonoverlapping(
1018 data.as_ptr(),
1019 buf.content.add(buf.use_ as usize),
1020 data.len(),
1021 );
1022 }
1023 buf.use_ = new_use as c_uint;
1024}
1025
1026unsafe extern "C" fn utf8_input_func(
1036 out: *mut c_uchar,
1037 outlen: *mut c_int,
1038 in_: *const c_uchar,
1039 inlen: *mut c_int,
1040) -> c_int {
1041 let avail_out = *outlen as usize;
1042 let avail_in = *inlen as usize;
1043 let to_copy = avail_out.min(avail_in);
1044
1045 if to_copy > 0 {
1046 ptr::copy_nonoverlapping(in_, out, to_copy);
1047 }
1048
1049 *outlen = to_copy as c_int;
1050 *inlen = to_copy as c_int;
1051 to_copy as c_int
1052}
1053
1054unsafe extern "C" fn utf8_output_func(
1056 out: *mut c_uchar,
1057 outlen: *mut c_int,
1058 in_: *const c_uchar,
1059 inlen: *mut c_int,
1060) -> c_int {
1061 utf8_input_func(out, outlen, in_, inlen)
1062}
1063
1064unsafe extern "C" fn utf16le_input_func(
1068 out: *mut c_uchar,
1069 outlen: *mut c_int,
1070 in_: *const c_uchar,
1071 inlen: *mut c_int,
1072) -> c_int {
1073 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1074 return -1;
1075 }
1076
1077 let avail_in = *inlen as usize;
1078 let avail_out = *outlen as usize;
1079
1080 if avail_in == 0 || avail_out == 0 {
1081 *outlen = 0;
1082 *inlen = 0;
1083 return 0;
1084 }
1085
1086 let in_data = core::slice::from_raw_parts(in_, avail_in);
1087 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1088
1089 let result = match utf16le_to_utf8(in_data) {
1091 Ok(v) => v,
1092 Err(()) => return -1,
1093 };
1094
1095 let written = result.len().min(avail_out);
1096 if written > 0 {
1097 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1098 }
1099
1100 *outlen = written as c_int;
1101 *inlen = avail_in as c_int; written as c_int
1103}
1104
1105unsafe extern "C" fn utf16le_output_func(
1107 out: *mut c_uchar,
1108 outlen: *mut c_int,
1109 in_: *const c_uchar,
1110 inlen: *mut c_int,
1111) -> c_int {
1112 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1113 return -1;
1114 }
1115
1116 let avail_in = *inlen as usize;
1117 let avail_out = *outlen as usize;
1118
1119 if avail_in == 0 || avail_out == 0 {
1120 *outlen = 0;
1121 *inlen = 0;
1122 return 0;
1123 }
1124
1125 let in_data = core::slice::from_raw_parts(in_, avail_in);
1126 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1127
1128 let result = match utf8_to_utf16le(in_data) {
1129 Ok(v) => v,
1130 Err(()) => return -1,
1131 };
1132
1133 let written = result.len().min(avail_out);
1134 if written > 0 {
1135 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1136 }
1137
1138 *outlen = written as c_int;
1139 *inlen = avail_in as c_int;
1140 written as c_int
1141}
1142
1143unsafe extern "C" fn utf16be_input_func(
1147 out: *mut c_uchar,
1148 outlen: *mut c_int,
1149 in_: *const c_uchar,
1150 inlen: *mut c_int,
1151) -> c_int {
1152 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1153 return -1;
1154 }
1155
1156 let avail_in = *inlen as usize;
1157 let avail_out = *outlen as usize;
1158
1159 if avail_in == 0 || avail_out == 0 {
1160 *outlen = 0;
1161 *inlen = 0;
1162 return 0;
1163 }
1164
1165 let in_data = core::slice::from_raw_parts(in_, avail_in);
1166 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1167
1168 let result = match utf16be_to_utf8(in_data) {
1169 Ok(v) => v,
1170 Err(()) => return -1,
1171 };
1172
1173 let written = result.len().min(avail_out);
1174 if written > 0 {
1175 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1176 }
1177
1178 *outlen = written as c_int;
1179 *inlen = avail_in as c_int;
1180 written as c_int
1181}
1182
1183unsafe extern "C" fn utf16be_output_func(
1185 out: *mut c_uchar,
1186 outlen: *mut c_int,
1187 in_: *const c_uchar,
1188 inlen: *mut c_int,
1189) -> c_int {
1190 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1191 return -1;
1192 }
1193
1194 let avail_in = *inlen as usize;
1195 let avail_out = *outlen as usize;
1196
1197 if avail_in == 0 || avail_out == 0 {
1198 *outlen = 0;
1199 *inlen = 0;
1200 return 0;
1201 }
1202
1203 let in_data = core::slice::from_raw_parts(in_, avail_in);
1204
1205 let le_result = match utf8_to_utf16le(in_data) {
1207 Ok(v) => v,
1208 Err(()) => return -1,
1209 };
1210
1211 let mut result = le_result;
1213 for chunk in result.as_chunks_mut::<2>().0 {
1214 chunk.swap(0, 1);
1215 }
1216
1217 let written = result.len().min(avail_out);
1218 if written > 0 {
1219 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1220 }
1221
1222 *outlen = written as c_int;
1223 *inlen = avail_in as c_int;
1224 written as c_int
1225}
1226
1227unsafe extern "C" fn latin1_input_func(
1231 out: *mut c_uchar,
1232 outlen: *mut c_int,
1233 in_: *const c_uchar,
1234 inlen: *mut c_int,
1235) -> c_int {
1236 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1237 return -1;
1238 }
1239
1240 let avail_in = *inlen as usize;
1241 let avail_out = *outlen as usize;
1242
1243 if avail_in == 0 || avail_out == 0 {
1244 *outlen = 0;
1245 *inlen = 0;
1246 return 0;
1247 }
1248
1249 let in_data = core::slice::from_raw_parts(in_, avail_in);
1250 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1251
1252 let mut in_pos = 0;
1253 let mut out_pos = 0;
1254
1255 while in_pos < avail_in && out_pos < avail_out {
1256 let byte = in_data[in_pos];
1257 in_pos += 1;
1258
1259 if byte < 0x80 {
1260 if out_pos < avail_out {
1262 out_slice[out_pos] = byte;
1263 out_pos += 1;
1264 } else {
1265 break;
1266 }
1267 } else {
1268 if out_pos + 1 < avail_out {
1271 out_slice[out_pos] = 0xC2 | (byte >> 6);
1272 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1273 out_pos += 2;
1274 } else {
1275 break;
1276 }
1277 }
1278 }
1279
1280 *outlen = out_pos as c_int;
1281 *inlen = in_pos as c_int;
1282 out_pos as c_int
1283}
1284
1285unsafe extern "C" fn latin1_output_func(
1287 out: *mut c_uchar,
1288 outlen: *mut c_int,
1289 in_: *const c_uchar,
1290 inlen: *mut c_int,
1291) -> c_int {
1292 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1293 return -1;
1294 }
1295
1296 let avail_in = *inlen as usize;
1297 let avail_out = *outlen as usize;
1298
1299 if avail_in == 0 || avail_out == 0 {
1300 *outlen = 0;
1301 *inlen = 0;
1302 return 0;
1303 }
1304
1305 let in_data = core::slice::from_raw_parts(in_, avail_in);
1306 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1307
1308 let mut in_pos = 0;
1309 let mut out_pos = 0;
1310
1311 while in_pos < avail_in && out_pos < avail_out {
1312 let byte = in_data[in_pos];
1313 in_pos += 1;
1314
1315 if byte < 0x80 {
1316 out_slice[out_pos] = byte;
1318 out_pos += 1;
1319 } else if (0xC2..=0xC3).contains(&byte) {
1320 if in_pos < avail_in {
1322 let second = in_data[in_pos];
1323 in_pos += 1;
1324 if second & 0xC0 != 0x80 {
1325 return -1; }
1327 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1328 if cp > 0xFF {
1329 return -1; }
1331 out_slice[out_pos] = cp as u8;
1332 out_pos += 1;
1333 } else {
1334 return -1; }
1336 } else if (0x80..=0xBF).contains(&byte) {
1337 return -1;
1339 } else {
1340 return -1;
1343 }
1344 }
1345
1346 *outlen = out_pos as c_int;
1347 *inlen = in_pos as c_int;
1348 out_pos as c_int
1349}
1350
1351unsafe extern "C" fn ascii_input_func(
1355 out: *mut c_uchar,
1356 outlen: *mut c_int,
1357 in_: *const c_uchar,
1358 inlen: *mut c_int,
1359) -> c_int {
1360 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1361 return -1;
1362 }
1363
1364 let avail_in = *inlen as usize;
1365 let avail_out = *outlen as usize;
1366
1367 if avail_in == 0 || avail_out == 0 {
1368 *outlen = 0;
1369 *inlen = 0;
1370 return 0;
1371 }
1372
1373 let in_data = core::slice::from_raw_parts(in_, avail_in);
1374 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1375
1376 let mut pos = 0;
1377 while pos < avail_in && pos < avail_out {
1378 let byte = in_data[pos];
1379 if byte > 0x7F {
1380 return -1; }
1382 out_slice[pos] = byte;
1383 pos += 1;
1384 }
1385
1386 *outlen = pos as c_int;
1387 *inlen = pos as c_int;
1388 pos as c_int
1389}
1390
1391unsafe extern "C" fn ascii_output_func(
1393 out: *mut c_uchar,
1394 outlen: *mut c_int,
1395 in_: *const c_uchar,
1396 inlen: *mut c_int,
1397) -> c_int {
1398 ascii_input_func(out, outlen, in_, inlen)
1400}
1401
1402pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
1411 if name.is_null() {
1412 return ptr::null_mut();
1413 }
1414 find_encoding_handler(name as *const xmlChar)
1415}
1416
1417pub(crate) const fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
1421 match enc {
1425 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
1426 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE | xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => {
1427 c"UTF-16".as_ptr()
1428 }
1429 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE | xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => {
1430 c"UCS-4".as_ptr()
1431 }
1432 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"IBM037".as_ptr(),
1433 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
1434 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
1435 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
1436 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
1437 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
1438 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
1439 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
1440 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
1441 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
1442 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
1443 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
1444 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"Shift_JIS".as_ptr(),
1445 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
1446 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"US-ASCII".as_ptr(),
1448 _ => ptr::null(),
1449 }
1450}
1451
1452pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
1457 if name.is_null() {
1458 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
1459 }
1460 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
1461 encoding_from_name(bytes) as c_int
1462}
1463
1464static ENCODING_ALIASES: std::sync::OnceLock<
1472 parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>>,
1473> = std::sync::OnceLock::new();
1474
1475fn encoding_aliases() -> &'static parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>> {
1476 ENCODING_ALIASES.get_or_init(|| parking_lot::RwLock::new(std::collections::HashMap::new()))
1477}
1478
1479pub(crate) fn add_encoding_alias(name: *const c_char, alias: *const c_char) -> c_int {
1482 if name.is_null() || alias.is_null() {
1483 return -1;
1484 }
1485 let n = unsafe { CStr::from_ptr(name).to_bytes().to_vec() };
1486 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1487 encoding_aliases().write().insert(a, n);
1488 0
1489}
1490
1491pub(crate) fn del_encoding_alias(alias: *const c_char) -> c_int {
1494 if alias.is_null() {
1495 return -1;
1496 }
1497 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1498 if encoding_aliases().write().remove(&a).is_some() {
1499 0
1500 } else {
1501 -1
1502 }
1503}
1504
1505pub(crate) fn get_encoding_alias(alias: *const c_char) -> *const c_char {
1508 if alias.is_null() {
1509 return ptr::null();
1510 }
1511 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1512 let guard = encoding_aliases().read();
1513 match guard.get(&a) {
1514 Some(v) => {
1515 let leaked: &'static [u8] = Box::leak(v.clone().into_boxed_slice());
1518 leaked.as_ptr() as *const c_char
1519 }
1520 None => ptr::null(),
1521 }
1522}
1523
1524pub(crate) fn cleanup_encoding_aliases() {
1526 encoding_aliases().write().clear();
1527}
1528
1529pub(crate) fn xmlCharEncInFunc(
1533 handler: *mut _xmlCharEncodingHandler,
1534 out: *mut _xmlBuffer,
1535 in_: *mut _xmlBuffer,
1536) -> c_int {
1537 char_enc_in(handler, out, in_)
1538}
1539
1540pub(crate) fn xmlCharEncOutFunc(
1544 handler: *mut _xmlCharEncodingHandler,
1545 out: *mut _xmlBuffer,
1546 in_: *mut _xmlBuffer,
1547) -> c_int {
1548 char_enc_out(handler, out, in_)
1549}
1550
1551pub(crate) fn xmlNewCharEncodingHandler(
1557 name: *const c_char,
1558 input: xmlCharEncodingInputFunc,
1559 output: xmlCharEncodingOutputFunc,
1560) -> *mut _xmlCharEncodingHandler {
1561 if name.is_null() {
1562 return ptr::null_mut();
1563 }
1564
1565 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) };
1566 if name_raw.is_null() {
1567 return ptr::null_mut();
1568 }
1569
1570 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
1571 as *mut _xmlCharEncodingHandler;
1572
1573 if handler.is_null() {
1574 unsafe { xmlFreeImpl(name_raw) };
1575 return ptr::null_mut();
1576 }
1577
1578 unsafe {
1579 ptr::write(
1580 handler,
1581 _xmlCharEncodingHandler {
1582 name: name_raw as *mut c_char,
1583 input: EncodingInputUnion {
1584 legacyFunc: Some(input),
1585 },
1586 output: EncodingOutputUnion {
1587 legacyFunc: Some(output),
1588 },
1589 inputCtxt: ptr::null_mut(),
1590 outputCtxt: ptr::null_mut(),
1591 ctxtDtor: None,
1592 flags: 0,
1593 },
1594 );
1595 }
1596
1597 handler
1598}
1599
1600#[allow(dead_code)]
1604pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
1605 if handler.is_null() {
1606 return;
1607 }
1608
1609 {
1611 let mut handlers = ENCODING_HANDLERS.write();
1612 handlers.retain(|&h| h.0 != handler);
1613 }
1614
1615 unsafe {
1616 if !(*handler).name.is_null() {
1617 xmlFreeImpl((*handler).name as *mut c_void);
1618 }
1619 xmlFreeImpl(handler as *mut c_void);
1620 }
1621}
1622
1623pub(crate) fn xmlInitCharEncodingHandlers() {
1625 init_encodings();
1626}
1627
1628pub(crate) fn xmlCleanupCharEncodingHandlers() {
1630 cleanup_encodings();
1631}
1632
1633pub(crate) fn xmlLookupCharEncodingHandler(enc: c_int, out: *mut *mut c_void) -> c_int {
1655 if out.is_null() {
1656 return crate::abi::types::XML_ERR_ARGUMENT;
1657 }
1658 unsafe {
1659 *out = ptr::null_mut();
1660 }
1661 if enc <= 0 || enc >= 32 {
1662 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1663 }
1664 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int {
1666 return crate::abi::types::XML_ERR_OK;
1667 }
1668 let canonical: &[u8] = match enc {
1669 2 => b"UTF-16LE\0",
1671 3 => b"UTF-16BE\0",
1673 10 => b"ISO-8859-1\0",
1675 22 => b"US-ASCII\0",
1677 23 => b"UTF-16\0",
1679 _ => return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING,
1680 };
1681 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1682 if h.is_null() {
1683 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1684 }
1685 unsafe {
1686 *out = h as *mut c_void;
1687 }
1688 crate::abi::types::XML_ERR_OK
1689}
1690
1691pub(crate) fn xmlGetCharEncodingHandler(enc: c_int) -> *mut c_void {
1693 let mut ret: *mut c_void = ptr::null_mut();
1694 let _rc = xmlLookupCharEncodingHandler(enc, &mut ret);
1695 ret
1696}
1697
1698pub(crate) fn xmlCreateCharEncodingHandler(
1705 name: *const c_char,
1706 flags: c_int,
1707 impl_: Option<xmlCharEncConvImpl>,
1708 implCtxt: *mut c_void,
1709 out: *mut *mut c_void,
1710) -> c_int {
1711 if out.is_null() {
1712 return crate::abi::types::XML_ERR_ARGUMENT;
1713 }
1714 unsafe {
1715 *out = ptr::null_mut();
1716 }
1717 if name.is_null() || flags == 0 {
1718 return crate::abi::types::XML_ERR_ARGUMENT;
1719 }
1720 let norig = unsafe { CStr::from_ptr(name).to_bytes() };
1721
1722 let mut eff: &[u8] = norig;
1724 let alias = get_encoding_alias(name);
1725 if !alias.is_null() {
1726 eff = unsafe { CStr::from_ptr(alias).to_bytes() };
1727 }
1728
1729 let enc = encoding_from_name(eff);
1730
1731 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
1733 return crate::abi::types::XML_ERR_OK;
1734 }
1735
1736 let canonical: &[u8] = match enc {
1737 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => b"UTF-16LE\0",
1738 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => b"UTF-16BE\0",
1739 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => b"ISO-8859-1\0",
1740 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => b"US-ASCII\0",
1741 _ => {
1742 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1743 }
1744 };
1745 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1746 if h.is_null() {
1747 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1748 }
1749 unsafe {
1750 let src = &*h;
1751 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1752 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1753 if !has_in || !has_out {
1754 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1755 }
1756 let copy =
1761 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler;
1762 if copy.is_null() {
1763 return crate::abi::types::XML_ERR_NO_MEMORY;
1764 }
1765 let name_copy = crate::abi::allocator::xmlMemStrdupImpl(name) as *mut c_char;
1766 if name_copy.is_null() {
1767 xmlFreeImpl(copy as *mut c_void);
1768 return crate::abi::types::XML_ERR_NO_MEMORY;
1769 }
1770 ptr::write(
1771 copy,
1772 _xmlCharEncodingHandler {
1773 name: name_copy,
1774 input: EncodingInputUnion {
1775 legacyFunc: src.input.legacyFunc,
1776 },
1777 output: EncodingOutputUnion {
1778 legacyFunc: src.output.legacyFunc,
1779 },
1780 inputCtxt: src.inputCtxt,
1781 outputCtxt: src.outputCtxt,
1782 ctxtDtor: src.ctxtDtor,
1783 flags: src.flags,
1784 },
1785 );
1786 *out = copy as *mut c_void;
1787 }
1788 crate::abi::types::XML_ERR_OK
1789}
1790
1791fn find_extra_handler(
1797 norig: &[u8],
1798 name: &[u8],
1799 flags: c_int,
1800 impl_: Option<xmlCharEncConvImpl>,
1801 implCtxt: *mut c_void,
1802 out: *mut *mut c_void,
1803) -> c_int {
1804 if let Some(f) = impl_ {
1806 let mut n = norig.to_vec();
1807 n.push(0);
1808 let rc = unsafe {
1809 f(
1810 implCtxt,
1811 n.as_ptr() as *const c_char,
1812 flags,
1813 out as *mut *mut crate::abi::structs::_xmlCharEncodingHandler,
1814 )
1815 };
1816 return rc;
1817 }
1818 let mut n = name.to_vec();
1820 n.push(0);
1821 let h = find_encoding_handler(n.as_ptr() as *const xmlChar);
1822 if !h.is_null() {
1823 unsafe {
1824 let src = &*h;
1825 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1826 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1827 if has_in && has_out {
1828 *out = h as *mut c_void;
1829 return crate::abi::types::XML_ERR_OK;
1830 }
1831 }
1832 }
1833 crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING
1834}
1835
1836pub(crate) fn xmlOpenCharEncodingHandler(
1838 name: *const c_char,
1839 output: c_int,
1840 out: *mut *mut c_void,
1841) -> c_int {
1842 let flags: c_int = if output != 0 { 2 } else { 1 };
1844 xmlCreateCharEncodingHandler(name, flags, None, ptr::null_mut(), out)
1845}
1846
1847pub(crate) fn xmlCharEncNewCustomHandler(
1853 name: *const c_char,
1854 input: xmlCharEncConvFunc,
1855 output: xmlCharEncConvFunc,
1856 ctxtDtor: Option<xmlCharEncConvCtxtDtor>,
1857 inputCtxt: *mut c_void,
1858 outputCtxt: *mut c_void,
1859 out: *mut *mut c_void,
1860) -> c_int {
1861 if out.is_null() {
1862 return crate::abi::types::XML_ERR_ARGUMENT;
1863 }
1864 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
1865 as *mut _xmlCharEncodingHandler;
1866 if handler.is_null() {
1867 unsafe {
1868 if let Some(d) = ctxtDtor {
1869 if !inputCtxt.is_null() {
1870 d(inputCtxt);
1871 }
1872 if !outputCtxt.is_null() {
1873 d(outputCtxt);
1874 }
1875 }
1876 }
1877 return crate::abi::types::XML_ERR_NO_MEMORY;
1878 }
1879 let name_copy = if name.is_null() {
1880 ptr::null_mut()
1881 } else {
1882 let nc = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) } as *mut c_char;
1883 if nc.is_null() {
1884 unsafe { xmlFreeImpl(handler as *mut c_void) };
1885 unsafe {
1886 if let Some(d) = ctxtDtor {
1887 if !inputCtxt.is_null() {
1888 d(inputCtxt);
1889 }
1890 if !outputCtxt.is_null() {
1891 d(outputCtxt);
1892 }
1893 }
1894 }
1895 return crate::abi::types::XML_ERR_NO_MEMORY;
1896 }
1897 nc
1898 };
1899 unsafe {
1900 ptr::write(
1901 handler,
1902 _xmlCharEncodingHandler {
1903 name: name_copy,
1904 input: EncodingInputUnion { func: Some(input) },
1905 output: EncodingOutputUnion { func: Some(output) },
1906 inputCtxt,
1907 outputCtxt,
1908 ctxtDtor,
1909 flags: 0,
1910 },
1911 );
1912 *out = handler as *mut c_void;
1913 }
1914 crate::abi::types::XML_ERR_OK
1915}
1916
1917#[cfg(test)]
1922mod tests {
1923 use super::*;
1924
1925 #[test]
1928 fn test_detect_bom_utf8() {
1929 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
1930 assert_eq!(
1931 detect_encoding_from_bom(&data),
1932 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1933 );
1934 }
1935
1936 #[test]
1937 fn test_detect_bom_utf16le() {
1938 let data = [0xFF, 0xFE, 0x00, 0x01];
1939 assert_eq!(
1940 detect_encoding_from_bom(&data),
1941 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1942 );
1943 }
1944
1945 #[test]
1946 fn test_detect_bom_utf16be() {
1947 let data = [0xFE, 0xFF, 0x00, 0x01];
1948 assert_eq!(
1949 detect_encoding_from_bom(&data),
1950 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1951 );
1952 }
1953
1954 #[test]
1955 fn test_detect_bom_none() {
1956 let data = b"<xml>";
1957 assert_eq!(
1958 detect_encoding_from_bom(data),
1959 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1960 );
1961 }
1962
1963 #[test]
1964 fn test_detect_bom_empty() {
1965 assert_eq!(
1966 detect_encoding_from_bom(b""),
1967 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1968 );
1969 }
1970
1971 #[test]
1974 fn test_detect_encoding_declaration_utf8() {
1975 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
1976 let result = detect_encoding_from_declaration(data);
1977 assert_eq!(result, Some(b"utf-8".to_vec()));
1978 }
1979
1980 #[test]
1981 fn test_detect_encoding_declaration_iso() {
1982 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
1983 let result = detect_encoding_from_declaration(data);
1984 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
1985 }
1986
1987 #[test]
1988 fn test_detect_encoding_declaration_none() {
1989 let data = b"<?xml version=\"1.0\"?>";
1990 let result = detect_encoding_from_declaration(data);
1991 assert!(result.is_none());
1992 }
1993
1994 #[test]
1995 fn test_detect_encoding_declaration_no_xml() {
1996 let data = b"<root>";
1997 let result = detect_encoding_from_declaration(data);
1998 assert!(result.is_none());
1999 }
2000
2001 #[test]
2002 fn test_detect_encoding_declaration_with_bom() {
2003 let mut data = vec![0xEF, 0xBB, 0xBF];
2004 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
2005 let result = detect_encoding_from_declaration(&data);
2006 assert_eq!(result, Some(b"utf-8".to_vec()));
2007 }
2008
2009 #[test]
2012 fn test_encoding_from_name_utf8() {
2013 assert_eq!(
2014 encoding_from_name(b"UTF-8"),
2015 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2016 );
2017 assert_eq!(
2018 encoding_from_name(b"utf8"),
2019 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2020 );
2021 }
2022
2023 #[test]
2024 fn test_encoding_from_name_utf16() {
2025 assert_eq!(
2026 encoding_from_name(b"UTF-16LE"),
2027 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2028 );
2029 assert_eq!(
2030 encoding_from_name(b"UTF-16BE"),
2031 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2032 );
2033 assert_eq!(
2034 encoding_from_name(b"utf-16"),
2035 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2036 );
2037 }
2038
2039 #[test]
2040 fn test_encoding_from_name_latin1() {
2041 assert_eq!(
2042 encoding_from_name(b"ISO-8859-1"),
2043 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2044 );
2045 assert_eq!(
2046 encoding_from_name(b"Latin1"),
2047 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2048 );
2049 }
2050
2051 #[test]
2052 fn test_encoding_from_name_ascii() {
2053 assert_eq!(
2054 encoding_from_name(b"ASCII"),
2055 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2056 );
2057 assert_eq!(
2058 encoding_from_name(b"US-ASCII"),
2059 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2060 );
2061 }
2062
2063 #[test]
2064 fn test_encoding_from_name_error() {
2065 assert_eq!(
2066 encoding_from_name(b"invalid-encoding"),
2067 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2068 );
2069 }
2070
2071 #[test]
2072 fn test_encoding_from_name_empty() {
2073 assert_eq!(
2074 encoding_from_name(b""),
2075 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2076 );
2077 }
2078
2079 #[test]
2082 fn test_encoding_name_utf8() {
2083 assert_eq!(
2084 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
2085 Some(b"UTF-8" as &[u8])
2086 );
2087 }
2088
2089 #[test]
2090 fn test_encoding_name_utf16le() {
2091 assert_eq!(
2092 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
2093 Some(b"UTF-16LE" as &[u8])
2094 );
2095 }
2096
2097 #[test]
2098 fn test_encoding_name_none() {
2099 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
2100 }
2101
2102 #[test]
2103 fn test_encoding_name_error() {
2104 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
2105 }
2106
2107 #[test]
2110 fn test_utf8_valid_ascii() {
2111 assert!(utf8_valid(b"hello world"));
2112 }
2113
2114 #[test]
2115 fn test_utf8_valid_multi_byte() {
2116 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
2117 }
2118
2119 #[test]
2120 fn test_utf8_valid_empty() {
2121 assert!(utf8_valid(b""));
2122 }
2123
2124 #[test]
2125 fn test_utf8_invalid() {
2126 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
2127 }
2128
2129 #[test]
2132 fn test_valid_xml_chars() {
2133 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));
2139 assert!(is_valid_xml_char(0xE000));
2140 assert!(is_valid_xml_char(0xFFFD));
2141 assert!(is_valid_xml_char(0x10000));
2142 assert!(is_valid_xml_char(0x10FFFF));
2143 }
2144
2145 #[test]
2146 fn test_invalid_xml_chars() {
2147 assert!(!is_valid_xml_char(0x00));
2148 assert!(!is_valid_xml_char(0x08));
2149 assert!(!is_valid_xml_char(0x0B));
2150 assert!(!is_valid_xml_char(0x0C));
2151 assert!(!is_valid_xml_char(0x0E));
2152 assert!(!is_valid_xml_char(0x1F));
2153 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
2156 assert!(!is_valid_xml_char(0xFFFF));
2157 assert!(!is_valid_xml_char(0x110000));
2158 }
2159
2160 #[test]
2163 fn test_utf16le_to_utf8_ascii() {
2164 let data = [b'A', 0x00, b'B', 0x00];
2166 let result = utf16le_to_utf8(&data).unwrap();
2167 assert_eq!(result, b"AB");
2168 }
2169
2170 #[test]
2171 fn test_utf16le_to_utf8_bom() {
2172 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
2174 let result = utf16le_to_utf8(&data).unwrap();
2175 assert_eq!(result, b"AB");
2176 }
2177
2178 #[test]
2179 fn test_utf16le_to_utf8_bmp() {
2180 let data = [0xE9, 0x00];
2182 let result = utf16le_to_utf8(&data).unwrap();
2183 assert_eq!(result, "é".as_bytes());
2184 }
2185
2186 #[test]
2187 fn test_utf16le_to_utf8_supplementary() {
2188 let data = [0x3D, 0xD8, 0x00, 0xDE];
2190 let result = utf16le_to_utf8(&data).unwrap();
2191 assert_eq!(result, "😀".as_bytes());
2192 }
2193
2194 #[test]
2195 fn test_utf16le_to_utf8_unpaired_surrogate() {
2196 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
2198 }
2199
2200 #[test]
2201 fn test_utf16le_to_utf8_truncated() {
2202 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
2204 }
2205
2206 #[test]
2207 fn test_utf16le_to_utf8_empty() {
2208 let result = utf16le_to_utf8(b"").unwrap();
2209 assert!(result.is_empty());
2210 }
2211
2212 #[test]
2215 fn test_utf16be_to_utf8_ascii() {
2216 let data = [0x00, b'A', 0x00, b'B'];
2217 let result = utf16be_to_utf8(&data).unwrap();
2218 assert_eq!(result, b"AB");
2219 }
2220
2221 #[test]
2222 fn test_utf16be_to_utf8_bom() {
2223 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
2225 let result = utf16be_to_utf8(&data).unwrap();
2226 assert_eq!(result, b"AB");
2227 }
2228
2229 #[test]
2230 fn test_utf16be_to_utf8_supplementary() {
2231 let data = [0xD8, 0x3D, 0xDE, 0x00];
2233 let result = utf16be_to_utf8(&data).unwrap();
2234 assert_eq!(result, "😀".as_bytes());
2235 }
2236
2237 #[test]
2238 fn test_utf16be_to_utf8_empty() {
2239 let result = utf16be_to_utf8(b"").unwrap();
2240 assert!(result.is_empty());
2241 }
2242
2243 #[test]
2246 fn test_utf8_to_utf16le_ascii() {
2247 let result = utf8_to_utf16le(b"AB").unwrap();
2248 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
2249 }
2250
2251 #[test]
2252 fn test_utf8_to_utf16le_bmp() {
2253 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
2254 assert_eq!(result, [0xE9, 0x00]);
2255 }
2256
2257 #[test]
2258 fn test_utf8_to_utf16le_supplementary() {
2259 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
2260 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
2261 }
2262
2263 #[test]
2264 fn test_utf8_to_utf16le_invalid_utf8() {
2265 assert!(utf8_to_utf16le(&[0xFF]).is_err());
2266 }
2267
2268 #[test]
2269 fn test_utf8_to_utf16le_empty() {
2270 let result = utf8_to_utf16le(b"").unwrap();
2271 assert!(result.is_empty());
2272 }
2273
2274 #[test]
2277 fn test_latin1_to_utf8_ascii() {
2278 let result = latin1_to_utf8(b"ABC");
2279 assert_eq!(result, b"ABC");
2280 }
2281
2282 #[test]
2283 fn test_latin1_to_utf8_accented() {
2284 let result = latin1_to_utf8(&[0xE9]);
2286 assert_eq!(result, "é".as_bytes());
2287 }
2288
2289 #[test]
2290 fn test_latin1_to_utf8_all_255() {
2291 let result = latin1_to_utf8(&[0xFF]);
2292 assert_eq!(result, [0xC3, 0xBF]);
2294 }
2295
2296 #[test]
2297 fn test_latin1_to_utf8_empty() {
2298 let result = latin1_to_utf8(b"");
2299 assert!(result.is_empty());
2300 }
2301
2302 #[test]
2303 fn test_latin1_to_utf8_mixed() {
2304 let result = latin1_to_utf8(b"caf\xE9");
2305 assert_eq!(result, "café".as_bytes());
2306 }
2307
2308 #[test]
2311 fn test_utf8_to_latin1_ascii() {
2312 let result = utf8_to_latin1(b"ABC").unwrap();
2313 assert_eq!(result, b"ABC");
2314 }
2315
2316 #[test]
2317 fn test_utf8_to_latin1_accented() {
2318 let result = utf8_to_latin1("é".as_bytes()).unwrap();
2319 assert_eq!(result, [0xE9]);
2320 }
2321
2322 #[test]
2323 fn test_utf8_to_latin1_out_of_range() {
2324 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
2326
2327 #[test]
2328 fn test_utf8_to_latin1_invalid_utf8() {
2329 assert!(utf8_to_latin1(&[0xFF]).is_err());
2330 }
2331
2332 #[test]
2333 fn test_utf8_to_latin1_empty() {
2334 let result = utf8_to_latin1(b"").unwrap();
2335 assert!(result.is_empty());
2336 }
2337
2338 #[test]
2341 fn test_init_and_find_encodings() {
2342 init_encodings();
2343
2344 let utf8_name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2345 assert!(!find_encoding_handler(utf8_name).is_null());
2346
2347 let utf16le_name: *const xmlChar = c"UTF-16LE".as_ptr() as *const xmlChar;
2348 assert!(!find_encoding_handler(utf16le_name).is_null());
2349
2350 let utf16be_name: *const xmlChar = c"UTF-16BE".as_ptr() as *const xmlChar;
2351 assert!(!find_encoding_handler(utf16be_name).is_null());
2352
2353 let latin1_name: *const xmlChar = c"ISO-8859-1".as_ptr() as *const xmlChar;
2354 assert!(!find_encoding_handler(latin1_name).is_null());
2355
2356 let ascii_name: *const xmlChar = c"ASCII".as_ptr() as *const xmlChar;
2357 assert!(!find_encoding_handler(ascii_name).is_null());
2358
2359 let lower_name: *const xmlChar = c"utf-8".as_ptr() as *const xmlChar;
2361 assert!(!find_encoding_handler(lower_name).is_null());
2362 }
2363
2364 #[test]
2365 fn test_find_encoding_handler_not_found() {
2366 let name: *const xmlChar = c"NONEXISTENT".as_ptr() as *const xmlChar;
2367 assert!(find_encoding_handler(name).is_null());
2368 }
2369
2370 #[test]
2371 fn test_find_encoding_handler_null() {
2372 assert!(find_encoding_handler(ptr::null()).is_null());
2373 }
2374
2375 #[test]
2376 fn test_add_encoding_handler() {
2377 let handler = unsafe {
2378 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
2379 };
2380 assert!(!handler.is_null());
2381
2382 let name = unsafe {
2383 crate::abi::allocator::xmlMemStrdupImpl(c"TEST-ENC".as_ptr() as *const c_char)
2384 };
2385 unsafe {
2386 ptr::write(
2387 handler,
2388 _xmlCharEncodingHandler {
2389 name: name as *mut c_char,
2390 input: EncodingInputUnion { legacyFunc: None },
2391 output: EncodingOutputUnion { legacyFunc: None },
2392 inputCtxt: ptr::null_mut(),
2393 outputCtxt: ptr::null_mut(),
2394 ctxtDtor: None,
2395 flags: 0,
2396 },
2397 );
2398 }
2399
2400 assert_eq!(add_encoding_handler(handler), 0);
2401
2402 let found = find_encoding_handler(c"TEST-ENC".as_ptr() as *const xmlChar);
2403 assert_eq!(found, handler);
2404
2405 {
2407 let mut handlers = ENCODING_HANDLERS.write();
2408 handlers.retain(|&h| h.0 != handler);
2409 }
2410
2411 unsafe {
2412 xmlFreeImpl(name as *mut c_void);
2413 xmlFreeImpl(handler as *mut c_void);
2414 }
2415 }
2416
2417 #[test]
2420 fn test_utf16le_roundtrip() {
2421 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
2422 let utf16 = utf8_to_utf16le(original).unwrap();
2423 let back = utf16le_to_utf8(&utf16).unwrap();
2424 assert_eq!(original.to_vec(), back);
2425 }
2426
2427 #[test]
2428 fn test_utf16be_roundtrip() {
2429 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
2430 let utf16le = utf8_to_utf16le(original).unwrap();
2431 let mut utf16be = utf16le.clone();
2433 for chunk in utf16be.as_chunks_mut::<2>().0 {
2434 chunk.swap(0, 1);
2435 }
2436 let back = utf16be_to_utf8(&utf16be).unwrap();
2437 assert_eq!(original.to_vec(), back);
2438 }
2439
2440 #[test]
2441 fn test_latin1_roundtrip() {
2442 let original: Vec<u8> = (0x00..=0xFF).collect();
2443 let utf8 = latin1_to_utf8(&original);
2444 let back = utf8_to_latin1(&utf8).unwrap();
2445 assert_eq!(original, back);
2446 }
2447
2448 #[test]
2451 fn test_utf8_handler_identity() {
2452 let input = b"Hello, UTF-8!";
2453 let mut output = [0u8; 64];
2454 let mut outlen = output.len() as c_int;
2455 let mut inlen = input.len() as c_int;
2456
2457 let ret = unsafe {
2458 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
2459 };
2460
2461 assert_eq!(ret, input.len() as c_int);
2462 assert_eq!(&output[..ret as usize], input);
2463 assert_eq!(inlen, input.len() as c_int);
2464 }
2465
2466 #[test]
2467 fn test_utf16le_handler_roundtrip() {
2468 init_encodings();
2469
2470 let original = b"Hello UTF-16LE!";
2471 let mut utf16_buf = [0u8; 128];
2472 let mut outlen = utf16_buf.len() as c_int;
2473 let mut inlen = original.len() as c_int;
2474
2475 let written = unsafe {
2476 utf16le_output_func(
2477 utf16_buf.as_mut_ptr(),
2478 &mut outlen,
2479 original.as_ptr(),
2480 &mut inlen,
2481 )
2482 };
2483 assert!(written > 0);
2484
2485 let mut decoded = [0u8; 128];
2487 let mut outlen2 = decoded.len() as c_int;
2488 let mut inlen2 = written;
2489
2490 let written2 = unsafe {
2491 utf16le_input_func(
2492 decoded.as_mut_ptr(),
2493 &mut outlen2,
2494 utf16_buf.as_ptr(),
2495 &mut inlen2,
2496 )
2497 };
2498 assert_eq!(written2 as usize, original.len());
2499 assert_eq!(&decoded[..written2 as usize], original);
2500 }
2501
2502 #[test]
2505 fn test_append_to_xml_buffer() {
2506 unsafe {
2507 let content = xmlMallocImpl(64) as *mut xmlChar;
2508 assert!(!content.is_null());
2509
2510 let mut buf = _xmlBuffer {
2511 content,
2512 use_: 0,
2513 size: 64,
2514 alloc: 0,
2515 contentIO: ptr::null_mut(),
2516 };
2517
2518 append_to_xml_buffer(&mut buf, b"Hello");
2519 assert_eq!(buf.use_, 5);
2520 let slice = core::slice::from_raw_parts(buf.content, 5);
2521 assert_eq!(slice, b"Hello");
2522
2523 append_to_xml_buffer(&mut buf, b" World");
2524 assert_eq!(buf.use_, 11);
2525 let slice = core::slice::from_raw_parts(buf.content, 11);
2526 assert_eq!(slice, b"Hello World");
2527
2528 xmlFreeImpl(buf.content as *mut c_void);
2529 }
2530 }
2531
2532 #[test]
2535 fn test_xml_parse_char_encoding() {
2536 let name = c"UTF-8".as_ptr() as *const c_char;
2537 assert_eq!(
2538 xmlParseCharEncoding(name),
2539 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
2540 );
2541
2542 let name = c"ISO-8859-1".as_ptr() as *const c_char;
2543 assert_eq!(
2544 xmlParseCharEncoding(name),
2545 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
2546 );
2547
2548 assert_eq!(
2549 xmlParseCharEncoding(ptr::null()),
2550 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
2551 );
2552 }
2553
2554 #[test]
2555 fn test_xml_new_and_del_encoding_handler() {
2556 let name = c"TestEnc".as_ptr() as *const c_char;
2557 let handler = xmlNewCharEncodingHandler(
2558 name,
2559 utf8_input_func as xmlCharEncodingInputFunc,
2560 utf8_output_func as xmlCharEncodingOutputFunc,
2561 );
2562 assert!(!handler.is_null());
2563
2564 unsafe {
2565 assert!(!(*handler).name.is_null());
2566 let cstr = CStr::from_ptr((*handler).name);
2567 assert_eq!(cstr.to_bytes(), b"TestEnc");
2568 }
2569
2570 xmlDelEncodingHandler(handler);
2571 }
2572
2573 #[test]
2574 fn test_xml_init_and_cleanup() {
2575 xmlInitCharEncodingHandlers();
2576
2577 let name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2578 assert!(!find_encoding_handler(name).is_null());
2579
2580 xmlCleanupCharEncodingHandlers();
2581 }
2583}