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(
716 name_bytes: &[u8],
717 _input_enc: xmlCharEncoding,
718 _output_enc: xmlCharEncoding,
719 input_func: Option<xmlCharEncodingInputFunc>,
720 output_func: Option<xmlCharEncodingOutputFunc>,
721) {
722 let name_raw =
723 unsafe { crate::abi::allocator::xmlMemStrdupImpl(name_bytes.as_ptr() as *const c_char) };
724 if name_raw.is_null() {
725 return;
726 }
727
728 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
729 as *mut _xmlCharEncodingHandler;
730
731 if handler.is_null() {
732 unsafe { xmlFreeImpl(name_raw) };
733 return;
734 }
735
736 unsafe {
737 ptr::write(
738 handler,
739 _xmlCharEncodingHandler {
740 name: name_raw as *mut c_char,
741 input: EncodingInputUnion {
742 legacyFunc: input_func,
743 },
744 output: EncodingOutputUnion {
745 legacyFunc: output_func,
746 },
747 inputCtxt: ptr::null_mut(),
748 outputCtxt: ptr::null_mut(),
749 ctxtDtor: None,
750 flags: 0,
751 },
752 );
753 }
754
755 add_encoding_handler(handler);
756}
757
758pub(crate) fn cleanup_encodings() {
769 let mut handlers = ENCODING_HANDLERS.write();
770 for &handler in handlers.iter() {
771 let ptr = handler.0;
772 if !ptr.is_null() {
773 unsafe {
774 if !(*ptr).name.is_null() {
775 xmlFreeImpl((*ptr).name as *mut c_void);
776 }
777 xmlFreeImpl(ptr as *mut c_void);
778 }
779 }
780 }
781 handlers.clear();
782 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
783}
784
785pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
797 if name.is_null() {
798 return ptr::null_mut();
799 }
800
801 init_encodings();
805
806 let name_str = unsafe {
807 match CStr::from_ptr(name as *const c_char).to_bytes() {
808 b"" => return ptr::null_mut(),
809 s => s,
810 }
811 };
812
813 let handlers = ENCODING_HANDLERS.read();
814 for &handler in handlers.iter() {
815 let ptr = handler.0;
816 if ptr.is_null() {
817 continue;
818 }
819 let h_name = unsafe {
820 if (*ptr).name.is_null() {
821 continue;
822 }
823 CStr::from_ptr((*ptr).name).to_bytes()
824 };
825
826 if name_str.eq_ignore_ascii_case(h_name) {
827 return ptr;
828 }
829 }
830
831 ptr::null_mut()
832}
833
834pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
838 if handler.is_null() {
839 return -1;
840 }
841
842 let mut handlers = ENCODING_HANDLERS.write();
843 handlers.push(HandlerPtr(handler));
844 0
845}
846
847#[allow(dead_code)]
863pub(crate) fn char_enc_in_func(
864 handler: *mut _xmlCharEncodingHandler,
865 out: &mut [u8],
866 in_data: &[u8],
867) -> c_int {
868 if handler.is_null() {
869 return -1;
870 }
871
872 let h = unsafe { &*handler };
873 let input_func = unsafe { h.input.legacyFunc };
874 let input_func = match input_func {
875 Some(f) => f,
876 None => return -1,
877 };
878
879 let mut outlen = out.len() as c_int;
880 let mut inlen = in_data.len() as c_int;
881
882 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
883}
884
885#[allow(dead_code)]
897pub(crate) fn char_enc_out_func(
898 handler: *mut _xmlCharEncodingHandler,
899 out: &mut [u8],
900 in_data: &[u8],
901) -> c_int {
902 if handler.is_null() {
903 return -1;
904 }
905
906 let h = unsafe { &*handler };
907 let output_func = unsafe { h.output.legacyFunc };
908 let output_func = match output_func {
909 Some(f) => f,
910 None => return -1,
911 };
912
913 let mut outlen = out.len() as c_int;
914 let mut inlen = in_data.len() as c_int;
915
916 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
917}
918
919pub(crate) fn char_enc_in(
935 handler: *mut _xmlCharEncodingHandler,
936 out: *mut _xmlBuffer,
937 in_: *mut _xmlBuffer,
938) -> c_int {
939 if handler.is_null() || out.is_null() || in_.is_null() {
940 return -1;
941 }
942
943 let h = unsafe { &*handler };
944 let input_func = unsafe { h.input.legacyFunc };
945 let input_func = match input_func {
946 Some(f) => f,
947 None => return -1,
948 };
949
950 let in_buf = unsafe { &*in_ };
951 let out_buf = unsafe { &mut *out };
952
953 if in_buf.content.is_null() || in_buf.use_ == 0 {
954 return 0;
955 }
956
957 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
958
959 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
961 let mut out_vec = vec![0u8; out_capacity];
962 let mut out_len = out_capacity as c_int;
963 let mut in_len = in_buf.use_ as c_int;
964
965 let ret = unsafe {
966 input_func(
967 out_vec.as_mut_ptr(),
968 &mut out_len,
969 in_data.as_ptr(),
970 &mut in_len,
971 )
972 };
973
974 if ret < 0 {
975 return -1;
976 }
977
978 let written = ret as usize;
979
980 append_to_xml_buffer(out_buf, &out_vec[..written]);
982
983 written as c_int
984}
985
986pub(crate) fn char_enc_out(
1002 handler: *mut _xmlCharEncodingHandler,
1003 out: *mut _xmlBuffer,
1004 in_: *mut _xmlBuffer,
1005) -> c_int {
1006 if handler.is_null() || out.is_null() || in_.is_null() {
1007 return -1;
1008 }
1009
1010 let h = unsafe { &*handler };
1011 let output_func = unsafe { h.output.legacyFunc };
1012 let output_func = match output_func {
1013 Some(f) => f,
1014 None => return -1,
1015 };
1016
1017 let in_buf = unsafe { &*in_ };
1018 let out_buf = unsafe { &mut *out };
1019
1020 if in_buf.content.is_null() || in_buf.use_ == 0 {
1021 return 0;
1022 }
1023
1024 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
1025
1026 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
1027 let mut out_vec = vec![0u8; out_capacity];
1028 let mut out_len = out_capacity as c_int;
1029 let mut in_len = in_buf.use_ as c_int;
1030
1031 let ret = unsafe {
1032 output_func(
1033 out_vec.as_mut_ptr(),
1034 &mut out_len,
1035 in_data.as_ptr(),
1036 &mut in_len,
1037 )
1038 };
1039
1040 if ret < 0 {
1041 return -1;
1042 }
1043
1044 let written = ret as usize;
1045
1046 append_to_xml_buffer(out_buf, &out_vec[..written]);
1048
1049 written as c_int
1050}
1051
1052fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
1062 if data.is_empty() {
1063 return;
1064 }
1065
1066 let new_use = (buf.use_ as usize).saturating_add(data.len());
1067 if new_use > buf.size as usize {
1068 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
1070 let new_content =
1071 unsafe { xmlReallocImpl(buf.content as *mut c_void, new_size) as *mut xmlChar };
1072 if new_content.is_null() {
1073 return; }
1075 buf.content = new_content;
1076 buf.size = new_size as c_uint;
1077 }
1078
1079 unsafe {
1080 ptr::copy_nonoverlapping(
1081 data.as_ptr(),
1082 buf.content.add(buf.use_ as usize),
1083 data.len(),
1084 );
1085 }
1086 buf.use_ = new_use as c_uint;
1087}
1088
1089unsafe extern "C" fn utf8_input_func(
1099 out: *mut c_uchar,
1100 outlen: *mut c_int,
1101 in_: *const c_uchar,
1102 inlen: *mut c_int,
1103) -> c_int {
1104 let avail_out = *outlen as usize;
1105 let avail_in = *inlen as usize;
1106 let to_copy = avail_out.min(avail_in);
1107
1108 if to_copy > 0 {
1109 ptr::copy_nonoverlapping(in_, out, to_copy);
1110 }
1111
1112 *outlen = to_copy as c_int;
1113 *inlen = to_copy as c_int;
1114 to_copy as c_int
1115}
1116
1117unsafe extern "C" fn utf8_output_func(
1119 out: *mut c_uchar,
1120 outlen: *mut c_int,
1121 in_: *const c_uchar,
1122 inlen: *mut c_int,
1123) -> c_int {
1124 utf8_input_func(out, outlen, in_, inlen)
1125}
1126
1127unsafe extern "C" fn utf16le_input_func(
1131 out: *mut c_uchar,
1132 outlen: *mut c_int,
1133 in_: *const c_uchar,
1134 inlen: *mut c_int,
1135) -> c_int {
1136 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1137 return -1;
1138 }
1139
1140 let avail_in = *inlen as usize;
1141 let avail_out = *outlen as usize;
1142
1143 if avail_in == 0 || avail_out == 0 {
1144 *outlen = 0;
1145 *inlen = 0;
1146 return 0;
1147 }
1148
1149 let in_data = core::slice::from_raw_parts(in_, avail_in);
1150 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1151
1152 let result = match utf16le_to_utf8(in_data) {
1154 Ok(v) => v,
1155 Err(()) => return -1,
1156 };
1157
1158 let written = result.len().min(avail_out);
1159 if written > 0 {
1160 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1161 }
1162
1163 *outlen = written as c_int;
1164 *inlen = avail_in as c_int; written as c_int
1166}
1167
1168unsafe extern "C" fn utf16le_output_func(
1170 out: *mut c_uchar,
1171 outlen: *mut c_int,
1172 in_: *const c_uchar,
1173 inlen: *mut c_int,
1174) -> c_int {
1175 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1176 return -1;
1177 }
1178
1179 let avail_in = *inlen as usize;
1180 let avail_out = *outlen as usize;
1181
1182 if avail_in == 0 || avail_out == 0 {
1183 *outlen = 0;
1184 *inlen = 0;
1185 return 0;
1186 }
1187
1188 let in_data = core::slice::from_raw_parts(in_, avail_in);
1189 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1190
1191 let result = match utf8_to_utf16le(in_data) {
1192 Ok(v) => v,
1193 Err(()) => return -1,
1194 };
1195
1196 let written = result.len().min(avail_out);
1197 if written > 0 {
1198 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1199 }
1200
1201 *outlen = written as c_int;
1202 *inlen = avail_in as c_int;
1203 written as c_int
1204}
1205
1206unsafe extern "C" fn utf16be_input_func(
1210 out: *mut c_uchar,
1211 outlen: *mut c_int,
1212 in_: *const c_uchar,
1213 inlen: *mut c_int,
1214) -> c_int {
1215 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1216 return -1;
1217 }
1218
1219 let avail_in = *inlen as usize;
1220 let avail_out = *outlen as usize;
1221
1222 if avail_in == 0 || avail_out == 0 {
1223 *outlen = 0;
1224 *inlen = 0;
1225 return 0;
1226 }
1227
1228 let in_data = core::slice::from_raw_parts(in_, avail_in);
1229 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1230
1231 let result = match utf16be_to_utf8(in_data) {
1232 Ok(v) => v,
1233 Err(()) => return -1,
1234 };
1235
1236 let written = result.len().min(avail_out);
1237 if written > 0 {
1238 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1239 }
1240
1241 *outlen = written as c_int;
1242 *inlen = avail_in as c_int;
1243 written as c_int
1244}
1245
1246unsafe extern "C" fn utf16be_output_func(
1248 out: *mut c_uchar,
1249 outlen: *mut c_int,
1250 in_: *const c_uchar,
1251 inlen: *mut c_int,
1252) -> c_int {
1253 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1254 return -1;
1255 }
1256
1257 let avail_in = *inlen as usize;
1258 let avail_out = *outlen as usize;
1259
1260 if avail_in == 0 || avail_out == 0 {
1261 *outlen = 0;
1262 *inlen = 0;
1263 return 0;
1264 }
1265
1266 let in_data = core::slice::from_raw_parts(in_, avail_in);
1267
1268 let le_result = match utf8_to_utf16le(in_data) {
1270 Ok(v) => v,
1271 Err(()) => return -1,
1272 };
1273
1274 let mut result = le_result;
1276 for chunk in result.as_chunks_mut::<2>().0 {
1277 chunk.swap(0, 1);
1278 }
1279
1280 let written = result.len().min(avail_out);
1281 if written > 0 {
1282 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1283 }
1284
1285 *outlen = written as c_int;
1286 *inlen = avail_in as c_int;
1287 written as c_int
1288}
1289
1290unsafe extern "C" fn latin1_input_func(
1294 out: *mut c_uchar,
1295 outlen: *mut c_int,
1296 in_: *const c_uchar,
1297 inlen: *mut c_int,
1298) -> c_int {
1299 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1300 return -1;
1301 }
1302
1303 let avail_in = *inlen as usize;
1304 let avail_out = *outlen as usize;
1305
1306 if avail_in == 0 || avail_out == 0 {
1307 *outlen = 0;
1308 *inlen = 0;
1309 return 0;
1310 }
1311
1312 let in_data = core::slice::from_raw_parts(in_, avail_in);
1313 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1314
1315 let mut in_pos = 0;
1316 let mut out_pos = 0;
1317
1318 while in_pos < avail_in && out_pos < avail_out {
1319 let byte = in_data[in_pos];
1320 in_pos += 1;
1321
1322 if byte < 0x80 {
1323 if out_pos < avail_out {
1325 out_slice[out_pos] = byte;
1326 out_pos += 1;
1327 } else {
1328 break;
1329 }
1330 } else {
1331 if out_pos + 1 < avail_out {
1334 out_slice[out_pos] = 0xC2 | (byte >> 6);
1335 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1336 out_pos += 2;
1337 } else {
1338 break;
1339 }
1340 }
1341 }
1342
1343 *outlen = out_pos as c_int;
1344 *inlen = in_pos as c_int;
1345 out_pos as c_int
1346}
1347
1348unsafe extern "C" fn latin1_output_func(
1350 out: *mut c_uchar,
1351 outlen: *mut c_int,
1352 in_: *const c_uchar,
1353 inlen: *mut c_int,
1354) -> c_int {
1355 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1356 return -1;
1357 }
1358
1359 let avail_in = *inlen as usize;
1360 let avail_out = *outlen as usize;
1361
1362 if avail_in == 0 || avail_out == 0 {
1363 *outlen = 0;
1364 *inlen = 0;
1365 return 0;
1366 }
1367
1368 let in_data = core::slice::from_raw_parts(in_, avail_in);
1369 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1370
1371 let mut in_pos = 0;
1372 let mut out_pos = 0;
1373
1374 while in_pos < avail_in && out_pos < avail_out {
1375 let byte = in_data[in_pos];
1376 in_pos += 1;
1377
1378 if byte < 0x80 {
1379 out_slice[out_pos] = byte;
1381 out_pos += 1;
1382 } else if (0xC2..=0xC3).contains(&byte) {
1383 if in_pos < avail_in {
1385 let second = in_data[in_pos];
1386 in_pos += 1;
1387 if second & 0xC0 != 0x80 {
1388 return -1; }
1390 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1391 if cp > 0xFF {
1392 return -1; }
1394 out_slice[out_pos] = cp as u8;
1395 out_pos += 1;
1396 } else {
1397 return -1; }
1399 } else if (0x80..=0xBF).contains(&byte) {
1400 return -1;
1402 } else {
1403 return -1;
1406 }
1407 }
1408
1409 *outlen = out_pos as c_int;
1410 *inlen = in_pos as c_int;
1411 out_pos as c_int
1412}
1413
1414unsafe extern "C" fn ascii_input_func(
1418 out: *mut c_uchar,
1419 outlen: *mut c_int,
1420 in_: *const c_uchar,
1421 inlen: *mut c_int,
1422) -> c_int {
1423 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1424 return -1;
1425 }
1426
1427 let avail_in = *inlen as usize;
1428 let avail_out = *outlen as usize;
1429
1430 if avail_in == 0 || avail_out == 0 {
1431 *outlen = 0;
1432 *inlen = 0;
1433 return 0;
1434 }
1435
1436 let in_data = core::slice::from_raw_parts(in_, avail_in);
1437 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1438
1439 let mut pos = 0;
1440 while pos < avail_in && pos < avail_out {
1441 let byte = in_data[pos];
1442 if byte > 0x7F {
1443 return -1; }
1445 out_slice[pos] = byte;
1446 pos += 1;
1447 }
1448
1449 *outlen = pos as c_int;
1450 *inlen = pos as c_int;
1451 pos as c_int
1452}
1453
1454unsafe extern "C" fn ascii_output_func(
1456 out: *mut c_uchar,
1457 outlen: *mut c_int,
1458 in_: *const c_uchar,
1459 inlen: *mut c_int,
1460) -> c_int {
1461 ascii_input_func(out, outlen, in_, inlen)
1463}
1464
1465pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
1474 if name.is_null() {
1475 return ptr::null_mut();
1476 }
1477 find_encoding_handler(name as *const xmlChar)
1478}
1479
1480pub(crate) const fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
1484 match enc {
1488 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
1489 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE | xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => {
1490 c"UTF-16".as_ptr()
1491 }
1492 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE | xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => {
1493 c"UCS-4".as_ptr()
1494 }
1495 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"IBM037".as_ptr(),
1496 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
1497 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
1498 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
1499 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
1500 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
1501 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
1502 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
1503 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
1504 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
1505 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
1506 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
1507 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"Shift_JIS".as_ptr(),
1508 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
1509 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"US-ASCII".as_ptr(),
1511 _ => ptr::null(),
1512 }
1513}
1514
1515pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
1524 if name.is_null() {
1525 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
1526 }
1527 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
1528 encoding_from_name(bytes) as c_int
1529}
1530
1531static ENCODING_ALIASES: std::sync::OnceLock<
1539 parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>>,
1540> = std::sync::OnceLock::new();
1541
1542fn encoding_aliases() -> &'static parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>> {
1543 ENCODING_ALIASES.get_or_init(|| parking_lot::RwLock::new(std::collections::HashMap::new()))
1544}
1545
1546pub(crate) fn add_encoding_alias(name: *const c_char, alias: *const c_char) -> c_int {
1554 if name.is_null() || alias.is_null() {
1555 return -1;
1556 }
1557 let n = unsafe { CStr::from_ptr(name).to_bytes().to_vec() };
1558 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1559 encoding_aliases().write().insert(a, n);
1560 0
1561}
1562
1563pub(crate) fn del_encoding_alias(alias: *const c_char) -> c_int {
1570 if alias.is_null() {
1571 return -1;
1572 }
1573 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1574 if encoding_aliases().write().remove(&a).is_some() {
1575 0
1576 } else {
1577 -1
1578 }
1579}
1580
1581pub(crate) fn get_encoding_alias(alias: *const c_char) -> *const c_char {
1590 if alias.is_null() {
1591 return ptr::null();
1592 }
1593 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1594 let guard = encoding_aliases().read();
1595 match guard.get(&a) {
1596 Some(v) => {
1597 let leaked: &'static [u8] = Box::leak(v.clone().into_boxed_slice());
1600 leaked.as_ptr() as *const c_char
1601 }
1602 None => ptr::null(),
1603 }
1604}
1605
1606pub(crate) fn cleanup_encoding_aliases() {
1608 encoding_aliases().write().clear();
1609}
1610
1611pub(crate) fn xmlCharEncInFunc(
1615 handler: *mut _xmlCharEncodingHandler,
1616 out: *mut _xmlBuffer,
1617 in_: *mut _xmlBuffer,
1618) -> c_int {
1619 char_enc_in(handler, out, in_)
1620}
1621
1622pub(crate) fn xmlCharEncOutFunc(
1626 handler: *mut _xmlCharEncodingHandler,
1627 out: *mut _xmlBuffer,
1628 in_: *mut _xmlBuffer,
1629) -> c_int {
1630 char_enc_out(handler, out, in_)
1631}
1632
1633pub(crate) fn xmlNewCharEncodingHandler(
1647 name: *const c_char,
1648 input: xmlCharEncodingInputFunc,
1649 output: xmlCharEncodingOutputFunc,
1650) -> *mut _xmlCharEncodingHandler {
1651 if name.is_null() {
1652 return ptr::null_mut();
1653 }
1654
1655 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) };
1656 if name_raw.is_null() {
1657 return ptr::null_mut();
1658 }
1659
1660 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
1661 as *mut _xmlCharEncodingHandler;
1662
1663 if handler.is_null() {
1664 unsafe { xmlFreeImpl(name_raw) };
1665 return ptr::null_mut();
1666 }
1667
1668 unsafe {
1669 ptr::write(
1670 handler,
1671 _xmlCharEncodingHandler {
1672 name: name_raw as *mut c_char,
1673 input: EncodingInputUnion {
1674 legacyFunc: Some(input),
1675 },
1676 output: EncodingOutputUnion {
1677 legacyFunc: Some(output),
1678 },
1679 inputCtxt: ptr::null_mut(),
1680 outputCtxt: ptr::null_mut(),
1681 ctxtDtor: None,
1682 flags: 0,
1683 },
1684 );
1685 }
1686
1687 handler
1688}
1689
1690#[allow(dead_code)]
1701pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
1702 if handler.is_null() {
1703 return;
1704 }
1705
1706 {
1708 let mut handlers = ENCODING_HANDLERS.write();
1709 handlers.retain(|&h| h.0 != handler);
1710 }
1711
1712 unsafe {
1713 if !(*handler).name.is_null() {
1714 xmlFreeImpl((*handler).name as *mut c_void);
1715 }
1716 xmlFreeImpl(handler as *mut c_void);
1717 }
1718}
1719
1720pub(crate) fn xmlInitCharEncodingHandlers() {
1722 init_encodings();
1723}
1724
1725pub(crate) fn xmlCleanupCharEncodingHandlers() {
1727 cleanup_encodings();
1728}
1729
1730pub(crate) fn xmlLookupCharEncodingHandler(enc: c_int, out: *mut *mut c_void) -> c_int {
1758 if out.is_null() {
1759 return crate::abi::types::XML_ERR_ARGUMENT;
1760 }
1761 unsafe {
1762 *out = ptr::null_mut();
1763 }
1764 if enc <= 0 || enc >= 32 {
1765 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1766 }
1767 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int {
1769 return crate::abi::types::XML_ERR_OK;
1770 }
1771 let canonical: &[u8] = match enc {
1772 2 => b"UTF-16LE\0",
1774 3 => b"UTF-16BE\0",
1776 10 => b"ISO-8859-1\0",
1778 22 => b"US-ASCII\0",
1780 23 => b"UTF-16\0",
1782 _ => return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING,
1783 };
1784 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1785 if h.is_null() {
1786 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1787 }
1788 unsafe {
1789 *out = h as *mut c_void;
1790 }
1791 crate::abi::types::XML_ERR_OK
1792}
1793
1794pub(crate) fn xmlGetCharEncodingHandler(enc: c_int) -> *mut c_void {
1796 let mut ret: *mut c_void = ptr::null_mut();
1797 let _rc = xmlLookupCharEncodingHandler(enc, &mut ret);
1798 ret
1799}
1800
1801pub(crate) fn xmlCreateCharEncodingHandler(
1816 name: *const c_char,
1817 flags: c_int,
1818 impl_: Option<xmlCharEncConvImpl>,
1819 implCtxt: *mut c_void,
1820 out: *mut *mut c_void,
1821) -> c_int {
1822 if out.is_null() {
1823 return crate::abi::types::XML_ERR_ARGUMENT;
1824 }
1825 unsafe {
1826 *out = ptr::null_mut();
1827 }
1828 if name.is_null() || flags == 0 {
1829 return crate::abi::types::XML_ERR_ARGUMENT;
1830 }
1831 let norig = unsafe { CStr::from_ptr(name).to_bytes() };
1832
1833 let mut eff: &[u8] = norig;
1835 let alias = get_encoding_alias(name);
1836 if !alias.is_null() {
1837 eff = unsafe { CStr::from_ptr(alias).to_bytes() };
1838 }
1839
1840 let enc = encoding_from_name(eff);
1841
1842 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
1844 return crate::abi::types::XML_ERR_OK;
1845 }
1846
1847 let canonical: &[u8] = match enc {
1848 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => b"UTF-16LE\0",
1849 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => b"UTF-16BE\0",
1850 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => b"ISO-8859-1\0",
1851 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => b"US-ASCII\0",
1852 _ => {
1853 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1854 }
1855 };
1856 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1857 if h.is_null() {
1858 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1859 }
1860 unsafe {
1861 let src = &*h;
1862 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1863 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1864 if !has_in || !has_out {
1865 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1866 }
1867 let copy =
1872 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler;
1873 if copy.is_null() {
1874 return crate::abi::types::XML_ERR_NO_MEMORY;
1875 }
1876 let name_copy = crate::abi::allocator::xmlMemStrdupImpl(name) as *mut c_char;
1877 if name_copy.is_null() {
1878 xmlFreeImpl(copy as *mut c_void);
1879 return crate::abi::types::XML_ERR_NO_MEMORY;
1880 }
1881 ptr::write(
1882 copy,
1883 _xmlCharEncodingHandler {
1884 name: name_copy,
1885 input: EncodingInputUnion {
1886 legacyFunc: src.input.legacyFunc,
1887 },
1888 output: EncodingOutputUnion {
1889 legacyFunc: src.output.legacyFunc,
1890 },
1891 inputCtxt: src.inputCtxt,
1892 outputCtxt: src.outputCtxt,
1893 ctxtDtor: src.ctxtDtor,
1894 flags: src.flags,
1895 },
1896 );
1897 *out = copy as *mut c_void;
1898 }
1899 crate::abi::types::XML_ERR_OK
1900}
1901
1902fn find_extra_handler(
1917 norig: &[u8],
1918 name: &[u8],
1919 flags: c_int,
1920 impl_: Option<xmlCharEncConvImpl>,
1921 implCtxt: *mut c_void,
1922 out: *mut *mut c_void,
1923) -> c_int {
1924 if let Some(f) = impl_ {
1926 let mut n = norig.to_vec();
1927 n.push(0);
1928 let rc = unsafe {
1929 f(
1930 implCtxt,
1931 n.as_ptr() as *const c_char,
1932 flags,
1933 out as *mut *mut crate::abi::structs::_xmlCharEncodingHandler,
1934 )
1935 };
1936 return rc;
1937 }
1938 let mut n = name.to_vec();
1940 n.push(0);
1941 let h = find_encoding_handler(n.as_ptr() as *const xmlChar);
1942 if !h.is_null() {
1943 unsafe {
1944 let src = &*h;
1945 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1946 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1947 if has_in && has_out {
1948 *out = h as *mut c_void;
1949 return crate::abi::types::XML_ERR_OK;
1950 }
1951 }
1952 }
1953 crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING
1954}
1955
1956pub(crate) fn xmlOpenCharEncodingHandler(
1958 name: *const c_char,
1959 output: c_int,
1960 out: *mut *mut c_void,
1961) -> c_int {
1962 let flags: c_int = if output != 0 { 2 } else { 1 };
1964 xmlCreateCharEncodingHandler(name, flags, None, ptr::null_mut(), out)
1965}
1966
1967pub(crate) fn xmlCharEncNewCustomHandler(
1982 name: *const c_char,
1983 input: xmlCharEncConvFunc,
1984 output: xmlCharEncConvFunc,
1985 ctxtDtor: Option<xmlCharEncConvCtxtDtor>,
1986 inputCtxt: *mut c_void,
1987 outputCtxt: *mut c_void,
1988 out: *mut *mut c_void,
1989) -> c_int {
1990 if out.is_null() {
1991 return crate::abi::types::XML_ERR_ARGUMENT;
1992 }
1993 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
1994 as *mut _xmlCharEncodingHandler;
1995 if handler.is_null() {
1996 unsafe {
1997 if let Some(d) = ctxtDtor {
1998 if !inputCtxt.is_null() {
1999 d(inputCtxt);
2000 }
2001 if !outputCtxt.is_null() {
2002 d(outputCtxt);
2003 }
2004 }
2005 }
2006 return crate::abi::types::XML_ERR_NO_MEMORY;
2007 }
2008 let name_copy = if name.is_null() {
2009 ptr::null_mut()
2010 } else {
2011 let nc = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) } as *mut c_char;
2012 if nc.is_null() {
2013 unsafe { xmlFreeImpl(handler as *mut c_void) };
2014 unsafe {
2015 if let Some(d) = ctxtDtor {
2016 if !inputCtxt.is_null() {
2017 d(inputCtxt);
2018 }
2019 if !outputCtxt.is_null() {
2020 d(outputCtxt);
2021 }
2022 }
2023 }
2024 return crate::abi::types::XML_ERR_NO_MEMORY;
2025 }
2026 nc
2027 };
2028 unsafe {
2029 ptr::write(
2030 handler,
2031 _xmlCharEncodingHandler {
2032 name: name_copy,
2033 input: EncodingInputUnion { func: Some(input) },
2034 output: EncodingOutputUnion { func: Some(output) },
2035 inputCtxt,
2036 outputCtxt,
2037 ctxtDtor,
2038 flags: 0,
2039 },
2040 );
2041 *out = handler as *mut c_void;
2042 }
2043 crate::abi::types::XML_ERR_OK
2044}
2045
2046#[cfg(test)]
2051mod tests {
2052 use super::*;
2053
2054 #[test]
2057 fn test_detect_bom_utf8() {
2058 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
2059 assert_eq!(
2060 detect_encoding_from_bom(&data),
2061 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2062 );
2063 }
2064
2065 #[test]
2066 fn test_detect_bom_utf16le() {
2067 let data = [0xFF, 0xFE, 0x00, 0x01];
2068 assert_eq!(
2069 detect_encoding_from_bom(&data),
2070 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2071 );
2072 }
2073
2074 #[test]
2075 fn test_detect_bom_utf16be() {
2076 let data = [0xFE, 0xFF, 0x00, 0x01];
2077 assert_eq!(
2078 detect_encoding_from_bom(&data),
2079 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2080 );
2081 }
2082
2083 #[test]
2084 fn test_detect_bom_none() {
2085 let data = b"<xml>";
2086 assert_eq!(
2087 detect_encoding_from_bom(data),
2088 xmlCharEncoding::XML_CHAR_ENCODING_NONE
2089 );
2090 }
2091
2092 #[test]
2093 fn test_detect_bom_empty() {
2094 assert_eq!(
2095 detect_encoding_from_bom(b""),
2096 xmlCharEncoding::XML_CHAR_ENCODING_NONE
2097 );
2098 }
2099
2100 #[test]
2103 fn test_detect_encoding_declaration_utf8() {
2104 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
2105 let result = detect_encoding_from_declaration(data);
2106 assert_eq!(result, Some(b"utf-8".to_vec()));
2107 }
2108
2109 #[test]
2110 fn test_detect_encoding_declaration_iso() {
2111 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
2112 let result = detect_encoding_from_declaration(data);
2113 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
2114 }
2115
2116 #[test]
2117 fn test_detect_encoding_declaration_none() {
2118 let data = b"<?xml version=\"1.0\"?>";
2119 let result = detect_encoding_from_declaration(data);
2120 assert!(result.is_none());
2121 }
2122
2123 #[test]
2124 fn test_detect_encoding_declaration_no_xml() {
2125 let data = b"<root>";
2126 let result = detect_encoding_from_declaration(data);
2127 assert!(result.is_none());
2128 }
2129
2130 #[test]
2131 fn test_detect_encoding_declaration_with_bom() {
2132 let mut data = vec![0xEF, 0xBB, 0xBF];
2133 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
2134 let result = detect_encoding_from_declaration(&data);
2135 assert_eq!(result, Some(b"utf-8".to_vec()));
2136 }
2137
2138 #[test]
2141 fn test_encoding_from_name_utf8() {
2142 assert_eq!(
2143 encoding_from_name(b"UTF-8"),
2144 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2145 );
2146 assert_eq!(
2147 encoding_from_name(b"utf8"),
2148 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2149 );
2150 }
2151
2152 #[test]
2153 fn test_encoding_from_name_utf16() {
2154 assert_eq!(
2155 encoding_from_name(b"UTF-16LE"),
2156 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2157 );
2158 assert_eq!(
2159 encoding_from_name(b"UTF-16BE"),
2160 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2161 );
2162 assert_eq!(
2163 encoding_from_name(b"utf-16"),
2164 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2165 );
2166 }
2167
2168 #[test]
2169 fn test_encoding_from_name_latin1() {
2170 assert_eq!(
2171 encoding_from_name(b"ISO-8859-1"),
2172 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2173 );
2174 assert_eq!(
2175 encoding_from_name(b"Latin1"),
2176 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2177 );
2178 }
2179
2180 #[test]
2181 fn test_encoding_from_name_ascii() {
2182 assert_eq!(
2183 encoding_from_name(b"ASCII"),
2184 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2185 );
2186 assert_eq!(
2187 encoding_from_name(b"US-ASCII"),
2188 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2189 );
2190 }
2191
2192 #[test]
2193 fn test_encoding_from_name_error() {
2194 assert_eq!(
2195 encoding_from_name(b"invalid-encoding"),
2196 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2197 );
2198 }
2199
2200 #[test]
2201 fn test_encoding_from_name_empty() {
2202 assert_eq!(
2203 encoding_from_name(b""),
2204 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2205 );
2206 }
2207
2208 #[test]
2211 fn test_encoding_name_utf8() {
2212 assert_eq!(
2213 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
2214 Some(b"UTF-8" as &[u8])
2215 );
2216 }
2217
2218 #[test]
2219 fn test_encoding_name_utf16le() {
2220 assert_eq!(
2221 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
2222 Some(b"UTF-16LE" as &[u8])
2223 );
2224 }
2225
2226 #[test]
2227 fn test_encoding_name_none() {
2228 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
2229 }
2230
2231 #[test]
2232 fn test_encoding_name_error() {
2233 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
2234 }
2235
2236 #[test]
2239 fn test_utf8_valid_ascii() {
2240 assert!(utf8_valid(b"hello world"));
2241 }
2242
2243 #[test]
2244 fn test_utf8_valid_multi_byte() {
2245 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
2246 }
2247
2248 #[test]
2249 fn test_utf8_valid_empty() {
2250 assert!(utf8_valid(b""));
2251 }
2252
2253 #[test]
2254 fn test_utf8_invalid() {
2255 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
2256 }
2257
2258 #[test]
2261 fn test_valid_xml_chars() {
2262 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));
2268 assert!(is_valid_xml_char(0xE000));
2269 assert!(is_valid_xml_char(0xFFFD));
2270 assert!(is_valid_xml_char(0x10000));
2271 assert!(is_valid_xml_char(0x10FFFF));
2272 }
2273
2274 #[test]
2275 fn test_invalid_xml_chars() {
2276 assert!(!is_valid_xml_char(0x00));
2277 assert!(!is_valid_xml_char(0x08));
2278 assert!(!is_valid_xml_char(0x0B));
2279 assert!(!is_valid_xml_char(0x0C));
2280 assert!(!is_valid_xml_char(0x0E));
2281 assert!(!is_valid_xml_char(0x1F));
2282 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
2285 assert!(!is_valid_xml_char(0xFFFF));
2286 assert!(!is_valid_xml_char(0x110000));
2287 }
2288
2289 #[test]
2292 fn test_utf16le_to_utf8_ascii() {
2293 let data = [b'A', 0x00, b'B', 0x00];
2295 let result = utf16le_to_utf8(&data).unwrap();
2296 assert_eq!(result, b"AB");
2297 }
2298
2299 #[test]
2300 fn test_utf16le_to_utf8_bom() {
2301 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
2303 let result = utf16le_to_utf8(&data).unwrap();
2304 assert_eq!(result, b"AB");
2305 }
2306
2307 #[test]
2308 fn test_utf16le_to_utf8_bmp() {
2309 let data = [0xE9, 0x00];
2311 let result = utf16le_to_utf8(&data).unwrap();
2312 assert_eq!(result, "é".as_bytes());
2313 }
2314
2315 #[test]
2316 fn test_utf16le_to_utf8_supplementary() {
2317 let data = [0x3D, 0xD8, 0x00, 0xDE];
2319 let result = utf16le_to_utf8(&data).unwrap();
2320 assert_eq!(result, "😀".as_bytes());
2321 }
2322
2323 #[test]
2324 fn test_utf16le_to_utf8_unpaired_surrogate() {
2325 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
2327 }
2328
2329 #[test]
2330 fn test_utf16le_to_utf8_truncated() {
2331 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
2333 }
2334
2335 #[test]
2336 fn test_utf16le_to_utf8_empty() {
2337 let result = utf16le_to_utf8(b"").unwrap();
2338 assert!(result.is_empty());
2339 }
2340
2341 #[test]
2344 fn test_utf16be_to_utf8_ascii() {
2345 let data = [0x00, b'A', 0x00, b'B'];
2346 let result = utf16be_to_utf8(&data).unwrap();
2347 assert_eq!(result, b"AB");
2348 }
2349
2350 #[test]
2351 fn test_utf16be_to_utf8_bom() {
2352 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
2354 let result = utf16be_to_utf8(&data).unwrap();
2355 assert_eq!(result, b"AB");
2356 }
2357
2358 #[test]
2359 fn test_utf16be_to_utf8_supplementary() {
2360 let data = [0xD8, 0x3D, 0xDE, 0x00];
2362 let result = utf16be_to_utf8(&data).unwrap();
2363 assert_eq!(result, "😀".as_bytes());
2364 }
2365
2366 #[test]
2367 fn test_utf16be_to_utf8_empty() {
2368 let result = utf16be_to_utf8(b"").unwrap();
2369 assert!(result.is_empty());
2370 }
2371
2372 #[test]
2375 fn test_utf8_to_utf16le_ascii() {
2376 let result = utf8_to_utf16le(b"AB").unwrap();
2377 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
2378 }
2379
2380 #[test]
2381 fn test_utf8_to_utf16le_bmp() {
2382 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
2383 assert_eq!(result, [0xE9, 0x00]);
2384 }
2385
2386 #[test]
2387 fn test_utf8_to_utf16le_supplementary() {
2388 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
2389 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
2390 }
2391
2392 #[test]
2393 fn test_utf8_to_utf16le_invalid_utf8() {
2394 assert!(utf8_to_utf16le(&[0xFF]).is_err());
2395 }
2396
2397 #[test]
2398 fn test_utf8_to_utf16le_empty() {
2399 let result = utf8_to_utf16le(b"").unwrap();
2400 assert!(result.is_empty());
2401 }
2402
2403 #[test]
2406 fn test_latin1_to_utf8_ascii() {
2407 let result = latin1_to_utf8(b"ABC");
2408 assert_eq!(result, b"ABC");
2409 }
2410
2411 #[test]
2412 fn test_latin1_to_utf8_accented() {
2413 let result = latin1_to_utf8(&[0xE9]);
2415 assert_eq!(result, "é".as_bytes());
2416 }
2417
2418 #[test]
2419 fn test_latin1_to_utf8_all_255() {
2420 let result = latin1_to_utf8(&[0xFF]);
2421 assert_eq!(result, [0xC3, 0xBF]);
2423 }
2424
2425 #[test]
2426 fn test_latin1_to_utf8_empty() {
2427 let result = latin1_to_utf8(b"");
2428 assert!(result.is_empty());
2429 }
2430
2431 #[test]
2432 fn test_latin1_to_utf8_mixed() {
2433 let result = latin1_to_utf8(b"caf\xE9");
2434 assert_eq!(result, "café".as_bytes());
2435 }
2436
2437 #[test]
2440 fn test_utf8_to_latin1_ascii() {
2441 let result = utf8_to_latin1(b"ABC").unwrap();
2442 assert_eq!(result, b"ABC");
2443 }
2444
2445 #[test]
2446 fn test_utf8_to_latin1_accented() {
2447 let result = utf8_to_latin1("é".as_bytes()).unwrap();
2448 assert_eq!(result, [0xE9]);
2449 }
2450
2451 #[test]
2452 fn test_utf8_to_latin1_out_of_range() {
2453 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
2455
2456 #[test]
2457 fn test_utf8_to_latin1_invalid_utf8() {
2458 assert!(utf8_to_latin1(&[0xFF]).is_err());
2459 }
2460
2461 #[test]
2462 fn test_utf8_to_latin1_empty() {
2463 let result = utf8_to_latin1(b"").unwrap();
2464 assert!(result.is_empty());
2465 }
2466
2467 #[test]
2470 fn test_init_and_find_encodings() {
2471 init_encodings();
2472
2473 let utf8_name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2474 assert!(!find_encoding_handler(utf8_name).is_null());
2475
2476 let utf16le_name: *const xmlChar = c"UTF-16LE".as_ptr() as *const xmlChar;
2477 assert!(!find_encoding_handler(utf16le_name).is_null());
2478
2479 let utf16be_name: *const xmlChar = c"UTF-16BE".as_ptr() as *const xmlChar;
2480 assert!(!find_encoding_handler(utf16be_name).is_null());
2481
2482 let latin1_name: *const xmlChar = c"ISO-8859-1".as_ptr() as *const xmlChar;
2483 assert!(!find_encoding_handler(latin1_name).is_null());
2484
2485 let ascii_name: *const xmlChar = c"ASCII".as_ptr() as *const xmlChar;
2486 assert!(!find_encoding_handler(ascii_name).is_null());
2487
2488 let lower_name: *const xmlChar = c"utf-8".as_ptr() as *const xmlChar;
2490 assert!(!find_encoding_handler(lower_name).is_null());
2491 }
2492
2493 #[test]
2494 fn test_find_encoding_handler_not_found() {
2495 let name: *const xmlChar = c"NONEXISTENT".as_ptr() as *const xmlChar;
2496 assert!(find_encoding_handler(name).is_null());
2497 }
2498
2499 #[test]
2500 fn test_find_encoding_handler_null() {
2501 assert!(find_encoding_handler(ptr::null()).is_null());
2502 }
2503
2504 #[test]
2513 fn test_add_encoding_handler() {
2514 let handler = unsafe {
2515 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
2516 };
2517 assert!(!handler.is_null());
2518
2519 let name = unsafe {
2520 crate::abi::allocator::xmlMemStrdupImpl(c"TEST-ENC".as_ptr() as *const c_char)
2521 };
2522 unsafe {
2523 ptr::write(
2524 handler,
2525 _xmlCharEncodingHandler {
2526 name: name as *mut c_char,
2527 input: EncodingInputUnion { legacyFunc: None },
2528 output: EncodingOutputUnion { legacyFunc: None },
2529 inputCtxt: ptr::null_mut(),
2530 outputCtxt: ptr::null_mut(),
2531 ctxtDtor: None,
2532 flags: 0,
2533 },
2534 );
2535 }
2536
2537 assert_eq!(add_encoding_handler(handler), 0);
2538
2539 let found = find_encoding_handler(c"TEST-ENC".as_ptr() as *const xmlChar);
2540 assert_eq!(found, handler);
2541
2542 {
2544 let mut handlers = ENCODING_HANDLERS.write();
2545 handlers.retain(|&h| h.0 != handler);
2546 }
2547
2548 unsafe {
2549 xmlFreeImpl(name as *mut c_void);
2550 xmlFreeImpl(handler as *mut c_void);
2551 }
2552 }
2553
2554 #[test]
2557 fn test_utf16le_roundtrip() {
2558 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
2559 let utf16 = utf8_to_utf16le(original).unwrap();
2560 let back = utf16le_to_utf8(&utf16).unwrap();
2561 assert_eq!(original.to_vec(), back);
2562 }
2563
2564 #[test]
2565 fn test_utf16be_roundtrip() {
2566 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
2567 let utf16le = utf8_to_utf16le(original).unwrap();
2568 let mut utf16be = utf16le.clone();
2570 for chunk in utf16be.as_chunks_mut::<2>().0 {
2571 chunk.swap(0, 1);
2572 }
2573 let back = utf16be_to_utf8(&utf16be).unwrap();
2574 assert_eq!(original.to_vec(), back);
2575 }
2576
2577 #[test]
2578 fn test_latin1_roundtrip() {
2579 let original: Vec<u8> = (0x00..=0xFF).collect();
2580 let utf8 = latin1_to_utf8(&original);
2581 let back = utf8_to_latin1(&utf8).unwrap();
2582 assert_eq!(original, back);
2583 }
2584
2585 #[test]
2595 fn test_utf8_handler_identity() {
2596 let input = b"Hello, UTF-8!";
2597 let mut output = [0u8; 64];
2598 let mut outlen = output.len() as c_int;
2599 let mut inlen = input.len() as c_int;
2600
2601 let ret = unsafe {
2602 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
2603 };
2604
2605 assert_eq!(ret, input.len() as c_int);
2606 assert_eq!(&output[..ret as usize], input);
2607 assert_eq!(inlen, input.len() as c_int);
2608 }
2609
2610 #[test]
2618 fn test_utf16le_handler_roundtrip() {
2619 init_encodings();
2620
2621 let original = b"Hello UTF-16LE!";
2622 let mut utf16_buf = [0u8; 128];
2623 let mut outlen = utf16_buf.len() as c_int;
2624 let mut inlen = original.len() as c_int;
2625
2626 let written = unsafe {
2627 utf16le_output_func(
2628 utf16_buf.as_mut_ptr(),
2629 &mut outlen,
2630 original.as_ptr(),
2631 &mut inlen,
2632 )
2633 };
2634 assert!(written > 0);
2635
2636 let mut decoded = [0u8; 128];
2638 let mut outlen2 = decoded.len() as c_int;
2639 let mut inlen2 = written;
2640
2641 let written2 = unsafe {
2642 utf16le_input_func(
2643 decoded.as_mut_ptr(),
2644 &mut outlen2,
2645 utf16_buf.as_ptr(),
2646 &mut inlen2,
2647 )
2648 };
2649 assert_eq!(written2 as usize, original.len());
2650 assert_eq!(&decoded[..written2 as usize], original);
2651 }
2652
2653 #[test]
2663 fn test_append_to_xml_buffer() {
2664 unsafe {
2665 let content = xmlMallocImpl(64) as *mut xmlChar;
2666 assert!(!content.is_null());
2667
2668 let mut buf = _xmlBuffer {
2669 content,
2670 use_: 0,
2671 size: 64,
2672 alloc: 0,
2673 contentIO: ptr::null_mut(),
2674 };
2675
2676 append_to_xml_buffer(&mut buf, b"Hello");
2677 assert_eq!(buf.use_, 5);
2678 let slice = core::slice::from_raw_parts(buf.content, 5);
2679 assert_eq!(slice, b"Hello");
2680
2681 append_to_xml_buffer(&mut buf, b" World");
2682 assert_eq!(buf.use_, 11);
2683 let slice = core::slice::from_raw_parts(buf.content, 11);
2684 assert_eq!(slice, b"Hello World");
2685
2686 xmlFreeImpl(buf.content as *mut c_void);
2687 }
2688 }
2689
2690 #[test]
2693 fn test_xml_parse_char_encoding() {
2694 let name = c"UTF-8".as_ptr() as *const c_char;
2695 assert_eq!(
2696 xmlParseCharEncoding(name),
2697 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
2698 );
2699
2700 let name = c"ISO-8859-1".as_ptr() as *const c_char;
2701 assert_eq!(
2702 xmlParseCharEncoding(name),
2703 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
2704 );
2705
2706 assert_eq!(
2707 xmlParseCharEncoding(ptr::null()),
2708 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
2709 );
2710 }
2711
2712 #[test]
2721 fn test_xml_new_and_del_encoding_handler() {
2722 let name = c"TestEnc".as_ptr() as *const c_char;
2723 let handler = xmlNewCharEncodingHandler(
2724 name,
2725 utf8_input_func as xmlCharEncodingInputFunc,
2726 utf8_output_func as xmlCharEncodingOutputFunc,
2727 );
2728 assert!(!handler.is_null());
2729
2730 unsafe {
2731 assert!(!(*handler).name.is_null());
2732 let cstr = CStr::from_ptr((*handler).name);
2733 assert_eq!(cstr.to_bytes(), b"TestEnc");
2734 }
2735
2736 xmlDelEncodingHandler(handler);
2737 }
2738
2739 #[test]
2740 fn test_xml_init_and_cleanup() {
2741 xmlInitCharEncodingHandlers();
2742
2743 let name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
2744 assert!(!find_encoding_handler(name).is_null());
2745
2746 xmlCleanupCharEncodingHandlers();
2747 }
2749}