1#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
90
91use std::ffi::CStr;
92use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};
93use std::ptr;
94use std::sync::atomic::{AtomicBool, Ordering};
95
96use once_cell::sync::Lazy;
97use parking_lot::RwLock;
98
99use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlReallocImpl};
100use crate::abi::callbacks::{
101 xmlCharEncConvCtxtDtor, xmlCharEncConvFunc, xmlCharEncConvImpl, xmlCharEncodingInputFunc,
102 xmlCharEncodingOutputFunc,
103};
104use crate::abi::structs::{
105 _xmlBuffer, _xmlCharEncodingHandler, EncodingInputUnion, EncodingOutputUnion,
106};
107use crate::abi::types::{xmlChar, xmlCharEncoding};
108
109#[allow(dead_code)]
113const MAX_CHAR_BYTES: usize = 6;
114
115#[allow(dead_code)]
117const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
118
119const UTF16LE_BOM: [u8; 2] = [0xFF, 0xFE];
121
122const UTF16BE_BOM: [u8; 2] = [0xFE, 0xFF];
124
125#[derive(Clone, Copy)]
133struct HandlerPtr(*mut _xmlCharEncodingHandler);
134
135unsafe impl Send for HandlerPtr {}
136unsafe impl Sync for HandlerPtr {}
137
138static ENCODING_HANDLERS: Lazy<RwLock<Vec<HandlerPtr>>> = Lazy::new(|| RwLock::new(Vec::new()));
145
146static ENCODING_INITIALIZED: AtomicBool = AtomicBool::new(false);
148
149static ENCODING_INIT_MUTEX: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
151
152#[allow(dead_code)]
161pub(crate) fn detect_encoding_from_bom(data: &[u8]) -> xmlCharEncoding {
162 if data.len() >= 3 && data[0..3] == UTF8_BOM {
163 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
164 } else if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
165 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
166 } else if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
167 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
168 } else {
169 xmlCharEncoding::XML_CHAR_ENCODING_NONE
170 }
171}
172
173#[allow(dead_code)]
178pub(crate) fn detect_encoding_from_declaration(data: &[u8]) -> Option<Vec<u8>> {
179 let start = if data.len() >= 3 && data[0..3] == UTF8_BOM {
181 3
182 } else if data.len() >= 2 && (data[0..2] == UTF16LE_BOM || data[0..2] == UTF16BE_BOM) {
183 return None;
185 } else {
186 0
187 };
188
189 let remaining = &data[start..];
190
191 if remaining.len() < 5 || !remaining[0..5].eq_ignore_ascii_case(b"<?xml") {
193 return None;
194 }
195
196 let pi_end = remaining.windows(2).position(|w| w == b"?>")?;
198 let decl_content = &remaining[5..pi_end];
199
200 let decl_str = core::str::from_utf8(decl_content).ok()?;
202 let lower = decl_str.to_ascii_lowercase();
203
204 let enc_pos = lower.find("encoding")?;
206
207 let after_enc = &decl_content[enc_pos + 8..];
209 let after_enc_str = core::str::from_utf8(after_enc).ok()?;
210 let after_enc_trimmed = after_enc_str.trim_start();
211
212 if !after_enc_trimmed.starts_with('=') {
213 return None;
214 }
215
216 let after_eq = after_enc_trimmed[1..].trim_start();
217
218 let quote = after_eq.chars().next()?;
220 if quote != '"' && quote != '\'' {
221 return None;
222 }
223
224 let value_end = after_eq[1..].find(quote)?;
226 let encoding_value = &after_eq[1..=value_end];
227
228 Some(encoding_value.to_ascii_lowercase().as_bytes().to_vec())
229}
230
231pub(crate) fn encoding_from_name(name: &[u8]) -> xmlCharEncoding {
236 let s = core::str::from_utf8(name).unwrap_or("");
237 let s = s.trim().to_ascii_lowercase();
238
239 match s.as_str() {
240 "utf-8" | "utf8" => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
242
243 "utf-16" | "utf-16le" | "utf16le" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
245 "utf-16be" | "utf16be" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
246
247 "iso-8859-1" | "iso_8859-1" | "latin1" | "latin-1" | "l1" | "cp819" | "ibm819"
249 | "iso-ir-100" | "iso_8859-1:1987" => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
250 "iso-8859-2" | "iso_8859-2" | "latin2" | "latin-2" | "l2" => {
251 xmlCharEncoding::XML_CHAR_ENCODING_8859_2
252 }
253 "iso-8859-3" | "iso_8859-3" | "latin3" | "latin-3" | "l3" => {
254 xmlCharEncoding::XML_CHAR_ENCODING_8859_3
255 }
256 "iso-8859-4" | "iso_8859-4" | "latin4" | "latin-4" | "l4" => {
257 xmlCharEncoding::XML_CHAR_ENCODING_8859_4
258 }
259 "iso-8859-5" | "iso_8859-5" | "cyrillic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
260 "iso-8859-6" | "iso_8859-6" | "arabic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
261 "iso-8859-7" | "iso_8859-7" | "greek" => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
262 "iso-8859-8" | "iso_8859-8" | "hebrew" => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
263 "iso-8859-9" | "iso_8859-9" | "latin5" | "latin-5" | "l5" | "turkish" => {
264 xmlCharEncoding::XML_CHAR_ENCODING_8859_9
265 }
266
267 "ascii" | "us-ascii" | "us" | "ansi_x3.4-1968" | "ansi_x3.4-1986" | "iso-ir-6"
269 | "iso_646.irv:1991" | "cp367" | "ibm367" => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
270
271 "iso-2022-jp" | "iso2022-jp" => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
273 "shift_jis" | "shift-jis" | "sjis" | "cp932" => {
274 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS
275 }
276 "euc-jp" | "eucjp" => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
277
278 "ucs-4" | "ucs4" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
280 "ucs-4le" | "ucs4le" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
281 "ucs-4be" | "ucs4be" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
282 "ucs-2" | "ucs2" => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
283
284 "ebcdic" | "cp037" | "ibm037" => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
286
287 _ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
288 }
289}
290
291pub(crate) const fn encoding_name(enc: xmlCharEncoding) -> Option<&'static [u8]> {
295 match enc {
296 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => Some(b"UTF-8" as &[u8]),
297 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => Some(b"UTF-16LE" as &[u8]),
298 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => Some(b"UTF-16BE" as &[u8]),
299 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => Some(b"UCS-4LE" as &[u8]),
300 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => Some(b"UCS-4BE" as &[u8]),
301 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => Some(b"EBCDIC" as &[u8]),
302 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => Some(b"UCS-4-2143" as &[u8]),
303 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => Some(b"UCS-4-3412" as &[u8]),
304 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => Some(b"UCS-2" as &[u8]),
305 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => Some(b"ISO-8859-1" as &[u8]),
306 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => Some(b"ISO-8859-2" as &[u8]),
307 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => Some(b"ISO-8859-3" as &[u8]),
308 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => Some(b"ISO-8859-4" as &[u8]),
309 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => Some(b"ISO-8859-5" as &[u8]),
310 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => Some(b"ISO-8859-6" as &[u8]),
311 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => Some(b"ISO-8859-7" as &[u8]),
312 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => Some(b"ISO-8859-8" as &[u8]),
313 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => Some(b"ISO-8859-9" as &[u8]),
314 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => Some(b"ISO-2022-JP" as &[u8]),
315 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => Some(b"SHIFT_JIS" as &[u8]),
316 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => Some(b"EUC-JP" as &[u8]),
317 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => Some(b"US-ASCII" as &[u8]),
318 _ => None,
319 }
320}
321
322#[allow(dead_code)]
330pub(crate) const fn utf8_valid(data: &[u8]) -> bool {
331 core::str::from_utf8(data).is_ok()
332}
333
334#[allow(dead_code)]
346pub(crate) const fn is_valid_xml_char(cp: u32) -> bool {
347 matches!(
348 cp,
349 0x9 | 0xA | 0xD | 0x20..=0xD7FF | 0xE000..=0xFFFD | 0x10000..=0x10FFFF
350 )
351}
352
353#[inline]
359const fn read_utf16le_unit(data: &[u8]) -> Option<u16> {
360 if data.len() < 2 {
361 return None;
362 }
363 Some(u16::from_le_bytes([data[0], data[1]]))
364}
365
366#[inline]
368const fn read_utf16be_unit(data: &[u8]) -> Option<u16> {
369 if data.len() < 2 {
370 return None;
371 }
372 Some(u16::from_be_bytes([data[0], data[1]]))
373}
374
375const fn encode_codepoint_to_utf8(cp: u32, out: &mut [u8]) -> usize {
379 if cp < 0x80 {
380 if !out.is_empty() {
381 out[0] = cp as u8;
382 }
383 1
384 } else if cp < 0x800 {
385 if out.len() < 2 {
386 return 0;
387 }
388 out[0] = 0xC0 | ((cp >> 6) as u8);
389 out[1] = 0x80 | (cp as u8 & 0x3F);
390 2
391 } else if cp < 0x10000 {
392 if out.len() < 3 {
393 return 0;
394 }
395 out[0] = 0xE0 | ((cp >> 12) as u8);
396 out[1] = 0x80 | ((cp >> 6) as u8 & 0x3F);
397 out[2] = 0x80 | (cp as u8 & 0x3F);
398 3
399 } else if cp < 0x110000 {
400 if out.len() < 4 {
401 return 0;
402 }
403 out[0] = 0xF0 | ((cp >> 18) as u8);
404 out[1] = 0x80 | ((cp >> 12) as u8 & 0x3F);
405 out[2] = 0x80 | ((cp >> 6) as u8 & 0x3F);
406 out[3] = 0x80 | (cp as u8 & 0x3F);
407 4
408 } else {
409 0
410 }
411}
412
413pub(crate) fn utf16le_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
418 if data.is_empty() {
419 return Ok(Vec::new());
420 }
421
422 let offset = if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
424 2
425 } else {
426 0
427 };
428
429 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
430 let mut i = offset;
431
432 while i < data.len() {
433 let unit = read_utf16le_unit(&data[i..]).ok_or(())?;
434 i += 2;
435
436 if (0xD800..=0xDBFF).contains(&unit) {
437 let low = read_utf16le_unit(&data[i..]).ok_or(())?;
439 i += 2;
440
441 if !(0xDC00..=0xDFFF).contains(&low) {
442 return Err(());
443 }
444
445 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
446 let mut buf = [0u8; 4];
447 let n = encode_codepoint_to_utf8(cp, &mut buf);
448 if n == 0 {
449 return Err(());
450 }
451 result.extend_from_slice(&buf[..n]);
452 } else if (0xDC00..=0xDFFF).contains(&unit) {
453 return Err(());
455 } else {
456 let cp = unit as u32;
457 let mut buf = [0u8; 4];
458 let n = encode_codepoint_to_utf8(cp, &mut buf);
459 result.extend_from_slice(&buf[..n]);
460 }
461 }
462
463 Ok(result)
464}
465
466pub(crate) fn utf16be_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
470 if data.is_empty() {
471 return Ok(Vec::new());
472 }
473
474 let offset = if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
476 2
477 } else {
478 0
479 };
480
481 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
482 let mut i = offset;
483
484 while i < data.len() {
485 let unit = read_utf16be_unit(&data[i..]).ok_or(())?;
486 i += 2;
487
488 if (0xD800..=0xDBFF).contains(&unit) {
489 let low = read_utf16be_unit(&data[i..]).ok_or(())?;
491 i += 2;
492
493 if !(0xDC00..=0xDFFF).contains(&low) {
494 return Err(());
495 }
496
497 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
498 let mut buf = [0u8; 4];
499 let n = encode_codepoint_to_utf8(cp, &mut buf);
500 if n == 0 {
501 return Err(());
502 }
503 result.extend_from_slice(&buf[..n]);
504 } else if (0xDC00..=0xDFFF).contains(&unit) {
505 return Err(());
507 } else {
508 let cp = unit as u32;
509 let mut buf = [0u8; 4];
510 let n = encode_codepoint_to_utf8(cp, &mut buf);
511 result.extend_from_slice(&buf[..n]);
512 }
513 }
514
515 Ok(result)
516}
517
518fn encode_codepoint_to_utf16le(cp: u32, out: &mut [u8]) -> usize {
522 if cp < 0x10000 {
523 if out.len() < 2 {
524 return 0;
525 }
526 let u = cp as u16;
527 out[..2].copy_from_slice(&u.to_le_bytes());
528 2
529 } else if cp < 0x110000 {
530 if out.len() < 4 {
531 return 0;
532 }
533 let cp = cp - 0x10000;
534 let high = 0xD800 | ((cp >> 10) as u16);
535 let low = 0xDC00 | (cp as u16 & 0x3FF);
536 out[..2].copy_from_slice(&high.to_le_bytes());
537 out[2..4].copy_from_slice(&low.to_le_bytes());
538 4
539 } else {
540 0
541 }
542}
543
544pub(crate) fn utf8_to_utf16le(data: &[u8]) -> Result<Vec<u8>, ()> {
548 let s = core::str::from_utf8(data).map_err(|_| ())?;
549 let mut result = Vec::with_capacity(data.len() * 2);
550
551 for ch in s.chars() {
552 let cp = ch as u32;
553 let mut buf = [0u8; 4];
554 let n = encode_codepoint_to_utf16le(cp, &mut buf);
555 if n == 0 {
556 return Err(());
557 }
558 result.extend_from_slice(&buf[..n]);
559 }
560
561 Ok(result)
562}
563
564#[allow(dead_code)]
573pub(crate) fn latin1_to_utf8(data: &[u8]) -> Vec<u8> {
574 let mut result = Vec::with_capacity(data.len() * 2);
575
576 for &byte in data {
577 let cp = byte as u32;
578 let mut buf = [0u8; 2];
579 let n = encode_codepoint_to_utf8(cp, &mut buf);
580 result.extend_from_slice(&buf[..n]);
581 }
582
583 result
584}
585
586pub(crate) fn utf8_to_latin1(data: &[u8]) -> Result<Vec<u8>, ()> {
591 let s = core::str::from_utf8(data).map_err(|_| ())?;
592 let mut result = Vec::with_capacity(data.len());
593
594 for ch in s.chars() {
595 let cp = ch as u32;
596 if cp > 0xFF {
597 return Err(());
598 }
599 result.push(cp as u8);
600 }
601
602 Ok(result)
603}
604
605pub(crate) fn init_encodings() {
620 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
621 return;
622 }
623 let _guard = ENCODING_INIT_MUTEX.lock();
629 if ENCODING_INITIALIZED.load(Ordering::SeqCst) {
630 return;
631 }
632 register_builtin_handlers();
633 ENCODING_INITIALIZED.store(true, Ordering::SeqCst);
634}
635
636fn register_builtin_handlers() {
638 register_handler(
640 b"UTF-8\0",
641 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
642 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
643 Some(utf8_input_func as xmlCharEncodingInputFunc),
644 Some(utf8_output_func as xmlCharEncodingOutputFunc),
645 );
646
647 register_handler(
649 b"UTF-16LE\0",
650 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
651 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
652 Some(utf16le_input_func as xmlCharEncodingInputFunc),
653 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
654 );
655
656 register_handler(
658 b"UTF-16BE\0",
659 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
660 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
661 Some(utf16be_input_func as xmlCharEncodingInputFunc),
662 Some(utf16be_output_func as xmlCharEncodingOutputFunc),
663 );
664
665 register_handler(
667 b"ISO-8859-1\0",
668 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
669 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
670 Some(latin1_input_func as xmlCharEncodingInputFunc),
671 Some(latin1_output_func as xmlCharEncodingOutputFunc),
672 );
673
674 register_handler(
680 b"windows-1252\0",
681 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
682 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
683 Some(cp1252_input_func as xmlCharEncodingInputFunc),
684 Some(cp1252_output_func as xmlCharEncodingOutputFunc),
685 );
686 register_handler(
687 b"cp1252\0",
688 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
689 xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
690 Some(cp1252_input_func as xmlCharEncodingInputFunc),
691 Some(cp1252_output_func as xmlCharEncodingOutputFunc),
692 );
693
694 register_handler(
699 b"US-ASCII\0",
700 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
701 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
702 Some(ascii_input_func as xmlCharEncodingInputFunc),
703 Some(ascii_output_func as xmlCharEncodingOutputFunc),
704 );
705 register_handler(
706 b"ASCII\0",
707 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
708 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
709 Some(ascii_input_func as xmlCharEncodingInputFunc),
710 Some(ascii_output_func as xmlCharEncodingOutputFunc),
711 );
712
713 register_handler(
718 b"UTF-16\0",
719 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
720 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
721 Some(utf16le_input_func as xmlCharEncodingInputFunc),
722 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
723 );
724
725 register_handler(
736 b"SHIFT_JIS\0",
737 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
738 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
739 Some(shift_jis_input_func as xmlCharEncodingInputFunc),
740 Some(shift_jis_output_func as xmlCharEncodingOutputFunc),
741 );
742 register_handler(
743 b"SJIS\0",
744 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
745 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
746 Some(shift_jis_input_func as xmlCharEncodingInputFunc),
747 Some(shift_jis_output_func as xmlCharEncodingOutputFunc),
748 );
749 register_handler(
750 b"CP932\0",
751 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
752 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
753 Some(shift_jis_input_func as xmlCharEncodingInputFunc),
754 Some(shift_jis_output_func as xmlCharEncodingOutputFunc),
755 );
756 register_handler(
757 b"EUC-JP\0",
758 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
759 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
760 Some(euc_jp_input_func as xmlCharEncodingInputFunc),
761 Some(euc_jp_output_func as xmlCharEncodingOutputFunc),
762 );
763 register_handler(
764 b"EUCJP\0",
765 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
766 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
767 Some(euc_jp_input_func as xmlCharEncodingInputFunc),
768 Some(euc_jp_output_func as xmlCharEncodingOutputFunc),
769 );
770}
771
772fn register_handler(
782 name_bytes: &[u8],
783 _input_enc: xmlCharEncoding,
784 _output_enc: xmlCharEncoding,
785 input_func: Option<xmlCharEncodingInputFunc>,
786 output_func: Option<xmlCharEncodingOutputFunc>,
787) {
788 let name_raw =
789 unsafe { crate::abi::allocator::xmlMemStrdupImpl(name_bytes.as_ptr() as *const c_char) };
790 if name_raw.is_null() {
791 return;
792 }
793
794 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
795 as *mut _xmlCharEncodingHandler;
796
797 if handler.is_null() {
798 unsafe { xmlFreeImpl(name_raw) };
799 return;
800 }
801
802 unsafe {
803 ptr::write(
804 handler,
805 _xmlCharEncodingHandler {
806 name: name_raw as *mut c_char,
807 input: EncodingInputUnion {
808 legacyFunc: input_func,
809 },
810 output: EncodingOutputUnion {
811 legacyFunc: output_func,
812 },
813 inputCtxt: ptr::null_mut(),
814 outputCtxt: ptr::null_mut(),
815 ctxtDtor: None,
816 flags: 0,
817 },
818 );
819 }
820
821 add_encoding_handler(handler);
822}
823
824pub(crate) fn cleanup_encodings() {
835 let mut handlers = ENCODING_HANDLERS.write();
836 for &handler in handlers.iter() {
837 let ptr = handler.0;
838 if !ptr.is_null() {
839 unsafe {
840 if !(*ptr).name.is_null() {
841 xmlFreeImpl((*ptr).name as *mut c_void);
842 }
843 xmlFreeImpl(ptr as *mut c_void);
844 }
845 }
846 }
847 handlers.clear();
848 drop(handlers);
853 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
854}
855
856pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
868 if name.is_null() {
869 return ptr::null_mut();
870 }
871
872 init_encodings();
876
877 let name_str = unsafe {
878 match CStr::from_ptr(name as *const c_char).to_bytes() {
879 b"" => return ptr::null_mut(),
880 s => s,
881 }
882 };
883
884 let handlers = ENCODING_HANDLERS.read();
885 for &handler in handlers.iter() {
886 let ptr = handler.0;
887 if ptr.is_null() {
888 continue;
889 }
890 let h_name = unsafe {
891 if (*ptr).name.is_null() {
892 continue;
893 }
894 CStr::from_ptr((*ptr).name).to_bytes()
895 };
896
897 if name_str.eq_ignore_ascii_case(h_name) {
898 return ptr;
899 }
900 }
901
902 ptr::null_mut()
903}
904
905pub(crate) fn clone_encoding_handler_for_find(
923 src: *mut _xmlCharEncodingHandler,
924) -> *mut _xmlCharEncodingHandler {
925 if src.is_null() {
926 return ptr::null_mut();
927 }
928 let name_raw = unsafe {
929 let nm = (*src).name;
930 if nm.is_null() {
931 ptr::null_mut()
932 } else {
933 crate::abi::allocator::xmlMemStrdupImpl(nm)
934 }
935 };
936 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
937 as *mut _xmlCharEncodingHandler;
938 if handler.is_null() {
939 if !name_raw.is_null() {
940 unsafe { crate::abi::allocator::xmlFreeImpl(name_raw) };
941 }
942 return ptr::null_mut();
943 }
944 unsafe {
945 ptr::write(
946 handler,
947 _xmlCharEncodingHandler {
948 name: name_raw as *mut c_char,
949 input: ptr::read(&(*src).input),
950 output: ptr::read(&(*src).output),
951 inputCtxt: (*src).inputCtxt,
952 outputCtxt: (*src).outputCtxt,
953 ctxtDtor: (*src).ctxtDtor,
954 flags: (*src).flags,
955 },
956 );
957 }
958 handler
959}
960
961pub(crate) const XML_HANDLER_STATIC: c_int = 0x01;
965
966pub(crate) fn xmlFindCharEncodingHandler_owned(
983 name: *const xmlChar,
984) -> *mut _xmlCharEncodingHandler {
985 if name.is_null() {
986 return ptr::null_mut();
987 }
988 let name_bytes = unsafe {
989 let len = libc::strlen(name as *const c_char);
990 core::slice::from_raw_parts(name as *const u8, len)
991 };
992
993 if encoding_from_name(name_bytes) == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
995 let utf8 = find_encoding_handler(c"UTF-8".as_ptr() as *const xmlChar);
996 if utf8.is_null() {
997 return ptr::null_mut();
998 }
999 unsafe {
1001 (*utf8).flags |= XML_HANDLER_STATIC;
1002 }
1003 return utf8;
1004 }
1005
1006 let mut entry = find_encoding_handler(name as *const xmlChar);
1010 if entry.is_null() {
1011 if let Some(canon) = encoding_name(encoding_from_name(name_bytes)) {
1012 entry = find_encoding_handler(canon.as_ptr() as *const xmlChar);
1013 }
1014 }
1015 clone_encoding_handler_for_find(entry)
1018}
1019
1020pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
1024 if handler.is_null() {
1025 return -1;
1026 }
1027
1028 let mut handlers = ENCODING_HANDLERS.write();
1029 handlers.push(HandlerPtr(handler));
1030 0
1031}
1032
1033#[allow(dead_code)]
1049pub(crate) fn char_enc_in_func(
1050 handler: *mut _xmlCharEncodingHandler,
1051 out: &mut [u8],
1052 in_data: &[u8],
1053) -> c_int {
1054 if handler.is_null() {
1055 return -1;
1056 }
1057
1058 let h = unsafe { &*handler };
1059 let input_func = unsafe { h.input.legacyFunc };
1060 let input_func = match input_func {
1061 Some(f) => f,
1062 None => return -1,
1063 };
1064
1065 let mut outlen = out.len() as c_int;
1066 let mut inlen = in_data.len() as c_int;
1067
1068 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
1069}
1070
1071#[allow(dead_code)]
1083pub(crate) fn char_enc_out_func(
1084 handler: *mut _xmlCharEncodingHandler,
1085 out: &mut [u8],
1086 in_data: &[u8],
1087) -> c_int {
1088 if handler.is_null() {
1089 return -1;
1090 }
1091
1092 let h = unsafe { &*handler };
1093 let output_func = unsafe { h.output.legacyFunc };
1094 let output_func = match output_func {
1095 Some(f) => f,
1096 None => return -1,
1097 };
1098
1099 let mut outlen = out.len() as c_int;
1100 let mut inlen = in_data.len() as c_int;
1101
1102 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
1103}
1104
1105pub(crate) fn char_enc_in(
1121 handler: *mut _xmlCharEncodingHandler,
1122 out: *mut _xmlBuffer,
1123 in_: *mut _xmlBuffer,
1124) -> c_int {
1125 if handler.is_null() || out.is_null() || in_.is_null() {
1126 return -1;
1127 }
1128
1129 let h = unsafe { &*handler };
1130 let input_func = unsafe { h.input.legacyFunc };
1131 let input_func = match input_func {
1132 Some(f) => f,
1133 None => return -1,
1134 };
1135
1136 let in_buf = unsafe { &*in_ };
1137 let out_buf = unsafe { &mut *out };
1138
1139 if in_buf.content.is_null() || in_buf.use_ == 0 {
1140 return 0;
1141 }
1142
1143 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
1144
1145 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
1147 let mut out_vec = vec![0u8; out_capacity];
1148 let mut out_len = out_capacity as c_int;
1149 let mut in_len = in_buf.use_ as c_int;
1150
1151 let ret = unsafe {
1152 input_func(
1153 out_vec.as_mut_ptr(),
1154 &mut out_len,
1155 in_data.as_ptr(),
1156 &mut in_len,
1157 )
1158 };
1159
1160 if ret < 0 {
1161 return -1;
1162 }
1163
1164 let written = ret as usize;
1165
1166 append_to_xml_buffer(out_buf, &out_vec[..written]);
1168
1169 written as c_int
1170}
1171
1172pub(crate) fn char_enc_out(
1188 handler: *mut _xmlCharEncodingHandler,
1189 out: *mut _xmlBuffer,
1190 in_: *mut _xmlBuffer,
1191) -> c_int {
1192 if handler.is_null() || out.is_null() || in_.is_null() {
1193 return -1;
1194 }
1195
1196 let h = unsafe { &*handler };
1197 let output_func = unsafe { h.output.legacyFunc };
1198 let output_func = match output_func {
1199 Some(f) => f,
1200 None => return -1,
1201 };
1202
1203 let in_buf = unsafe { &*in_ };
1204 let out_buf = unsafe { &mut *out };
1205
1206 if in_buf.content.is_null() || in_buf.use_ == 0 {
1207 return 0;
1208 }
1209
1210 let mut in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
1211
1212 const ENC_INPUT_ERROR: c_int = -2;
1221 let mut total_written: usize = 0;
1222 loop {
1223 let out_capacity = (in_data.len().saturating_mul(3)).max(64) + 16;
1224 let mut out_vec = vec![0u8; out_capacity];
1225 let mut out_len = out_capacity as c_int;
1226 let mut in_len = in_data.len() as c_int;
1227 let ret = unsafe {
1228 output_func(
1229 out_vec.as_mut_ptr(),
1230 &mut out_len,
1231 in_data.as_ptr(),
1232 &mut in_len,
1233 )
1234 };
1235 let written = out_len.max(0) as usize;
1236 if written > 0 {
1237 append_to_xml_buffer(out_buf, &out_vec[..written]);
1238 total_written += written;
1239 }
1240 let consumed = in_len.max(0) as usize;
1241 if ret == ENC_INPUT_ERROR && consumed < in_data.len() {
1242 let mut clen: c_int = 4;
1245 let cp = unsafe {
1246 crate::abi::exports_misc::xmlGetUTF8Char(in_data[consumed..].as_ptr(), &mut clen)
1247 };
1248 if cp <= 0 || clen <= 0 || (consumed + clen as usize) > in_data.len() {
1249 return -1;
1250 }
1251 let ref_str = format!("&#{};", cp);
1252 append_to_xml_buffer(out_buf, ref_str.as_bytes());
1253 total_written += ref_str.len();
1254 in_data = &in_data[consumed + clen as usize..];
1255 if in_data.is_empty() {
1256 break;
1257 }
1258 continue;
1259 }
1260 if ret < 0 {
1261 return -1;
1262 }
1263 break;
1264 }
1265
1266 total_written as c_int
1267}
1268
1269fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
1279 if data.is_empty() {
1280 return;
1281 }
1282
1283 let new_use = (buf.use_ as usize).saturating_add(data.len());
1284 if new_use > buf.size as usize {
1285 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
1287 let new_content =
1288 unsafe { xmlReallocImpl(buf.content as *mut c_void, new_size) as *mut xmlChar };
1289 if new_content.is_null() {
1290 return; }
1292 buf.content = new_content;
1293 buf.contentIO = new_content;
1299 buf.size = new_size as c_uint;
1300 }
1301
1302 unsafe {
1303 ptr::copy_nonoverlapping(
1304 data.as_ptr(),
1305 buf.content.add(buf.use_ as usize),
1306 data.len(),
1307 );
1308 }
1309 buf.use_ = new_use as c_uint;
1310}
1311
1312unsafe extern "C" fn utf8_input_func(
1322 out: *mut c_uchar,
1323 outlen: *mut c_int,
1324 in_: *const c_uchar,
1325 inlen: *mut c_int,
1326) -> c_int {
1327 let avail_out = *outlen as usize;
1328 let avail_in = *inlen as usize;
1329 let to_copy = avail_out.min(avail_in);
1330
1331 if to_copy > 0 {
1332 ptr::copy_nonoverlapping(in_, out, to_copy);
1333 }
1334
1335 *outlen = to_copy as c_int;
1336 *inlen = to_copy as c_int;
1337 to_copy as c_int
1338}
1339
1340unsafe extern "C" fn utf8_output_func(
1342 out: *mut c_uchar,
1343 outlen: *mut c_int,
1344 in_: *const c_uchar,
1345 inlen: *mut c_int,
1346) -> c_int {
1347 utf8_input_func(out, outlen, in_, inlen)
1348}
1349
1350unsafe extern "C" fn utf16le_input_func(
1354 out: *mut c_uchar,
1355 outlen: *mut c_int,
1356 in_: *const c_uchar,
1357 inlen: *mut c_int,
1358) -> c_int {
1359 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1360 return -1;
1361 }
1362
1363 let avail_in = *inlen as usize;
1364 let avail_out = *outlen as usize;
1365
1366 if avail_in == 0 || avail_out == 0 {
1367 *outlen = 0;
1368 *inlen = 0;
1369 return 0;
1370 }
1371
1372 let in_data = core::slice::from_raw_parts(in_, avail_in);
1373 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1374
1375 let result = match utf16le_to_utf8(in_data) {
1377 Ok(v) => v,
1378 Err(()) => return -1,
1379 };
1380
1381 let written = result.len().min(avail_out);
1382 if written > 0 {
1383 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1384 }
1385
1386 *outlen = written as c_int;
1387 *inlen = avail_in as c_int; written as c_int
1389}
1390
1391unsafe extern "C" fn utf16le_output_func(
1393 out: *mut c_uchar,
1394 outlen: *mut c_int,
1395 in_: *const c_uchar,
1396 inlen: *mut c_int,
1397) -> c_int {
1398 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1399 return -1;
1400 }
1401
1402 let avail_in = *inlen as usize;
1403 let avail_out = *outlen as usize;
1404
1405 if avail_in == 0 || avail_out == 0 {
1406 *outlen = 0;
1407 *inlen = 0;
1408 return 0;
1409 }
1410
1411 let in_data = core::slice::from_raw_parts(in_, avail_in);
1412 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1413
1414 let result = match utf8_to_utf16le(in_data) {
1415 Ok(v) => v,
1416 Err(()) => return -1,
1417 };
1418
1419 let written = result.len().min(avail_out);
1420 if written > 0 {
1421 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1422 }
1423
1424 *outlen = written as c_int;
1425 *inlen = avail_in as c_int;
1426 written as c_int
1427}
1428
1429unsafe extern "C" fn utf16be_input_func(
1433 out: *mut c_uchar,
1434 outlen: *mut c_int,
1435 in_: *const c_uchar,
1436 inlen: *mut c_int,
1437) -> c_int {
1438 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1439 return -1;
1440 }
1441
1442 let avail_in = *inlen as usize;
1443 let avail_out = *outlen as usize;
1444
1445 if avail_in == 0 || avail_out == 0 {
1446 *outlen = 0;
1447 *inlen = 0;
1448 return 0;
1449 }
1450
1451 let in_data = core::slice::from_raw_parts(in_, avail_in);
1452 let _out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1453
1454 let result = match utf16be_to_utf8(in_data) {
1455 Ok(v) => v,
1456 Err(()) => return -1,
1457 };
1458
1459 let written = result.len().min(avail_out);
1460 if written > 0 {
1461 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1462 }
1463
1464 *outlen = written as c_int;
1465 *inlen = avail_in as c_int;
1466 written as c_int
1467}
1468
1469unsafe extern "C" fn utf16be_output_func(
1471 out: *mut c_uchar,
1472 outlen: *mut c_int,
1473 in_: *const c_uchar,
1474 inlen: *mut c_int,
1475) -> c_int {
1476 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1477 return -1;
1478 }
1479
1480 let avail_in = *inlen as usize;
1481 let avail_out = *outlen as usize;
1482
1483 if avail_in == 0 || avail_out == 0 {
1484 *outlen = 0;
1485 *inlen = 0;
1486 return 0;
1487 }
1488
1489 let in_data = core::slice::from_raw_parts(in_, avail_in);
1490
1491 let le_result = match utf8_to_utf16le(in_data) {
1493 Ok(v) => v,
1494 Err(()) => return -1,
1495 };
1496
1497 let mut result = le_result;
1499 for chunk in result.as_chunks_mut::<2>().0 {
1500 chunk.swap(0, 1);
1501 }
1502
1503 let written = result.len().min(avail_out);
1504 if written > 0 {
1505 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1506 }
1507
1508 *outlen = written as c_int;
1509 *inlen = avail_in as c_int;
1510 written as c_int
1511}
1512
1513unsafe extern "C" fn latin1_input_func(
1517 out: *mut c_uchar,
1518 outlen: *mut c_int,
1519 in_: *const c_uchar,
1520 inlen: *mut c_int,
1521) -> c_int {
1522 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1523 return -1;
1524 }
1525
1526 let avail_in = *inlen as usize;
1527 let avail_out = *outlen as usize;
1528
1529 if avail_in == 0 || avail_out == 0 {
1530 *outlen = 0;
1531 *inlen = 0;
1532 return 0;
1533 }
1534
1535 let in_data = core::slice::from_raw_parts(in_, avail_in);
1536 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1537
1538 let mut in_pos = 0;
1539 let mut out_pos = 0;
1540
1541 while in_pos < avail_in && out_pos < avail_out {
1542 let byte = in_data[in_pos];
1543 in_pos += 1;
1544
1545 if byte < 0x80 {
1546 if out_pos < avail_out {
1548 out_slice[out_pos] = byte;
1549 out_pos += 1;
1550 } else {
1551 break;
1552 }
1553 } else {
1554 if out_pos + 1 < avail_out {
1557 out_slice[out_pos] = 0xC2 | (byte >> 6);
1558 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1559 out_pos += 2;
1560 } else {
1561 break;
1562 }
1563 }
1564 }
1565
1566 *outlen = out_pos as c_int;
1567 *inlen = in_pos as c_int;
1568 out_pos as c_int
1569}
1570
1571unsafe extern "C" fn latin1_output_func(
1573 out: *mut c_uchar,
1574 outlen: *mut c_int,
1575 in_: *const c_uchar,
1576 inlen: *mut c_int,
1577) -> c_int {
1578 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1579 return -1;
1580 }
1581
1582 let avail_in = *inlen as usize;
1583 let avail_out = *outlen as usize;
1584
1585 if avail_in == 0 || avail_out == 0 {
1586 *outlen = 0;
1587 *inlen = 0;
1588 return 0;
1589 }
1590
1591 let in_data = core::slice::from_raw_parts(in_, avail_in);
1592 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1593
1594 let mut in_pos = 0;
1595 let mut out_pos = 0;
1596
1597 while in_pos < avail_in && out_pos < avail_out {
1598 let byte = in_data[in_pos];
1599 in_pos += 1;
1600
1601 if byte < 0x80 {
1602 out_slice[out_pos] = byte;
1604 out_pos += 1;
1605 } else if (0xC2..=0xC3).contains(&byte) {
1606 if in_pos < avail_in {
1608 let second = in_data[in_pos];
1609 in_pos += 1;
1610 if second & 0xC0 != 0x80 {
1611 return -1; }
1613 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1614 if cp > 0xFF {
1615 return -1; }
1617 out_slice[out_pos] = cp as u8;
1618 out_pos += 1;
1619 } else {
1620 return -1; }
1622 } else if (0x80..=0xBF).contains(&byte) {
1623 return -1;
1625 } else {
1626 return -1;
1629 }
1630 }
1631
1632 *outlen = out_pos as c_int;
1633 *inlen = in_pos as c_int;
1634 out_pos as c_int
1635}
1636
1637const CP1252_C1: [u16; 32] = [
1648 0x20AC, 0xFFFF, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFF, 0x017D, 0xFFFF, 0xFFFF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFF, 0x017E, 0x0178, ];
1653
1654#[allow(dead_code)]
1657pub(crate) const fn cp1252_byte_to_cp(byte: u8) -> Option<u32> {
1658 match byte {
1659 0x00..=0x7F => Some(byte as u32),
1660 0x80..=0x9F => {
1661 let cp = CP1252_C1[(byte - 0x80) as usize];
1662 if cp == 0xFFFF {
1663 None
1664 } else {
1665 Some(cp as u32)
1666 }
1667 }
1668 _ => Some(byte as u32), }
1670}
1671
1672#[allow(dead_code)]
1675pub(crate) const fn cp_to_cp1252_byte(cp: u32) -> Option<u8> {
1676 if cp < 0x80 || (cp >= 0xA0 && cp <= 0xFF) {
1677 Some(cp as u8)
1678 } else if cp >= 0x80 && cp <= 0x9F {
1679 let mut i = 0;
1682 while i < 32 {
1683 if CP1252_C1[i] == cp as u16 {
1684 return Some(0x80 + i as u8);
1685 }
1686 i += 1;
1687 }
1688 None
1689 } else {
1690 None
1691 }
1692}
1693
1694fn decode_utf8_char(data: &[u8], in_pos: usize) -> Option<(u32, usize)> {
1697 let b0 = *data.get(in_pos)?;
1698 if b0 < 0x80 {
1699 return Some((u32::from(b0), 1));
1700 }
1701 let (len, cp0) = match b0 {
1702 0xC2..=0xDF => (2, u32::from(b0 & 0x1F)),
1703 0xE0..=0xEF => (3, u32::from(b0 & 0x0F)),
1704 0xF0..=0xF4 => (4, u32::from(b0 & 0x07)),
1705 _ => return None,
1706 };
1707 if in_pos + len > data.len() {
1708 return None;
1709 }
1710 let mut cp = cp0;
1711 for k in 1..len {
1712 let b = data[in_pos + k];
1713 if b & 0xC0 != 0x80 {
1714 return None;
1715 }
1716 cp = (cp << 6) | u32::from(b & 0x3F);
1717 }
1718 Some((cp, len))
1719}
1720
1721pub(crate) fn cp1252_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
1726 let mut result = Vec::with_capacity(data.len() * 2);
1727 for &byte in data {
1728 let cp = match cp1252_byte_to_cp(byte) {
1729 None => return Err(()),
1730 Some(cp) => cp,
1731 };
1732 let mut buf = [0u8; 4];
1733 let n = encode_codepoint_to_utf8(cp, &mut buf);
1734 result.extend_from_slice(&buf[..n]);
1735 }
1736 Ok(result)
1737}
1738
1739#[allow(dead_code)]
1743pub(crate) fn utf8_to_cp1252(data: &[u8]) -> Result<Vec<u8>, ()> {
1744 let mut result = Vec::with_capacity(data.len());
1745 let mut pos = 0;
1746 while pos < data.len() {
1747 let (cp, consumed) = match decode_utf8_char(data, pos) {
1748 None => return Err(()),
1749 Some(v) => v,
1750 };
1751 let byte = match cp_to_cp1252_byte(cp) {
1752 None => return Err(()),
1753 Some(b) => b,
1754 };
1755 result.push(byte);
1756 pos += consumed;
1757 }
1758 Ok(result)
1759}
1760
1761unsafe extern "C" fn cp1252_input_func(
1763 out: *mut c_uchar,
1764 outlen: *mut c_int,
1765 in_: *const c_uchar,
1766 inlen: *mut c_int,
1767) -> c_int {
1768 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1769 return -1;
1770 }
1771
1772 let avail_in = *inlen as usize;
1773 let avail_out = *outlen as usize;
1774
1775 if avail_in == 0 || avail_out == 0 {
1776 *outlen = 0;
1777 *inlen = 0;
1778 return 0;
1779 }
1780
1781 let in_data = core::slice::from_raw_parts(in_, avail_in);
1782 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1783
1784 let mut in_pos = 0;
1785 let mut out_pos = 0;
1786
1787 while in_pos < avail_in && out_pos < avail_out {
1788 let byte = in_data[in_pos];
1789 let cp = match cp1252_byte_to_cp(byte) {
1790 None => {
1792 *outlen = out_pos as c_int;
1793 *inlen = in_pos as c_int;
1794 return -1;
1795 }
1796 Some(cp) => cp,
1797 };
1798 let mut buf = [0u8; 4];
1799 let n = encode_codepoint_to_utf8(cp, &mut buf);
1800 if out_pos + n > avail_out {
1801 break;
1802 }
1803 out_slice[out_pos..out_pos + n].copy_from_slice(&buf[..n]);
1804 out_pos += n;
1805 in_pos += 1;
1806 }
1807
1808 *outlen = out_pos as c_int;
1809 *inlen = in_pos as c_int;
1810 out_pos as c_int
1811}
1812
1813unsafe extern "C" fn cp1252_output_func(
1815 out: *mut c_uchar,
1816 outlen: *mut c_int,
1817 in_: *const c_uchar,
1818 inlen: *mut c_int,
1819) -> c_int {
1820 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1821 return -1;
1822 }
1823
1824 let avail_in = *inlen as usize;
1825 let avail_out = *outlen as usize;
1826
1827 if avail_in == 0 || avail_out == 0 {
1828 *outlen = 0;
1829 *inlen = 0;
1830 return 0;
1831 }
1832
1833 let in_data = core::slice::from_raw_parts(in_, avail_in);
1834 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1835
1836 let mut in_pos = 0;
1837 let mut out_pos = 0;
1838
1839 while in_pos < avail_in && out_pos < avail_out {
1840 let (cp, consumed) = match decode_utf8_char(in_data, in_pos) {
1841 None => {
1842 *outlen = out_pos as c_int;
1843 *inlen = in_pos as c_int;
1844 return -1;
1845 }
1846 Some(v) => v,
1847 };
1848 let byte = match cp_to_cp1252_byte(cp) {
1849 None => {
1850 *outlen = out_pos as c_int;
1852 *inlen = in_pos as c_int;
1853 return -1;
1854 }
1855 Some(b) => b,
1856 };
1857 out_slice[out_pos] = byte;
1858 out_pos += 1;
1859 in_pos += consumed;
1860 }
1861
1862 *outlen = out_pos as c_int;
1863 *inlen = in_pos as c_int;
1864 out_pos as c_int
1865}
1866
1867unsafe extern "C" fn ascii_input_func(
1878 out: *mut c_uchar,
1879 outlen: *mut c_int,
1880 in_: *const c_uchar,
1881 inlen: *mut c_int,
1882) -> c_int {
1883 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1884 return -1;
1885 }
1886
1887 let avail_in = *inlen as usize;
1888 let avail_out = *outlen as usize;
1889
1890 if avail_in == 0 || avail_out == 0 {
1891 *outlen = 0;
1892 *inlen = 0;
1893 return 0;
1894 }
1895
1896 let in_data = core::slice::from_raw_parts(in_, avail_in);
1897 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1898
1899 let mut pos = 0;
1900 while pos < avail_in && pos < avail_out {
1901 let byte = in_data[pos];
1902 if byte > 0x7F {
1903 *outlen = pos as c_int;
1906 *inlen = pos as c_int;
1907 return -2;
1908 }
1909 out_slice[pos] = byte;
1910 pos += 1;
1911 }
1912
1913 *outlen = pos as c_int;
1914 *inlen = pos as c_int;
1915 pos as c_int
1916}
1917
1918unsafe extern "C" fn ascii_output_func(
1920 out: *mut c_uchar,
1921 outlen: *mut c_int,
1922 in_: *const c_uchar,
1923 inlen: *mut c_int,
1924) -> c_int {
1925 ascii_input_func(out, outlen, in_, inlen)
1927}
1928
1929const ENC_INPUT_ERROR: c_int = -2;
1936
1937unsafe fn enc_rs_output(
1948 target: &'static encoding_rs::Encoding,
1949 out: *mut c_uchar,
1950 outlen: *mut c_int,
1951 in_: *const c_uchar,
1952 inlen: *mut c_int,
1953) -> c_int {
1954 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1955 return -1;
1956 }
1957 let avail_in = *inlen as usize;
1958 let avail_out = *outlen as usize;
1959
1960 if avail_in == 0 || avail_out == 0 {
1961 *outlen = 0;
1962 *inlen = 0;
1963 return 0;
1964 }
1965
1966 let in_data = core::slice::from_raw_parts(in_, avail_in);
1967 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1968
1969 let (s, error_at) = match core::str::from_utf8(in_data) {
1974 Ok(s) => (s, None),
1975 Err(e) => {
1976 let valid = e.valid_up_to();
1977 if valid == 0 {
1978 *outlen = 0;
1979 *inlen = 0;
1980 return -1;
1981 }
1982 (
1985 unsafe { core::str::from_utf8_unchecked(&in_data[..valid]) },
1986 Some(valid),
1987 )
1988 }
1989 };
1990
1991 let mut encoder = target.new_encoder();
1992 let mut in_pos: usize = 0;
1993 let mut out_pos: usize = 0;
1994 while in_pos < s.len() && out_pos < avail_out {
1995 let dst = &mut out_slice[out_pos..];
1996 let (res, read, written) =
1997 encoder.encode_from_utf8_without_replacement(&s[in_pos..], dst, true);
1998 out_pos += written;
1999 in_pos += read;
2000 match res {
2001 encoding_rs::EncoderResult::InputEmpty => break,
2002 encoding_rs::EncoderResult::OutputFull => {
2003 break;
2007 }
2008 encoding_rs::EncoderResult::Unmappable(c) => {
2009 *outlen = out_pos as c_int;
2015 *inlen = (in_pos - c.len_utf8()) as c_int;
2016 return ENC_INPUT_ERROR;
2017 }
2018 }
2019 }
2020
2021 if let Some(err) = error_at {
2022 if in_pos == s.len() {
2023 *outlen = out_pos as c_int;
2027 *inlen = err as c_int;
2028 return -1;
2029 }
2030 }
2031 *outlen = out_pos as c_int;
2032 *inlen = in_pos as c_int;
2033 out_pos as c_int
2034}
2035
2036unsafe fn enc_rs_input(
2042 source: &'static encoding_rs::Encoding,
2043 out: *mut c_uchar,
2044 outlen: *mut c_int,
2045 in_: *const c_uchar,
2046 inlen: *mut c_int,
2047) -> c_int {
2048 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
2049 return -1;
2050 }
2051 let avail_in = *inlen as usize;
2052 let avail_out = *outlen as usize;
2053
2054 if avail_in == 0 || avail_out == 0 {
2055 *outlen = 0;
2056 *inlen = 0;
2057 return 0;
2058 }
2059
2060 let in_data = core::slice::from_raw_parts(in_, avail_in);
2061 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
2062
2063 let mut decoder = source.new_decoder_without_bom_handling();
2064 let mut in_pos: usize = 0;
2065 let mut out_pos: usize = 0;
2066 while in_pos < avail_in && out_pos < avail_out {
2067 let (res, read, written) = decoder.decode_to_utf8_without_replacement(
2068 &in_data[in_pos..],
2069 &mut out_slice[out_pos..],
2070 true,
2071 );
2072 out_pos += written;
2073 in_pos += read;
2074 match res {
2075 encoding_rs::DecoderResult::InputEmpty => break,
2076 encoding_rs::DecoderResult::OutputFull => break,
2077 encoding_rs::DecoderResult::Malformed(..) => {
2078 *outlen = out_pos as c_int;
2081 *inlen = in_pos as c_int;
2082 return -1;
2083 }
2084 }
2085 }
2086
2087 *outlen = out_pos as c_int;
2088 *inlen = in_pos as c_int;
2089 out_pos as c_int
2090}
2091
2092unsafe extern "C" fn shift_jis_input_func(
2094 out: *mut c_uchar,
2095 outlen: *mut c_int,
2096 in_: *const c_uchar,
2097 inlen: *mut c_int,
2098) -> c_int {
2099 enc_rs_input(encoding_rs::SHIFT_JIS, out, outlen, in_, inlen)
2100}
2101
2102unsafe extern "C" fn shift_jis_output_func(
2104 out: *mut c_uchar,
2105 outlen: *mut c_int,
2106 in_: *const c_uchar,
2107 inlen: *mut c_int,
2108) -> c_int {
2109 enc_rs_output(encoding_rs::SHIFT_JIS, out, outlen, in_, inlen)
2110}
2111
2112unsafe extern "C" fn euc_jp_input_func(
2114 out: *mut c_uchar,
2115 outlen: *mut c_int,
2116 in_: *const c_uchar,
2117 inlen: *mut c_int,
2118) -> c_int {
2119 enc_rs_input(encoding_rs::EUC_JP, out, outlen, in_, inlen)
2120}
2121
2122unsafe extern "C" fn euc_jp_output_func(
2124 out: *mut c_uchar,
2125 outlen: *mut c_int,
2126 in_: *const c_uchar,
2127 inlen: *mut c_int,
2128) -> c_int {
2129 enc_rs_output(encoding_rs::EUC_JP, out, outlen, in_, inlen)
2130}
2131
2132pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
2141 if name.is_null() {
2142 return ptr::null_mut();
2143 }
2144 find_encoding_handler(name as *const xmlChar)
2145}
2146
2147pub(crate) const fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
2151 match enc {
2155 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
2156 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE | xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => {
2157 c"UTF-16".as_ptr()
2158 }
2159 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE | xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => {
2160 c"UCS-4".as_ptr()
2161 }
2162 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"IBM037".as_ptr(),
2163 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
2164 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
2165 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
2166 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
2167 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
2168 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
2169 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
2170 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
2171 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
2172 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
2173 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
2174 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"Shift_JIS".as_ptr(),
2175 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
2176 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"US-ASCII".as_ptr(),
2178 _ => ptr::null(),
2179 }
2180}
2181
2182pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
2191 if name.is_null() {
2192 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
2193 }
2194 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
2195 encoding_from_name(bytes) as c_int
2196}
2197
2198static ENCODING_ALIASES: std::sync::OnceLock<
2206 parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>>,
2207> = std::sync::OnceLock::new();
2208
2209fn encoding_aliases() -> &'static parking_lot::RwLock<std::collections::HashMap<Vec<u8>, Vec<u8>>> {
2210 ENCODING_ALIASES.get_or_init(|| parking_lot::RwLock::new(std::collections::HashMap::new()))
2211}
2212
2213pub(crate) fn add_encoding_alias(name: *const c_char, alias: *const c_char) -> c_int {
2221 if name.is_null() || alias.is_null() {
2222 return -1;
2223 }
2224 let n = unsafe { CStr::from_ptr(name).to_bytes().to_vec() };
2225 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
2226 encoding_aliases().write().insert(a, n);
2227 0
2228}
2229
2230pub(crate) fn del_encoding_alias(alias: *const c_char) -> c_int {
2237 if alias.is_null() {
2238 return -1;
2239 }
2240 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
2241 if encoding_aliases().write().remove(&a).is_some() {
2242 0
2243 } else {
2244 -1
2245 }
2246}
2247
2248pub(crate) fn get_encoding_alias(alias: *const c_char) -> *const c_char {
2257 if alias.is_null() {
2258 return ptr::null();
2259 }
2260 let a = unsafe { CStr::from_ptr(alias).to_bytes().to_vec() };
2261 let guard = encoding_aliases().read();
2262 match guard.get(&a) {
2263 Some(v) => {
2264 let leaked: &'static [u8] = Box::leak(v.clone().into_boxed_slice());
2267 leaked.as_ptr() as *const c_char
2268 }
2269 None => ptr::null(),
2270 }
2271}
2272
2273pub(crate) fn cleanup_encoding_aliases() {
2275 encoding_aliases().write().clear();
2276}
2277
2278pub(crate) fn xmlCharEncInFunc(
2282 handler: *mut _xmlCharEncodingHandler,
2283 out: *mut _xmlBuffer,
2284 in_: *mut _xmlBuffer,
2285) -> c_int {
2286 char_enc_in(handler, out, in_)
2287}
2288
2289pub(crate) fn xmlCharEncOutFunc(
2293 handler: *mut _xmlCharEncodingHandler,
2294 out: *mut _xmlBuffer,
2295 in_: *mut _xmlBuffer,
2296) -> c_int {
2297 char_enc_out(handler, out, in_)
2298}
2299
2300pub(crate) fn xmlNewCharEncodingHandler(
2314 name: *const c_char,
2315 input: xmlCharEncodingInputFunc,
2316 output: xmlCharEncodingOutputFunc,
2317) -> *mut _xmlCharEncodingHandler {
2318 if name.is_null() {
2319 return ptr::null_mut();
2320 }
2321
2322 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) };
2323 if name_raw.is_null() {
2324 return ptr::null_mut();
2325 }
2326
2327 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
2328 as *mut _xmlCharEncodingHandler;
2329
2330 if handler.is_null() {
2331 unsafe { xmlFreeImpl(name_raw) };
2332 return ptr::null_mut();
2333 }
2334
2335 unsafe {
2336 ptr::write(
2337 handler,
2338 _xmlCharEncodingHandler {
2339 name: name_raw as *mut c_char,
2340 input: EncodingInputUnion {
2341 legacyFunc: Some(input),
2342 },
2343 output: EncodingOutputUnion {
2344 legacyFunc: Some(output),
2345 },
2346 inputCtxt: ptr::null_mut(),
2347 outputCtxt: ptr::null_mut(),
2348 ctxtDtor: None,
2349 flags: 0,
2350 },
2351 );
2352 }
2353
2354 handler
2355}
2356
2357#[allow(dead_code)]
2368pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
2369 if handler.is_null() {
2370 return;
2371 }
2372
2373 {
2375 let mut handlers = ENCODING_HANDLERS.write();
2376 handlers.retain(|&h| h.0 != handler);
2377 }
2378
2379 unsafe {
2380 if !(*handler).name.is_null() {
2381 xmlFreeImpl((*handler).name as *mut c_void);
2382 }
2383 xmlFreeImpl(handler as *mut c_void);
2384 }
2385}
2386
2387pub(crate) fn xmlInitCharEncodingHandlers() {
2389 init_encodings();
2390}
2391
2392pub(crate) fn xmlCleanupCharEncodingHandlers() {
2394 cleanup_encodings();
2395}
2396
2397pub(crate) fn xmlLookupCharEncodingHandler(enc: c_int, out: *mut *mut c_void) -> c_int {
2425 if out.is_null() {
2426 return crate::abi::types::XML_ERR_ARGUMENT;
2427 }
2428 unsafe {
2429 *out = ptr::null_mut();
2430 }
2431 if enc <= 0 || enc >= 32 {
2432 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
2433 }
2434 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int {
2436 return crate::abi::types::XML_ERR_OK;
2437 }
2438 let canonical: &[u8] = match enc {
2439 2 => b"UTF-16LE\0",
2441 3 => b"UTF-16BE\0",
2443 10 => b"ISO-8859-1\0",
2445 22 => b"US-ASCII\0",
2447 23 => b"UTF-16\0",
2449 _ => return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING,
2450 };
2451 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
2452 if h.is_null() {
2453 return crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING;
2454 }
2455 unsafe {
2456 *out = h as *mut c_void;
2457 }
2458 crate::abi::types::XML_ERR_OK
2459}
2460
2461pub(crate) fn xmlGetCharEncodingHandler(enc: c_int) -> *mut c_void {
2463 let mut ret: *mut c_void = ptr::null_mut();
2464 let _rc = xmlLookupCharEncodingHandler(enc, &mut ret);
2465 ret
2466}
2467
2468pub(crate) fn xmlCreateCharEncodingHandler(
2483 name: *const c_char,
2484 flags: c_int,
2485 impl_: Option<xmlCharEncConvImpl>,
2486 implCtxt: *mut c_void,
2487 out: *mut *mut c_void,
2488) -> c_int {
2489 if out.is_null() {
2490 return crate::abi::types::XML_ERR_ARGUMENT;
2491 }
2492 unsafe {
2493 *out = ptr::null_mut();
2494 }
2495 if name.is_null() || flags == 0 {
2496 return crate::abi::types::XML_ERR_ARGUMENT;
2497 }
2498 let norig = unsafe { CStr::from_ptr(name).to_bytes() };
2499
2500 let mut eff: &[u8] = norig;
2502 let alias = get_encoding_alias(name);
2503 if !alias.is_null() {
2504 eff = unsafe { CStr::from_ptr(alias).to_bytes() };
2505 }
2506
2507 let enc = encoding_from_name(eff);
2508
2509 if enc == xmlCharEncoding::XML_CHAR_ENCODING_UTF8 {
2511 return crate::abi::types::XML_ERR_OK;
2512 }
2513
2514 let canonical: &[u8] = match enc {
2515 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => b"UTF-16LE\0",
2516 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => b"UTF-16BE\0",
2517 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => b"ISO-8859-1\0",
2518 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => b"US-ASCII\0",
2519 _ => {
2520 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
2521 }
2522 };
2523 let h = find_encoding_handler(canonical.as_ptr() as *const xmlChar);
2524 if h.is_null() {
2525 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
2526 }
2527 unsafe {
2528 let src = &*h;
2529 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
2530 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
2531 if !has_in || !has_out {
2532 return find_extra_handler(norig, eff, flags, impl_, implCtxt, out);
2533 }
2534 let copy =
2539 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler;
2540 if copy.is_null() {
2541 return crate::abi::types::XML_ERR_NO_MEMORY;
2542 }
2543 let name_copy = crate::abi::allocator::xmlMemStrdupImpl(name) as *mut c_char;
2544 if name_copy.is_null() {
2545 xmlFreeImpl(copy as *mut c_void);
2546 return crate::abi::types::XML_ERR_NO_MEMORY;
2547 }
2548 ptr::write(
2549 copy,
2550 _xmlCharEncodingHandler {
2551 name: name_copy,
2552 input: EncodingInputUnion {
2553 legacyFunc: src.input.legacyFunc,
2554 },
2555 output: EncodingOutputUnion {
2556 legacyFunc: src.output.legacyFunc,
2557 },
2558 inputCtxt: src.inputCtxt,
2559 outputCtxt: src.outputCtxt,
2560 ctxtDtor: src.ctxtDtor,
2561 flags: src.flags,
2562 },
2563 );
2564 *out = copy as *mut c_void;
2565 }
2566 crate::abi::types::XML_ERR_OK
2567}
2568
2569fn find_extra_handler(
2584 norig: &[u8],
2585 name: &[u8],
2586 flags: c_int,
2587 impl_: Option<xmlCharEncConvImpl>,
2588 implCtxt: *mut c_void,
2589 out: *mut *mut c_void,
2590) -> c_int {
2591 if let Some(f) = impl_ {
2593 let mut n = norig.to_vec();
2594 n.push(0);
2595 let rc = unsafe {
2596 f(
2597 implCtxt,
2598 n.as_ptr() as *const c_char,
2599 flags,
2600 out as *mut *mut crate::abi::structs::_xmlCharEncodingHandler,
2601 )
2602 };
2603 return rc;
2604 }
2605 let mut n = name.to_vec();
2607 n.push(0);
2608 let h = find_encoding_handler(n.as_ptr() as *const xmlChar);
2609 if !h.is_null() {
2610 unsafe {
2611 let src = &*h;
2612 let has_in = (flags & 1) == 0 || !src.input.legacyFunc.is_none();
2613 let has_out = (flags & 2) == 0 || !src.output.legacyFunc.is_none();
2614 if has_in && has_out {
2615 *out = h as *mut c_void;
2616 return crate::abi::types::XML_ERR_OK;
2617 }
2618 }
2619 }
2620 crate::abi::types::XML_ERR_UNSUPPORTED_ENCODING
2621}
2622
2623pub(crate) fn xmlOpenCharEncodingHandler(
2625 name: *const c_char,
2626 output: c_int,
2627 out: *mut *mut c_void,
2628) -> c_int {
2629 let flags: c_int = if output != 0 { 2 } else { 1 };
2631 xmlCreateCharEncodingHandler(name, flags, None, ptr::null_mut(), out)
2632}
2633
2634pub(crate) fn xmlCharEncNewCustomHandler(
2649 name: *const c_char,
2650 input: xmlCharEncConvFunc,
2651 output: xmlCharEncConvFunc,
2652 ctxtDtor: Option<xmlCharEncConvCtxtDtor>,
2653 inputCtxt: *mut c_void,
2654 outputCtxt: *mut c_void,
2655 out: *mut *mut c_void,
2656) -> c_int {
2657 if out.is_null() {
2658 return crate::abi::types::XML_ERR_ARGUMENT;
2659 }
2660 let handler = unsafe { xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) }
2661 as *mut _xmlCharEncodingHandler;
2662 if handler.is_null() {
2663 unsafe {
2664 if let Some(d) = ctxtDtor {
2665 if !inputCtxt.is_null() {
2666 d(inputCtxt);
2667 }
2668 if !outputCtxt.is_null() {
2669 d(outputCtxt);
2670 }
2671 }
2672 }
2673 return crate::abi::types::XML_ERR_NO_MEMORY;
2674 }
2675 let name_copy = if name.is_null() {
2676 ptr::null_mut()
2677 } else {
2678 let nc = unsafe { crate::abi::allocator::xmlMemStrdupImpl(name) } as *mut c_char;
2679 if nc.is_null() {
2680 unsafe { xmlFreeImpl(handler as *mut c_void) };
2681 unsafe {
2682 if let Some(d) = ctxtDtor {
2683 if !inputCtxt.is_null() {
2684 d(inputCtxt);
2685 }
2686 if !outputCtxt.is_null() {
2687 d(outputCtxt);
2688 }
2689 }
2690 }
2691 return crate::abi::types::XML_ERR_NO_MEMORY;
2692 }
2693 nc
2694 };
2695 unsafe {
2696 ptr::write(
2697 handler,
2698 _xmlCharEncodingHandler {
2699 name: name_copy,
2700 input: EncodingInputUnion { func: Some(input) },
2701 output: EncodingOutputUnion { func: Some(output) },
2702 inputCtxt,
2703 outputCtxt,
2704 ctxtDtor,
2705 flags: 0,
2706 },
2707 );
2708 *out = handler as *mut c_void;
2709 }
2710 crate::abi::types::XML_ERR_OK
2711}
2712
2713#[cfg(test)]
2718mod tests {
2719 use super::*;
2720
2721 #[test]
2724 fn test_detect_bom_utf8() {
2725 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
2726 assert_eq!(
2727 detect_encoding_from_bom(&data),
2728 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2729 );
2730 }
2731
2732 #[test]
2733 fn test_detect_bom_utf16le() {
2734 let data = [0xFF, 0xFE, 0x00, 0x01];
2735 assert_eq!(
2736 detect_encoding_from_bom(&data),
2737 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2738 );
2739 }
2740
2741 #[test]
2742 fn test_detect_bom_utf16be() {
2743 let data = [0xFE, 0xFF, 0x00, 0x01];
2744 assert_eq!(
2745 detect_encoding_from_bom(&data),
2746 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2747 );
2748 }
2749
2750 #[test]
2751 fn test_detect_bom_none() {
2752 let data = b"<xml>";
2753 assert_eq!(
2754 detect_encoding_from_bom(data),
2755 xmlCharEncoding::XML_CHAR_ENCODING_NONE
2756 );
2757 }
2758
2759 #[test]
2760 fn test_detect_bom_empty() {
2761 assert_eq!(
2762 detect_encoding_from_bom(b""),
2763 xmlCharEncoding::XML_CHAR_ENCODING_NONE
2764 );
2765 }
2766
2767 #[test]
2770 fn test_detect_encoding_declaration_utf8() {
2771 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
2772 let result = detect_encoding_from_declaration(data);
2773 assert_eq!(result, Some(b"utf-8".to_vec()));
2774 }
2775
2776 #[test]
2777 fn test_detect_encoding_declaration_iso() {
2778 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
2779 let result = detect_encoding_from_declaration(data);
2780 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
2781 }
2782
2783 #[test]
2784 fn test_detect_encoding_declaration_none() {
2785 let data = b"<?xml version=\"1.0\"?>";
2786 let result = detect_encoding_from_declaration(data);
2787 assert!(result.is_none());
2788 }
2789
2790 #[test]
2791 fn test_detect_encoding_declaration_no_xml() {
2792 let data = b"<root>";
2793 let result = detect_encoding_from_declaration(data);
2794 assert!(result.is_none());
2795 }
2796
2797 #[test]
2798 fn test_detect_encoding_declaration_with_bom() {
2799 let mut data = vec![0xEF, 0xBB, 0xBF];
2800 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
2801 let result = detect_encoding_from_declaration(&data);
2802 assert_eq!(result, Some(b"utf-8".to_vec()));
2803 }
2804
2805 #[test]
2808 fn test_encoding_from_name_utf8() {
2809 assert_eq!(
2810 encoding_from_name(b"UTF-8"),
2811 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2812 );
2813 assert_eq!(
2814 encoding_from_name(b"utf8"),
2815 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2816 );
2817 }
2818
2819 #[test]
2820 fn test_encoding_from_name_utf16() {
2821 assert_eq!(
2822 encoding_from_name(b"UTF-16LE"),
2823 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2824 );
2825 assert_eq!(
2826 encoding_from_name(b"UTF-16BE"),
2827 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
2828 );
2829 assert_eq!(
2830 encoding_from_name(b"utf-16"),
2831 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
2832 );
2833 }
2834
2835 #[test]
2836 fn test_encoding_from_name_latin1() {
2837 assert_eq!(
2838 encoding_from_name(b"ISO-8859-1"),
2839 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2840 );
2841 assert_eq!(
2842 encoding_from_name(b"Latin1"),
2843 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2844 );
2845 }
2846
2847 #[test]
2848 fn test_encoding_from_name_ascii() {
2849 assert_eq!(
2850 encoding_from_name(b"ASCII"),
2851 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2852 );
2853 assert_eq!(
2854 encoding_from_name(b"US-ASCII"),
2855 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2856 );
2857 }
2858
2859 #[test]
2860 fn test_encoding_from_name_error() {
2861 assert_eq!(
2862 encoding_from_name(b"invalid-encoding"),
2863 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2864 );
2865 }
2866
2867 #[test]
2868 fn test_encoding_from_name_empty() {
2869 assert_eq!(
2870 encoding_from_name(b""),
2871 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2872 );
2873 }
2874
2875 #[test]
2878 fn test_encoding_name_utf8() {
2879 assert_eq!(
2880 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
2881 Some(b"UTF-8" as &[u8])
2882 );
2883 }
2884
2885 #[test]
2886 fn test_encoding_name_utf16le() {
2887 assert_eq!(
2888 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
2889 Some(b"UTF-16LE" as &[u8])
2890 );
2891 }
2892
2893 #[test]
2894 fn test_encoding_name_none() {
2895 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
2896 }
2897
2898 #[test]
2899 fn test_encoding_name_error() {
2900 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
2901 }
2902
2903 #[test]
2906 fn test_utf8_valid_ascii() {
2907 assert!(utf8_valid(b"hello world"));
2908 }
2909
2910 #[test]
2911 fn test_utf8_valid_multi_byte() {
2912 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
2913 }
2914
2915 #[test]
2916 fn test_utf8_valid_empty() {
2917 assert!(utf8_valid(b""));
2918 }
2919
2920 #[test]
2921 fn test_utf8_invalid() {
2922 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
2923 }
2924
2925 #[test]
2928 fn test_valid_xml_chars() {
2929 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));
2935 assert!(is_valid_xml_char(0xE000));
2936 assert!(is_valid_xml_char(0xFFFD));
2937 assert!(is_valid_xml_char(0x10000));
2938 assert!(is_valid_xml_char(0x10FFFF));
2939 }
2940
2941 #[test]
2942 fn test_invalid_xml_chars() {
2943 assert!(!is_valid_xml_char(0x00));
2944 assert!(!is_valid_xml_char(0x08));
2945 assert!(!is_valid_xml_char(0x0B));
2946 assert!(!is_valid_xml_char(0x0C));
2947 assert!(!is_valid_xml_char(0x0E));
2948 assert!(!is_valid_xml_char(0x1F));
2949 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
2952 assert!(!is_valid_xml_char(0xFFFF));
2953 assert!(!is_valid_xml_char(0x110000));
2954 }
2955
2956 #[test]
2959 fn test_utf16le_to_utf8_ascii() {
2960 let data = [b'A', 0x00, b'B', 0x00];
2962 let result = utf16le_to_utf8(&data).unwrap();
2963 assert_eq!(result, b"AB");
2964 }
2965
2966 #[test]
2967 fn test_utf16le_to_utf8_bom() {
2968 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
2970 let result = utf16le_to_utf8(&data).unwrap();
2971 assert_eq!(result, b"AB");
2972 }
2973
2974 #[test]
2975 fn test_utf16le_to_utf8_bmp() {
2976 let data = [0xE9, 0x00];
2978 let result = utf16le_to_utf8(&data).unwrap();
2979 assert_eq!(result, "é".as_bytes());
2980 }
2981
2982 #[test]
2983 fn test_utf16le_to_utf8_supplementary() {
2984 let data = [0x3D, 0xD8, 0x00, 0xDE];
2986 let result = utf16le_to_utf8(&data).unwrap();
2987 assert_eq!(result, "😀".as_bytes());
2988 }
2989
2990 #[test]
2991 fn test_utf16le_to_utf8_unpaired_surrogate() {
2992 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
2994 }
2995
2996 #[test]
2997 fn test_utf16le_to_utf8_truncated() {
2998 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
3000 }
3001
3002 #[test]
3003 fn test_utf16le_to_utf8_empty() {
3004 let result = utf16le_to_utf8(b"").unwrap();
3005 assert!(result.is_empty());
3006 }
3007
3008 #[test]
3011 fn test_utf16be_to_utf8_ascii() {
3012 let data = [0x00, b'A', 0x00, b'B'];
3013 let result = utf16be_to_utf8(&data).unwrap();
3014 assert_eq!(result, b"AB");
3015 }
3016
3017 #[test]
3018 fn test_utf16be_to_utf8_bom() {
3019 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
3021 let result = utf16be_to_utf8(&data).unwrap();
3022 assert_eq!(result, b"AB");
3023 }
3024
3025 #[test]
3026 fn test_utf16be_to_utf8_supplementary() {
3027 let data = [0xD8, 0x3D, 0xDE, 0x00];
3029 let result = utf16be_to_utf8(&data).unwrap();
3030 assert_eq!(result, "😀".as_bytes());
3031 }
3032
3033 #[test]
3034 fn test_utf16be_to_utf8_empty() {
3035 let result = utf16be_to_utf8(b"").unwrap();
3036 assert!(result.is_empty());
3037 }
3038
3039 #[test]
3042 fn test_utf8_to_utf16le_ascii() {
3043 let result = utf8_to_utf16le(b"AB").unwrap();
3044 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
3045 }
3046
3047 #[test]
3048 fn test_utf8_to_utf16le_bmp() {
3049 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
3050 assert_eq!(result, [0xE9, 0x00]);
3051 }
3052
3053 #[test]
3054 fn test_utf8_to_utf16le_supplementary() {
3055 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
3056 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
3057 }
3058
3059 #[test]
3060 fn test_utf8_to_utf16le_invalid_utf8() {
3061 assert!(utf8_to_utf16le(&[0xFF]).is_err());
3062 }
3063
3064 #[test]
3065 fn test_utf8_to_utf16le_empty() {
3066 let result = utf8_to_utf16le(b"").unwrap();
3067 assert!(result.is_empty());
3068 }
3069
3070 #[test]
3073 fn test_latin1_to_utf8_ascii() {
3074 let result = latin1_to_utf8(b"ABC");
3075 assert_eq!(result, b"ABC");
3076 }
3077
3078 #[test]
3079 fn test_latin1_to_utf8_accented() {
3080 let result = latin1_to_utf8(&[0xE9]);
3082 assert_eq!(result, "é".as_bytes());
3083 }
3084
3085 #[test]
3086 fn test_latin1_to_utf8_all_255() {
3087 let result = latin1_to_utf8(&[0xFF]);
3088 assert_eq!(result, [0xC3, 0xBF]);
3090 }
3091
3092 #[test]
3093 fn test_latin1_to_utf8_empty() {
3094 let result = latin1_to_utf8(b"");
3095 assert!(result.is_empty());
3096 }
3097
3098 #[test]
3099 fn test_latin1_to_utf8_mixed() {
3100 let result = latin1_to_utf8(b"caf\xE9");
3101 assert_eq!(result, "café".as_bytes());
3102 }
3103
3104 #[test]
3107 fn test_utf8_to_latin1_ascii() {
3108 let result = utf8_to_latin1(b"ABC").unwrap();
3109 assert_eq!(result, b"ABC");
3110 }
3111
3112 #[test]
3113 fn test_utf8_to_latin1_accented() {
3114 let result = utf8_to_latin1("é".as_bytes()).unwrap();
3115 assert_eq!(result, [0xE9]);
3116 }
3117
3118 #[test]
3119 fn test_utf8_to_latin1_out_of_range() {
3120 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
3122
3123 #[test]
3124 fn test_utf8_to_latin1_invalid_utf8() {
3125 assert!(utf8_to_latin1(&[0xFF]).is_err());
3126 }
3127
3128 #[test]
3129 fn test_utf8_to_latin1_empty() {
3130 let result = utf8_to_latin1(b"").unwrap();
3131 assert!(result.is_empty());
3132 }
3133
3134 #[test]
3137 fn test_init_and_find_encodings() {
3138 init_encodings();
3139
3140 let utf8_name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
3141 assert!(!find_encoding_handler(utf8_name).is_null());
3142
3143 let utf16le_name: *const xmlChar = c"UTF-16LE".as_ptr() as *const xmlChar;
3144 assert!(!find_encoding_handler(utf16le_name).is_null());
3145
3146 let utf16be_name: *const xmlChar = c"UTF-16BE".as_ptr() as *const xmlChar;
3147 assert!(!find_encoding_handler(utf16be_name).is_null());
3148
3149 let latin1_name: *const xmlChar = c"ISO-8859-1".as_ptr() as *const xmlChar;
3150 assert!(!find_encoding_handler(latin1_name).is_null());
3151
3152 let ascii_name: *const xmlChar = c"ASCII".as_ptr() as *const xmlChar;
3153 assert!(!find_encoding_handler(ascii_name).is_null());
3154
3155 let lower_name: *const xmlChar = c"utf-8".as_ptr() as *const xmlChar;
3157 assert!(!find_encoding_handler(lower_name).is_null());
3158 }
3159
3160 #[test]
3174 fn test_find_owned_close_keeps_registry_intact() {
3175 init_encodings();
3176 let name: *const xmlChar = c"ISO-8859-1".as_ptr() as *const xmlChar;
3177
3178 let registry = find_encoding_handler(name);
3180 assert!(!registry.is_null());
3181 let h1 = xmlFindCharEncodingHandler_owned(name);
3183 assert!(!h1.is_null());
3184 assert_ne!(h1 as *const c_void, registry as *const c_void);
3185
3186 unsafe {
3189 if !(*h1).name.is_null() {
3190 crate::abi::allocator::xmlFreeImpl((*h1).name as *mut c_void);
3191 }
3192 xmlFreeImpl(h1 as *mut c_void);
3193 }
3194
3195 let registry2 = find_encoding_handler(name);
3200 assert_eq!(registry2 as *const c_void, registry as *const c_void);
3201 assert!(!unsafe { (*registry2).name }.is_null());
3202 let reg_name = unsafe { CStr::from_ptr((*registry2).name as *const c_char) };
3203 assert_eq!(reg_name.to_bytes(), b"ISO-8859-1");
3204
3205 let h2 = xmlFindCharEncodingHandler_owned(name);
3207 assert!(!h2.is_null());
3208 assert_ne!(h2 as *const c_void, registry as *const c_void);
3209 unsafe {
3210 if !(*h2).name.is_null() {
3211 crate::abi::allocator::xmlFreeImpl((*h2).name as *mut c_void);
3212 }
3213 xmlFreeImpl(h2 as *mut c_void);
3214 }
3215 }
3216
3217 #[test]
3223 fn test_find_owned_utf8_static_and_persistent() {
3224 init_encodings();
3225 let name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
3226 let u1 = xmlFindCharEncodingHandler_owned(name);
3227 assert!(!u1.is_null());
3228 let u2 = xmlFindCharEncodingHandler_owned(c"utf8".as_ptr() as *const xmlChar);
3231 assert_eq!(u1, u2);
3232 assert_eq!(
3233 unsafe { (*u1).flags } & XML_HANDLER_STATIC,
3234 XML_HANDLER_STATIC
3235 );
3236 }
3237
3238 #[test]
3239 fn test_find_encoding_handler_not_found() {
3240 let name: *const xmlChar = c"NONEXISTENT".as_ptr() as *const xmlChar;
3241 assert!(find_encoding_handler(name).is_null());
3242 }
3243
3244 #[test]
3245 fn test_find_encoding_handler_null() {
3246 assert!(find_encoding_handler(ptr::null()).is_null());
3247 }
3248
3249 #[test]
3258 fn test_add_encoding_handler() {
3259 let handler = unsafe {
3260 xmlMallocImpl(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
3261 };
3262 assert!(!handler.is_null());
3263
3264 let name = unsafe {
3265 crate::abi::allocator::xmlMemStrdupImpl(c"TEST-ENC".as_ptr() as *const c_char)
3266 };
3267 unsafe {
3268 ptr::write(
3269 handler,
3270 _xmlCharEncodingHandler {
3271 name: name as *mut c_char,
3272 input: EncodingInputUnion { legacyFunc: None },
3273 output: EncodingOutputUnion { legacyFunc: None },
3274 inputCtxt: ptr::null_mut(),
3275 outputCtxt: ptr::null_mut(),
3276 ctxtDtor: None,
3277 flags: 0,
3278 },
3279 );
3280 }
3281
3282 assert_eq!(add_encoding_handler(handler), 0);
3283
3284 let found = find_encoding_handler(c"TEST-ENC".as_ptr() as *const xmlChar);
3285 assert_eq!(found, handler);
3286
3287 {
3289 let mut handlers = ENCODING_HANDLERS.write();
3290 handlers.retain(|&h| h.0 != handler);
3291 }
3292
3293 unsafe {
3294 xmlFreeImpl(name as *mut c_void);
3295 xmlFreeImpl(handler as *mut c_void);
3296 }
3297 }
3298
3299 #[test]
3302 fn test_utf16le_roundtrip() {
3303 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
3304 let utf16 = utf8_to_utf16le(original).unwrap();
3305 let back = utf16le_to_utf8(&utf16).unwrap();
3306 assert_eq!(original.to_vec(), back);
3307 }
3308
3309 #[test]
3310 fn test_utf16be_roundtrip() {
3311 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
3312 let utf16le = utf8_to_utf16le(original).unwrap();
3313 let mut utf16be = utf16le.clone();
3315 for chunk in utf16be.as_chunks_mut::<2>().0 {
3316 chunk.swap(0, 1);
3317 }
3318 let back = utf16be_to_utf8(&utf16be).unwrap();
3319 assert_eq!(original.to_vec(), back);
3320 }
3321
3322 #[test]
3323 fn test_latin1_roundtrip() {
3324 let original: Vec<u8> = (0x00..=0xFF).collect();
3325 let utf8 = latin1_to_utf8(&original);
3326 let back = utf8_to_latin1(&utf8).unwrap();
3327 assert_eq!(original, back);
3328 }
3329
3330 #[test]
3340 fn test_utf8_handler_identity() {
3341 let input = b"Hello, UTF-8!";
3342 let mut output = [0u8; 64];
3343 let mut outlen = output.len() as c_int;
3344 let mut inlen = input.len() as c_int;
3345
3346 let ret = unsafe {
3347 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
3348 };
3349
3350 assert_eq!(ret, input.len() as c_int);
3351 assert_eq!(&output[..ret as usize], input);
3352 assert_eq!(inlen, input.len() as c_int);
3353 }
3354
3355 #[test]
3363 fn test_utf16le_handler_roundtrip() {
3364 init_encodings();
3365
3366 let original = b"Hello UTF-16LE!";
3367 let mut utf16_buf = [0u8; 128];
3368 let mut outlen = utf16_buf.len() as c_int;
3369 let mut inlen = original.len() as c_int;
3370
3371 let written = unsafe {
3372 utf16le_output_func(
3373 utf16_buf.as_mut_ptr(),
3374 &mut outlen,
3375 original.as_ptr(),
3376 &mut inlen,
3377 )
3378 };
3379 assert!(written > 0);
3380
3381 let mut decoded = [0u8; 128];
3383 let mut outlen2 = decoded.len() as c_int;
3384 let mut inlen2 = written;
3385
3386 let written2 = unsafe {
3387 utf16le_input_func(
3388 decoded.as_mut_ptr(),
3389 &mut outlen2,
3390 utf16_buf.as_ptr(),
3391 &mut inlen2,
3392 )
3393 };
3394 assert_eq!(written2 as usize, original.len());
3395 assert_eq!(&decoded[..written2 as usize], original);
3396 }
3397
3398 #[test]
3408 fn test_append_to_xml_buffer() {
3409 unsafe {
3410 let content = xmlMallocImpl(64) as *mut xmlChar;
3411 assert!(!content.is_null());
3412
3413 let mut buf = _xmlBuffer {
3414 content,
3415 use_: 0,
3416 size: 64,
3417 alloc: 0,
3418 contentIO: ptr::null_mut(),
3419 };
3420
3421 append_to_xml_buffer(&mut buf, b"Hello");
3422 assert_eq!(buf.use_, 5);
3423 let slice = core::slice::from_raw_parts(buf.content, 5);
3424 assert_eq!(slice, b"Hello");
3425
3426 append_to_xml_buffer(&mut buf, b" World");
3427 assert_eq!(buf.use_, 11);
3428 let slice = core::slice::from_raw_parts(buf.content, 11);
3429 assert_eq!(slice, b"Hello World");
3430
3431 xmlFreeImpl(buf.content as *mut c_void);
3432 }
3433 }
3434
3435 #[test]
3438 fn test_xml_parse_char_encoding() {
3439 let name = c"UTF-8".as_ptr() as *const c_char;
3440 assert_eq!(
3441 xmlParseCharEncoding(name),
3442 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
3443 );
3444
3445 let name = c"ISO-8859-1".as_ptr() as *const c_char;
3446 assert_eq!(
3447 xmlParseCharEncoding(name),
3448 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
3449 );
3450
3451 assert_eq!(
3452 xmlParseCharEncoding(ptr::null()),
3453 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
3454 );
3455 }
3456
3457 #[test]
3466 fn test_xml_new_and_del_encoding_handler() {
3467 let name = c"TestEnc".as_ptr() as *const c_char;
3468 let handler = xmlNewCharEncodingHandler(
3469 name,
3470 utf8_input_func as xmlCharEncodingInputFunc,
3471 utf8_output_func as xmlCharEncodingOutputFunc,
3472 );
3473 assert!(!handler.is_null());
3474
3475 unsafe {
3476 assert!(!(*handler).name.is_null());
3477 let cstr = CStr::from_ptr((*handler).name);
3478 assert_eq!(cstr.to_bytes(), b"TestEnc");
3479 }
3480
3481 xmlDelEncodingHandler(handler);
3482 }
3483
3484 #[test]
3485 fn test_xml_init_and_cleanup() {
3486 xmlInitCharEncodingHandlers();
3487
3488 let name: *const xmlChar = c"UTF-8".as_ptr() as *const xmlChar;
3489 assert!(!find_encoding_handler(name).is_null());
3490
3491 xmlCleanupCharEncodingHandlers();
3492 }
3494
3495 fn call_func(
3499 func: unsafe extern "C" fn(*mut c_uchar, *mut c_int, *const c_uchar, *mut c_int) -> c_int,
3500 input: &[u8],
3501 ) -> (c_int, Vec<u8>, usize) {
3502 let mut out = vec![0u8; input.len() * 6 + 64];
3503 let mut outlen = out.len() as c_int;
3504 let mut inlen = input.len() as c_int;
3505 let rc = unsafe { func(out.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen) };
3506 out.truncate(outlen.max(0) as usize);
3507 (rc, out, inlen.max(0) as usize)
3508 }
3509
3510 #[test]
3511 fn test_shift_jis_output_roundtrip() {
3512 let (rc, out, consumed) = call_func(shift_jis_output_func, "ぁ漢ア".as_bytes());
3515 assert!(rc >= 0);
3516 assert_eq!(out, [0x82, 0x9F, 0x8A, 0xBF, 0xB1]);
3517 assert_eq!(consumed, "ぁ漢ア".len());
3518
3519 let (rc, back, _) = call_func(shift_jis_input_func, &out);
3520 assert!(rc >= 0);
3521 assert_eq!(back, "ぁ漢ア".as_bytes());
3522 }
3523
3524 #[test]
3525 fn test_shift_jis_output_unmappable_reports_input_error() {
3526 let (rc, out, consumed) = call_func(shift_jis_output_func, "A😀B".as_bytes());
3530 assert_eq!(rc, ENC_INPUT_ERROR);
3531 assert_eq!(out, b"A");
3532 assert_eq!(consumed, 1); let handler = find_encoding_handler(c"SHIFT_JIS".as_ptr() as *const xmlChar);
3537 assert!(!handler.is_null());
3538 let in_buf = crate::xml::io::buf_create(64);
3539 let src = "A\u{1F600}B".as_bytes();
3540 assert!(
3541 crate::xml::io::buf_add(in_buf, src.as_ptr() as *const xmlChar, src.len() as c_int)
3542 >= 0
3543 );
3544 let out_buf = crate::xml::io::buf_create(64);
3545 let n = char_enc_out(handler, out_buf, in_buf);
3546 assert!(n >= 0);
3547 let bytes =
3548 unsafe { core::slice::from_raw_parts((*out_buf).content, (*out_buf).use_ as usize) };
3549 assert_eq!(bytes, b"A😀B");
3550 crate::xml::io::buf_free(in_buf);
3551 crate::xml::io::buf_free(out_buf);
3552 }
3553
3554 #[test]
3555 fn test_euc_jp_output_roundtrip() {
3556 let (rc, out, consumed) = call_func(euc_jp_output_func, "ぁ漢ア".as_bytes());
3559 assert!(rc >= 0);
3560 assert_eq!(out, [0xA4, 0xA1, 0xB4, 0xC1, 0x8E, 0xB1]);
3561 assert_eq!(consumed, "ぁ漢ア".len());
3562
3563 let (rc, back, _) = call_func(euc_jp_input_func, &out);
3564 assert!(rc >= 0);
3565 assert_eq!(back, "ぁ漢ア".as_bytes());
3566 }
3567
3568 #[test]
3569 fn test_east_asian_handlers_registered_and_findable() {
3570 for name in [
3571 c"SHIFT_JIS".as_ptr(),
3572 c"Shift_JIS".as_ptr(),
3573 c"SJIS".as_ptr(),
3574 c"CP932".as_ptr(),
3575 c"EUC-JP".as_ptr(),
3576 c"euc-jp".as_ptr(),
3577 ] {
3578 assert!(
3579 !find_encoding_handler(name as *const xmlChar).is_null(),
3580 "handler not found for {name:?}"
3581 );
3582 }
3583 }
3584
3585 #[test]
3586 fn test_shift_jis_output_invalid_utf8_errors() {
3587 let (rc, out, consumed) = call_func(shift_jis_output_func, b"A\xFFB");
3588 assert_eq!(rc, -1);
3589 assert_eq!(out, b"A");
3590 assert_eq!(consumed, 1);
3591 }
3592}