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(
680 b"windows-1252\0",
681 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
682 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
683 Some(cp1252_input_func as xmlCharEncodingInputFunc),
684 Some(cp1252_output_func as xmlCharEncodingOutputFunc),
685 );
686 register_handler(
687 b"cp1252\0",
688 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
689 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
690 Some(cp1252_input_func as xmlCharEncodingInputFunc),
691 Some(cp1252_output_func as xmlCharEncodingOutputFunc),
692 );
693
694 register_handler(
699 b"US-ASCII\0",
700 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
701 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
702 Some(ascii_input_func as xmlCharEncodingInputFunc),
703 Some(ascii_output_func as xmlCharEncodingOutputFunc),
704 );
705 register_handler(
706 b"ASCII\0",
707 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
708 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
709 Some(ascii_input_func as xmlCharEncodingInputFunc),
710 Some(ascii_output_func as xmlCharEncodingOutputFunc),
711 );
712
713 register_handler(
718 b"UTF-16\0",
719 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
720 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
721 Some(utf16le_input_func as xmlCharEncodingInputFunc),
722 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
723 );
724}
725
726fn register_handler(
736 name_bytes: &[u8],
737 _input_enc: xmlCharEncoding,
738 _output_enc: xmlCharEncoding,
739 input_func: Option<xmlCharEncodingInputFunc>,
740 output_func: Option<xmlCharEncodingOutputFunc>,
741) {
742 let name_raw =
743 unsafe { crate::abi::allocator::xmlMemStrdupImpl(name_bytes.as_ptr() as *const c_char) };
744 if name_raw.is_null() {
745 return;
746 }
747
748 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
749 as *mut _xmlCharEncodingHandler;
750
751 if handler.is_null() {
752 unsafe { xmlFreeImpl(name_raw) };
753 return;
754 }
755
756 unsafe {
757 ptr::write(
758 handler,
759 _xmlCharEncodingHandler {
760 name: name_raw as *mut c_char,
761 input: EncodingInputUnion {
762 legacyFunc: input_func,
763 },
764 output: EncodingOutputUnion {
765 legacyFunc: output_func,
766 },
767 inputCtxt: ptr::null_mut(),
768 outputCtxt: ptr::null_mut(),
769 ctxtDtor: None,
770 flags: 0,
771 },
772 );
773 }
774
775 add_encoding_handler(handler);
776}
777
778pub(crate) fn cleanup_encodings() {
789 let mut handlers = ENCODING_HANDLERS.write();
790 for &handler in handlers.iter() {
791 let ptr = handler.0;
792 if !ptr.is_null() {
793 unsafe {
794 if !(*ptr).name.is_null() {
795 xmlFreeImpl((*ptr).name as *mut c_void);
796 }
797 xmlFreeImpl(ptr as *mut c_void);
798 }
799 }
800 }
801 handlers.clear();
802 drop(handlers);
807 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
808}
809
810pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
822 if name.is_null() {
823 return ptr::null_mut();
824 }
825
826 init_encodings();
830
831 let name_str = unsafe {
832 match CStr::from_ptr(name as *const c_char).to_bytes() {
833 b"" => return ptr::null_mut(),
834 s => s,
835 }
836 };
837
838 let handlers = ENCODING_HANDLERS.read();
839 for &handler in handlers.iter() {
840 let ptr = handler.0;
841 if ptr.is_null() {
842 continue;
843 }
844 let h_name = unsafe {
845 if (*ptr).name.is_null() {
846 continue;
847 }
848 CStr::from_ptr((*ptr).name).to_bytes()
849 };
850
851 if name_str.eq_ignore_ascii_case(h_name) {
852 return ptr;
853 }
854 }
855
856 ptr::null_mut()
857}
858
859pub(crate) fn clone_encoding_handler_for_find(
877 src: *mut _xmlCharEncodingHandler,
878) -> *mut _xmlCharEncodingHandler {
879 if src.is_null() {
880 return ptr::null_mut();
881 }
882 let name_raw = unsafe {
883 let nm = (*src).name;
884 if nm.is_null() {
885 ptr::null_mut()
886 } else {
887 crate::abi::allocator::xmlMemStrdupImpl(nm)
888 }
889 };
890 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
891 as *mut _xmlCharEncodingHandler;
892 if handler.is_null() {
893 if !name_raw.is_null() {
894 unsafe { crate::abi::allocator::xmlFreeImpl(name_raw) };
895 }
896 return ptr::null_mut();
897 }
898 unsafe {
899 ptr::write(
900 handler,
901 _xmlCharEncodingHandler {
902 name: name_raw as *mut c_char,
903 input: ptr::read(&(*src).input),
904 output: ptr::read(&(*src).output),
905 inputCtxt: (*src).inputCtxt,
906 outputCtxt: (*src).outputCtxt,
907 ctxtDtor: (*src).ctxtDtor,
908 flags: (*src).flags,
909 },
910 );
911 }
912 handler
913}
914
915pub(crate) const XML_HANDLER_STATIC: c_int = 0x01;
919
920pub(crate) fn xmlFindCharEncodingHandler_owned(
937 name: *const xmlChar,
938) -> *mut _xmlCharEncodingHandler {
939 if name.is_null() {
940 return ptr::null_mut();
941 }
942 let name_bytes = unsafe {
943 let len = libc::strlen(name as *const c_char);
944 core::slice::from_raw_parts(name as *const u8, len)
945 };
946
947 if encoding_from_name(name_bytes) == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
949 let utf8 = find_encoding_handler(c"UTF-8".as_ptr() as *const xmlChar);
950 if utf8.is_null() {
951 return ptr::null_mut();
952 }
953 unsafe {
955 (*utf8).flags |= XML_HANDLER_STATIC;
956 }
957 return utf8;
958 }
959
960 let mut entry = find_encoding_handler(name as *const xmlChar);
964 if entry.is_null() {
965 if let Some(canon) = encoding_name(encoding_from_name(name_bytes)) {
966 entry = find_encoding_handler(canon.as_ptr() as *const xmlChar);
967 }
968 }
969 clone_encoding_handler_for_find(entry)
972}
973
974pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
978 if handler.is_null() {
979 return -1;
980 }
981
982 let mut handlers = ENCODING_HANDLERS.write();
983 handlers.push(HandlerPtr(handler));
984 0
985}
986
987#[allow(dead_code)]
1003pub(crate) fn char_enc_in_func(
1004 handler: *mut _xmlCharEncodingHandler,
1005 out: &mut [u8],
1006 in_data: &[u8],
1007) -> c_int {
1008 if handler.is_null() {
1009 return -1;
1010 }
1011
1012 let h = unsafe { &*handler };
1013 let input_func = unsafe { h.input.legacyFunc };
1014 let input_func = match input_func {
1015 Some(f) => f,
1016 None => return -1,
1017 };
1018
1019 let mut outlen = out.len() as c_int;
1020 let mut inlen = in_data.len() as c_int;
1021
1022 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
1023}
1024
1025#[allow(dead_code)]
1037pub(crate) fn char_enc_out_func(
1038 handler: *mut _xmlCharEncodingHandler,
1039 out: &mut [u8],
1040 in_data: &[u8],
1041) -> c_int {
1042 if handler.is_null() {
1043 return -1;
1044 }
1045
1046 let h = unsafe { &*handler };
1047 let output_func = unsafe { h.output.legacyFunc };
1048 let output_func = match output_func {
1049 Some(f) => f,
1050 None => return -1,
1051 };
1052
1053 let mut outlen = out.len() as c_int;
1054 let mut inlen = in_data.len() as c_int;
1055
1056 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
1057}
1058
1059pub(crate) fn char_enc_in(
1075 handler: *mut _xmlCharEncodingHandler,
1076 out: *mut _xmlBuffer,
1077 in_: *mut _xmlBuffer,
1078) -> c_int {
1079 if handler.is_null() || out.is_null() || in_.is_null() {
1080 return -1;
1081 }
1082
1083 let h = unsafe { &*handler };
1084 let input_func = unsafe { h.input.legacyFunc };
1085 let input_func = match input_func {
1086 Some(f) => f,
1087 None => return -1,
1088 };
1089
1090 let in_buf = unsafe { &*in_ };
1091 let out_buf = unsafe { &mut *out };
1092
1093 if in_buf.content.is_null() || in_buf.use_ == 0 {
1094 return 0;
1095 }
1096
1097 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
1098
1099 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
1101 let mut out_vec = vec![0u8; out_capacity];
1102 let mut out_len = out_capacity as c_int;
1103 let mut in_len = in_buf.use_ as c_int;
1104
1105 let ret = unsafe {
1106 input_func(
1107 out_vec.as_mut_ptr(),
1108 &mut out_len,
1109 in_data.as_ptr(),
1110 &mut in_len,
1111 )
1112 };
1113
1114 if ret < 0 {
1115 return -1;
1116 }
1117
1118 let written = ret as usize;
1119
1120 append_to_xml_buffer(out_buf, &out_vec[..written]);
1122
1123 written as c_int
1124}
1125
1126pub(crate) fn char_enc_out(
1142 handler: *mut _xmlCharEncodingHandler,
1143 out: *mut _xmlBuffer,
1144 in_: *mut _xmlBuffer,
1145) -> c_int {
1146 if handler.is_null() || out.is_null() || in_.is_null() {
1147 return -1;
1148 }
1149
1150 let h = unsafe { &*handler };
1151 let output_func = unsafe { h.output.legacyFunc };
1152 let output_func = match output_func {
1153 Some(f) => f,
1154 None => return -1,
1155 };
1156
1157 let in_buf = unsafe { &*in_ };
1158 let out_buf = unsafe { &mut *out };
1159
1160 if in_buf.content.is_null() || in_buf.use_ == 0 {
1161 return 0;
1162 }
1163
1164 let mut in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
1165
1166 const ENC_INPUT_ERROR: c_int = -2;
1175 let mut total_written: usize = 0;
1176 loop {
1177 let out_capacity = (in_data.len().saturating_mul(3)).max(64) + 16;
1178 let mut out_vec = vec![0u8; out_capacity];
1179 let mut out_len = out_capacity as c_int;
1180 let mut in_len = in_data.len() as c_int;
1181 let ret = unsafe {
1182 output_func(
1183 out_vec.as_mut_ptr(),
1184 &mut out_len,
1185 in_data.as_ptr(),
1186 &mut in_len,
1187 )
1188 };
1189 let written = out_len.max(0) as usize;
1190 if written > 0 {
1191 append_to_xml_buffer(out_buf, &out_vec[..written]);
1192 total_written += written;
1193 }
1194 let consumed = in_len.max(0) as usize;
1195 if ret == ENC_INPUT_ERROR && consumed < in_data.len() {
1196 let mut clen: c_int = 4;
1199 let cp = unsafe {
1200 crate::abi::exports_misc::xmlGetUTF8Char(in_data[consumed..].as_ptr(), &mut clen)
1201 };
1202 if cp <= 0 || clen <= 0 || (consumed + clen as usize) > in_data.len() {
1203 return -1;
1204 }
1205 let ref_str = format!("&#{};", cp);
1206 append_to_xml_buffer(out_buf, ref_str.as_bytes());
1207 total_written += ref_str.len();
1208 in_data = &in_data[consumed + clen as usize..];
1209 if in_data.is_empty() {
1210 break;
1211 }
1212 continue;
1213 }
1214 if ret < 0 {
1215 return -1;
1216 }
1217 break;
1218 }
1219
1220 total_written as c_int
1221}
1222
1223fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
1233 if data.is_empty() {
1234 return;
1235 }
1236
1237 let new_use = (buf.use_ as usize).saturating_add(data.len());
1238 if new_use > buf.size as usize {
1239 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
1241 let new_content =
1242 unsafe { xmlReallocImpl(buf.content as *mut c_void, new_size) as *mut xmlChar };
1243 if new_content.is_null() {
1244 return; }
1246 buf.content = new_content;
1247 buf.contentIO = new_content;
1253 buf.size = new_size as c_uint;
1254 }
1255
1256 unsafe {
1257 ptr::copy_nonoverlapping(
1258 data.as_ptr(),
1259 buf.content.add(buf.use_ as usize),
1260 data.len(),
1261 );
1262 }
1263 buf.use_ = new_use as c_uint;
1264}
1265
1266unsafe extern "C" fn utf8_input_func(
1276 out: *mut c_uchar,
1277 outlen: *mut c_int,
1278 in_: *const c_uchar,
1279 inlen: *mut c_int,
1280) -> c_int {
1281 let avail_out = *outlen as usize;
1282 let avail_in = *inlen as usize;
1283 let to_copy = avail_out.min(avail_in);
1284
1285 if to_copy > 0 {
1286 ptr::copy_nonoverlapping(in_, out, to_copy);
1287 }
1288
1289 *outlen = to_copy as c_int;
1290 *inlen = to_copy as c_int;
1291 to_copy as c_int
1292}
1293
1294unsafe extern "C" fn utf8_output_func(
1296 out: *mut c_uchar,
1297 outlen: *mut c_int,
1298 in_: *const c_uchar,
1299 inlen: *mut c_int,
1300) -> c_int {
1301 utf8_input_func(out, outlen, in_, inlen)
1302}
1303
1304unsafe extern "C" fn utf16le_input_func(
1308 out: *mut c_uchar,
1309 outlen: *mut c_int,
1310 in_: *const c_uchar,
1311 inlen: *mut c_int,
1312) -> c_int {
1313 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1314 return -1;
1315 }
1316
1317 let avail_in = *inlen as usize;
1318 let avail_out = *outlen as usize;
1319
1320 if avail_in == 0 || avail_out == 0 {
1321 *outlen = 0;
1322 *inlen = 0;
1323 return 0;
1324 }
1325
1326 let in_data = core::slice::from_raw_parts(in_, avail_in);
1327 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1328
1329 let result = match utf16le_to_utf8(in_data) {
1331 Ok(v) => v,
1332 Err(()) => return -1,
1333 };
1334
1335 let written = result.len().min(avail_out);
1336 if written > 0 {
1337 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1338 }
1339
1340 *outlen = written as c_int;
1341 *inlen = avail_in as c_int; written as c_int
1343}
1344
1345unsafe extern "C" fn utf16le_output_func(
1347 out: *mut c_uchar,
1348 outlen: *mut c_int,
1349 in_: *const c_uchar,
1350 inlen: *mut c_int,
1351) -> c_int {
1352 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1353 return -1;
1354 }
1355
1356 let avail_in = *inlen as usize;
1357 let avail_out = *outlen as usize;
1358
1359 if avail_in == 0 || avail_out == 0 {
1360 *outlen = 0;
1361 *inlen = 0;
1362 return 0;
1363 }
1364
1365 let in_data = core::slice::from_raw_parts(in_, avail_in);
1366 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1367
1368 let result = match utf8_to_utf16le(in_data) {
1369 Ok(v) => v,
1370 Err(()) => return -1,
1371 };
1372
1373 let written = result.len().min(avail_out);
1374 if written > 0 {
1375 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1376 }
1377
1378 *outlen = written as c_int;
1379 *inlen = avail_in as c_int;
1380 written as c_int
1381}
1382
1383unsafe extern "C" fn utf16be_input_func(
1387 out: *mut c_uchar,
1388 outlen: *mut c_int,
1389 in_: *const c_uchar,
1390 inlen: *mut c_int,
1391) -> c_int {
1392 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1393 return -1;
1394 }
1395
1396 let avail_in = *inlen as usize;
1397 let avail_out = *outlen as usize;
1398
1399 if avail_in == 0 || avail_out == 0 {
1400 *outlen = 0;
1401 *inlen = 0;
1402 return 0;
1403 }
1404
1405 let in_data = core::slice::from_raw_parts(in_, avail_in);
1406 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1407
1408 let result = match utf16be_to_utf8(in_data) {
1409 Ok(v) => v,
1410 Err(()) => return -1,
1411 };
1412
1413 let written = result.len().min(avail_out);
1414 if written > 0 {
1415 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1416 }
1417
1418 *outlen = written as c_int;
1419 *inlen = avail_in as c_int;
1420 written as c_int
1421}
1422
1423unsafe extern "C" fn utf16be_output_func(
1425 out: *mut c_uchar,
1426 outlen: *mut c_int,
1427 in_: *const c_uchar,
1428 inlen: *mut c_int,
1429) -> c_int {
1430 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1431 return -1;
1432 }
1433
1434 let avail_in = *inlen as usize;
1435 let avail_out = *outlen as usize;
1436
1437 if avail_in == 0 || avail_out == 0 {
1438 *outlen = 0;
1439 *inlen = 0;
1440 return 0;
1441 }
1442
1443 let in_data = core::slice::from_raw_parts(in_, avail_in);
1444
1445 let le_result = match utf8_to_utf16le(in_data) {
1447 Ok(v) => v,
1448 Err(()) => return -1,
1449 };
1450
1451 let mut result = le_result;
1453 for chunk in result.as_chunks_mut::<2>().0 {
1454 chunk.swap(0, 1);
1455 }
1456
1457 let written = result.len().min(avail_out);
1458 if written > 0 {
1459 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1460 }
1461
1462 *outlen = written as c_int;
1463 *inlen = avail_in as c_int;
1464 written as c_int
1465}
1466
1467unsafe extern "C" fn latin1_input_func(
1471 out: *mut c_uchar,
1472 outlen: *mut c_int,
1473 in_: *const c_uchar,
1474 inlen: *mut c_int,
1475) -> c_int {
1476 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1477 return -1;
1478 }
1479
1480 let avail_in = *inlen as usize;
1481 let avail_out = *outlen as usize;
1482
1483 if avail_in == 0 || avail_out == 0 {
1484 *outlen = 0;
1485 *inlen = 0;
1486 return 0;
1487 }
1488
1489 let in_data = core::slice::from_raw_parts(in_, avail_in);
1490 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1491
1492 let mut in_pos = 0;
1493 let mut out_pos = 0;
1494
1495 while in_pos < avail_in && out_pos < avail_out {
1496 let byte = in_data[in_pos];
1497 in_pos += 1;
1498
1499 if byte < 0x80 {
1500 if out_pos < avail_out {
1502 out_slice[out_pos] = byte;
1503 out_pos += 1;
1504 } else {
1505 break;
1506 }
1507 } else {
1508 if out_pos + 1 < avail_out {
1511 out_slice[out_pos] = 0xC2 | (byte >> 6);
1512 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1513 out_pos += 2;
1514 } else {
1515 break;
1516 }
1517 }
1518 }
1519
1520 *outlen = out_pos as c_int;
1521 *inlen = in_pos as c_int;
1522 out_pos as c_int
1523}
1524
1525unsafe extern "C" fn latin1_output_func(
1527 out: *mut c_uchar,
1528 outlen: *mut c_int,
1529 in_: *const c_uchar,
1530 inlen: *mut c_int,
1531) -> c_int {
1532 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1533 return -1;
1534 }
1535
1536 let avail_in = *inlen as usize;
1537 let avail_out = *outlen as usize;
1538
1539 if avail_in == 0 || avail_out == 0 {
1540 *outlen = 0;
1541 *inlen = 0;
1542 return 0;
1543 }
1544
1545 let in_data = core::slice::from_raw_parts(in_, avail_in);
1546 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1547
1548 let mut in_pos = 0;
1549 let mut out_pos = 0;
1550
1551 while in_pos < avail_in && out_pos < avail_out {
1552 let byte = in_data[in_pos];
1553 in_pos += 1;
1554
1555 if byte < 0x80 {
1556 out_slice[out_pos] = byte;
1558 out_pos += 1;
1559 } else if (0xC2..=0xC3).contains(&byte) {
1560 if in_pos < avail_in {
1562 let second = in_data[in_pos];
1563 in_pos += 1;
1564 if second & 0xC0 != 0x80 {
1565 return -1; }
1567 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1568 if cp > 0xFF {
1569 return -1; }
1571 out_slice[out_pos] = cp as u8;
1572 out_pos += 1;
1573 } else {
1574 return -1; }
1576 } else if (0x80..=0xBF).contains(&byte) {
1577 return -1;
1579 } else {
1580 return -1;
1583 }
1584 }
1585
1586 *outlen = out_pos as c_int;
1587 *inlen = in_pos as c_int;
1588 out_pos as c_int
1589}
1590
1591const CP1252_C1: [u16; 32] = [
1602 0x20AC, 0xFFFF, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFF, 0x017D, 0xFFFF, 0xFFFF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFF, 0x017E, 0x0178, ];
1607
1608#[allow(dead_code)]
1611pub(crate) const fn cp1252_byte_to_cp(byte: u8) -> Option<u32> {
1612 match byte {
1613 0x00..=0x7F => Some(byte as u32),
1614 0x80..=0x9F => {
1615 let cp = CP1252_C1[(byte - 0x80) as usize];
1616 if cp == 0xFFFF {
1617 None
1618 } else {
1619 Some(cp as u32)
1620 }
1621 }
1622 _ => Some(byte as u32), }
1624}
1625
1626#[allow(dead_code)]
1629pub(crate) const fn cp_to_cp1252_byte(cp: u32) -> Option<u8> {
1630 if cp < 0x80 || (cp >= 0xA0 && cp <= 0xFF) {
1631 Some(cp as u8)
1632 } else if cp >= 0x80 && cp <= 0x9F {
1633 let mut i = 0;
1636 while i < 32 {
1637 if CP1252_C1[i] == cp as u16 {
1638 return Some(0x80 + i as u8);
1639 }
1640 i += 1;
1641 }
1642 None
1643 } else {
1644 None
1645 }
1646}
1647
1648fn decode_utf8_char(data: &[u8], in_pos: usize) -> Option<(u32, usize)> {
1651 let b0 = *data.get(in_pos)?;
1652 if b0 < 0x80 {
1653 return Some((u32::from(b0), 1));
1654 }
1655 let (len, cp0) = match b0 {
1656 0xC2..=0xDF => (2, u32::from(b0 & 0x1F)),
1657 0xE0..=0xEF => (3, u32::from(b0 & 0x0F)),
1658 0xF0..=0xF4 => (4, u32::from(b0 & 0x07)),
1659 _ => return None,
1660 };
1661 if in_pos + len > data.len() {
1662 return None;
1663 }
1664 let mut cp = cp0;
1665 for k in 1..len {
1666 let b = data[in_pos + k];
1667 if b & 0xC0 != 0x80 {
1668 return None;
1669 }
1670 cp = (cp << 6) | u32::from(b & 0x3F);
1671 }
1672 Some((cp, len))
1673}
1674
1675pub(crate) fn cp1252_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
1680 let mut result = Vec::with_capacity(data.len() * 2);
1681 for &byte in data {
1682 let cp = match cp1252_byte_to_cp(byte) {
1683 None => return Err(()),
1684 Some(cp) => cp,
1685 };
1686 let mut buf = [0u8; 4];
1687 let n = encode_codepoint_to_utf8(cp, &mut buf);
1688 result.extend_from_slice(&buf[..n]);
1689 }
1690 Ok(result)
1691}
1692
1693#[allow(dead_code)]
1697pub(crate) fn utf8_to_cp1252(data: &[u8]) -> Result<Vec<u8>, ()> {
1698 let mut result = Vec::with_capacity(data.len());
1699 let mut pos = 0;
1700 while pos < data.len() {
1701 let (cp, consumed) = match decode_utf8_char(data, pos) {
1702 None => return Err(()),
1703 Some(v) => v,
1704 };
1705 let byte = match cp_to_cp1252_byte(cp) {
1706 None => return Err(()),
1707 Some(b) => b,
1708 };
1709 result.push(byte);
1710 pos += consumed;
1711 }
1712 Ok(result)
1713}
1714
1715unsafe extern "C" fn cp1252_input_func(
1717 out: *mut c_uchar,
1718 outlen: *mut c_int,
1719 in_: *const c_uchar,
1720 inlen: *mut c_int,
1721) -> c_int {
1722 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1723 return -1;
1724 }
1725
1726 let avail_in = *inlen as usize;
1727 let avail_out = *outlen as usize;
1728
1729 if avail_in == 0 || avail_out == 0 {
1730 *outlen = 0;
1731 *inlen = 0;
1732 return 0;
1733 }
1734
1735 let in_data = core::slice::from_raw_parts(in_, avail_in);
1736 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1737
1738 let mut in_pos = 0;
1739 let mut out_pos = 0;
1740
1741 while in_pos < avail_in && out_pos < avail_out {
1742 let byte = in_data[in_pos];
1743 let cp = match cp1252_byte_to_cp(byte) {
1744 None => {
1746 *outlen = out_pos as c_int;
1747 *inlen = in_pos as c_int;
1748 return -1;
1749 }
1750 Some(cp) => cp,
1751 };
1752 let mut buf = [0u8; 4];
1753 let n = encode_codepoint_to_utf8(cp, &mut buf);
1754 if out_pos + n > avail_out {
1755 break;
1756 }
1757 out_slice[out_pos..out_pos + n].copy_from_slice(&buf[..n]);
1758 out_pos += n;
1759 in_pos += 1;
1760 }
1761
1762 *outlen = out_pos as c_int;
1763 *inlen = in_pos as c_int;
1764 out_pos as c_int
1765}
1766
1767unsafe extern "C" fn cp1252_output_func(
1769 out: *mut c_uchar,
1770 outlen: *mut c_int,
1771 in_: *const c_uchar,
1772 inlen: *mut c_int,
1773) -> c_int {
1774 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1775 return -1;
1776 }
1777
1778 let avail_in = *inlen as usize;
1779 let avail_out = *outlen as usize;
1780
1781 if avail_in == 0 || avail_out == 0 {
1782 *outlen = 0;
1783 *inlen = 0;
1784 return 0;
1785 }
1786
1787 let in_data = core::slice::from_raw_parts(in_, avail_in);
1788 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1789
1790 let mut in_pos = 0;
1791 let mut out_pos = 0;
1792
1793 while in_pos < avail_in && out_pos < avail_out {
1794 let (cp, consumed) = match decode_utf8_char(in_data, in_pos) {
1795 None => {
1796 *outlen = out_pos as c_int;
1797 *inlen = in_pos as c_int;
1798 return -1;
1799 }
1800 Some(v) => v,
1801 };
1802 let byte = match cp_to_cp1252_byte(cp) {
1803 None => {
1804 *outlen = out_pos as c_int;
1806 *inlen = in_pos as c_int;
1807 return -1;
1808 }
1809 Some(b) => b,
1810 };
1811 out_slice[out_pos] = byte;
1812 out_pos += 1;
1813 in_pos += consumed;
1814 }
1815
1816 *outlen = out_pos as c_int;
1817 *inlen = in_pos as c_int;
1818 out_pos as c_int
1819}
1820
1821unsafe extern "C" fn ascii_input_func(
1832 out: *mut c_uchar,
1833 outlen: *mut c_int,
1834 in_: *const c_uchar,
1835 inlen: *mut c_int,
1836) -> c_int {
1837 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1838 return -1;
1839 }
1840
1841 let avail_in = *inlen as usize;
1842 let avail_out = *outlen as usize;
1843
1844 if avail_in == 0 || avail_out == 0 {
1845 *outlen = 0;
1846 *inlen = 0;
1847 return 0;
1848 }
1849
1850 let in_data = core::slice::from_raw_parts(in_, avail_in);
1851 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1852
1853 let mut pos = 0;
1854 while pos < avail_in && pos < avail_out {
1855 let byte = in_data[pos];
1856 if byte > 0x7F {
1857 *outlen = pos as c_int;
1860 *inlen = pos as c_int;
1861 return -2;
1862 }
1863 out_slice[pos] = byte;
1864 pos += 1;
1865 }
1866
1867 *outlen = pos as c_int;
1868 *inlen = pos as c_int;
1869 pos as c_int
1870}
1871
1872unsafe extern "C" fn ascii_output_func(
1874 out: *mut c_uchar,
1875 outlen: *mut c_int,
1876 in_: *const c_uchar,
1877 inlen: *mut c_int,
1878) -> c_int {
1879 ascii_input_func(out, outlen, in_, inlen)
1881}
1882
1883pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
1892 if name.is_null() {
1893 return ptr::null_mut();
1894 }
1895 find_encoding_handler(name as *const xmlChar)
1896}
1897
1898pub(crate) const fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
1902 match enc {
1906 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
1907 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE | xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => {
1908 c"UTF-16".as_ptr()
1909 }
1910 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE | xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => {
1911 c"UCS-4".as_ptr()
1912 }
1913 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"IBM037".as_ptr(),
1914 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
1915 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
1916 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
1917 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
1918 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
1919 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
1920 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
1921 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
1922 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
1923 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
1924 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
1925 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"Shift_JIS".as_ptr(),
1926 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
1927 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"US-ASCII".as_ptr(),
1929 _ => ptr::null(),
1930 }
1931}
1932
1933pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
1942 if name.is_null() {
1943 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
1944 }
1945 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
1946 encoding_from_name(bytes) as c_int
1947}
1948
1949static ENCODING_ALIASES: std::sync::OnceLock<
1957 parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>>,
1958> = std::sync::OnceLock::new();
1959
1960fn encoding_aliases() -> &'static parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>> {
1961 ENCODING_ALIASES.get_or_init(|| parking_lot::RwLock::new(std::collections::HashMap::new()))
1962}
1963
1964pub(crate) fn add_encoding_alias(name: *const c_char, alias: *const c_char) -> c_int {
1972 if name.is_null() || alias.is_null() {
1973 return -1;
1974 }
1975 let n = unsafe { CStr::from_ptr(name).to_bytes().to_vec() };
1976 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1977 encoding_aliases().write().insert(a, n);
1978 0
1979}
1980
1981pub(crate) fn del_encoding_alias(alias: *const c_char) -> c_int {
1988 if alias.is_null() {
1989 return -1;
1990 }
1991 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1992 if encoding_aliases().write().remove(&a).is_some() {
1993 0
1994 } else {
1995 -1
1996 }
1997}
1998
1999pub(crate) fn get_encoding_alias(alias: *const c_char) -> *const c_char {
2008 if alias.is_null() {
2009 return ptr::null();
2010 }
2011 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
2012 let guard = encoding_aliases().read();
2013 match guard.get(&a) {
2014 Some(v) => {
2015 let leaked: &'static [u8] = Box::leak(v.clone().into_boxed_slice());
2018 leaked.as_ptr() as *const c_char
2019 }
2020 None => ptr::null(),
2021 }
2022}
2023
2024pub(crate) fn cleanup_encoding_aliases() {
2026 encoding_aliases().write().clear();
2027}
2028
2029pub(crate) fn xmlCharEncInFunc(
2033 handler: *mut _xmlCharEncodingHandler,
2034 out: *mut _xmlBuffer,
2035 in_: *mut _xmlBuffer,
2036) -> c_int {
2037 char_enc_in(handler, out, in_)
2038}
2039
2040pub(crate) fn xmlCharEncOutFunc(
2044 handler: *mut _xmlCharEncodingHandler,
2045 out: *mut _xmlBuffer,
2046 in_: *mut _xmlBuffer,
2047) -> c_int {
2048 char_enc_out(handler, out, in_)
2049}
2050
2051pub(crate) fn xmlNewCharEncodingHandler(
2065 name: *const c_char,
2066 input: xmlCharEncodingInputFunc,
2067 output: xmlCharEncodingOutputFunc,
2068) -> *mut _xmlCharEncodingHandler {
2069 if name.is_null() {
2070 return ptr::null_mut();
2071 }
2072
2073 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) };
2074 if name_raw.is_null() {
2075 return ptr::null_mut();
2076 }
2077
2078 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
2079 as *mut _xmlCharEncodingHandler;
2080
2081 if handler.is_null() {
2082 unsafe { xmlFreeImpl(name_raw) };
2083 return ptr::null_mut();
2084 }
2085
2086 unsafe {
2087 ptr::write(
2088 handler,
2089 _xmlCharEncodingHandler {
2090 name: name_raw as *mut c_char,
2091 input: EncodingInputUnion {
2092 legacyFunc: Some(input),
2093 },
2094 output: EncodingOutputUnion {
2095 legacyFunc: Some(output),
2096 },
2097 inputCtxt: ptr::null_mut(),
2098 outputCtxt: ptr::null_mut(),
2099 ctxtDtor: None,
2100 flags: 0,
2101 },
2102 );
2103 }
2104
2105 handler
2106}
2107
2108#[allow(dead_code)]
2119pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
2120 if handler.is_null() {
2121 return;
2122 }
2123
2124 {
2126 let mut handlers = ENCODING_HANDLERS.write();
2127 handlers.retain(|&h| h.0 != handler);
2128 }
2129
2130 unsafe {
2131 if !(*handler).name.is_null() {
2132 xmlFreeImpl((*handler).name as *mut c_void);
2133 }
2134 xmlFreeImpl(handler as *mut c_void);
2135 }
2136}
2137
2138pub(crate) fn xmlInitCharEncodingHandlers() {
2140 init_encodings();
2141}
2142
2143pub(crate) fn xmlCleanupCharEncodingHandlers() {
2145 cleanup_encodings();
2146}
2147
2148pub(crate) fn xmlLookupCharEncodingHandler(enc: c_int, out: *mut *mut c_void) -> c_int {
2176 if out.is_null() {
2177 return crate::abi::types::XML_ERR_ARGUMENT;
2178 }
2179 unsafe {
2180 *out = ptr::null_mut();
2181 }
2182 if enc <= 0 || enc >= 32 {
2183 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
2184 }
2185 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int {
2187 return crate::abi::types::XML_ERR_OK;
2188 }
2189 let canonical: &[u8] = match enc {
2190 2 => b"UTF-16LE\0",
2192 3 => b"UTF-16BE\0",
2194 10 => b"ISO-8859-1\0",
2196 22 => b"US-ASCII\0",
2198 23 => b"UTF-16\0",
2200 _ => return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING,
2201 };
2202 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
2203 if h.is_null() {
2204 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
2205 }
2206 unsafe {
2207 *out = h as *mut c_void;
2208 }
2209 crate::abi::types::XML_ERR_OK
2210}
2211
2212pub(crate) fn xmlGetCharEncodingHandler(enc: c_int) -> *mut c_void {
2214 let mut ret: *mut c_void = ptr::null_mut();
2215 let _rc = xmlLookupCharEncodingHandler(enc, &mut ret);
2216 ret
2217}
2218
2219pub(crate) fn xmlCreateCharEncodingHandler(
2234 name: *const c_char,
2235 flags: c_int,
2236 impl_: Option<xmlCharEncConvImpl>,
2237 implCtxt: *mut c_void,
2238 out: *mut *mut c_void,
2239) -> c_int {
2240 if out.is_null() {
2241 return crate::abi::types::XML_ERR_ARGUMENT;
2242 }
2243 unsafe {
2244 *out = ptr::null_mut();
2245 }
2246 if name.is_null() || flags == 0 {
2247 return crate::abi::types::XML_ERR_ARGUMENT;
2248 }
2249 let norig = unsafe { CStr::from_ptr(name).to_bytes() };
2250
2251 let mut eff: &[u8] = norig;
2253 let alias = get_encoding_alias(name);
2254 if !alias.is_null() {
2255 eff = unsafe { CStr::from_ptr(alias).to_bytes() };
2256 }
2257
2258 let enc = encoding_from_name(eff);
2259
2260 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
2262 return crate::abi::types::XML_ERR_OK;
2263 }
2264
2265 let canonical: &[u8] = match enc {
2266 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => b"UTF-16LE\0",
2267 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => b"UTF-16BE\0",
2268 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => b"ISO-8859-1\0",
2269 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => b"US-ASCII\0",
2270 _ => {
2271 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
2272 }
2273 };
2274 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
2275 if h.is_null() {
2276 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
2277 }
2278 unsafe {
2279 let src = &*h;
2280 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
2281 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
2282 if !has_in || !has_out {
2283 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
2284 }
2285 let copy =
2290 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler;
2291 if copy.is_null() {
2292 return crate::abi::types::XML_ERR_NO_MEMORY;
2293 }
2294 let name_copy = crate::abi::allocator::xmlMemStrdupImpl(name) as *mut c_char;
2295 if name_copy.is_null() {
2296 xmlFreeImpl(copy as *mut c_void);
2297 return crate::abi::types::XML_ERR_NO_MEMORY;
2298 }
2299 ptr::write(
2300 copy,
2301 _xmlCharEncodingHandler {
2302 name: name_copy,
2303 input: EncodingInputUnion {
2304 legacyFunc: src.input.legacyFunc,
2305 },
2306 output: EncodingOutputUnion {
2307 legacyFunc: src.output.legacyFunc,
2308 },
2309 inputCtxt: src.inputCtxt,
2310 outputCtxt: src.outputCtxt,
2311 ctxtDtor: src.ctxtDtor,
2312 flags: src.flags,
2313 },
2314 );
2315 *out = copy as *mut c_void;
2316 }
2317 crate::abi::types::XML_ERR_OK
2318}
2319
2320fn find_extra_handler(
2335 norig: &[u8],
2336 name: &[u8],
2337 flags: c_int,
2338 impl_: Option<xmlCharEncConvImpl>,
2339 implCtxt: *mut c_void,
2340 out: *mut *mut c_void,
2341) -> c_int {
2342 if let Some(f) = impl_ {
2344 let mut n = norig.to_vec();
2345 n.push(0);
2346 let rc = unsafe {
2347 f(
2348 implCtxt,
2349 n.as_ptr() as *const c_char,
2350 flags,
2351 out as *mut *mut crate::abi::structs::_xmlCharEncodingHandler,
2352 )
2353 };
2354 return rc;
2355 }
2356 let mut n = name.to_vec();
2358 n.push(0);
2359 let h = find_encoding_handler(n.as_ptr() as *const xmlChar);
2360 if !h.is_null() {
2361 unsafe {
2362 let src = &*h;
2363 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
2364 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
2365 if has_in && has_out {
2366 *out = h as *mut c_void;
2367 return crate::abi::types::XML_ERR_OK;
2368 }
2369 }
2370 }
2371 crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING
2372}
2373
2374pub(crate) fn xmlOpenCharEncodingHandler(
2376 name: *const c_char,
2377 output: c_int,
2378 out: *mut *mut c_void,
2379) -> c_int {
2380 let flags: c_int = if output != 0 { 2 } else { 1 };
2382 xmlCreateCharEncodingHandler(name, flags, None, ptr::null_mut(), out)
2383}
2384
2385pub(crate) fn xmlCharEncNewCustomHandler(
2400 name: *const c_char,
2401 input: xmlCharEncConvFunc,
2402 output: xmlCharEncConvFunc,
2403 ctxtDtor: Option<xmlCharEncConvCtxtDtor>,
2404 inputCtxt: *mut c_void,
2405 outputCtxt: *mut c_void,
2406 out: *mut *mut c_void,
2407) -> c_int {
2408 if out.is_null() {
2409 return crate::abi::types::XML_ERR_ARGUMENT;
2410 }
2411 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
2412 as *mut _xmlCharEncodingHandler;
2413 if handler.is_null() {
2414 unsafe {
2415 if let Some(d) = ctxtDtor {
2416 if !inputCtxt.is_null() {
2417 d(inputCtxt);
2418 }
2419 if !outputCtxt.is_null() {
2420 d(outputCtxt);
2421 }
2422 }
2423 }
2424 return crate::abi::types::XML_ERR_NO_MEMORY;
2425 }
2426 let name_copy = if name.is_null() {
2427 ptr::null_mut()
2428 } else {
2429 let nc = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) } as *mut c_char;
2430 if nc.is_null() {
2431 unsafe { xmlFreeImpl(handler as *mut c_void) };
2432 unsafe {
2433 if let Some(d) = ctxtDtor {
2434 if !inputCtxt.is_null() {
2435 d(inputCtxt);
2436 }
2437 if !outputCtxt.is_null() {
2438 d(outputCtxt);
2439 }
2440 }
2441 }
2442 return crate::abi::types::XML_ERR_NO_MEMORY;
2443 }
2444 nc
2445 };
2446 unsafe {
2447 ptr::write(
2448 handler,
2449 _xmlCharEncodingHandler {
2450 name: name_copy,
2451 input: EncodingInputUnion { func: Some(input) },
2452 output: EncodingOutputUnion { func: Some(output) },
2453 inputCtxt,
2454 outputCtxt,
2455 ctxtDtor,
2456 flags: 0,
2457 },
2458 );
2459 *out = handler as *mut c_void;
2460 }
2461 crate::abi::types::XML_ERR_OK
2462}
2463
2464#[cfg(test)]
2469mod tests {
2470 use super::*;
2471
2472 #[test]
2475 fn test_detect_bom_utf8() {
2476 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
2477 assert_eq!(
2478 detect_encoding_from_bom(&data),
2479 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2480 );
2481 }
2482
2483 #[test]
2484 fn test_detect_bom_utf16le() {
2485 let data = [0xFF, 0xFE, 0x00, 0x01];
2486 assert_eq!(
2487 detect_encoding_from_bom(&data),
2488 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2489 );
2490 }
2491
2492 #[test]
2493 fn test_detect_bom_utf16be() {
2494 let data = [0xFE, 0xFF, 0x00, 0x01];
2495 assert_eq!(
2496 detect_encoding_from_bom(&data),
2497 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2498 );
2499 }
2500
2501 #[test]
2502 fn test_detect_bom_none() {
2503 let data = b"<xml>";
2504 assert_eq!(
2505 detect_encoding_from_bom(data),
2506 xmlCharEncoding::XML_CHAR_ENCODING_NONE
2507 );
2508 }
2509
2510 #[test]
2511 fn test_detect_bom_empty() {
2512 assert_eq!(
2513 detect_encoding_from_bom(b""),
2514 xmlCharEncoding::XML_CHAR_ENCODING_NONE
2515 );
2516 }
2517
2518 #[test]
2521 fn test_detect_encoding_declaration_utf8() {
2522 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
2523 let result = detect_encoding_from_declaration(data);
2524 assert_eq!(result, Some(b"utf-8".to_vec()));
2525 }
2526
2527 #[test]
2528 fn test_detect_encoding_declaration_iso() {
2529 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
2530 let result = detect_encoding_from_declaration(data);
2531 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
2532 }
2533
2534 #[test]
2535 fn test_detect_encoding_declaration_none() {
2536 let data = b"<?xml version=\"1.0\"?>";
2537 let result = detect_encoding_from_declaration(data);
2538 assert!(result.is_none());
2539 }
2540
2541 #[test]
2542 fn test_detect_encoding_declaration_no_xml() {
2543 let data = b"<root>";
2544 let result = detect_encoding_from_declaration(data);
2545 assert!(result.is_none());
2546 }
2547
2548 #[test]
2549 fn test_detect_encoding_declaration_with_bom() {
2550 let mut data = vec![0xEF, 0xBB, 0xBF];
2551 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
2552 let result = detect_encoding_from_declaration(&data);
2553 assert_eq!(result, Some(b"utf-8".to_vec()));
2554 }
2555
2556 #[test]
2559 fn test_encoding_from_name_utf8() {
2560 assert_eq!(
2561 encoding_from_name(b"UTF-8"),
2562 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2563 );
2564 assert_eq!(
2565 encoding_from_name(b"utf8"),
2566 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2567 );
2568 }
2569
2570 #[test]
2571 fn test_encoding_from_name_utf16() {
2572 assert_eq!(
2573 encoding_from_name(b"UTF-16LE"),
2574 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2575 );
2576 assert_eq!(
2577 encoding_from_name(b"UTF-16BE"),
2578 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2579 );
2580 assert_eq!(
2581 encoding_from_name(b"utf-16"),
2582 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2583 );
2584 }
2585
2586 #[test]
2587 fn test_encoding_from_name_latin1() {
2588 assert_eq!(
2589 encoding_from_name(b"ISO-8859-1"),
2590 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2591 );
2592 assert_eq!(
2593 encoding_from_name(b"Latin1"),
2594 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2595 );
2596 }
2597
2598 #[test]
2599 fn test_encoding_from_name_ascii() {
2600 assert_eq!(
2601 encoding_from_name(b"ASCII"),
2602 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2603 );
2604 assert_eq!(
2605 encoding_from_name(b"US-ASCII"),
2606 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2607 );
2608 }
2609
2610 #[test]
2611 fn test_encoding_from_name_error() {
2612 assert_eq!(
2613 encoding_from_name(b"invalid-encoding"),
2614 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2615 );
2616 }
2617
2618 #[test]
2619 fn test_encoding_from_name_empty() {
2620 assert_eq!(
2621 encoding_from_name(b""),
2622 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2623 );
2624 }
2625
2626 #[test]
2629 fn test_encoding_name_utf8() {
2630 assert_eq!(
2631 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
2632 Some(b"UTF-8" as &[u8])
2633 );
2634 }
2635
2636 #[test]
2637 fn test_encoding_name_utf16le() {
2638 assert_eq!(
2639 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
2640 Some(b"UTF-16LE" as &[u8])
2641 );
2642 }
2643
2644 #[test]
2645 fn test_encoding_name_none() {
2646 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
2647 }
2648
2649 #[test]
2650 fn test_encoding_name_error() {
2651 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
2652 }
2653
2654 #[test]
2657 fn test_utf8_valid_ascii() {
2658 assert!(utf8_valid(b"hello world"));
2659 }
2660
2661 #[test]
2662 fn test_utf8_valid_multi_byte() {
2663 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
2664 }
2665
2666 #[test]
2667 fn test_utf8_valid_empty() {
2668 assert!(utf8_valid(b""));
2669 }
2670
2671 #[test]
2672 fn test_utf8_invalid() {
2673 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
2674 }
2675
2676 #[test]
2679 fn test_valid_xml_chars() {
2680 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));
2686 assert!(is_valid_xml_char(0xE000));
2687 assert!(is_valid_xml_char(0xFFFD));
2688 assert!(is_valid_xml_char(0x10000));
2689 assert!(is_valid_xml_char(0x10FFFF));
2690 }
2691
2692 #[test]
2693 fn test_invalid_xml_chars() {
2694 assert!(!is_valid_xml_char(0x00));
2695 assert!(!is_valid_xml_char(0x08));
2696 assert!(!is_valid_xml_char(0x0B));
2697 assert!(!is_valid_xml_char(0x0C));
2698 assert!(!is_valid_xml_char(0x0E));
2699 assert!(!is_valid_xml_char(0x1F));
2700 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
2703 assert!(!is_valid_xml_char(0xFFFF));
2704 assert!(!is_valid_xml_char(0x110000));
2705 }
2706
2707 #[test]
2710 fn test_utf16le_to_utf8_ascii() {
2711 let data = [b'A', 0x00, b'B', 0x00];
2713 let result = utf16le_to_utf8(&data).unwrap();
2714 assert_eq!(result, b"AB");
2715 }
2716
2717 #[test]
2718 fn test_utf16le_to_utf8_bom() {
2719 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
2721 let result = utf16le_to_utf8(&data).unwrap();
2722 assert_eq!(result, b"AB");
2723 }
2724
2725 #[test]
2726 fn test_utf16le_to_utf8_bmp() {
2727 let data = [0xE9, 0x00];
2729 let result = utf16le_to_utf8(&data).unwrap();
2730 assert_eq!(result, "é".as_bytes());
2731 }
2732
2733 #[test]
2734 fn test_utf16le_to_utf8_supplementary() {
2735 let data = [0x3D, 0xD8, 0x00, 0xDE];
2737 let result = utf16le_to_utf8(&data).unwrap();
2738 assert_eq!(result, "😀".as_bytes());
2739 }
2740
2741 #[test]
2742 fn test_utf16le_to_utf8_unpaired_surrogate() {
2743 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
2745 }
2746
2747 #[test]
2748 fn test_utf16le_to_utf8_truncated() {
2749 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
2751 }
2752
2753 #[test]
2754 fn test_utf16le_to_utf8_empty() {
2755 let result = utf16le_to_utf8(b"").unwrap();
2756 assert!(result.is_empty());
2757 }
2758
2759 #[test]
2762 fn test_utf16be_to_utf8_ascii() {
2763 let data = [0x00, b'A', 0x00, b'B'];
2764 let result = utf16be_to_utf8(&data).unwrap();
2765 assert_eq!(result, b"AB");
2766 }
2767
2768 #[test]
2769 fn test_utf16be_to_utf8_bom() {
2770 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
2772 let result = utf16be_to_utf8(&data).unwrap();
2773 assert_eq!(result, b"AB");
2774 }
2775
2776 #[test]
2777 fn test_utf16be_to_utf8_supplementary() {
2778 let data = [0xD8, 0x3D, 0xDE, 0x00];
2780 let result = utf16be_to_utf8(&data).unwrap();
2781 assert_eq!(result, "😀".as_bytes());
2782 }
2783
2784 #[test]
2785 fn test_utf16be_to_utf8_empty() {
2786 let result = utf16be_to_utf8(b"").unwrap();
2787 assert!(result.is_empty());
2788 }
2789
2790 #[test]
2793 fn test_utf8_to_utf16le_ascii() {
2794 let result = utf8_to_utf16le(b"AB").unwrap();
2795 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
2796 }
2797
2798 #[test]
2799 fn test_utf8_to_utf16le_bmp() {
2800 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
2801 assert_eq!(result, [0xE9, 0x00]);
2802 }
2803
2804 #[test]
2805 fn test_utf8_to_utf16le_supplementary() {
2806 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
2807 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
2808 }
2809
2810 #[test]
2811 fn test_utf8_to_utf16le_invalid_utf8() {
2812 assert!(utf8_to_utf16le(&[0xFF]).is_err());
2813 }
2814
2815 #[test]
2816 fn test_utf8_to_utf16le_empty() {
2817 let result = utf8_to_utf16le(b"").unwrap();
2818 assert!(result.is_empty());
2819 }
2820
2821 #[test]
2824 fn test_latin1_to_utf8_ascii() {
2825 let result = latin1_to_utf8(b"ABC");
2826 assert_eq!(result, b"ABC");
2827 }
2828
2829 #[test]
2830 fn test_latin1_to_utf8_accented() {
2831 let result = latin1_to_utf8(&[0xE9]);
2833 assert_eq!(result, "é".as_bytes());
2834 }
2835
2836 #[test]
2837 fn test_latin1_to_utf8_all_255() {
2838 let result = latin1_to_utf8(&[0xFF]);
2839 assert_eq!(result, [0xC3, 0xBF]);
2841 }
2842
2843 #[test]
2844 fn test_latin1_to_utf8_empty() {
2845 let result = latin1_to_utf8(b"");
2846 assert!(result.is_empty());
2847 }
2848
2849 #[test]
2850 fn test_latin1_to_utf8_mixed() {
2851 let result = latin1_to_utf8(b"caf\xE9");
2852 assert_eq!(result, "café".as_bytes());
2853 }
2854
2855 #[test]
2858 fn test_utf8_to_latin1_ascii() {
2859 let result = utf8_to_latin1(b"ABC").unwrap();
2860 assert_eq!(result, b"ABC");
2861 }
2862
2863 #[test]
2864 fn test_utf8_to_latin1_accented() {
2865 let result = utf8_to_latin1("é".as_bytes()).unwrap();
2866 assert_eq!(result, [0xE9]);
2867 }
2868
2869 #[test]
2870 fn test_utf8_to_latin1_out_of_range() {
2871 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
2873
2874 #[test]
2875 fn test_utf8_to_latin1_invalid_utf8() {
2876 assert!(utf8_to_latin1(&[0xFF]).is_err());
2877 }
2878
2879 #[test]
2880 fn test_utf8_to_latin1_empty() {
2881 let result = utf8_to_latin1(b"").unwrap();
2882 assert!(result.is_empty());
2883 }
2884
2885 #[test]
2888 fn test_init_and_find_encodings() {
2889 init_encodings();
2890
2891 let utf8_name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2892 assert!(!find_encoding_handler(utf8_name).is_null());
2893
2894 let utf16le_name: *const xmlChar = c"UTF-16LE".as_ptr() as *const xmlChar;
2895 assert!(!find_encoding_handler(utf16le_name).is_null());
2896
2897 let utf16be_name: *const xmlChar = c"UTF-16BE".as_ptr() as *const xmlChar;
2898 assert!(!find_encoding_handler(utf16be_name).is_null());
2899
2900 let latin1_name: *const xmlChar = c"ISO-8859-1".as_ptr() as *const xmlChar;
2901 assert!(!find_encoding_handler(latin1_name).is_null());
2902
2903 let ascii_name: *const xmlChar = c"ASCII".as_ptr() as *const xmlChar;
2904 assert!(!find_encoding_handler(ascii_name).is_null());
2905
2906 let lower_name: *const xmlChar = c"utf-8".as_ptr() as *const xmlChar;
2908 assert!(!find_encoding_handler(lower_name).is_null());
2909 }
2910
2911 #[test]
2925 fn test_find_owned_close_keeps_registry_intact() {
2926 init_encodings();
2927 let name: *const xmlChar = c"ISO-8859-1".as_ptr() as *const xmlChar;
2928
2929 let registry = find_encoding_handler(name);
2931 assert!(!registry.is_null());
2932 let h1 = xmlFindCharEncodingHandler_owned(name);
2934 assert!(!h1.is_null());
2935 assert_ne!(h1 as *const c_void, registry as *const c_void);
2936
2937 unsafe {
2940 if !(*h1).name.is_null() {
2941 crate::abi::allocator::xmlFreeImpl((*h1).name as *mut c_void);
2942 }
2943 xmlFreeImpl(h1 as *mut c_void);
2944 }
2945
2946 let registry2 = find_encoding_handler(name);
2951 assert_eq!(registry2 as *const c_void, registry as *const c_void);
2952 assert!(!unsafe { (*registry2).name }.is_null());
2953 let reg_name = unsafe { CStr::from_ptr((*registry2).name as *const c_char) };
2954 assert_eq!(reg_name.to_bytes(), b"ISO-8859-1");
2955
2956 let h2 = xmlFindCharEncodingHandler_owned(name);
2958 assert!(!h2.is_null());
2959 assert_ne!(h2 as *const c_void, registry as *const c_void);
2960 unsafe {
2961 if !(*h2).name.is_null() {
2962 crate::abi::allocator::xmlFreeImpl((*h2).name as *mut c_void);
2963 }
2964 xmlFreeImpl(h2 as *mut c_void);
2965 }
2966 }
2967
2968 #[test]
2974 fn test_find_owned_utf8_static_and_persistent() {
2975 init_encodings();
2976 let name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2977 let u1 = xmlFindCharEncodingHandler_owned(name);
2978 assert!(!u1.is_null());
2979 let u2 = xmlFindCharEncodingHandler_owned(c"utf8".as_ptr() as *const xmlChar);
2982 assert_eq!(u1, u2);
2983 assert_eq!(
2984 unsafe { (*u1).flags } & XML_HANDLER_STATIC,
2985 XML_HANDLER_STATIC
2986 );
2987 }
2988
2989 #[test]
2990 fn test_find_encoding_handler_not_found() {
2991 let name: *const xmlChar = c"NONEXISTENT".as_ptr() as *const xmlChar;
2992 assert!(find_encoding_handler(name).is_null());
2993 }
2994
2995 #[test]
2996 fn test_find_encoding_handler_null() {
2997 assert!(find_encoding_handler(ptr::null()).is_null());
2998 }
2999
3000 #[test]
3009 fn test_add_encoding_handler() {
3010 let handler = unsafe {
3011 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
3012 };
3013 assert!(!handler.is_null());
3014
3015 let name = unsafe {
3016 crate::abi::allocator::xmlMemStrdupImpl(c"TEST-ENC".as_ptr() as *const c_char)
3017 };
3018 unsafe {
3019 ptr::write(
3020 handler,
3021 _xmlCharEncodingHandler {
3022 name: name as *mut c_char,
3023 input: EncodingInputUnion { legacyFunc: None },
3024 output: EncodingOutputUnion { legacyFunc: None },
3025 inputCtxt: ptr::null_mut(),
3026 outputCtxt: ptr::null_mut(),
3027 ctxtDtor: None,
3028 flags: 0,
3029 },
3030 );
3031 }
3032
3033 assert_eq!(add_encoding_handler(handler), 0);
3034
3035 let found = find_encoding_handler(c"TEST-ENC".as_ptr() as *const xmlChar);
3036 assert_eq!(found, handler);
3037
3038 {
3040 let mut handlers = ENCODING_HANDLERS.write();
3041 handlers.retain(|&h| h.0 != handler);
3042 }
3043
3044 unsafe {
3045 xmlFreeImpl(name as *mut c_void);
3046 xmlFreeImpl(handler as *mut c_void);
3047 }
3048 }
3049
3050 #[test]
3053 fn test_utf16le_roundtrip() {
3054 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
3055 let utf16 = utf8_to_utf16le(original).unwrap();
3056 let back = utf16le_to_utf8(&utf16).unwrap();
3057 assert_eq!(original.to_vec(), back);
3058 }
3059
3060 #[test]
3061 fn test_utf16be_roundtrip() {
3062 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
3063 let utf16le = utf8_to_utf16le(original).unwrap();
3064 let mut utf16be = utf16le.clone();
3066 for chunk in utf16be.as_chunks_mut::<2>().0 {
3067 chunk.swap(0, 1);
3068 }
3069 let back = utf16be_to_utf8(&utf16be).unwrap();
3070 assert_eq!(original.to_vec(), back);
3071 }
3072
3073 #[test]
3074 fn test_latin1_roundtrip() {
3075 let original: Vec<u8> = (0x00..=0xFF).collect();
3076 let utf8 = latin1_to_utf8(&original);
3077 let back = utf8_to_latin1(&utf8).unwrap();
3078 assert_eq!(original, back);
3079 }
3080
3081 #[test]
3091 fn test_utf8_handler_identity() {
3092 let input = b"Hello, UTF-8!";
3093 let mut output = [0u8; 64];
3094 let mut outlen = output.len() as c_int;
3095 let mut inlen = input.len() as c_int;
3096
3097 let ret = unsafe {
3098 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
3099 };
3100
3101 assert_eq!(ret, input.len() as c_int);
3102 assert_eq!(&output[..ret as usize], input);
3103 assert_eq!(inlen, input.len() as c_int);
3104 }
3105
3106 #[test]
3114 fn test_utf16le_handler_roundtrip() {
3115 init_encodings();
3116
3117 let original = b"Hello UTF-16LE!";
3118 let mut utf16_buf = [0u8; 128];
3119 let mut outlen = utf16_buf.len() as c_int;
3120 let mut inlen = original.len() as c_int;
3121
3122 let written = unsafe {
3123 utf16le_output_func(
3124 utf16_buf.as_mut_ptr(),
3125 &mut outlen,
3126 original.as_ptr(),
3127 &mut inlen,
3128 )
3129 };
3130 assert!(written > 0);
3131
3132 let mut decoded = [0u8; 128];
3134 let mut outlen2 = decoded.len() as c_int;
3135 let mut inlen2 = written;
3136
3137 let written2 = unsafe {
3138 utf16le_input_func(
3139 decoded.as_mut_ptr(),
3140 &mut outlen2,
3141 utf16_buf.as_ptr(),
3142 &mut inlen2,
3143 )
3144 };
3145 assert_eq!(written2 as usize, original.len());
3146 assert_eq!(&decoded[..written2 as usize], original);
3147 }
3148
3149 #[test]
3159 fn test_append_to_xml_buffer() {
3160 unsafe {
3161 let content = xmlMallocImpl(64) as *mut xmlChar;
3162 assert!(!content.is_null());
3163
3164 let mut buf = _xmlBuffer {
3165 content,
3166 use_: 0,
3167 size: 64,
3168 alloc: 0,
3169 contentIO: ptr::null_mut(),
3170 };
3171
3172 append_to_xml_buffer(&mut buf, b"Hello");
3173 assert_eq!(buf.use_, 5);
3174 let slice = core::slice::from_raw_parts(buf.content, 5);
3175 assert_eq!(slice, b"Hello");
3176
3177 append_to_xml_buffer(&mut buf, b" World");
3178 assert_eq!(buf.use_, 11);
3179 let slice = core::slice::from_raw_parts(buf.content, 11);
3180 assert_eq!(slice, b"Hello World");
3181
3182 xmlFreeImpl(buf.content as *mut c_void);
3183 }
3184 }
3185
3186 #[test]
3189 fn test_xml_parse_char_encoding() {
3190 let name = c"UTF-8".as_ptr() as *const c_char;
3191 assert_eq!(
3192 xmlParseCharEncoding(name),
3193 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
3194 );
3195
3196 let name = c"ISO-8859-1".as_ptr() as *const c_char;
3197 assert_eq!(
3198 xmlParseCharEncoding(name),
3199 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
3200 );
3201
3202 assert_eq!(
3203 xmlParseCharEncoding(ptr::null()),
3204 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
3205 );
3206 }
3207
3208 #[test]
3217 fn test_xml_new_and_del_encoding_handler() {
3218 let name = c"TestEnc".as_ptr() as *const c_char;
3219 let handler = xmlNewCharEncodingHandler(
3220 name,
3221 utf8_input_func as xmlCharEncodingInputFunc,
3222 utf8_output_func as xmlCharEncodingOutputFunc,
3223 );
3224 assert!(!handler.is_null());
3225
3226 unsafe {
3227 assert!(!(*handler).name.is_null());
3228 let cstr = CStr::from_ptr((*handler).name);
3229 assert_eq!(cstr.to_bytes(), b"TestEnc");
3230 }
3231
3232 xmlDelEncodingHandler(handler);
3233 }
3234
3235 #[test]
3236 fn test_xml_init_and_cleanup() {
3237 xmlInitCharEncodingHandlers();
3238
3239 let name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
3240 assert!(!find_encoding_handler(name).is_null());
3241
3242 xmlCleanupCharEncodingHandlers();
3243 }
3245}