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::{xmlFree, xmlMalloc, xmlRealloc};
32use crate::abi::callbacks::{xmlCharEncodingInputFunc, xmlCharEncodingOutputFunc};
33use crate::abi::constants::XML_MAX_ENCODING_NAME_LEN;
34use crate::abi::structs::{_xmlBuffer, _xmlCharEncodingHandler};
35use crate::abi::types::{xmlChar, xmlCharEncoding, xmlCharPtr};
36
37const MAX_CHAR_BYTES: usize = 6;
41
42const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
44
45const UTF16LE_BOM: [u8; 2] = [0xFF, 0xFE];
47
48const UTF16BE_BOM: [u8; 2] = [0xFE, 0xFF];
50
51#[derive(Clone, Copy)]
59struct HandlerPtr(*mut _xmlCharEncodingHandler);
60
61unsafe impl Send for HandlerPtr {}
62unsafe impl Sync for HandlerPtr {}
63
64static ENCODING_HANDLERS: Lazy<RwLock<Vec<HandlerPtr>>> = Lazy::new(|| RwLock::new(Vec::new()));
66
67static ENCODING_INITIALIZED: AtomicBool = AtomicBool::new(false);
69
70pub(crate) fn detect_encoding_from_bom(data: &[u8]) -> xmlCharEncoding {
79 if data.len() >= 3 && data[0..3] == UTF8_BOM {
80 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
81 } else if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
82 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
83 } else if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
84 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
85 } else {
86 xmlCharEncoding::XML_CHAR_ENCODING_NONE
87 }
88}
89
90pub(crate) fn detect_encoding_from_declaration(data: &[u8]) -> Option<Vec<u8>> {
95 let start = if data.len() >= 3 && data[0..3] == UTF8_BOM {
97 3
98 } else if data.len() >= 2 && (data[0..2] == UTF16LE_BOM || data[0..2] == UTF16BE_BOM) {
99 return None;
101 } else {
102 0
103 };
104
105 let remaining = &data[start..];
106
107 if remaining.len() < 5 || !remaining[0..5].eq_ignore_ascii_case(b"<?xml") {
109 return None;
110 }
111
112 let pi_end = remaining.windows(2).position(|w| w == b"?>")?;
114 let decl_content = &remaining[5..pi_end];
115
116 let decl_str = core::str::from_utf8(decl_content).ok()?;
118 let lower = decl_str.to_ascii_lowercase();
119
120 let enc_pos = lower.find("encoding")?;
122
123 let after_enc = &decl_content[enc_pos + 8..];
125 let after_enc_str = core::str::from_utf8(after_enc).ok()?;
126 let after_enc_trimmed = after_enc_str.trim_start();
127
128 if !after_enc_trimmed.starts_with('=') {
129 return None;
130 }
131
132 let after_eq = after_enc_trimmed[1..].trim_start();
133
134 let quote = after_eq.chars().next()?;
136 if quote != '"' && quote != '\'' {
137 return None;
138 }
139
140 let value_end = after_eq[1..].find(quote)?;
142 let encoding_value = &after_eq[1..=value_end];
143
144 Some(encoding_value.to_ascii_lowercase().as_bytes().to_vec())
145}
146
147pub(crate) fn encoding_from_name(name: &[u8]) -> xmlCharEncoding {
152 let s = core::str::from_utf8(name).unwrap_or("");
153 let s = s.trim().to_ascii_lowercase();
154
155 match s.as_str() {
156 "utf-8" | "utf8" => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
158
159 "utf-16" | "utf-16le" | "utf16le" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
161 "utf-16be" | "utf16be" => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
162
163 "iso-8859-1" | "iso_8859-1" | "latin1" | "latin-1" | "l1" | "cp819" | "ibm819"
165 | "iso-ir-100" | "iso_8859-1:1987" => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
166 "iso-8859-2" | "iso_8859-2" | "latin2" | "latin-2" | "l2" => {
167 xmlCharEncoding::XML_CHAR_ENCODING_8859_2
168 }
169 "iso-8859-3" | "iso_8859-3" | "latin3" | "latin-3" | "l3" => {
170 xmlCharEncoding::XML_CHAR_ENCODING_8859_3
171 }
172 "iso-8859-4" | "iso_8859-4" | "latin4" | "latin-4" | "l4" => {
173 xmlCharEncoding::XML_CHAR_ENCODING_8859_4
174 }
175 "iso-8859-5" | "iso_8859-5" | "cyrillic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
176 "iso-8859-6" | "iso_8859-6" | "arabic" => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
177 "iso-8859-7" | "iso_8859-7" | "greek" => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
178 "iso-8859-8" | "iso_8859-8" | "hebrew" => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
179 "iso-8859-9" | "iso_8859-9" | "latin5" | "latin-5" | "l5" | "turkish" => {
180 xmlCharEncoding::XML_CHAR_ENCODING_8859_9
181 }
182
183 "ascii" | "us-ascii" | "us" | "ansi_x3.4-1968" | "ansi_x3.4-1986" | "iso-ir-6"
185 | "iso_646.irv:1991" | "cp367" | "ibm367" => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
186
187 "iso-2022-jp" | "iso2022-jp" => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
189 "shift_jis" | "shift-jis" | "sjis" | "cp932" => {
190 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS
191 }
192 "euc-jp" | "eucjp" => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
193
194 "ucs-4" | "ucs4" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
196 "ucs-4le" | "ucs4le" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
197 "ucs-4be" | "ucs4be" => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
198 "ucs-2" | "ucs2" => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
199
200 "ebcdic" | "cp037" | "ibm037" => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
202
203 _ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
204 }
205}
206
207pub(crate) fn encoding_name(enc: xmlCharEncoding) -> Option<&'static [u8]> {
211 match enc {
212 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => Some(b"UTF-8" as &[u8]),
213 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => Some(b"UTF-16LE" as &[u8]),
214 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => Some(b"UTF-16BE" as &[u8]),
215 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => Some(b"UCS-4LE" as &[u8]),
216 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => Some(b"UCS-4BE" as &[u8]),
217 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => Some(b"EBCDIC" as &[u8]),
218 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => Some(b"UCS-4-2143" as &[u8]),
219 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => Some(b"UCS-4-3412" as &[u8]),
220 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => Some(b"UCS-2" as &[u8]),
221 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => Some(b"ISO-8859-1" as &[u8]),
222 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => Some(b"ISO-8859-2" as &[u8]),
223 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => Some(b"ISO-8859-3" as &[u8]),
224 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => Some(b"ISO-8859-4" as &[u8]),
225 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => Some(b"ISO-8859-5" as &[u8]),
226 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => Some(b"ISO-8859-6" as &[u8]),
227 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => Some(b"ISO-8859-7" as &[u8]),
228 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => Some(b"ISO-8859-8" as &[u8]),
229 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => Some(b"ISO-8859-9" as &[u8]),
230 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => Some(b"ISO-2022-JP" as &[u8]),
231 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => Some(b"SHIFT_JIS" as &[u8]),
232 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => Some(b"EUC-JP" as &[u8]),
233 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => Some(b"ASCII" as &[u8]),
234 _ => None,
235 }
236}
237
238pub(crate) fn utf8_valid(data: &[u8]) -> bool {
246 core::str::from_utf8(data).is_ok()
247}
248
249pub(crate) fn is_valid_xml_char(cp: u32) -> bool {
261 match cp {
262 0x9 | 0xA | 0xD => true,
263 0x20..=0xD7FF => true,
264 0xE000..=0xFFFD => true,
265 0x10000..=0x10FFFF => true,
266 _ => false,
267 }
268}
269
270#[inline]
276fn read_utf16le_unit(data: &[u8]) -> Option<u16> {
277 if data.len() < 2 {
278 return None;
279 }
280 Some(u16::from_le_bytes([data[0], data[1]]))
281}
282
283#[inline]
285fn read_utf16be_unit(data: &[u8]) -> Option<u16> {
286 if data.len() < 2 {
287 return None;
288 }
289 Some(u16::from_be_bytes([data[0], data[1]]))
290}
291
292fn encode_codepoint_to_utf8(cp: u32, out: &mut [u8]) -> usize {
296 if cp < 0x80 {
297 if !out.is_empty() {
298 out[0] = cp as u8;
299 }
300 1
301 } else if cp < 0x800 {
302 if out.len() < 2 {
303 return 0;
304 }
305 out[0] = 0xC0 | ((cp >> 6) as u8);
306 out[1] = 0x80 | (cp as u8 & 0x3F);
307 2
308 } else if cp < 0x10000 {
309 if out.len() < 3 {
310 return 0;
311 }
312 out[0] = 0xE0 | ((cp >> 12) as u8);
313 out[1] = 0x80 | ((cp >> 6) as u8 & 0x3F);
314 out[2] = 0x80 | (cp as u8 & 0x3F);
315 3
316 } else if cp < 0x110000 {
317 if out.len() < 4 {
318 return 0;
319 }
320 out[0] = 0xF0 | ((cp >> 18) as u8);
321 out[1] = 0x80 | ((cp >> 12) as u8 & 0x3F);
322 out[2] = 0x80 | ((cp >> 6) as u8 & 0x3F);
323 out[3] = 0x80 | (cp as u8 & 0x3F);
324 4
325 } else {
326 0
327 }
328}
329
330pub(crate) fn utf16le_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
335 if data.is_empty() {
336 return Ok(Vec::new());
337 }
338
339 let offset = if data.len() >= 2 && data[0..2] == UTF16LE_BOM {
341 2
342 } else {
343 0
344 };
345
346 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
347 let mut i = offset;
348
349 while i < data.len() {
350 let unit = read_utf16le_unit(&data[i..]).ok_or(())?;
351 i += 2;
352
353 if unit >= 0xD800 && unit <= 0xDBFF {
354 let low = read_utf16le_unit(&data[i..]).ok_or(())?;
356 i += 2;
357
358 if !(0xDC00..=0xDFFF).contains(&low) {
359 return Err(());
360 }
361
362 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
363 let mut buf = [0u8; 4];
364 let n = encode_codepoint_to_utf8(cp, &mut buf);
365 if n == 0 {
366 return Err(());
367 }
368 result.extend_from_slice(&buf[..n]);
369 } else if unit >= 0xDC00 && unit <= 0xDFFF {
370 return Err(());
372 } else {
373 let cp = unit as u32;
374 let mut buf = [0u8; 4];
375 let n = encode_codepoint_to_utf8(cp, &mut buf);
376 result.extend_from_slice(&buf[..n]);
377 }
378 }
379
380 Ok(result)
381}
382
383pub(crate) fn utf16be_to_utf8(data: &[u8]) -> Result<Vec<u8>, ()> {
387 if data.is_empty() {
388 return Ok(Vec::new());
389 }
390
391 let offset = if data.len() >= 2 && data[0..2] == UTF16BE_BOM {
393 2
394 } else {
395 0
396 };
397
398 let mut result = Vec::with_capacity(data.len() / 2 + data.len() / 4);
399 let mut i = offset;
400
401 while i < data.len() {
402 let unit = read_utf16be_unit(&data[i..]).ok_or(())?;
403 i += 2;
404
405 if unit >= 0xD800 && unit <= 0xDBFF {
406 let low = read_utf16be_unit(&data[i..]).ok_or(())?;
408 i += 2;
409
410 if !(0xDC00..=0xDFFF).contains(&low) {
411 return Err(());
412 }
413
414 let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
415 let mut buf = [0u8; 4];
416 let n = encode_codepoint_to_utf8(cp, &mut buf);
417 if n == 0 {
418 return Err(());
419 }
420 result.extend_from_slice(&buf[..n]);
421 } else if unit >= 0xDC00 && unit <= 0xDFFF {
422 return Err(());
424 } else {
425 let cp = unit as u32;
426 let mut buf = [0u8; 4];
427 let n = encode_codepoint_to_utf8(cp, &mut buf);
428 result.extend_from_slice(&buf[..n]);
429 }
430 }
431
432 Ok(result)
433}
434
435fn encode_codepoint_to_utf16le(cp: u32, out: &mut [u8]) -> usize {
439 if cp < 0x10000 {
440 if out.len() < 2 {
441 return 0;
442 }
443 let u = cp as u16;
444 out[..2].copy_from_slice(&u.to_le_bytes());
445 2
446 } else if cp < 0x110000 {
447 if out.len() < 4 {
448 return 0;
449 }
450 let cp = cp - 0x10000;
451 let high = 0xD800 | ((cp >> 10) as u16);
452 let low = 0xDC00 | (cp as u16 & 0x3FF);
453 out[..2].copy_from_slice(&high.to_le_bytes());
454 out[2..4].copy_from_slice(&low.to_le_bytes());
455 4
456 } else {
457 0
458 }
459}
460
461pub(crate) fn utf8_to_utf16le(data: &[u8]) -> Result<Vec<u8>, ()> {
465 let s = core::str::from_utf8(data).map_err(|_| ())?;
466 let mut result = Vec::with_capacity(data.len() * 2);
467
468 for ch in s.chars() {
469 let cp = ch as u32;
470 let mut buf = [0u8; 4];
471 let n = encode_codepoint_to_utf16le(cp, &mut buf);
472 if n == 0 {
473 return Err(());
474 }
475 result.extend_from_slice(&buf[..n]);
476 }
477
478 Ok(result)
479}
480
481pub(crate) fn latin1_to_utf8(data: &[u8]) -> Vec<u8> {
490 let mut result = Vec::with_capacity(data.len() * 2);
491
492 for &byte in data {
493 let cp = byte as u32;
494 let mut buf = [0u8; 2];
495 let n = encode_codepoint_to_utf8(cp, &mut buf);
496 result.extend_from_slice(&buf[..n]);
497 }
498
499 result
500}
501
502pub(crate) fn utf8_to_latin1(data: &[u8]) -> Result<Vec<u8>, ()> {
507 let s = core::str::from_utf8(data).map_err(|_| ())?;
508 let mut result = Vec::with_capacity(data.len());
509
510 for ch in s.chars() {
511 let cp = ch as u32;
512 if cp > 0xFF {
513 return Err(());
514 }
515 result.push(cp as u8);
516 }
517
518 Ok(result)
519}
520
521pub(crate) fn init_encodings() {
536 if ENCODING_INITIALIZED.swap(true, Ordering::SeqCst) {
537 return;
538 }
539
540 register_builtin_handlers();
541}
542
543fn register_builtin_handlers() {
545 register_handler(
547 b"UTF-8\0",
548 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
549 xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
550 Some(utf8_input_func as xmlCharEncodingInputFunc),
551 Some(utf8_output_func as xmlCharEncodingOutputFunc),
552 );
553
554 register_handler(
556 b"UTF-16LE\0",
557 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
558 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
559 Some(utf16le_input_func as xmlCharEncodingInputFunc),
560 Some(utf16le_output_func as xmlCharEncodingOutputFunc),
561 );
562
563 register_handler(
565 b"UTF-16BE\0",
566 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
567 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
568 Some(utf16be_input_func as xmlCharEncodingInputFunc),
569 Some(utf16be_output_func as xmlCharEncodingOutputFunc),
570 );
571
572 register_handler(
574 b"ISO-8859-1\0",
575 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
576 xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
577 Some(latin1_input_func as xmlCharEncodingInputFunc),
578 Some(latin1_output_func as xmlCharEncodingOutputFunc),
579 );
580
581 register_handler(
583 b"ASCII\0",
584 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
585 xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
586 Some(ascii_input_func as xmlCharEncodingInputFunc),
587 Some(ascii_output_func as xmlCharEncodingOutputFunc),
588 );
589}
590
591fn register_handler(
593 name_bytes: &[u8],
594 input_enc: xmlCharEncoding,
595 output_enc: xmlCharEncoding,
596 input_func: Option<xmlCharEncodingInputFunc>,
597 output_func: Option<xmlCharEncodingOutputFunc>,
598) {
599 let name_raw =
600 unsafe { crate::abi::allocator::xmlMemStrdup(name_bytes.as_ptr() as *const c_char) };
601 if name_raw.is_null() {
602 return;
603 }
604
605 let handler =
606 unsafe { xmlMalloc(size_of::<_xmlCharEncodingHandler>()) } as *mut _xmlCharEncodingHandler;
607
608 if handler.is_null() {
609 unsafe { xmlFree(name_raw) };
610 return;
611 }
612
613 unsafe {
614 ptr::write(
615 handler,
616 _xmlCharEncodingHandler {
617 name: name_raw as *mut c_char,
618 input: input_enc as c_int,
619 output: output_enc as c_int,
620 input_func,
621 output_func,
622 iconv_in: ptr::null_mut(),
623 iconv_out: ptr::null_mut(),
624 },
625 );
626 }
627
628 add_encoding_handler(handler);
629}
630
631pub(crate) fn cleanup_encodings() {
635 let mut handlers = ENCODING_HANDLERS.write();
636 for &handler in handlers.iter() {
637 let ptr = handler.0;
638 if !ptr.is_null() {
639 unsafe {
640 if !(*ptr).name.is_null() {
641 xmlFree((*ptr).name as *mut c_void);
642 }
643 xmlFree(ptr as *mut c_void);
644 }
645 }
646 }
647 handlers.clear();
648 ENCODING_INITIALIZED.store(false, Ordering::SeqCst);
649}
650
651pub(crate) fn find_encoding_handler(name: *const xmlChar) -> *mut _xmlCharEncodingHandler {
657 if name.is_null() {
658 return ptr::null_mut();
659 }
660
661 let name_str = unsafe {
662 match CStr::from_ptr(name as *const c_char).to_bytes() {
663 b"" => return ptr::null_mut(),
664 s => s,
665 }
666 };
667
668 let handlers = ENCODING_HANDLERS.read();
669 for &handler in handlers.iter() {
670 let ptr = handler.0;
671 if ptr.is_null() {
672 continue;
673 }
674 let h_name = unsafe {
675 if (*ptr).name.is_null() {
676 continue;
677 }
678 CStr::from_ptr((*ptr).name).to_bytes()
679 };
680
681 if name_str.eq_ignore_ascii_case(h_name) {
682 return ptr;
683 }
684 }
685
686 ptr::null_mut()
687}
688
689pub(crate) fn add_encoding_handler(handler: *mut _xmlCharEncodingHandler) -> c_int {
693 if handler.is_null() {
694 return -1;
695 }
696
697 let mut handlers = ENCODING_HANDLERS.write();
698 handlers.push(HandlerPtr(handler));
699 0
700}
701
702pub(crate) fn char_enc_in_func(
710 handler: *mut _xmlCharEncodingHandler,
711 out: &mut [u8],
712 in_data: &[u8],
713) -> c_int {
714 if handler.is_null() {
715 return -1;
716 }
717
718 let h = unsafe { &*handler };
719 let input_func = match h.input_func {
720 Some(f) => f,
721 None => return -1,
722 };
723
724 let mut outlen = out.len() as c_int;
725 let mut inlen = in_data.len() as c_int;
726
727 unsafe { input_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
728}
729
730pub(crate) fn char_enc_out_func(
734 handler: *mut _xmlCharEncodingHandler,
735 out: &mut [u8],
736 in_data: &[u8],
737) -> c_int {
738 if handler.is_null() {
739 return -1;
740 }
741
742 let h = unsafe { &*handler };
743 let output_func = match h.output_func {
744 Some(f) => f,
745 None => return -1,
746 };
747
748 let mut outlen = out.len() as c_int;
749 let mut inlen = in_data.len() as c_int;
750
751 unsafe { output_func(out.as_mut_ptr(), &mut outlen, in_data.as_ptr(), &mut inlen) }
752}
753
754pub(crate) fn char_enc_in(
761 handler: *mut _xmlCharEncodingHandler,
762 out: *mut _xmlBuffer,
763 in_: *mut _xmlBuffer,
764) -> c_int {
765 if handler.is_null() || out.is_null() || in_.is_null() {
766 return -1;
767 }
768
769 let h = unsafe { &*handler };
770 let input_func = match h.input_func {
771 Some(f) => f,
772 None => return -1,
773 };
774
775 let in_buf = unsafe { &*in_ };
776 let out_buf = unsafe { &mut *out };
777
778 if in_buf.content.is_null() || in_buf.use_ == 0 {
779 return 0;
780 }
781
782 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
783
784 let out_capacity = (in_buf.use_ as usize).saturating_mul(3).max(256);
786 let mut out_vec = vec![0u8; out_capacity];
787 let mut out_len = out_capacity as c_int;
788 let mut in_len = in_buf.use_ as c_int;
789
790 let ret = unsafe {
791 input_func(
792 out_vec.as_mut_ptr(),
793 &mut out_len,
794 in_data.as_ptr(),
795 &mut in_len,
796 )
797 };
798
799 if ret < 0 {
800 return -1;
801 }
802
803 let written = ret as usize;
804
805 append_to_xml_buffer(out_buf, &out_vec[..written]);
807
808 written as c_int
809}
810
811pub(crate) fn char_enc_out(
818 handler: *mut _xmlCharEncodingHandler,
819 out: *mut _xmlBuffer,
820 in_: *mut _xmlBuffer,
821) -> c_int {
822 if handler.is_null() || out.is_null() || in_.is_null() {
823 return -1;
824 }
825
826 let h = unsafe { &*handler };
827 let output_func = match h.output_func {
828 Some(f) => f,
829 None => return -1,
830 };
831
832 let in_buf = unsafe { &*in_ };
833 let out_buf = unsafe { &mut *out };
834
835 if in_buf.content.is_null() || in_buf.use_ == 0 {
836 return 0;
837 }
838
839 let in_data = unsafe { core::slice::from_raw_parts(in_buf.content, in_buf.use_ as usize) };
840
841 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 output_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
867fn append_to_xml_buffer(buf: &mut _xmlBuffer, data: &[u8]) {
869 if data.is_empty() {
870 return;
871 }
872
873 let new_use = (buf.use_ as usize).saturating_add(data.len());
874 if new_use > buf.size as usize {
875 let new_size = (buf.size as usize).saturating_mul(2).max(new_use).max(256);
877 let new_content =
878 unsafe { xmlRealloc(buf.content as *mut c_void, new_size) as *mut xmlChar };
879 if new_content.is_null() {
880 return; }
882 buf.content = new_content;
883 buf.size = new_size as c_uint;
884 }
885
886 unsafe {
887 ptr::copy_nonoverlapping(
888 data.as_ptr(),
889 buf.content.add(buf.use_ as usize),
890 data.len(),
891 );
892 }
893 buf.use_ = new_use as c_uint;
894}
895
896unsafe extern "C" fn utf8_input_func(
906 out: *mut c_uchar,
907 outlen: *mut c_int,
908 in_: *const c_uchar,
909 inlen: *mut c_int,
910) -> c_int {
911 let avail_out = *outlen as usize;
912 let avail_in = *inlen as usize;
913 let to_copy = avail_out.min(avail_in);
914
915 if to_copy > 0 {
916 ptr::copy_nonoverlapping(in_, out, to_copy);
917 }
918
919 *outlen = to_copy as c_int;
920 *inlen = to_copy as c_int;
921 to_copy as c_int
922}
923
924unsafe extern "C" fn utf8_output_func(
926 out: *mut c_uchar,
927 outlen: *mut c_int,
928 in_: *const c_uchar,
929 inlen: *mut c_int,
930) -> c_int {
931 utf8_input_func(out, outlen, in_, inlen)
932}
933
934unsafe extern "C" fn utf16le_input_func(
938 out: *mut c_uchar,
939 outlen: *mut c_int,
940 in_: *const c_uchar,
941 inlen: *mut c_int,
942) -> c_int {
943 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
944 return -1;
945 }
946
947 let avail_in = *inlen as usize;
948 let avail_out = *outlen as usize;
949
950 if avail_in == 0 || avail_out == 0 {
951 *outlen = 0;
952 *inlen = 0;
953 return 0;
954 }
955
956 let in_data = core::slice::from_raw_parts(in_, avail_in);
957 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
958
959 let result = match utf16le_to_utf8(in_data) {
961 Ok(v) => v,
962 Err(()) => return -1,
963 };
964
965 let written = result.len().min(avail_out);
966 if written > 0 {
967 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
968 }
969
970 *outlen = written as c_int;
971 *inlen = avail_in as c_int; written as c_int
973}
974
975unsafe extern "C" fn utf16le_output_func(
977 out: *mut c_uchar,
978 outlen: *mut c_int,
979 in_: *const c_uchar,
980 inlen: *mut c_int,
981) -> c_int {
982 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
983 return -1;
984 }
985
986 let avail_in = *inlen as usize;
987 let avail_out = *outlen as usize;
988
989 if avail_in == 0 || avail_out == 0 {
990 *outlen = 0;
991 *inlen = 0;
992 return 0;
993 }
994
995 let in_data = core::slice::from_raw_parts(in_, avail_in);
996 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
997
998 let result = match utf8_to_utf16le(in_data) {
999 Ok(v) => v,
1000 Err(()) => return -1,
1001 };
1002
1003 let written = result.len().min(avail_out);
1004 if written > 0 {
1005 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1006 }
1007
1008 *outlen = written as c_int;
1009 *inlen = avail_in as c_int;
1010 written as c_int
1011}
1012
1013unsafe extern "C" fn utf16be_input_func(
1017 out: *mut c_uchar,
1018 outlen: *mut c_int,
1019 in_: *const c_uchar,
1020 inlen: *mut c_int,
1021) -> c_int {
1022 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1023 return -1;
1024 }
1025
1026 let avail_in = *inlen as usize;
1027 let avail_out = *outlen as usize;
1028
1029 if avail_in == 0 || avail_out == 0 {
1030 *outlen = 0;
1031 *inlen = 0;
1032 return 0;
1033 }
1034
1035 let in_data = core::slice::from_raw_parts(in_, avail_in);
1036 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1037
1038 let result = match utf16be_to_utf8(in_data) {
1039 Ok(v) => v,
1040 Err(()) => return -1,
1041 };
1042
1043 let written = result.len().min(avail_out);
1044 if written > 0 {
1045 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1046 }
1047
1048 *outlen = written as c_int;
1049 *inlen = avail_in as c_int;
1050 written as c_int
1051}
1052
1053unsafe extern "C" fn utf16be_output_func(
1055 out: *mut c_uchar,
1056 outlen: *mut c_int,
1057 in_: *const c_uchar,
1058 inlen: *mut c_int,
1059) -> c_int {
1060 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1061 return -1;
1062 }
1063
1064 let avail_in = *inlen as usize;
1065 let avail_out = *outlen as usize;
1066
1067 if avail_in == 0 || avail_out == 0 {
1068 *outlen = 0;
1069 *inlen = 0;
1070 return 0;
1071 }
1072
1073 let in_data = core::slice::from_raw_parts(in_, avail_in);
1074
1075 let le_result = match utf8_to_utf16le(in_data) {
1077 Ok(v) => v,
1078 Err(()) => return -1,
1079 };
1080
1081 let mut result = le_result;
1083 for chunk in result.chunks_exact_mut(2) {
1084 chunk.swap(0, 1);
1085 }
1086
1087 let written = result.len().min(avail_out);
1088 if written > 0 {
1089 ptr::copy_nonoverlapping(result.as_ptr(), out, written);
1090 }
1091
1092 *outlen = written as c_int;
1093 *inlen = avail_in as c_int;
1094 written as c_int
1095}
1096
1097unsafe extern "C" fn latin1_input_func(
1101 out: *mut c_uchar,
1102 outlen: *mut c_int,
1103 in_: *const c_uchar,
1104 inlen: *mut c_int,
1105) -> c_int {
1106 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1107 return -1;
1108 }
1109
1110 let avail_in = *inlen as usize;
1111 let avail_out = *outlen as usize;
1112
1113 if avail_in == 0 || avail_out == 0 {
1114 *outlen = 0;
1115 *inlen = 0;
1116 return 0;
1117 }
1118
1119 let in_data = core::slice::from_raw_parts(in_, avail_in);
1120 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1121
1122 let mut in_pos = 0;
1123 let mut out_pos = 0;
1124
1125 while in_pos < avail_in && out_pos < avail_out {
1126 let byte = in_data[in_pos];
1127 in_pos += 1;
1128
1129 if byte < 0x80 {
1130 if out_pos < avail_out {
1132 out_slice[out_pos] = byte;
1133 out_pos += 1;
1134 } else {
1135 break;
1136 }
1137 } else {
1138 if out_pos + 1 < avail_out {
1141 out_slice[out_pos] = 0xC2 | (byte >> 6);
1142 out_slice[out_pos + 1] = 0x80 | (byte & 0x3F);
1143 out_pos += 2;
1144 } else {
1145 break;
1146 }
1147 }
1148 }
1149
1150 *outlen = out_pos as c_int;
1151 *inlen = in_pos as c_int;
1152 out_pos as c_int
1153}
1154
1155unsafe extern "C" fn latin1_output_func(
1157 out: *mut c_uchar,
1158 outlen: *mut c_int,
1159 in_: *const c_uchar,
1160 inlen: *mut c_int,
1161) -> c_int {
1162 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1163 return -1;
1164 }
1165
1166 let avail_in = *inlen as usize;
1167 let avail_out = *outlen as usize;
1168
1169 if avail_in == 0 || avail_out == 0 {
1170 *outlen = 0;
1171 *inlen = 0;
1172 return 0;
1173 }
1174
1175 let in_data = core::slice::from_raw_parts(in_, avail_in);
1176 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1177
1178 let mut in_pos = 0;
1179 let mut out_pos = 0;
1180
1181 while in_pos < avail_in && out_pos < avail_out {
1182 let byte = in_data[in_pos];
1183 in_pos += 1;
1184
1185 if byte < 0x80 {
1186 out_slice[out_pos] = byte;
1188 out_pos += 1;
1189 } else if byte >= 0xC2 && byte <= 0xC3 {
1190 if in_pos < avail_in {
1192 let second = in_data[in_pos];
1193 in_pos += 1;
1194 if second & 0xC0 != 0x80 {
1195 return -1; }
1197 let cp = ((byte as u32 & 0x1F) << 6) | (second as u32 & 0x3F);
1198 if cp > 0xFF {
1199 return -1; }
1201 out_slice[out_pos] = cp as u8;
1202 out_pos += 1;
1203 } else {
1204 return -1; }
1206 } else if byte >= 0x80 && byte <= 0xBF {
1207 return -1;
1209 } else {
1210 return -1;
1213 }
1214 }
1215
1216 *outlen = out_pos as c_int;
1217 *inlen = in_pos as c_int;
1218 out_pos as c_int
1219}
1220
1221unsafe extern "C" fn ascii_input_func(
1225 out: *mut c_uchar,
1226 outlen: *mut c_int,
1227 in_: *const c_uchar,
1228 inlen: *mut c_int,
1229) -> c_int {
1230 if out.is_null() || outlen.is_null() || in_.is_null() || inlen.is_null() {
1231 return -1;
1232 }
1233
1234 let avail_in = *inlen as usize;
1235 let avail_out = *outlen as usize;
1236
1237 if avail_in == 0 || avail_out == 0 {
1238 *outlen = 0;
1239 *inlen = 0;
1240 return 0;
1241 }
1242
1243 let in_data = core::slice::from_raw_parts(in_, avail_in);
1244 let out_slice = core::slice::from_raw_parts_mut(out, avail_out);
1245
1246 let mut pos = 0;
1247 while pos < avail_in && pos < avail_out {
1248 let byte = in_data[pos];
1249 if byte > 0x7F {
1250 return -1; }
1252 out_slice[pos] = byte;
1253 pos += 1;
1254 }
1255
1256 *outlen = pos as c_int;
1257 *inlen = pos as c_int;
1258 pos as c_int
1259}
1260
1261unsafe extern "C" fn ascii_output_func(
1263 out: *mut c_uchar,
1264 outlen: *mut c_int,
1265 in_: *const c_uchar,
1266 inlen: *mut c_int,
1267) -> c_int {
1268 ascii_input_func(out, outlen, in_, inlen)
1270}
1271
1272pub(crate) fn xmlFindCharEncodingHandler(name: *const c_char) -> *mut _xmlCharEncodingHandler {
1281 if name.is_null() {
1282 return ptr::null_mut();
1283 }
1284 find_encoding_handler(name as *const xmlChar)
1285}
1286
1287pub(crate) fn xmlGetCharEncodingName(enc: xmlCharEncoding) -> *const c_char {
1291 match enc {
1293 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 => c"UTF-8".as_ptr(),
1294 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE => c"UTF-16LE".as_ptr(),
1295 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE => c"UTF-16BE".as_ptr(),
1296 xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE => c"UCS-4LE".as_ptr(),
1297 xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE => c"UCS-4BE".as_ptr(),
1298 xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC => c"EBCDIC".as_ptr(),
1299 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143 => c"UCS-4-2143".as_ptr(),
1300 xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412 => c"UCS-4-3412".as_ptr(),
1301 xmlCharEncoding::XML_CHAR_ENCODING_UCS2 => c"UCS-2".as_ptr(),
1302 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 => c"ISO-8859-1".as_ptr(),
1303 xmlCharEncoding::XML_CHAR_ENCODING_8859_2 => c"ISO-8859-2".as_ptr(),
1304 xmlCharEncoding::XML_CHAR_ENCODING_8859_3 => c"ISO-8859-3".as_ptr(),
1305 xmlCharEncoding::XML_CHAR_ENCODING_8859_4 => c"ISO-8859-4".as_ptr(),
1306 xmlCharEncoding::XML_CHAR_ENCODING_8859_5 => c"ISO-8859-5".as_ptr(),
1307 xmlCharEncoding::XML_CHAR_ENCODING_8859_6 => c"ISO-8859-6".as_ptr(),
1308 xmlCharEncoding::XML_CHAR_ENCODING_8859_7 => c"ISO-8859-7".as_ptr(),
1309 xmlCharEncoding::XML_CHAR_ENCODING_8859_8 => c"ISO-8859-8".as_ptr(),
1310 xmlCharEncoding::XML_CHAR_ENCODING_8859_9 => c"ISO-8859-9".as_ptr(),
1311 xmlCharEncoding::XML_CHAR_ENCODING_2022_JP => c"ISO-2022-JP".as_ptr(),
1312 xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS => c"SHIFT_JIS".as_ptr(),
1313 xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP => c"EUC-JP".as_ptr(),
1314 xmlCharEncoding::XML_CHAR_ENCODING_ASCII => c"ASCII".as_ptr(),
1315 _ => ptr::null(),
1316 }
1317}
1318
1319pub(crate) fn xmlParseCharEncoding(name: *const c_char) -> c_int {
1324 if name.is_null() {
1325 return xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int;
1326 }
1327 let bytes = unsafe { CStr::from_ptr(name).to_bytes() };
1328 encoding_from_name(bytes) as c_int
1329}
1330
1331pub(crate) fn xmlCharEncInFunc(
1335 handler: *mut _xmlCharEncodingHandler,
1336 out: *mut _xmlBuffer,
1337 in_: *mut _xmlBuffer,
1338) -> c_int {
1339 char_enc_in(handler, out, in_)
1340}
1341
1342pub(crate) fn xmlCharEncOutFunc(
1346 handler: *mut _xmlCharEncodingHandler,
1347 out: *mut _xmlBuffer,
1348 in_: *mut _xmlBuffer,
1349) -> c_int {
1350 char_enc_out(handler, out, in_)
1351}
1352
1353pub(crate) fn xmlNewCharEncodingHandler(
1359 name: *const c_char,
1360 input: xmlCharEncodingInputFunc,
1361 output: xmlCharEncodingOutputFunc,
1362) -> *mut _xmlCharEncodingHandler {
1363 if name.is_null() {
1364 return ptr::null_mut();
1365 }
1366
1367 let name_raw = unsafe { crate::abi::allocator::xmlMemStrdup(name) };
1368 if name_raw.is_null() {
1369 return ptr::null_mut();
1370 }
1371
1372 let handler =
1373 unsafe { xmlMalloc(size_of::<_xmlCharEncodingHandler>()) } as *mut _xmlCharEncodingHandler;
1374
1375 if handler.is_null() {
1376 unsafe { xmlFree(name_raw) };
1377 return ptr::null_mut();
1378 }
1379
1380 unsafe {
1381 ptr::write(
1382 handler,
1383 _xmlCharEncodingHandler {
1384 name: name_raw as *mut c_char,
1385 input: xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int,
1386 output: xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int,
1387 input_func: Some(input),
1388 output_func: Some(output),
1389 iconv_in: ptr::null_mut(),
1390 iconv_out: ptr::null_mut(),
1391 },
1392 );
1393 }
1394
1395 handler
1396}
1397
1398pub(crate) fn xmlDelEncodingHandler(handler: *mut _xmlCharEncodingHandler) {
1402 if handler.is_null() {
1403 return;
1404 }
1405
1406 {
1408 let mut handlers = ENCODING_HANDLERS.write();
1409 handlers.retain(|&h| h.0 != handler);
1410 }
1411
1412 unsafe {
1413 if !(*handler).name.is_null() {
1414 xmlFree((*handler).name as *mut c_void);
1415 }
1416 xmlFree(handler as *mut c_void);
1417 }
1418}
1419
1420pub(crate) fn xmlInitCharEncodingHandlers() {
1422 init_encodings();
1423}
1424
1425pub(crate) fn xmlCleanupCharEncodingHandlers() {
1427 cleanup_encodings();
1428}
1429
1430#[cfg(test)]
1435mod tests {
1436 use super::*;
1437
1438 #[test]
1441 fn test_detect_bom_utf8() {
1442 let data = [0xEF, 0xBB, 0xBF, b'<', b'?', b'x', b'm', b'l'];
1443 assert_eq!(
1444 detect_encoding_from_bom(&data),
1445 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1446 );
1447 }
1448
1449 #[test]
1450 fn test_detect_bom_utf16le() {
1451 let data = [0xFF, 0xFE, 0x00, 0x01];
1452 assert_eq!(
1453 detect_encoding_from_bom(&data),
1454 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1455 );
1456 }
1457
1458 #[test]
1459 fn test_detect_bom_utf16be() {
1460 let data = [0xFE, 0xFF, 0x00, 0x01];
1461 assert_eq!(
1462 detect_encoding_from_bom(&data),
1463 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1464 );
1465 }
1466
1467 #[test]
1468 fn test_detect_bom_none() {
1469 let data = b"<xml>";
1470 assert_eq!(
1471 detect_encoding_from_bom(data),
1472 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1473 );
1474 }
1475
1476 #[test]
1477 fn test_detect_bom_empty() {
1478 assert_eq!(
1479 detect_encoding_from_bom(b""),
1480 xmlCharEncoding::XML_CHAR_ENCODING_NONE
1481 );
1482 }
1483
1484 #[test]
1487 fn test_detect_encoding_declaration_utf8() {
1488 let data = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
1489 let result = detect_encoding_from_declaration(data);
1490 assert_eq!(result, Some(b"utf-8".to_vec()));
1491 }
1492
1493 #[test]
1494 fn test_detect_encoding_declaration_iso() {
1495 let data = b"<?xml version='1.0' encoding='ISO-8859-1'?>";
1496 let result = detect_encoding_from_declaration(data);
1497 assert_eq!(result, Some(b"iso-8859-1".to_vec()));
1498 }
1499
1500 #[test]
1501 fn test_detect_encoding_declaration_none() {
1502 let data = b"<?xml version=\"1.0\"?>";
1503 let result = detect_encoding_from_declaration(data);
1504 assert!(result.is_none());
1505 }
1506
1507 #[test]
1508 fn test_detect_encoding_declaration_no_xml() {
1509 let data = b"<root>";
1510 let result = detect_encoding_from_declaration(data);
1511 assert!(result.is_none());
1512 }
1513
1514 #[test]
1515 fn test_detect_encoding_declaration_with_bom() {
1516 let mut data = vec![0xEF, 0xBB, 0xBF];
1517 data.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1518 let result = detect_encoding_from_declaration(&data);
1519 assert_eq!(result, Some(b"utf-8".to_vec()));
1520 }
1521
1522 #[test]
1525 fn test_encoding_from_name_utf8() {
1526 assert_eq!(
1527 encoding_from_name(b"UTF-8"),
1528 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1529 );
1530 assert_eq!(
1531 encoding_from_name(b"utf8"),
1532 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
1533 );
1534 }
1535
1536 #[test]
1537 fn test_encoding_from_name_utf16() {
1538 assert_eq!(
1539 encoding_from_name(b"UTF-16LE"),
1540 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1541 );
1542 assert_eq!(
1543 encoding_from_name(b"UTF-16BE"),
1544 xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE
1545 );
1546 assert_eq!(
1547 encoding_from_name(b"utf-16"),
1548 xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE
1549 );
1550 }
1551
1552 #[test]
1553 fn test_encoding_from_name_latin1() {
1554 assert_eq!(
1555 encoding_from_name(b"ISO-8859-1"),
1556 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
1557 );
1558 assert_eq!(
1559 encoding_from_name(b"Latin1"),
1560 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
1561 );
1562 }
1563
1564 #[test]
1565 fn test_encoding_from_name_ascii() {
1566 assert_eq!(
1567 encoding_from_name(b"ASCII"),
1568 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
1569 );
1570 assert_eq!(
1571 encoding_from_name(b"US-ASCII"),
1572 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
1573 );
1574 }
1575
1576 #[test]
1577 fn test_encoding_from_name_error() {
1578 assert_eq!(
1579 encoding_from_name(b"invalid-encoding"),
1580 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
1581 );
1582 }
1583
1584 #[test]
1585 fn test_encoding_from_name_empty() {
1586 assert_eq!(
1587 encoding_from_name(b""),
1588 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
1589 );
1590 }
1591
1592 #[test]
1595 fn test_encoding_name_utf8() {
1596 assert_eq!(
1597 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF8),
1598 Some(b"UTF-8" as &[u8])
1599 );
1600 }
1601
1602 #[test]
1603 fn test_encoding_name_utf16le() {
1604 assert_eq!(
1605 encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE),
1606 Some(b"UTF-16LE" as &[u8])
1607 );
1608 }
1609
1610 #[test]
1611 fn test_encoding_name_none() {
1612 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_NONE).is_none());
1613 }
1614
1615 #[test]
1616 fn test_encoding_name_error() {
1617 assert!(encoding_name(xmlCharEncoding::XML_CHAR_ENCODING_ERROR).is_none());
1618 }
1619
1620 #[test]
1623 fn test_utf8_valid_ascii() {
1624 assert!(utf8_valid(b"hello world"));
1625 }
1626
1627 #[test]
1628 fn test_utf8_valid_multi_byte() {
1629 assert!(utf8_valid("héllo wörld 🌍".as_bytes()));
1630 }
1631
1632 #[test]
1633 fn test_utf8_valid_empty() {
1634 assert!(utf8_valid(b""));
1635 }
1636
1637 #[test]
1638 fn test_utf8_invalid() {
1639 assert!(!utf8_valid(&[0xFF, 0xFE, 0x00]));
1640 }
1641
1642 #[test]
1645 fn test_valid_xml_chars() {
1646 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));
1652 assert!(is_valid_xml_char(0xE000));
1653 assert!(is_valid_xml_char(0xFFFD));
1654 assert!(is_valid_xml_char(0x10000));
1655 assert!(is_valid_xml_char(0x10FFFF));
1656 }
1657
1658 #[test]
1659 fn test_invalid_xml_chars() {
1660 assert!(!is_valid_xml_char(0x00));
1661 assert!(!is_valid_xml_char(0x08));
1662 assert!(!is_valid_xml_char(0x0B));
1663 assert!(!is_valid_xml_char(0x0C));
1664 assert!(!is_valid_xml_char(0x0E));
1665 assert!(!is_valid_xml_char(0x1F));
1666 assert!(!is_valid_xml_char(0xD800)); assert!(!is_valid_xml_char(0xDFFF)); assert!(!is_valid_xml_char(0xFFFE));
1669 assert!(!is_valid_xml_char(0xFFFF));
1670 assert!(!is_valid_xml_char(0x110000));
1671 }
1672
1673 #[test]
1676 fn test_utf16le_to_utf8_ascii() {
1677 let data = [b'A', 0x00, b'B', 0x00];
1679 let result = utf16le_to_utf8(&data).unwrap();
1680 assert_eq!(result, b"AB");
1681 }
1682
1683 #[test]
1684 fn test_utf16le_to_utf8_bom() {
1685 let mut data = vec![0xFF, 0xFE]; data.extend_from_slice(&[b'A', 0x00, b'B', 0x00]);
1687 let result = utf16le_to_utf8(&data).unwrap();
1688 assert_eq!(result, b"AB");
1689 }
1690
1691 #[test]
1692 fn test_utf16le_to_utf8_bmp() {
1693 let data = [0xE9, 0x00];
1695 let result = utf16le_to_utf8(&data).unwrap();
1696 assert_eq!(result, "é".as_bytes());
1697 }
1698
1699 #[test]
1700 fn test_utf16le_to_utf8_supplementary() {
1701 let data = [0x3D, 0xD8, 0x00, 0xDE];
1703 let result = utf16le_to_utf8(&data).unwrap();
1704 assert_eq!(result, "😀".as_bytes());
1705 }
1706
1707 #[test]
1708 fn test_utf16le_to_utf8_unpaired_surrogate() {
1709 let data = [0x00, 0xD8]; assert!(utf16le_to_utf8(&data).is_err());
1711 }
1712
1713 #[test]
1714 fn test_utf16le_to_utf8_truncated() {
1715 let data = [0x00]; assert!(utf16le_to_utf8(&data).is_err());
1717 }
1718
1719 #[test]
1720 fn test_utf16le_to_utf8_empty() {
1721 let result = utf16le_to_utf8(b"").unwrap();
1722 assert!(result.is_empty());
1723 }
1724
1725 #[test]
1728 fn test_utf16be_to_utf8_ascii() {
1729 let data = [0x00, b'A', 0x00, b'B'];
1730 let result = utf16be_to_utf8(&data).unwrap();
1731 assert_eq!(result, b"AB");
1732 }
1733
1734 #[test]
1735 fn test_utf16be_to_utf8_bom() {
1736 let mut data = vec![0xFE, 0xFF]; data.extend_from_slice(&[0x00, b'A', 0x00, b'B']);
1738 let result = utf16be_to_utf8(&data).unwrap();
1739 assert_eq!(result, b"AB");
1740 }
1741
1742 #[test]
1743 fn test_utf16be_to_utf8_supplementary() {
1744 let data = [0xD8, 0x3D, 0xDE, 0x00];
1746 let result = utf16be_to_utf8(&data).unwrap();
1747 assert_eq!(result, "😀".as_bytes());
1748 }
1749
1750 #[test]
1751 fn test_utf16be_to_utf8_empty() {
1752 let result = utf16be_to_utf8(b"").unwrap();
1753 assert!(result.is_empty());
1754 }
1755
1756 #[test]
1759 fn test_utf8_to_utf16le_ascii() {
1760 let result = utf8_to_utf16le(b"AB").unwrap();
1761 assert_eq!(result, [b'A', 0x00, b'B', 0x00]);
1762 }
1763
1764 #[test]
1765 fn test_utf8_to_utf16le_bmp() {
1766 let result = utf8_to_utf16le("é".as_bytes()).unwrap();
1767 assert_eq!(result, [0xE9, 0x00]);
1768 }
1769
1770 #[test]
1771 fn test_utf8_to_utf16le_supplementary() {
1772 let result = utf8_to_utf16le("😀".as_bytes()).unwrap();
1773 assert_eq!(result, [0x3D, 0xD8, 0x00, 0xDE]);
1774 }
1775
1776 #[test]
1777 fn test_utf8_to_utf16le_invalid_utf8() {
1778 assert!(utf8_to_utf16le(&[0xFF]).is_err());
1779 }
1780
1781 #[test]
1782 fn test_utf8_to_utf16le_empty() {
1783 let result = utf8_to_utf16le(b"").unwrap();
1784 assert!(result.is_empty());
1785 }
1786
1787 #[test]
1790 fn test_latin1_to_utf8_ascii() {
1791 let result = latin1_to_utf8(b"ABC");
1792 assert_eq!(result, b"ABC");
1793 }
1794
1795 #[test]
1796 fn test_latin1_to_utf8_accented() {
1797 let result = latin1_to_utf8(&[0xE9]);
1799 assert_eq!(result, "é".as_bytes());
1800 }
1801
1802 #[test]
1803 fn test_latin1_to_utf8_all_255() {
1804 let result = latin1_to_utf8(&[0xFF]);
1805 assert_eq!(result, [0xC3, 0xBF]);
1807 }
1808
1809 #[test]
1810 fn test_latin1_to_utf8_empty() {
1811 let result = latin1_to_utf8(b"");
1812 assert!(result.is_empty());
1813 }
1814
1815 #[test]
1816 fn test_latin1_to_utf8_mixed() {
1817 let result = latin1_to_utf8(b"caf\xE9");
1818 assert_eq!(result, "café".as_bytes());
1819 }
1820
1821 #[test]
1824 fn test_utf8_to_latin1_ascii() {
1825 let result = utf8_to_latin1(b"ABC").unwrap();
1826 assert_eq!(result, b"ABC");
1827 }
1828
1829 #[test]
1830 fn test_utf8_to_latin1_accented() {
1831 let result = utf8_to_latin1("é".as_bytes()).unwrap();
1832 assert_eq!(result, [0xE9]);
1833 }
1834
1835 #[test]
1836 fn test_utf8_to_latin1_out_of_range() {
1837 assert!(utf8_to_latin1("€".as_bytes()).is_err()); }
1839
1840 #[test]
1841 fn test_utf8_to_latin1_invalid_utf8() {
1842 assert!(utf8_to_latin1(&[0xFF]).is_err());
1843 }
1844
1845 #[test]
1846 fn test_utf8_to_latin1_empty() {
1847 let result = utf8_to_latin1(b"").unwrap();
1848 assert!(result.is_empty());
1849 }
1850
1851 #[test]
1854 fn test_init_and_find_encodings() {
1855 init_encodings();
1856
1857 let utf8_name: *const xmlChar = b"UTF-8\0".as_ptr();
1858 assert!(!find_encoding_handler(utf8_name).is_null());
1859
1860 let utf16le_name: *const xmlChar = b"UTF-16LE\0".as_ptr();
1861 assert!(!find_encoding_handler(utf16le_name).is_null());
1862
1863 let utf16be_name: *const xmlChar = b"UTF-16BE\0".as_ptr();
1864 assert!(!find_encoding_handler(utf16be_name).is_null());
1865
1866 let latin1_name: *const xmlChar = b"ISO-8859-1\0".as_ptr();
1867 assert!(!find_encoding_handler(latin1_name).is_null());
1868
1869 let ascii_name: *const xmlChar = b"ASCII\0".as_ptr();
1870 assert!(!find_encoding_handler(ascii_name).is_null());
1871
1872 let lower_name: *const xmlChar = b"utf-8\0".as_ptr();
1874 assert!(!find_encoding_handler(lower_name).is_null());
1875 }
1876
1877 #[test]
1878 fn test_find_encoding_handler_not_found() {
1879 let name: *const xmlChar = b"NONEXISTENT\0".as_ptr();
1880 assert!(find_encoding_handler(name).is_null());
1881 }
1882
1883 #[test]
1884 fn test_find_encoding_handler_null() {
1885 assert!(find_encoding_handler(ptr::null()).is_null());
1886 }
1887
1888 #[test]
1889 fn test_add_encoding_handler() {
1890 let handler = unsafe {
1891 xmlMalloc(size_of::<_xmlCharEncodingHandler>()) as *mut _xmlCharEncodingHandler
1892 };
1893 assert!(!handler.is_null());
1894
1895 let name =
1896 unsafe { crate::abi::allocator::xmlMemStrdup(b"TEST-ENC\0".as_ptr() as *const c_char) };
1897 unsafe {
1898 ptr::write(
1899 handler,
1900 _xmlCharEncodingHandler {
1901 name: name as *mut c_char,
1902 input: xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int,
1903 output: xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int,
1904 input_func: None,
1905 output_func: None,
1906 iconv_in: ptr::null_mut(),
1907 iconv_out: ptr::null_mut(),
1908 },
1909 );
1910 }
1911
1912 assert_eq!(add_encoding_handler(handler), 0);
1913
1914 let found = find_encoding_handler(b"TEST-ENC\0".as_ptr() as *const xmlChar);
1915 assert_eq!(found, handler);
1916
1917 {
1919 let mut handlers = ENCODING_HANDLERS.write();
1920 handlers.retain(|&h| h.0 != handler);
1921 }
1922
1923 unsafe {
1924 xmlFree(name as *mut c_void);
1925 xmlFree(handler as *mut c_void);
1926 }
1927 }
1928
1929 #[test]
1932 fn test_utf16le_roundtrip() {
1933 let original = b"Hello, World! UTF-16LE test: \xC3\xA9\xF0\x9F\x98\x80";
1934 let utf16 = utf8_to_utf16le(original).unwrap();
1935 let back = utf16le_to_utf8(&utf16).unwrap();
1936 assert_eq!(original.to_vec(), back);
1937 }
1938
1939 #[test]
1940 fn test_utf16be_roundtrip() {
1941 let original = b"Hello, World! UTF-16BE test: \xC3\xA9\xF0\x9F\x98\x80";
1942 let utf16le = utf8_to_utf16le(original).unwrap();
1943 let mut utf16be = utf16le.clone();
1945 for chunk in utf16be.chunks_exact_mut(2) {
1946 chunk.swap(0, 1);
1947 }
1948 let back = utf16be_to_utf8(&utf16be).unwrap();
1949 assert_eq!(original.to_vec(), back);
1950 }
1951
1952 #[test]
1953 fn test_latin1_roundtrip() {
1954 let original: Vec<u8> = (0x00..=0xFF).collect();
1955 let utf8 = latin1_to_utf8(&original);
1956 let back = utf8_to_latin1(&utf8).unwrap();
1957 assert_eq!(original, back);
1958 }
1959
1960 #[test]
1963 fn test_utf8_handler_identity() {
1964 let input = b"Hello, UTF-8!";
1965 let mut output = [0u8; 64];
1966 let mut outlen = output.len() as c_int;
1967 let mut inlen = input.len() as c_int;
1968
1969 let ret = unsafe {
1970 utf8_input_func(output.as_mut_ptr(), &mut outlen, input.as_ptr(), &mut inlen)
1971 };
1972
1973 assert_eq!(ret, input.len() as c_int);
1974 assert_eq!(&output[..ret as usize], input);
1975 assert_eq!(inlen, input.len() as c_int);
1976 }
1977
1978 #[test]
1979 fn test_utf16le_handler_roundtrip() {
1980 init_encodings();
1981
1982 let original = b"Hello UTF-16LE!";
1983 let mut utf16_buf = [0u8; 128];
1984 let mut outlen = utf16_buf.len() as c_int;
1985 let mut inlen = original.len() as c_int;
1986
1987 let written = unsafe {
1988 utf16le_output_func(
1989 utf16_buf.as_mut_ptr(),
1990 &mut outlen,
1991 original.as_ptr(),
1992 &mut inlen,
1993 )
1994 };
1995 assert!(written > 0);
1996
1997 let mut decoded = [0u8; 128];
1999 let mut outlen2 = decoded.len() as c_int;
2000 let mut inlen2 = written;
2001
2002 let written2 = unsafe {
2003 utf16le_input_func(
2004 decoded.as_mut_ptr(),
2005 &mut outlen2,
2006 utf16_buf.as_ptr(),
2007 &mut inlen2,
2008 )
2009 };
2010 assert_eq!(written2 as usize, original.len());
2011 assert_eq!(&decoded[..written2 as usize], original);
2012 }
2013
2014 #[test]
2017 fn test_append_to_xml_buffer() {
2018 unsafe {
2019 let content = xmlMalloc(64) as *mut xmlChar;
2020 assert!(!content.is_null());
2021
2022 let mut buf = _xmlBuffer {
2023 content,
2024 use_: 0,
2025 size: 64,
2026 alloc: 0,
2027 contentIO: ptr::null_mut(),
2028 };
2029
2030 append_to_xml_buffer(&mut buf, b"Hello");
2031 assert_eq!(buf.use_, 5);
2032 let slice = core::slice::from_raw_parts(buf.content, 5);
2033 assert_eq!(slice, b"Hello");
2034
2035 append_to_xml_buffer(&mut buf, b" World");
2036 assert_eq!(buf.use_, 11);
2037 let slice = core::slice::from_raw_parts(buf.content, 11);
2038 assert_eq!(slice, b"Hello World");
2039
2040 xmlFree(buf.content as *mut c_void);
2041 }
2042 }
2043
2044 #[test]
2047 fn test_xml_parse_char_encoding() {
2048 let name = b"UTF-8\0".as_ptr() as *const c_char;
2049 assert_eq!(
2050 xmlParseCharEncoding(name),
2051 xmlCharEncoding::XML_CHAR_ENCODING_UTF8 as c_int
2052 );
2053
2054 let name = b"ISO-8859-1\0".as_ptr() as *const c_char;
2055 assert_eq!(
2056 xmlParseCharEncoding(name),
2057 xmlCharEncoding::XML_CHAR_ENCODING_8859_1 as c_int
2058 );
2059
2060 assert_eq!(
2061 xmlParseCharEncoding(ptr::null()),
2062 xmlCharEncoding::XML_CHAR_ENCODING_NONE as c_int
2063 );
2064 }
2065
2066 #[test]
2067 fn test_xml_new_and_del_encoding_handler() {
2068 let name = b"TestEnc\0".as_ptr() as *const c_char;
2069 let handler = xmlNewCharEncodingHandler(
2070 name,
2071 utf8_input_func as xmlCharEncodingInputFunc,
2072 utf8_output_func as xmlCharEncodingOutputFunc,
2073 );
2074 assert!(!handler.is_null());
2075
2076 unsafe {
2077 assert!(!(*handler).name.is_null());
2078 let cstr = CStr::from_ptr((*handler).name);
2079 assert_eq!(cstr.to_bytes(), b"TestEnc");
2080 }
2081
2082 xmlDelEncodingHandler(handler);
2083 }
2084
2085 #[test]
2086 fn test_xml_init_and_cleanup() {
2087 xmlInitCharEncodingHandlers();
2088
2089 let name: *const xmlChar = b"UTF-8\0".as_ptr();
2090 assert!(!find_encoding_handler(name).is_null());
2091
2092 xmlCleanupCharEncodingHandlers();
2093 }
2095}