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