1#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
22
23use std::ffi::CStr;
24use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};
25use std::ptr;
26use std::sync::atomic::{AtomicBool, Ordering};
27
28use once_cell::sync::Lazy;
29use parking_lot::RwLock;
30
31use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlReallocImpl};
32use crate::abi::callbacks::{
33 xmlCharEncConvCtxtDtor, xmlCharEncConvFunc, xmlCharEncConvImpl, xmlCharEncodingInputFunc,
34 xmlCharEncodingOutputFunc,
35};
36use crate::abi::constants::XML_MAX_ENCODING_NAME_LEN;
37use crate::abi::structs::{
38 _xmlBuffer, _xmlCharEncodingHandler, EncodingInputUnion, EncodingOutputUnion,
39};
40use crate::abi::types::{xmlChar, xmlCharEncoding, xmlCharPtr};
41
42const MAX_CHAR_BYTES: usize = 6;
46
47const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
49
50const UTF16LE_BOM: [u8; 2] = [0xFF, 0xFE];
52
53const UTF16BE_BOM: [u8; 2] = [0xFE, 0xFF];
55
56#[derive(Clone, Copy)]
64struct HandlerPtr(*mut _xmlCharEncodingHandler);
65
66unsafe impl Send for HandlerPtr {}
67unsafe impl Sync for HandlerPtr {}
68
69static ENCODING_HANDLERS: Lazy<RwLock<Vec<HandlerPtr>>> = Lazy::new(|| RwLock::new(Vec::new()));
71
72static ENCODING_INITIALIZED: AtomicBool = AtomicBool::new(false);
74
75static ENCODING_INIT_MUTEX: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
77
78pub(crate) fn detect_encoding_from_bom(data: &[u8]) -> xmlCharEncoding {
87 if data.len() >= 3 && data[0..3] == UTF8_BOM {
88 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
89 } else if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
90 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
91 } else if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
92 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
93 } else {
94 xmlCharEncoding::XML_CHAR_ENCODING_NONE
95 }
96}
97
98pub(crate) fn detect_encoding_from_declaration(data: &[u8]) -> Option<Vec<u8>> {
103 let start = if data.len() >= 3 && data[0..3] == UTF8_BOM {
105 3
106 } else if data.len() >= 2 && (data[0..2] == UTF16LE_BOM || data[0..2] == UTF16BE_BOM) {
107 return None;
109 } else {
110 0
111 };
112
113 let remaining = &data[start..];
114
115 if remaining.len() < 5 || !remaining[0..5].eq_ignore_ascii_case(b"<?xml") {
117 return None;
118 }
119
120 let pi_end = remaining.windows(2).position(|w| w == b"?>")?;
122 let decl_content = &remaining[5..pi_end];
123
124 let decl_str = core::str::from_utf8(decl_content).ok()?;
126 let lower = decl_str.to_ascii_lowercase();
127
128 let enc_pos = lower.find("encoding")?;
130
131 let after_enc = &decl_content[enc_pos + 8..];
133 let after_enc_str = core::str::from_utf8(after_enc).ok()?;
134 let after_enc_trimmed = after_enc_str.trim_start();
135
136 if !after_enc_trimmed.starts_with('=') {
137 return None;
138 }
139
140 let after_eq = after_enc_trimmed[1..].trim_start();
141
142 let quote = after_eq.chars().next()?;
144 if quote != '"' && quote != '\'' {
145 return None;
146 }
147
148 let value_end = after_eq[1..].find(quote)?;
150 let encoding_value = &after_eq[1..=value_end];
151
152 Some(encoding_value.to_ascii_lowercase().as_bytes().to_vec())
153}
154
155pub(crate) fn encoding_from_name(name: &[u8]) -> xmlCharEncoding {
160 let s = core::str::from_utf8(name).unwrap_or("");
161 let s = s.trim().to_ascii_lowercase();
162
163 match s.as_str() {
164 "utf-8" | "utf8" => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
166
167 "utf-16" | "utf-16le" | "utf16le" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
169 "utf-16be" | "utf16be" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
170
171 "iso-8859-1" | "iso_8859-1" | "latin1" | "latin-1" | "l1" | "cp819" | "ibm819"
173 | "iso-ir-100" | "iso_8859-1:1987" => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
174 "iso-8859-2" | "iso_8859-2" | "latin2" | "latin-2" | "l2" => {
175 xmlCharEncoding::XML_CHAR_ENCODING_8859_2
176 }
177 "iso-8859-3" | "iso_8859-3" | "latin3" | "latin-3" | "l3" => {
178 xmlCharEncoding::XML_CHAR_ENCODING_8859_3
179 }
180 "iso-8859-4" | "iso_8859-4" | "latin4" | "latin-4" | "l4" => {
181 xmlCharEncoding::XML_CHAR_ENCODING_8859_4
182 }
183 "iso-8859-5" | "iso_8859-5" | "cyrillic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
184 "iso-8859-6" | "iso_8859-6" | "arabic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
185 "iso-8859-7" | "iso_8859-7" | "greek" => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
186 "iso-8859-8" | "iso_8859-8" | "hebrew" => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
187 "iso-8859-9" | "iso_8859-9" | "latin5" | "latin-5" | "l5" | "turkish" => {
188 xmlCharEncoding::XML_CHAR_ENCODING_8859_9
189 }
190
191 "ascii" | "us-ascii" | "us" | "ansi_x3.4-1968" | "ansi_x3.4-1986" | "iso-ir-6"
193 | "iso_646.irv:1991" | "cp367" | "ibm367" => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
194
195 "iso-2022-jp" | "iso2022-jp" => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
197 "shift_jis" | "shift-jis" | "sjis" | "cp932" => {
198 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS
199 }
200 "euc-jp" | "eucjp" => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
201
202 "ucs-4" | "ucs4" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
204 "ucs-4le" | "ucs4le" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
205 "ucs-4be" | "ucs4be" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
206 "ucs-2" | "ucs2" => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
207
208 "ebcdic" | "cp037" | "ibm037" => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
210
211 _ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
212 }
213}
214
215pub(crate) fn encoding_name(enc: xmlCharEncoding) -> Option<&'static [u8]> {
219 match enc {
220 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => Some(b"UTF-8" as &[u8]),
221 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => Some(b"UTF-16LE" as &[u8]),
222 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => Some(b"UTF-16BE" as &[u8]),
223 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => Some(b"UCS-4LE" as &[u8]),
224 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => Some(b"UCS-4BE" as &[u8]),
225 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => Some(b"EBCDIC" as &[u8]),
226 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => Some(b"UCS-4-2143" as &[u8]),
227 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => Some(b"UCS-4-3412" as &[u8]),
228 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => Some(b"UCS-2" as &[u8]),
229 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => Some(b"ISO-8859-1" as &[u8]),
230 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => Some(b"ISO-8859-2" as &[u8]),
231 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => Some(b"ISO-8859-3" as &[u8]),
232 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => Some(b"ISO-8859-4" as &[u8]),
233 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => Some(b"ISO-8859-5" as &[u8]),
234 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => Some(b"ISO-8859-6" as &[u8]),
235 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => Some(b"ISO-8859-7" as &[u8]),
236 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => Some(b"ISO-8859-8" as &[u8]),
237 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => Some(b"ISO-8859-9" as &[u8]),
238 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => Some(b"ISO-2022-JP" as &[u8]),
239 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => Some(b"SHIFT_JIS" as &[u8]),
240 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => Some(b"EUC-JP" as &[u8]),
241 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => Some(b"US-ASCII" as &[u8]),
242 _ => None,
243 }
244}
245
246pub(crate) fn utf8_valid(data: &[u8]) -> bool {
254 core::str::from_utf8(data).is_ok()
255}
256
257pub(crate) fn is_valid_xml_char(cp: u32) -> bool {
269 match cp {
270 0x9 | 0xA | 0xD => true,
271 0x20..=0xD7FF => true,
272 0xE000..=0xFFFD => true,
273 0x10000..=0x10FFFF => true,
274 _ => false,
275 }
276}
277
278#[inline]
284fn read_utf16le_unit(data: &[u8]) -> Option<u16> {
285 if data.len() < 2 {
286 return None;
287 }
288 Some(u16::from_le_bytes([data[0], data[1]]))
289}
290
291#[inline]
293fn read_utf16be_unit(data: &[u8]) -> Option<u16> {
294 if data.len() < 2 {
295 return None;
296 }
297 Some(u16::from_be_bytes([data[0], data[1]]))
298}
299
300fn encode_codepoint_to_utf8(cp: u32, out: &mut [u8]) -> usize {
304 if cp < 0x80 {
305 if !out.is_empty() {
306 out[0] = cp as u8;
307 }
308 1
309 } else if cp < 0x800 {
310 if out.len() < 2 {
311 return 0;
312 }
313 out[0] = 0xC0 | ((cp >> 6) as u8);
314 out[1] = 0x80 | (cp as u8 & 0x3F);
315 2
316 } else if cp < 0x10000 {
317 if out.len() < 3 {
318 return 0;
319 }
320 out[0] = 0xE0 | ((cp >> 12) as u8);
321 out[1] = 0x80 | ((cp >> 6) as u8 & 0x3F);
322 out[2] = 0x80 | (cp as u8 & 0x3F);
323 3
324 } else if cp < 0x110000 {
325 if out.len() < 4 {
326 return 0;
327 }
328 out[0] = 0xF0 | ((cp >> 18) as u8);
329 out[1] = 0x80 | ((cp >> 12) as u8 & 0x3F);
330 out[2] = 0x80 | ((cp >> 6) as u8 & 0x3F);
331 out[3] = 0x80 | (cp as u8 & 0x3F);
332 4
333 } else {
334 0
335 }
336}
337
338pub(crate) fn utf16le_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
343 if data.is_empty() {
344 return Ok(Vec::new());
345 }
346
347 let offset = if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
349 2
350 } else {
351 0
352 };
353
354 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
355 let mut i = offset;
356
357 while i < data.len() {
358 let unit = read_utf16le_unit(&data[i..]).ok_or(())?;
359 i += 2;
360
361 if unit >= 0xD800 && unit <= 0xDBFF {
362 let low = read_utf16le_unit(&data[i..]).ok_or(())?;
364 i += 2;
365
366 if !(0xDC00..=0xDFFF).contains(&low) {
367 return Err(());
368 }
369
370 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
371 let mut buf = [0u8; 4];
372 let n = encode_codepoint_to_utf8(cp, &mut buf);
373 if n == 0 {
374 return Err(());
375 }
376 result.extend_from_slice(&buf[..n]);
377 } else if unit >= 0xDC00 && unit <= 0xDFFF {
378 return Err(());
380 } else {
381 let cp = unit as u32;
382 let mut buf = [0u8; 4];
383 let n = encode_codepoint_to_utf8(cp, &mut buf);
384 result.extend_from_slice(&buf[..n]);
385 }
386 }
387
388 Ok(result)
389}
390
391pub(crate) fn utf16be_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
395 if data.is_empty() {
396 return Ok(Vec::new());
397 }
398
399 let offset = if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
401 2
402 } else {
403 0
404 };
405
406 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
407 let mut i = offset;
408
409 while i < data.len() {
410 let unit = read_utf16be_unit(&data[i..]).ok_or(())?;
411 i += 2;
412
413 if unit >= 0xD800 && unit <= 0xDBFF {
414 let low = read_utf16be_unit(&data[i..]).ok_or(())?;
416 i += 2;
417
418 if !(0xDC00..=0xDFFF).contains(&low) {
419 return Err(());
420 }
421
422 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
423 let mut buf = [0u8; 4];
424 let n = encode_codepoint_to_utf8(cp, &mut buf);
425 if n == 0 {
426 return Err(());
427 }
428 result.extend_from_slice(&buf[..n]);
429 } else if unit >= 0xDC00 && unit <= 0xDFFF {
430 return Err(());
432 } else {
433 let cp = unit as u32;
434 let mut buf = [0u8; 4];
435 let n = encode_codepoint_to_utf8(cp, &mut buf);
436 result.extend_from_slice(&buf[..n]);
437 }
438 }
439
440 Ok(result)
441}
442
443fn encode_codepoint_to_utf16le(cp: u32, out: &mut [u8]) -> usize {
447 if cp < 0x10000 {
448 if out.len() < 2 {
449 return 0;
450 }
451 let u = cp as u16;
452 out[..2].copy_from_slice(&u.to_le_bytes());
453 2
454 } else if cp < 0x110000 {
455 if out.len() < 4 {
456 return 0;
457 }
458 let cp = cp - 0x10000;
459 let high = 0xD800 | ((cp >> 10) as u16);
460 let low = 0xDC00 | (cp as u16 & 0x3FF);
461 out[..2].copy_from_slice(&high.to_le_bytes());
462 out[2..4].copy_from_slice(&low.to_le_bytes());
463 4
464 } else {
465 0
466 }
467}
468
469pub(crate) fn utf8_to_utf16le(data: &[u8]) -> Result<Vec<u8>, ()> {
473 let s = core::str::from_utf8(data).map_err(|_| ())?;
474 let mut result = Vec::with_capacity(data.len() * 2);
475
476 for ch in s.chars() {
477 let cp = ch as u32;
478 let mut buf = [0u8; 4];
479 let n = encode_codepoint_to_utf16le(cp, &mut buf);
480 if n == 0 {
481 return Err(());
482 }
483 result.extend_from_slice(&buf[..n]);
484 }
485
486 Ok(result)
487}
488
489pub(crate) fn latin1_to_utf8(data: &[u8]) -> Vec<u8> {
498 let mut result = Vec::with_capacity(data.len() * 2);
499
500 for &byte in data {
501 let cp = byte as u32;
502 let mut buf = [0u8; 2];
503 let n = encode_codepoint_to_utf8(cp, &mut buf);
504 result.extend_from_slice(&buf[..n]);
505 }
506
507 result
508}
509
510pub(crate) fn utf8_to_latin1(data: &[u8]) -> Result<Vec<u8>, ()> {
515 let s = core::str::from_utf8(data).map_err(|_| ())?;
516 let mut result = Vec::with_capacity(data.len());
517
518 for ch in s.chars() {
519 let cp = ch as u32;
520 if cp > 0xFF {
521 return Err(());
522 }
523 result.push(cp as u8);
524 }
525
526 Ok(result)
527}
528
529pub(crate) fn init_encodings() {
544 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
545 return;
546 }
547 let _guard = ENCODING_INIT_MUTEX.lock();
553 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
554 return;
555 }
556 register_builtin_handlers();
557 ENCODING_INITIALIZED.store(true, Ordering::SeqCst);
558}
559
560fn register_builtin_handlers() {
562 register_handler(
564 b"UTF-8\0",
565 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
566 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
567 Some(utf8_input_func as xmlCharEncodingInputFunc),
568 Some(utf8_output_func as xmlCharEncodingOutputFunc),
569 );
570
571 register_handler(
573 b"UTF-16LE\0",
574 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
575 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
576 Some(utf16le_input_func as xmlCharEncodingInputFunc),
577 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
578 );
579
580 register_handler(
582 b"UTF-16BE\0",
583 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
584 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
585 Some(utf16be_input_func as xmlCharEncodingInputFunc),
586 Some(utf16be_output_func as xmlCharEncodingOutputFunc),
587 );
588
589 register_handler(
591 b"ISO-8859-1\0",
592 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
593 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
594 Some(latin1_input_func as xmlCharEncodingInputFunc),
595 Some(latin1_output_func as xmlCharEncodingOutputFunc),
596 );
597
598 register_handler(
603 b"US-ASCII\0",
604 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
605 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
606 Some(ascii_input_func as xmlCharEncodingInputFunc),
607 Some(ascii_output_func as xmlCharEncodingOutputFunc),
608 );
609 register_handler(
610 b"ASCII\0",
611 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
612 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
613 Some(ascii_input_func as xmlCharEncodingInputFunc),
614 Some(ascii_output_func as xmlCharEncodingOutputFunc),
615 );
616
617 register_handler(
622 b"UTF-16\0",
623 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
624 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
625 Some(utf16le_input_func as xmlCharEncodingInputFunc),
626 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
627 );
628}
629
630fn register_handler(
632 name_bytes: &[u8],
633 input_enc: xmlCharEncoding,
634 output_enc: xmlCharEncoding,
635 input_func: Option<xmlCharEncodingInputFunc>,
636 output_func: Option<xmlCharEncodingOutputFunc>,
637) {
638 let name_raw =
639 unsafe { crate::abi::allocator::xmlMemStrdupImpl(name_bytes.as_ptr() as *const c_char) };
640 if name_raw.is_null() {
641 return;
642 }
643
644 let handler =
645 unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) } as *mut _xmlCharEncodingHandler;
646
647 if handler.is_null() {
648 unsafe { xmlFreeImpl(name_raw) };
649 return;
650 }
651
652 unsafe {
653 ptr::write(
654 handler,
655 _xmlCharEncodingHandler {
656 name: name_raw as *mut c_char,
657 input: EncodingInputUnion {
658 legacyFunc: input_func,
659 },
660 output: EncodingOutputUnion {
661 legacyFunc: output_func,
662 },
663 inputCtxt: ptr::null_mut(),
664 outputCtxt: ptr::null_mut(),
665 ctxtDtor: None,
666 flags: 0,
667 },
668 );
669 }
670
671 add_encoding_handler(handler);
672}
673
674pub(crate) fn cleanup_encodings() {
678 let mut handlers = ENCODING_HANDLERS.write();
679 for &handler in handlers.iter() {
680 let ptr = handler.0;
681 if !ptr.is_null() {
682 unsafe {
683 if !(*ptr).name.is_null() {
684 xmlFreeImpl((*ptr).name as *mut c_void);
685 }
686 xmlFreeImpl(ptr as *mut c_void);
687 }
688 }
689 }
690 handlers.clear();
691 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
692}
693
694pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
700 if name.is_null() {
701 return ptr::null_mut();
702 }
703
704 init_encodings();
708
709 let name_str = unsafe {
710 match CStr::from_ptr(name as *const c_char).to_bytes() {
711 b"" => return ptr::null_mut(),
712 s => s,
713 }
714 };
715
716 let handlers = ENCODING_HANDLERS.read();
717 for &handler in handlers.iter() {
718 let ptr = handler.0;
719 if ptr.is_null() {
720 continue;
721 }
722 let h_name = unsafe {
723 if (*ptr).name.is_null() {
724 continue;
725 }
726 CStr::from_ptr((*ptr).name).to_bytes()
727 };
728
729 if name_str.eq_ignore_ascii_case(h_name) {
730 return ptr;
731 }
732 }
733
734 ptr::null_mut()
735}
736
737pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
741 if handler.is_null() {
742 return -1;
743 }
744
745 let mut handlers = ENCODING_HANDLERS.write();
746 handlers.push(HandlerPtr(handler));
747 0
748}
749
750pub(crate) fn char_enc_in_func(
758 handler: *mut _xmlCharEncodingHandler,
759 out: &mut [u8],
760 in_data: &[u8],
761) -> c_int {
762 if handler.is_null() {
763 return -1;
764 }
765
766 let h = unsafe { &*handler };
767 let input_func = unsafe { h.input.legacyFunc };
768 let input_func = match input_func {
769 Some(f) => f,
770 None => return -1,
771 };
772
773 let mut outlen = out.len() as c_int;
774 let mut inlen = in_data.len() as c_int;
775
776 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
777}
778
779pub(crate) fn char_enc_out_func(
783 handler: *mut _xmlCharEncodingHandler,
784 out: &mut [u8],
785 in_data: &[u8],
786) -> c_int {
787 if handler.is_null() {
788 return -1;
789 }
790
791 let h = unsafe { &*handler };
792 let output_func = unsafe { h.output.legacyFunc };
793 let output_func = match output_func {
794 Some(f) => f,
795 None => return -1,
796 };
797
798 let mut outlen = out.len() as c_int;
799 let mut inlen = in_data.len() as c_int;
800
801 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
802}
803
804pub(crate) fn char_enc_in(
811 handler: *mut _xmlCharEncodingHandler,
812 out: *mut _xmlBuffer,
813 in_: *mut _xmlBuffer,
814) -> c_int {
815 if handler.is_null() || out.is_null() || in_.is_null() {
816 return -1;
817 }
818
819 let h = unsafe { &*handler };
820 let input_func = unsafe { h.input.legacyFunc };
821 let input_func = match input_func {
822 Some(f) => f,
823 None => return -1,
824 };
825
826 let in_buf = unsafe { &*in_ };
827 let out_buf = unsafe { &mut *out };
828
829 if in_buf.content.is_null() || in_buf.use_ == 0 {
830 return 0;
831 }
832
833 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
834
835 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
837 let mut out_vec = vec![0u8; out_capacity];
838 let mut out_len = out_capacity as c_int;
839 let mut in_len = in_buf.use_ as c_int;
840
841 let ret = unsafe {
842 input_func(
843 out_vec.as_mut_ptr(),
844 &mut out_len,
845 in_data.as_ptr(),
846 &mut in_len,
847 )
848 };
849
850 if ret < 0 {
851 return -1;
852 }
853
854 let written = ret as usize;
855
856 append_to_xml_buffer(out_buf, &out_vec[..written]);
858
859 written as c_int
860}
861
862pub(crate) fn char_enc_out(
869 handler: *mut _xmlCharEncodingHandler,
870 out: *mut _xmlBuffer,
871 in_: *mut _xmlBuffer,
872) -> c_int {
873 if handler.is_null() || out.is_null() || in_.is_null() {
874 return -1;
875 }
876
877 let h = unsafe { &*handler };
878 let output_func = unsafe { h.output.legacyFunc };
879 let output_func = match output_func {
880 Some(f) => f,
881 None => return -1,
882 };
883
884 let in_buf = unsafe { &*in_ };
885 let out_buf = unsafe { &mut *out };
886
887 if in_buf.content.is_null() || in_buf.use_ == 0 {
888 return 0;
889 }
890
891 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
892
893 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
894 let mut out_vec = vec![0u8; out_capacity];
895 let mut out_len = out_capacity as c_int;
896 let mut in_len = in_buf.use_ as c_int;
897
898 let ret = unsafe {
899 output_func(
900 out_vec.as_mut_ptr(),
901 &mut out_len,
902 in_data.as_ptr(),
903 &mut in_len,
904 )
905 };
906
907 if ret < 0 {
908 return -1;
909 }
910
911 let written = ret as usize;
912
913 append_to_xml_buffer(out_buf, &out_vec[..written]);
915
916 written as c_int
917}
918
919fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
921 if data.is_empty() {
922 return;
923 }
924
925 let new_use = (buf.use_ as usize).saturating_add(data.len());
926 if new_use > buf.size as usize {
927 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
929 let new_content =
930 unsafe { xmlReallocImpl(buf.content as *mut c_void, new_size) as *mut xmlChar };
931 if new_content.is_null() {
932 return; }
934 buf.content = new_content;
935 buf.size = new_size as c_uint;
936 }
937
938 unsafe {
939 ptr::copy_nonoverlapping(
940 data.as_ptr(),
941 buf.content.add(buf.use_ as usize),
942 data.len(),
943 );
944 }
945 buf.use_ = new_use as c_uint;
946}
947
948unsafe extern "C" fn utf8_input_func(
958 out: *mut c_uchar,
959 outlen: *mut c_int,
960 in_: *const c_uchar,
961 inlen: *mut c_int,
962) -> c_int {
963 let avail_out = *outlen as usize;
964 let avail_in = *inlen as usize;
965 let to_copy = avail_out.min(avail_in);
966
967 if to_copy > 0 {
968 ptr::copy_nonoverlapping(in_, out, to_copy);
969 }
970
971 *outlen = to_copy as c_int;
972 *inlen = to_copy as c_int;
973 to_copy as c_int
974}
975
976unsafe extern "C" fn utf8_output_func(
978 out: *mut c_uchar,
979 outlen: *mut c_int,
980 in_: *const c_uchar,
981 inlen: *mut c_int,
982) -> c_int {
983 utf8_input_func(out, outlen, in_, inlen)
984}
985
986unsafe extern "C" fn utf16le_input_func(
990 out: *mut c_uchar,
991 outlen: *mut c_int,
992 in_: *const c_uchar,
993 inlen: *mut c_int,
994) -> c_int {
995 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
996 return -1;
997 }
998
999 let avail_in = *inlen as usize;
1000 let avail_out = *outlen as usize;
1001
1002 if avail_in == 0 || avail_out == 0 {
1003 *outlen = 0;
1004 *inlen = 0;
1005 return 0;
1006 }
1007
1008 let in_data = core::slice::from_raw_parts(in_, avail_in);
1009 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1010
1011 let result = match utf16le_to_utf8(in_data) {
1013 Ok(v) => v,
1014 Err(()) => return -1,
1015 };
1016
1017 let written = result.len().min(avail_out);
1018 if written > 0 {
1019 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1020 }
1021
1022 *outlen = written as c_int;
1023 *inlen = avail_in as c_int; written as c_int
1025}
1026
1027unsafe extern "C" fn utf16le_output_func(
1029 out: *mut c_uchar,
1030 outlen: *mut c_int,
1031 in_: *const c_uchar,
1032 inlen: *mut c_int,
1033) -> c_int {
1034 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1035 return -1;
1036 }
1037
1038 let avail_in = *inlen as usize;
1039 let avail_out = *outlen as usize;
1040
1041 if avail_in == 0 || avail_out == 0 {
1042 *outlen = 0;
1043 *inlen = 0;
1044 return 0;
1045 }
1046
1047 let in_data = core::slice::from_raw_parts(in_, avail_in);
1048 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1049
1050 let result = match utf8_to_utf16le(in_data) {
1051 Ok(v) => v,
1052 Err(()) => return -1,
1053 };
1054
1055 let written = result.len().min(avail_out);
1056 if written > 0 {
1057 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1058 }
1059
1060 *outlen = written as c_int;
1061 *inlen = avail_in as c_int;
1062 written as c_int
1063}
1064
1065unsafe extern "C" fn utf16be_input_func(
1069 out: *mut c_uchar,
1070 outlen: *mut c_int,
1071 in_: *const c_uchar,
1072 inlen: *mut c_int,
1073) -> c_int {
1074 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1075 return -1;
1076 }
1077
1078 let avail_in = *inlen as usize;
1079 let avail_out = *outlen as usize;
1080
1081 if avail_in == 0 || avail_out == 0 {
1082 *outlen = 0;
1083 *inlen = 0;
1084 return 0;
1085 }
1086
1087 let in_data = core::slice::from_raw_parts(in_, avail_in);
1088 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1089
1090 let result = match utf16be_to_utf8(in_data) {
1091 Ok(v) => v,
1092 Err(()) => return -1,
1093 };
1094
1095 let written = result.len().min(avail_out);
1096 if written > 0 {
1097 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1098 }
1099
1100 *outlen = written as c_int;
1101 *inlen = avail_in as c_int;
1102 written as c_int
1103}
1104
1105unsafe extern "C" fn utf16be_output_func(
1107 out: *mut c_uchar,
1108 outlen: *mut c_int,
1109 in_: *const c_uchar,
1110 inlen: *mut c_int,
1111) -> c_int {
1112 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1113 return -1;
1114 }
1115
1116 let avail_in = *inlen as usize;
1117 let avail_out = *outlen as usize;
1118
1119 if avail_in == 0 || avail_out == 0 {
1120 *outlen = 0;
1121 *inlen = 0;
1122 return 0;
1123 }
1124
1125 let in_data = core::slice::from_raw_parts(in_, avail_in);
1126
1127 let le_result = match utf8_to_utf16le(in_data) {
1129 Ok(v) => v,
1130 Err(()) => return -1,
1131 };
1132
1133 let mut result = le_result;
1135 for chunk in result.chunks_exact_mut(2) {
1136 chunk.swap(0, 1);
1137 }
1138
1139 let written = result.len().min(avail_out);
1140 if written > 0 {
1141 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1142 }
1143
1144 *outlen = written as c_int;
1145 *inlen = avail_in as c_int;
1146 written as c_int
1147}
1148
1149unsafe extern "C" fn latin1_input_func(
1153 out: *mut c_uchar,
1154 outlen: *mut c_int,
1155 in_: *const c_uchar,
1156 inlen: *mut c_int,
1157) -> c_int {
1158 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1159 return -1;
1160 }
1161
1162 let avail_in = *inlen as usize;
1163 let avail_out = *outlen as usize;
1164
1165 if avail_in == 0 || avail_out == 0 {
1166 *outlen = 0;
1167 *inlen = 0;
1168 return 0;
1169 }
1170
1171 let in_data = core::slice::from_raw_parts(in_, avail_in);
1172 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1173
1174 let mut in_pos = 0;
1175 let mut out_pos = 0;
1176
1177 while in_pos < avail_in && out_pos < avail_out {
1178 let byte = in_data[in_pos];
1179 in_pos += 1;
1180
1181 if byte < 0x80 {
1182 if out_pos < avail_out {
1184 out_slice[out_pos] = byte;
1185 out_pos += 1;
1186 } else {
1187 break;
1188 }
1189 } else {
1190 if out_pos + 1 < avail_out {
1193 out_slice[out_pos] = 0xC2 | (byte >> 6);
1194 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1195 out_pos += 2;
1196 } else {
1197 break;
1198 }
1199 }
1200 }
1201
1202 *outlen = out_pos as c_int;
1203 *inlen = in_pos as c_int;
1204 out_pos as c_int
1205}
1206
1207unsafe extern "C" fn latin1_output_func(
1209 out: *mut c_uchar,
1210 outlen: *mut c_int,
1211 in_: *const c_uchar,
1212 inlen: *mut c_int,
1213) -> c_int {
1214 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1215 return -1;
1216 }
1217
1218 let avail_in = *inlen as usize;
1219 let avail_out = *outlen as usize;
1220
1221 if avail_in == 0 || avail_out == 0 {
1222 *outlen = 0;
1223 *inlen = 0;
1224 return 0;
1225 }
1226
1227 let in_data = core::slice::from_raw_parts(in_, avail_in);
1228 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1229
1230 let mut in_pos = 0;
1231 let mut out_pos = 0;
1232
1233 while in_pos < avail_in && out_pos < avail_out {
1234 let byte = in_data[in_pos];
1235 in_pos += 1;
1236
1237 if byte < 0x80 {
1238 out_slice[out_pos] = byte;
1240 out_pos += 1;
1241 } else if byte >= 0xC2 && byte <= 0xC3 {
1242 if in_pos < avail_in {
1244 let second = in_data[in_pos];
1245 in_pos += 1;
1246 if second & 0xC0 != 0x80 {
1247 return -1; }
1249 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1250 if cp > 0xFF {
1251 return -1; }
1253 out_slice[out_pos] = cp as u8;
1254 out_pos += 1;
1255 } else {
1256 return -1; }
1258 } else if byte >= 0x80 && byte <= 0xBF {
1259 return -1;
1261 } else {
1262 return -1;
1265 }
1266 }
1267
1268 *outlen = out_pos as c_int;
1269 *inlen = in_pos as c_int;
1270 out_pos as c_int
1271}
1272
1273unsafe extern "C" fn ascii_input_func(
1277 out: *mut c_uchar,
1278 outlen: *mut c_int,
1279 in_: *const c_uchar,
1280 inlen: *mut c_int,
1281) -> c_int {
1282 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1283 return -1;
1284 }
1285
1286 let avail_in = *inlen as usize;
1287 let avail_out = *outlen as usize;
1288
1289 if avail_in == 0 || avail_out == 0 {
1290 *outlen = 0;
1291 *inlen = 0;
1292 return 0;
1293 }
1294
1295 let in_data = core::slice::from_raw_parts(in_, avail_in);
1296 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1297
1298 let mut pos = 0;
1299 while pos < avail_in && pos < avail_out {
1300 let byte = in_data[pos];
1301 if byte > 0x7F {
1302 return -1; }
1304 out_slice[pos] = byte;
1305 pos += 1;
1306 }
1307
1308 *outlen = pos as c_int;
1309 *inlen = pos as c_int;
1310 pos as c_int
1311}
1312
1313unsafe extern "C" fn ascii_output_func(
1315 out: *mut c_uchar,
1316 outlen: *mut c_int,
1317 in_: *const c_uchar,
1318 inlen: *mut c_int,
1319) -> c_int {
1320 ascii_input_func(out, outlen, in_, inlen)
1322}
1323
1324pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
1333 if name.is_null() {
1334 return ptr::null_mut();
1335 }
1336 find_encoding_handler(name as *const xmlChar)
1337}
1338
1339pub(crate) fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
1343 match enc {
1347 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
1348 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE | xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => {
1349 c"UTF-16".as_ptr()
1350 }
1351 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE | xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => {
1352 c"UCS-4".as_ptr()
1353 }
1354 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"IBM037".as_ptr(),
1355 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
1356 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
1357 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
1358 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
1359 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
1360 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
1361 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
1362 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
1363 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
1364 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
1365 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
1366 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"Shift_JIS".as_ptr(),
1367 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
1368 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"US-ASCII".as_ptr(),
1370 _ => ptr::null(),
1371 }
1372}
1373
1374pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
1379 if name.is_null() {
1380 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
1381 }
1382 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
1383 encoding_from_name(bytes) as c_int
1384}
1385
1386static ENCODING_ALIASES: std::sync::OnceLock<
1394 parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>>,
1395> = std::sync::OnceLock::new();
1396
1397fn encoding_aliases() -> &'static parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>> {
1398 ENCODING_ALIASES.get_or_init(|| {
1399 let m = parking_lot::RwLock::new(std::collections::HashMap::new());
1400 m
1401 })
1402}
1403
1404pub(crate) fn add_encoding_alias(name: *const c_char, alias: *const c_char) -> c_int {
1407 if name.is_null() || alias.is_null() {
1408 return -1;
1409 }
1410 let n = unsafe { CStr::from_ptr(name).to_bytes().to_vec() };
1411 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1412 encoding_aliases().write().insert(a, n);
1413 0
1414}
1415
1416pub(crate) fn del_encoding_alias(alias: *const c_char) -> c_int {
1419 if alias.is_null() {
1420 return -1;
1421 }
1422 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1423 if encoding_aliases().write().remove(&a).is_some() {
1424 0
1425 } else {
1426 -1
1427 }
1428}
1429
1430pub(crate) fn get_encoding_alias(alias: *const c_char) -> *const c_char {
1433 if alias.is_null() {
1434 return ptr::null();
1435 }
1436 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
1437 let guard = encoding_aliases().read();
1438 match guard.get(&a) {
1439 Some(v) => {
1440 let leaked: &'static [u8] = Box::leak(v.clone().into_boxed_slice());
1443 leaked.as_ptr() as *const c_char
1444 }
1445 None => ptr::null(),
1446 }
1447}
1448
1449pub(crate) fn cleanup_encoding_aliases() {
1451 encoding_aliases().write().clear();
1452}
1453
1454pub(crate) fn xmlCharEncInFunc(
1458 handler: *mut _xmlCharEncodingHandler,
1459 out: *mut _xmlBuffer,
1460 in_: *mut _xmlBuffer,
1461) -> c_int {
1462 char_enc_in(handler, out, in_)
1463}
1464
1465pub(crate) fn xmlCharEncOutFunc(
1469 handler: *mut _xmlCharEncodingHandler,
1470 out: *mut _xmlBuffer,
1471 in_: *mut _xmlBuffer,
1472) -> c_int {
1473 char_enc_out(handler, out, in_)
1474}
1475
1476pub(crate) fn xmlNewCharEncodingHandler(
1482 name: *const c_char,
1483 input: xmlCharEncodingInputFunc,
1484 output: xmlCharEncodingOutputFunc,
1485) -> *mut _xmlCharEncodingHandler {
1486 if name.is_null() {
1487 return ptr::null_mut();
1488 }
1489
1490 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) };
1491 if name_raw.is_null() {
1492 return ptr::null_mut();
1493 }
1494
1495 let handler =
1496 unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) } as *mut _xmlCharEncodingHandler;
1497
1498 if handler.is_null() {
1499 unsafe { xmlFreeImpl(name_raw) };
1500 return ptr::null_mut();
1501 }
1502
1503 unsafe {
1504 ptr::write(
1505 handler,
1506 _xmlCharEncodingHandler {
1507 name: name_raw as *mut c_char,
1508 input: EncodingInputUnion {
1509 legacyFunc: Some(input),
1510 },
1511 output: EncodingOutputUnion {
1512 legacyFunc: Some(output),
1513 },
1514 inputCtxt: ptr::null_mut(),
1515 outputCtxt: ptr::null_mut(),
1516 ctxtDtor: None,
1517 flags: 0,
1518 },
1519 );
1520 }
1521
1522 handler
1523}
1524
1525pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
1529 if handler.is_null() {
1530 return;
1531 }
1532
1533 {
1535 let mut handlers = ENCODING_HANDLERS.write();
1536 handlers.retain(|&h| h.0 != handler);
1537 }
1538
1539 unsafe {
1540 if !(*handler).name.is_null() {
1541 xmlFreeImpl((*handler).name as *mut c_void);
1542 }
1543 xmlFreeImpl(handler as *mut c_void);
1544 }
1545}
1546
1547pub(crate) fn xmlInitCharEncodingHandlers() {
1549 init_encodings();
1550}
1551
1552pub(crate) fn xmlCleanupCharEncodingHandlers() {
1554 cleanup_encodings();
1555}
1556
1557pub(crate) fn xmlLookupCharEncodingHandler(enc: c_int, out: *mut *mut c_void) -> c_int {
1579 if out.is_null() {
1580 return crate::abi::types::XML_ERR_ARGUMENT;
1581 }
1582 unsafe {
1583 *out = ptr::null_mut();
1584 }
1585 if enc <= 0 || enc >= 32 {
1586 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1587 }
1588 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int {
1590 return crate::abi::types::XML_ERR_OK;
1591 }
1592 let canonical: &[u8] = match enc {
1593 2 => b"UTF-16LE\0",
1595 3 => b"UTF-16BE\0",
1597 10 => b"ISO-8859-1\0",
1599 22 => b"US-ASCII\0",
1601 23 => b"UTF-16\0",
1603 _ => return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING,
1604 };
1605 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1606 if h.is_null() {
1607 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
1608 }
1609 unsafe {
1610 *out = h as *mut c_void;
1611 }
1612 crate::abi::types::XML_ERR_OK
1613}
1614
1615pub(crate) fn xmlGetCharEncodingHandler(enc: c_int) -> *mut c_void {
1617 let mut ret: *mut c_void = ptr::null_mut();
1618 let _rc = xmlLookupCharEncodingHandler(enc, &mut ret);
1619 ret
1620}
1621
1622pub(crate) fn xmlCreateCharEncodingHandler(
1629 name: *const c_char,
1630 flags: c_int,
1631 impl_: Option<xmlCharEncConvImpl>,
1632 implCtxt: *mut c_void,
1633 out: *mut *mut c_void,
1634) -> c_int {
1635 if out.is_null() {
1636 return crate::abi::types::XML_ERR_ARGUMENT;
1637 }
1638 unsafe {
1639 *out = ptr::null_mut();
1640 }
1641 if name.is_null() || flags == 0 {
1642 return crate::abi::types::XML_ERR_ARGUMENT;
1643 }
1644 let norig = unsafe { CStr::from_ptr(name).to_bytes() };
1645
1646 let mut eff: &[u8] = norig;
1648 let alias = get_encoding_alias(name);
1649 if !alias.is_null() {
1650 eff = unsafe { CStr::from_ptr(alias).to_bytes() };
1651 }
1652
1653 let enc = encoding_from_name(eff);
1654
1655 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
1657 return crate::abi::types::XML_ERR_OK;
1658 }
1659
1660 let canonical: &[u8] = match enc {
1661 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => b"UTF-16LE\0",
1662 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => b"UTF-16BE\0",
1663 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => b"ISO-8859-1\0",
1664 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => b"US-ASCII\0",
1665 _ => {
1666 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1667 }
1668 };
1669 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
1670 if h.is_null() {
1671 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1672 }
1673 unsafe {
1674 let src = &*h;
1675 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1676 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1677 if !has_in || !has_out {
1678 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
1679 }
1680 let copy = xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler;
1685 if copy.is_null() {
1686 return crate::abi::types::XML_ERR_NO_MEMORY;
1687 }
1688 let name_copy = crate::abi::allocator::xmlMemStrdupImpl(name) as *mut c_char;
1689 if name_copy.is_null() {
1690 xmlFreeImpl(copy as *mut c_void);
1691 return crate::abi::types::XML_ERR_NO_MEMORY;
1692 }
1693 ptr::write(
1694 copy,
1695 _xmlCharEncodingHandler {
1696 name: name_copy,
1697 input: EncodingInputUnion {
1698 legacyFunc: src.input.legacyFunc,
1699 },
1700 output: EncodingOutputUnion {
1701 legacyFunc: src.output.legacyFunc,
1702 },
1703 inputCtxt: src.inputCtxt,
1704 outputCtxt: src.outputCtxt,
1705 ctxtDtor: src.ctxtDtor,
1706 flags: src.flags,
1707 },
1708 );
1709 *out = copy as *mut c_void;
1710 }
1711 crate::abi::types::XML_ERR_OK
1712}
1713
1714fn find_extra_handler(
1720 norig: &[u8],
1721 name: &[u8],
1722 flags: c_int,
1723 impl_: Option<xmlCharEncConvImpl>,
1724 implCtxt: *mut c_void,
1725 out: *mut *mut c_void,
1726) -> c_int {
1727 if let Some(f) = impl_ {
1729 let mut n = norig.to_vec();
1730 n.push(0);
1731 let rc = unsafe {
1732 f(
1733 implCtxt,
1734 n.as_ptr() as *const c_char,
1735 flags,
1736 out as *mut *mut crate::abi::structs::_xmlCharEncodingHandler,
1737 )
1738 };
1739 return rc;
1740 }
1741 let mut n = name.to_vec();
1743 n.push(0);
1744 let h = find_encoding_handler(n.as_ptr() as *const xmlChar);
1745 if !h.is_null() {
1746 unsafe {
1747 let src = &*h;
1748 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
1749 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
1750 if has_in && has_out {
1751 *out = h as *mut c_void;
1752 return crate::abi::types::XML_ERR_OK;
1753 }
1754 }
1755 }
1756 crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING
1757}
1758
1759pub(crate) fn xmlOpenCharEncodingHandler(
1761 name: *const c_char,
1762 output: c_int,
1763 out: *mut *mut c_void,
1764) -> c_int {
1765 let flags: c_int = if output != 0 { 2 } else { 1 };
1767 xmlCreateCharEncodingHandler(name, flags, None, ptr::null_mut(), out)
1768}
1769
1770pub(crate) fn xmlCharEncNewCustomHandler(
1776 name: *const c_char,
1777 input: xmlCharEncConvFunc,
1778 output: xmlCharEncConvFunc,
1779 ctxtDtor: Option<xmlCharEncConvCtxtDtor>,
1780 inputCtxt: *mut c_void,
1781 outputCtxt: *mut c_void,
1782 out: *mut *mut c_void,
1783) -> c_int {
1784 if out.is_null() {
1785 return crate::abi::types::XML_ERR_ARGUMENT;
1786 }
1787 let handler =
1788 unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) } as *mut _xmlCharEncodingHandler;
1789 if handler.is_null() {
1790 unsafe {
1791 if let Some(d) = ctxtDtor {
1792 if !inputCtxt.is_null() {
1793 d(inputCtxt);
1794 }
1795 if !outputCtxt.is_null() {
1796 d(outputCtxt);
1797 }
1798 }
1799 }
1800 return crate::abi::types::XML_ERR_NO_MEMORY;
1801 }
1802 let name_copy = if name.is_null() {
1803 ptr::null_mut()
1804 } else {
1805 let nc = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) } as *mut c_char;
1806 if nc.is_null() {
1807 unsafe { xmlFreeImpl(handler as *mut c_void) };
1808 unsafe {
1809 if let Some(d) = ctxtDtor {
1810 if !inputCtxt.is_null() {
1811 d(inputCtxt);
1812 }
1813 if !outputCtxt.is_null() {
1814 d(outputCtxt);
1815 }
1816 }
1817 }
1818 return crate::abi::types::XML_ERR_NO_MEMORY;
1819 }
1820 nc
1821 };
1822 unsafe {
1823 ptr::write(
1824 handler,
1825 _xmlCharEncodingHandler {
1826 name: name_copy,
1827 input: EncodingInputUnion { func: Some(input) },
1828 output: EncodingOutputUnion { func: Some(output) },
1829 inputCtxt,
1830 outputCtxt,
1831 ctxtDtor,
1832 flags: 0,
1833 },
1834 );
1835 *out = handler as *mut c_void;
1836 }
1837 crate::abi::types::XML_ERR_OK
1838}
1839
1840#[cfg(test)]
1845mod tests {
1846 use super::*;
1847
1848 #[test]
1851 fn test_detect_bom_utf8() {
1852 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
1853 assert_eq!(
1854 detect_encoding_from_bom(&data),
1855 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1856 );
1857 }
1858
1859 #[test]
1860 fn test_detect_bom_utf16le() {
1861 let data = [0xFF, 0xFE, 0x00, 0x01];
1862 assert_eq!(
1863 detect_encoding_from_bom(&data),
1864 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1865 );
1866 }
1867
1868 #[test]
1869 fn test_detect_bom_utf16be() {
1870 let data = [0xFE, 0xFF, 0x00, 0x01];
1871 assert_eq!(
1872 detect_encoding_from_bom(&data),
1873 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1874 );
1875 }
1876
1877 #[test]
1878 fn test_detect_bom_none() {
1879 let data = b"<xml>";
1880 assert_eq!(
1881 detect_encoding_from_bom(data),
1882 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1883 );
1884 }
1885
1886 #[test]
1887 fn test_detect_bom_empty() {
1888 assert_eq!(
1889 detect_encoding_from_bom(b""),
1890 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1891 );
1892 }
1893
1894 #[test]
1897 fn test_detect_encoding_declaration_utf8() {
1898 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
1899 let result = detect_encoding_from_declaration(data);
1900 assert_eq!(result, Some(b"utf-8".to_vec()));
1901 }
1902
1903 #[test]
1904 fn test_detect_encoding_declaration_iso() {
1905 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
1906 let result = detect_encoding_from_declaration(data);
1907 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
1908 }
1909
1910 #[test]
1911 fn test_detect_encoding_declaration_none() {
1912 let data = b"<?xml version=\"1.0\"?>";
1913 let result = detect_encoding_from_declaration(data);
1914 assert!(result.is_none());
1915 }
1916
1917 #[test]
1918 fn test_detect_encoding_declaration_no_xml() {
1919 let data = b"<root>";
1920 let result = detect_encoding_from_declaration(data);
1921 assert!(result.is_none());
1922 }
1923
1924 #[test]
1925 fn test_detect_encoding_declaration_with_bom() {
1926 let mut data = vec![0xEF, 0xBB, 0xBF];
1927 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1928 let result = detect_encoding_from_declaration(&data);
1929 assert_eq!(result, Some(b"utf-8".to_vec()));
1930 }
1931
1932 #[test]
1935 fn test_encoding_from_name_utf8() {
1936 assert_eq!(
1937 encoding_from_name(b"UTF-8"),
1938 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1939 );
1940 assert_eq!(
1941 encoding_from_name(b"utf8"),
1942 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1943 );
1944 }
1945
1946 #[test]
1947 fn test_encoding_from_name_utf16() {
1948 assert_eq!(
1949 encoding_from_name(b"UTF-16LE"),
1950 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1951 );
1952 assert_eq!(
1953 encoding_from_name(b"UTF-16BE"),
1954 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1955 );
1956 assert_eq!(
1957 encoding_from_name(b"utf-16"),
1958 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1959 );
1960 }
1961
1962 #[test]
1963 fn test_encoding_from_name_latin1() {
1964 assert_eq!(
1965 encoding_from_name(b"ISO-8859-1"),
1966 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
1967 );
1968 assert_eq!(
1969 encoding_from_name(b"Latin1"),
1970 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
1971 );
1972 }
1973
1974 #[test]
1975 fn test_encoding_from_name_ascii() {
1976 assert_eq!(
1977 encoding_from_name(b"ASCII"),
1978 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
1979 );
1980 assert_eq!(
1981 encoding_from_name(b"US-ASCII"),
1982 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
1983 );
1984 }
1985
1986 #[test]
1987 fn test_encoding_from_name_error() {
1988 assert_eq!(
1989 encoding_from_name(b"invalid-encoding"),
1990 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
1991 );
1992 }
1993
1994 #[test]
1995 fn test_encoding_from_name_empty() {
1996 assert_eq!(
1997 encoding_from_name(b""),
1998 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
1999 );
2000 }
2001
2002 #[test]
2005 fn test_encoding_name_utf8() {
2006 assert_eq!(
2007 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
2008 Some(b"UTF-8" as &[u8])
2009 );
2010 }
2011
2012 #[test]
2013 fn test_encoding_name_utf16le() {
2014 assert_eq!(
2015 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
2016 Some(b"UTF-16LE" as &[u8])
2017 );
2018 }
2019
2020 #[test]
2021 fn test_encoding_name_none() {
2022 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
2023 }
2024
2025 #[test]
2026 fn test_encoding_name_error() {
2027 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
2028 }
2029
2030 #[test]
2033 fn test_utf8_valid_ascii() {
2034 assert!(utf8_valid(b"hello world"));
2035 }
2036
2037 #[test]
2038 fn test_utf8_valid_multi_byte() {
2039 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
2040 }
2041
2042 #[test]
2043 fn test_utf8_valid_empty() {
2044 assert!(utf8_valid(b""));
2045 }
2046
2047 #[test]
2048 fn test_utf8_invalid() {
2049 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
2050 }
2051
2052 #[test]
2055 fn test_valid_xml_chars() {
2056 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));
2062 assert!(is_valid_xml_char(0xE000));
2063 assert!(is_valid_xml_char(0xFFFD));
2064 assert!(is_valid_xml_char(0x10000));
2065 assert!(is_valid_xml_char(0x10FFFF));
2066 }
2067
2068 #[test]
2069 fn test_invalid_xml_chars() {
2070 assert!(!is_valid_xml_char(0x00));
2071 assert!(!is_valid_xml_char(0x08));
2072 assert!(!is_valid_xml_char(0x0B));
2073 assert!(!is_valid_xml_char(0x0C));
2074 assert!(!is_valid_xml_char(0x0E));
2075 assert!(!is_valid_xml_char(0x1F));
2076 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
2079 assert!(!is_valid_xml_char(0xFFFF));
2080 assert!(!is_valid_xml_char(0x110000));
2081 }
2082
2083 #[test]
2086 fn test_utf16le_to_utf8_ascii() {
2087 let data = [b'A', 0x00, b'B', 0x00];
2089 let result = utf16le_to_utf8(&data).unwrap();
2090 assert_eq!(result, b"AB");
2091 }
2092
2093 #[test]
2094 fn test_utf16le_to_utf8_bom() {
2095 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
2097 let result = utf16le_to_utf8(&data).unwrap();
2098 assert_eq!(result, b"AB");
2099 }
2100
2101 #[test]
2102 fn test_utf16le_to_utf8_bmp() {
2103 let data = [0xE9, 0x00];
2105 let result = utf16le_to_utf8(&data).unwrap();
2106 assert_eq!(result, "é".as_bytes());
2107 }
2108
2109 #[test]
2110 fn test_utf16le_to_utf8_supplementary() {
2111 let data = [0x3D, 0xD8, 0x00, 0xDE];
2113 let result = utf16le_to_utf8(&data).unwrap();
2114 assert_eq!(result, "😀".as_bytes());
2115 }
2116
2117 #[test]
2118 fn test_utf16le_to_utf8_unpaired_surrogate() {
2119 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
2121 }
2122
2123 #[test]
2124 fn test_utf16le_to_utf8_truncated() {
2125 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
2127 }
2128
2129 #[test]
2130 fn test_utf16le_to_utf8_empty() {
2131 let result = utf16le_to_utf8(b"").unwrap();
2132 assert!(result.is_empty());
2133 }
2134
2135 #[test]
2138 fn test_utf16be_to_utf8_ascii() {
2139 let data = [0x00, b'A', 0x00, b'B'];
2140 let result = utf16be_to_utf8(&data).unwrap();
2141 assert_eq!(result, b"AB");
2142 }
2143
2144 #[test]
2145 fn test_utf16be_to_utf8_bom() {
2146 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
2148 let result = utf16be_to_utf8(&data).unwrap();
2149 assert_eq!(result, b"AB");
2150 }
2151
2152 #[test]
2153 fn test_utf16be_to_utf8_supplementary() {
2154 let data = [0xD8, 0x3D, 0xDE, 0x00];
2156 let result = utf16be_to_utf8(&data).unwrap();
2157 assert_eq!(result, "😀".as_bytes());
2158 }
2159
2160 #[test]
2161 fn test_utf16be_to_utf8_empty() {
2162 let result = utf16be_to_utf8(b"").unwrap();
2163 assert!(result.is_empty());
2164 }
2165
2166 #[test]
2169 fn test_utf8_to_utf16le_ascii() {
2170 let result = utf8_to_utf16le(b"AB").unwrap();
2171 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
2172 }
2173
2174 #[test]
2175 fn test_utf8_to_utf16le_bmp() {
2176 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
2177 assert_eq!(result, [0xE9, 0x00]);
2178 }
2179
2180 #[test]
2181 fn test_utf8_to_utf16le_supplementary() {
2182 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
2183 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
2184 }
2185
2186 #[test]
2187 fn test_utf8_to_utf16le_invalid_utf8() {
2188 assert!(utf8_to_utf16le(&[0xFF]).is_err());
2189 }
2190
2191 #[test]
2192 fn test_utf8_to_utf16le_empty() {
2193 let result = utf8_to_utf16le(b"").unwrap();
2194 assert!(result.is_empty());
2195 }
2196
2197 #[test]
2200 fn test_latin1_to_utf8_ascii() {
2201 let result = latin1_to_utf8(b"ABC");
2202 assert_eq!(result, b"ABC");
2203 }
2204
2205 #[test]
2206 fn test_latin1_to_utf8_accented() {
2207 let result = latin1_to_utf8(&[0xE9]);
2209 assert_eq!(result, "é".as_bytes());
2210 }
2211
2212 #[test]
2213 fn test_latin1_to_utf8_all_255() {
2214 let result = latin1_to_utf8(&[0xFF]);
2215 assert_eq!(result, [0xC3, 0xBF]);
2217 }
2218
2219 #[test]
2220 fn test_latin1_to_utf8_empty() {
2221 let result = latin1_to_utf8(b"");
2222 assert!(result.is_empty());
2223 }
2224
2225 #[test]
2226 fn test_latin1_to_utf8_mixed() {
2227 let result = latin1_to_utf8(b"caf\xE9");
2228 assert_eq!(result, "café".as_bytes());
2229 }
2230
2231 #[test]
2234 fn test_utf8_to_latin1_ascii() {
2235 let result = utf8_to_latin1(b"ABC").unwrap();
2236 assert_eq!(result, b"ABC");
2237 }
2238
2239 #[test]
2240 fn test_utf8_to_latin1_accented() {
2241 let result = utf8_to_latin1("é".as_bytes()).unwrap();
2242 assert_eq!(result, [0xE9]);
2243 }
2244
2245 #[test]
2246 fn test_utf8_to_latin1_out_of_range() {
2247 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
2249
2250 #[test]
2251 fn test_utf8_to_latin1_invalid_utf8() {
2252 assert!(utf8_to_latin1(&[0xFF]).is_err());
2253 }
2254
2255 #[test]
2256 fn test_utf8_to_latin1_empty() {
2257 let result = utf8_to_latin1(b"").unwrap();
2258 assert!(result.is_empty());
2259 }
2260
2261 #[test]
2264 fn test_init_and_find_encodings() {
2265 init_encodings();
2266
2267 let utf8_name: *const xmlChar = b"UTF-8\0".as_ptr();
2268 assert!(!find_encoding_handler(utf8_name).is_null());
2269
2270 let utf16le_name: *const xmlChar = b"UTF-16LE\0".as_ptr();
2271 assert!(!find_encoding_handler(utf16le_name).is_null());
2272
2273 let utf16be_name: *const xmlChar = b"UTF-16BE\0".as_ptr();
2274 assert!(!find_encoding_handler(utf16be_name).is_null());
2275
2276 let latin1_name: *const xmlChar = b"ISO-8859-1\0".as_ptr();
2277 assert!(!find_encoding_handler(latin1_name).is_null());
2278
2279 let ascii_name: *const xmlChar = b"ASCII\0".as_ptr();
2280 assert!(!find_encoding_handler(ascii_name).is_null());
2281
2282 let lower_name: *const xmlChar = b"utf-8\0".as_ptr();
2284 assert!(!find_encoding_handler(lower_name).is_null());
2285 }
2286
2287 #[test]
2288 fn test_find_encoding_handler_not_found() {
2289 let name: *const xmlChar = b"NONEXISTENT\0".as_ptr();
2290 assert!(find_encoding_handler(name).is_null());
2291 }
2292
2293 #[test]
2294 fn test_find_encoding_handler_null() {
2295 assert!(find_encoding_handler(ptr::null()).is_null());
2296 }
2297
2298 #[test]
2299 fn test_add_encoding_handler() {
2300 let handler = unsafe {
2301 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
2302 };
2303 assert!(!handler.is_null());
2304
2305 let name =
2306 unsafe { crate::abi::allocator::xmlMemStrdupImpl(b"TEST-ENC\0".as_ptr() as *const c_char) };
2307 unsafe {
2308 ptr::write(
2309 handler,
2310 _xmlCharEncodingHandler {
2311 name: name as *mut c_char,
2312 input: EncodingInputUnion { legacyFunc: None },
2313 output: EncodingOutputUnion { legacyFunc: None },
2314 inputCtxt: ptr::null_mut(),
2315 outputCtxt: ptr::null_mut(),
2316 ctxtDtor: None,
2317 flags: 0,
2318 },
2319 );
2320 }
2321
2322 assert_eq!(add_encoding_handler(handler), 0);
2323
2324 let found = find_encoding_handler(b"TEST-ENC\0".as_ptr() as *const xmlChar);
2325 assert_eq!(found, handler);
2326
2327 {
2329 let mut handlers = ENCODING_HANDLERS.write();
2330 handlers.retain(|&h| h.0 != handler);
2331 }
2332
2333 unsafe {
2334 xmlFreeImpl(name as *mut c_void);
2335 xmlFreeImpl(handler as *mut c_void);
2336 }
2337 }
2338
2339 #[test]
2342 fn test_utf16le_roundtrip() {
2343 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
2344 let utf16 = utf8_to_utf16le(original).unwrap();
2345 let back = utf16le_to_utf8(&utf16).unwrap();
2346 assert_eq!(original.to_vec(), back);
2347 }
2348
2349 #[test]
2350 fn test_utf16be_roundtrip() {
2351 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
2352 let utf16le = utf8_to_utf16le(original).unwrap();
2353 let mut utf16be = utf16le.clone();
2355 for chunk in utf16be.chunks_exact_mut(2) {
2356 chunk.swap(0, 1);
2357 }
2358 let back = utf16be_to_utf8(&utf16be).unwrap();
2359 assert_eq!(original.to_vec(), back);
2360 }
2361
2362 #[test]
2363 fn test_latin1_roundtrip() {
2364 let original: Vec<u8> = (0x00..=0xFF).collect();
2365 let utf8 = latin1_to_utf8(&original);
2366 let back = utf8_to_latin1(&utf8).unwrap();
2367 assert_eq!(original, back);
2368 }
2369
2370 #[test]
2373 fn test_utf8_handler_identity() {
2374 let input = b"Hello, UTF-8!";
2375 let mut output = [0u8; 64];
2376 let mut outlen = output.len() as c_int;
2377 let mut inlen = input.len() as c_int;
2378
2379 let ret = unsafe {
2380 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
2381 };
2382
2383 assert_eq!(ret, input.len() as c_int);
2384 assert_eq!(&output[..ret as usize], input);
2385 assert_eq!(inlen, input.len() as c_int);
2386 }
2387
2388 #[test]
2389 fn test_utf16le_handler_roundtrip() {
2390 init_encodings();
2391
2392 let original = b"Hello UTF-16LE!";
2393 let mut utf16_buf = [0u8; 128];
2394 let mut outlen = utf16_buf.len() as c_int;
2395 let mut inlen = original.len() as c_int;
2396
2397 let written = unsafe {
2398 utf16le_output_func(
2399 utf16_buf.as_mut_ptr(),
2400 &mut outlen,
2401 original.as_ptr(),
2402 &mut inlen,
2403 )
2404 };
2405 assert!(written > 0);
2406
2407 let mut decoded = [0u8; 128];
2409 let mut outlen2 = decoded.len() as c_int;
2410 let mut inlen2 = written;
2411
2412 let written2 = unsafe {
2413 utf16le_input_func(
2414 decoded.as_mut_ptr(),
2415 &mut outlen2,
2416 utf16_buf.as_ptr(),
2417 &mut inlen2,
2418 )
2419 };
2420 assert_eq!(written2 as usize, original.len());
2421 assert_eq!(&decoded[..written2 as usize], original);
2422 }
2423
2424 #[test]
2427 fn test_append_to_xml_buffer() {
2428 unsafe {
2429 let content = xmlMallocImpl(64) as *mut xmlChar;
2430 assert!(!content.is_null());
2431
2432 let mut buf = _xmlBuffer {
2433 content,
2434 use_: 0,
2435 size: 64,
2436 alloc: 0,
2437 contentIO: ptr::null_mut(),
2438 };
2439
2440 append_to_xml_buffer(&mut buf, b"Hello");
2441 assert_eq!(buf.use_, 5);
2442 let slice = core::slice::from_raw_parts(buf.content, 5);
2443 assert_eq!(slice, b"Hello");
2444
2445 append_to_xml_buffer(&mut buf, b" World");
2446 assert_eq!(buf.use_, 11);
2447 let slice = core::slice::from_raw_parts(buf.content, 11);
2448 assert_eq!(slice, b"Hello World");
2449
2450 xmlFreeImpl(buf.content as *mut c_void);
2451 }
2452 }
2453
2454 #[test]
2457 fn test_xml_parse_char_encoding() {
2458 let name = b"UTF-8\0".as_ptr() as *const c_char;
2459 assert_eq!(
2460 xmlParseCharEncoding(name),
2461 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
2462 );
2463
2464 let name = b"ISO-8859-1\0".as_ptr() as *const c_char;
2465 assert_eq!(
2466 xmlParseCharEncoding(name),
2467 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
2468 );
2469
2470 assert_eq!(
2471 xmlParseCharEncoding(ptr::null()),
2472 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
2473 );
2474 }
2475
2476 #[test]
2477 fn test_xml_new_and_del_encoding_handler() {
2478 let name = b"TestEnc\0".as_ptr() as *const c_char;
2479 let handler = xmlNewCharEncodingHandler(
2480 name,
2481 utf8_input_func as xmlCharEncodingInputFunc,
2482 utf8_output_func as xmlCharEncodingOutputFunc,
2483 );
2484 assert!(!handler.is_null());
2485
2486 unsafe {
2487 assert!(!(*handler).name.is_null());
2488 let cstr = CStr::from_ptr((*handler).name);
2489 assert_eq!(cstr.to_bytes(), b"TestEnc");
2490 }
2491
2492 xmlDelEncodingHandler(handler);
2493 }
2494
2495 #[test]
2496 fn test_xml_init_and_cleanup() {
2497 xmlInitCharEncodingHandlers();
2498
2499 let name: *const xmlChar = b"UTF-8\0".as_ptr();
2500 assert!(!find_encoding_handler(name).is_null());
2501
2502 xmlCleanupCharEncodingHandlers();
2503 }
2505}