1# crate instead of \
9`encoding_rs`."
10)]
11#![cfg_attr(
12 not(feature = "encoding"),
13 doc = "is backed by the Pure-Rust `oxixml-encoding` crate instead of `encoding_rs`."
14)]
15
16use std::borrow::Cow;
17use std::str::Utf8Error;
18
19#[cfg(feature = "encoding")]
20use oxixml_encoding::Encoding;
21
22pub(crate) const UTF8_BOM: &[u8] = &[0xEF, 0xBB, 0xBF];
24pub(crate) const UTF16_LE_BOM: &[u8] = &[0xFF, 0xFE];
26pub(crate) const UTF16_BE_BOM: &[u8] = &[0xFE, 0xFF];
28
29#[derive(Clone, Debug, PartialEq, Eq)]
34#[non_exhaustive]
35pub enum EncodingError {
36 Utf8(Utf8Error),
38 #[cfg(feature = "encoding")]
40 #[cfg_attr(docsrs, doc(cfg(feature = "encoding")))]
41 Other(Encoding),
42}
43
44impl From<Utf8Error> for EncodingError {
45 #[inline]
46 fn from(e: Utf8Error) -> Self {
47 Self::Utf8(e)
48 }
49}
50
51impl std::error::Error for EncodingError {
52 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
53 match self {
54 Self::Utf8(e) => Some(e),
55 #[cfg(feature = "encoding")]
56 Self::Other(_) => None,
57 }
58 }
59}
60
61impl std::fmt::Display for EncodingError {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Self::Utf8(e) => write!(f, "cannot decode input using UTF-8: {}", e),
65 #[cfg(feature = "encoding")]
66 Self::Other(encoding) => write!(f, "cannot decode input using {}", encoding.name()),
67 }
68 }
69}
70
71#[derive(Clone, Copy, Debug, Eq, PartialEq)]
80pub struct Decoder {
81 #[cfg(feature = "encoding")]
82 pub(crate) encoding: Encoding,
83}
84
85impl Decoder {
86 pub(crate) const fn utf8() -> Self {
88 Decoder {
89 #[cfg(feature = "encoding")]
90 encoding: Encoding::Utf8,
91 }
92 }
93
94 #[cfg(all(test, feature = "encoding", feature = "serialize"))]
97 pub(crate) const fn utf16() -> Self {
98 Decoder {
99 encoding: Encoding::Utf16Le,
100 }
101 }
102
103 #[cfg(feature = "encoding")]
107 #[cfg_attr(docsrs, doc(cfg(feature = "encoding")))]
108 pub const fn encoding(&self) -> Encoding {
109 self.encoding
110 }
111
112 pub fn decode<'b>(&self, bytes: &'b [u8]) -> Result<Cow<'b, str>, EncodingError> {
121 #[cfg(not(feature = "encoding"))]
122 {
123 Ok(Cow::Borrowed(std::str::from_utf8(bytes)?))
124 }
125
126 #[cfg(feature = "encoding")]
127 {
128 decode(bytes, self.encoding)
129 }
130 }
131
132 pub fn decode_into(&self, bytes: &[u8], buf: &mut String) -> Result<(), EncodingError> {
134 #[cfg(not(feature = "encoding"))]
135 {
136 buf.push_str(std::str::from_utf8(bytes)?);
137 }
138
139 #[cfg(feature = "encoding")]
140 {
141 decode_into(bytes, self.encoding, buf)?;
142 }
143
144 Ok(())
145 }
146
147 pub(crate) fn decode_cow<'b>(
149 &self,
150 bytes: &Cow<'b, [u8]>,
151 ) -> Result<Cow<'b, str>, EncodingError> {
152 match bytes {
153 Cow::Borrowed(bytes) => self.decode(bytes),
154 Cow::Owned(bytes) => Ok(self.decode(bytes)?.into_owned().into()),
157 }
158 }
159
160 pub(crate) fn content<'b>(
163 &self,
164 bytes: &Cow<'b, [u8]>,
165 normalize_eol: impl Fn(&str) -> Cow<str>,
166 ) -> Result<Cow<'b, str>, EncodingError> {
167 match bytes {
168 Cow::Borrowed(bytes) => {
169 let text = self.decode(bytes)?;
170 match normalize_eol(&text) {
171 Cow::Borrowed(_) => Ok(text),
173 Cow::Owned(s) => Ok(Cow::Owned(s)),
174 }
175 }
176 Cow::Owned(bytes) => {
177 let text = self.decode(bytes)?;
178 let text = normalize_eol(&text);
179 Ok(text.into_owned().into())
180 }
181 }
182 }
183}
184
185#[cfg(feature = "encoding")]
189#[cfg_attr(docsrs, doc(cfg(feature = "encoding")))]
190pub fn decode(bytes: &[u8], encoding: Encoding) -> Result<Cow<'_, str>, EncodingError> {
191 if matches!(encoding, Encoding::Utf8) {
192 return std::str::from_utf8(bytes)
198 .map(Cow::Borrowed)
199 .map_err(|_| EncodingError::Other(Encoding::Utf8));
200 }
201 let mut out = String::new();
202 let mut decoder = encoding.new_decoder();
203 decoder
204 .decode_to_string(bytes, &mut out, true)
205 .map_err(|_| EncodingError::Other(encoding))?;
206 Ok(Cow::Owned(out))
207}
208
209#[cfg(feature = "encoding")]
211#[cfg_attr(docsrs, doc(cfg(feature = "encoding")))]
212pub fn decode_into(
213 bytes: &[u8],
214 encoding: Encoding,
215 buf: &mut String,
216) -> Result<(), EncodingError> {
217 if matches!(encoding, Encoding::Utf8) {
218 buf.push_str(std::str::from_utf8(bytes)?);
219 return Ok(());
220 }
221 let mut decoder = encoding.new_decoder();
222 decoder
223 .decode_to_string(bytes, buf, true)
224 .map_err(|_| EncodingError::Other(encoding))?;
225 Ok(())
226}
227
228pub fn detect_encoding(bytes: &[u8]) -> Option<DetectedEncoding> {
236 match bytes {
237 _ if bytes.starts_with(UTF16_BE_BOM) => Some(DetectedEncoding::Utf16BeBom),
239 _ if bytes.starts_with(UTF16_LE_BOM) => Some(DetectedEncoding::Utf16LeBom),
240 _ if bytes.starts_with(UTF8_BOM) => Some(DetectedEncoding::Utf8Bom),
241
242 _ if bytes.starts_with(&[0x00, b'<', 0x00, b'?']) => Some(DetectedEncoding::Utf16BeLike),
244 _ if bytes.starts_with(&[b'<', 0x00, b'?', 0x00]) => Some(DetectedEncoding::Utf16LeLike),
245 _ if bytes.starts_with(b"<?xm") => Some(DetectedEncoding::AsciiCompatible),
246
247 _ => None,
248 }
249}
250
251#[derive(Clone, Copy, Debug, Eq, PartialEq)]
255pub enum DetectedEncoding {
256 AsciiCompatible,
258 Utf8Bom,
260 Utf16LeLike,
262 Utf16LeBom,
264 Utf16BeLike,
266 Utf16BeBom,
268}
269
270impl DetectedEncoding {
271 #[cfg(feature = "encoding")]
273 #[cfg_attr(docsrs, doc(cfg(feature = "encoding")))]
274 pub const fn encoding(&self) -> Encoding {
275 match self {
276 DetectedEncoding::AsciiCompatible | DetectedEncoding::Utf8Bom => Encoding::Utf8,
277 DetectedEncoding::Utf16LeLike | DetectedEncoding::Utf16LeBom => Encoding::Utf16Le,
278 DetectedEncoding::Utf16BeLike | DetectedEncoding::Utf16BeBom => Encoding::Utf16Be,
279 }
280 }
281
282 pub const fn bom_len(&self) -> usize {
284 match self {
285 DetectedEncoding::Utf8Bom => 3,
286 DetectedEncoding::Utf16LeBom | DetectedEncoding::Utf16BeBom => 2,
287 DetectedEncoding::AsciiCompatible
288 | DetectedEncoding::Utf16LeLike
289 | DetectedEncoding::Utf16BeLike => 0,
290 }
291 }
292}
293
294#[cfg(feature = "encoding")]
299const PREFIX_CAP: usize = 64;
300
301#[cfg(feature = "encoding")]
366#[cfg_attr(docsrs, doc(cfg(feature = "encoding")))]
367pub struct DecodingReader<R> {
368 inner: R,
370 decoder: oxixml_encoding::Decoder,
372 finished: bool,
375 out: String,
377 out_pos: usize,
379 prefix: Option<Box<Prefix>>,
381 inner_eof: bool,
383}
384
385#[cfg(feature = "encoding")]
387struct Prefix {
388 buf: [u8; PREFIX_CAP],
390 len: usize,
392 detected: bool,
394}
395
396#[cfg(feature = "encoding")]
397impl<R: std::fmt::Debug> std::fmt::Debug for DecodingReader<R> {
398 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
399 f.debug_struct("DecodingReader")
400 .field("inner", &self.inner)
401 .field("encoding", &self.decoder.encoding())
402 .field("inner_eof", &self.inner_eof)
403 .field("prefix_active", &self.prefix.is_some())
404 .finish()
405 }
406}
407
408#[cfg(feature = "encoding")]
409impl<R> DecodingReader<R> {
410 pub fn new(inner: R) -> Self {
415 Self {
416 inner,
417 decoder: Encoding::Utf8.new_decoder(),
418 finished: false,
419 out: String::new(),
420 out_pos: 0,
421 prefix: Some(Box::new(Prefix {
422 buf: [0; PREFIX_CAP],
423 len: 0,
424 detected: false,
425 })),
426 inner_eof: false,
427 }
428 }
429
430 pub const fn get_ref(&self) -> &R {
432 &self.inner
433 }
434
435 pub const fn get_mut(&mut self) -> &mut R {
437 &mut self.inner
438 }
439
440 pub fn into_inner(self) -> R {
442 self.inner
443 }
444
445 pub const fn encoding(&self) -> Encoding {
450 self.decoder.encoding()
451 }
452
453 pub fn set_encoding(&mut self, encoding: Encoding) -> bool {
465 if self.decoder.encoding() == encoding {
466 return true;
467 }
468 if self.prefix.is_none() {
469 return false;
470 }
471 self.decoder = encoding.new_decoder();
472 self.finished = false;
473 true
474 }
475
476 fn decode_chunk(&mut self, bytes: &[u8], last: bool) -> std::io::Result<()> {
479 let encoding = self.decoder.encoding();
480 match self.decoder.decode_to_string(bytes, &mut self.out, last) {
481 Ok(_) => Ok(()),
482 Err(_) => Err(std::io::Error::new(
483 std::io::ErrorKind::InvalidData,
484 EncodingError::Other(encoding),
485 )),
486 }
487 }
488}
489
490#[cfg(feature = "encoding")]
491impl<R: std::io::BufRead> std::io::BufRead for DecodingReader<R> {
492 fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
493 if self.out_pos < self.out.len() {
495 return Ok(&self.out.as_bytes()[self.out_pos..]);
496 }
497 self.out.clear();
498 self.out_pos = 0;
499
500 if let Some(mut prefix) = self.prefix.take() {
501 if !prefix.detected {
502 prefix.detected = true;
503 while prefix.len < PREFIX_CAP {
504 match std::io::Read::read(&mut self.inner, &mut prefix.buf[prefix.len..]) {
505 Ok(0) => {
506 self.inner_eof = true;
507 break;
508 }
509 Ok(read) => prefix.len += read,
510 Err(error) if error.kind() == std::io::ErrorKind::Interrupted => continue,
511 Err(error) => {
512 self.prefix = Some(prefix);
513 return Err(error);
514 }
515 }
516 }
517 if let Some(detected) = detect_encoding(&prefix.buf[..prefix.len]) {
518 let bom_len = detected.bom_len();
519 if bom_len > 0 {
520 prefix.buf.copy_within(bom_len..prefix.len, 0);
521 prefix.len -= bom_len;
522 }
523 let encoding = detected.encoding();
524 if encoding != Encoding::Utf8 {
525 self.decoder = encoding.new_decoder();
526 }
527 }
528 }
529
530 if prefix.len > 0 {
531 let len = prefix.len;
532 let mut bytes = [0; PREFIX_CAP];
533 bytes[..len].copy_from_slice(&prefix.buf[..len]);
534 prefix.len = 0;
535 self.prefix = Some(prefix);
539 self.decode_chunk(&bytes[..len], false)?;
540 if !self.out.is_empty() {
541 return Ok(self.out.as_bytes());
542 }
543 self.prefix = None;
546 }
547 }
548
549 if self.finished {
550 return Ok(&[]);
551 }
552
553 loop {
554 if self.inner_eof {
555 self.finished = true;
556 self.decode_chunk(&[], true)?;
557 return Ok(self.out.as_bytes());
558 }
559
560 let chunk = {
561 let available = self.inner.fill_buf()?;
562 if available.is_empty() {
563 self.inner_eof = true;
564 continue;
565 }
566 available.to_vec()
567 };
568 self.inner.consume(chunk.len());
569 self.decode_chunk(&chunk, false)?;
570 if !self.out.is_empty() {
571 return Ok(self.out.as_bytes());
572 }
573 }
577 }
578
579 fn consume(&mut self, amount: usize) {
580 self.out_pos = (self.out_pos + amount).min(self.out.len());
581 }
582}
583
584#[cfg(feature = "encoding")]
585impl<R: std::io::BufRead> std::io::Read for DecodingReader<R> {
586 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
587 if buf.is_empty() {
588 return Ok(0);
589 }
590 let available = std::io::BufRead::fill_buf(self)?;
591 if available.is_empty() {
592 return Ok(0);
593 }
594 let len = available.len().min(buf.len());
595 buf[..len].copy_from_slice(&available[..len]);
596 std::io::BufRead::consume(self, len);
597 Ok(len)
598 }
599}
600
601#[cfg(test)]
602mod tests {
603 use super::*;
604
605 #[test]
606 fn utf8_decode_borrows() -> Result<(), EncodingError> {
607 let decoder = Decoder::utf8();
608 assert_eq!(decoder.decode(b"hello")?, Cow::Borrowed("hello"));
609 Ok(())
610 }
611
612 #[test]
617 fn utf8_decode_rejects_invalid() {
618 let decoder = Decoder::utf8();
619 #[cfg(not(feature = "encoding"))]
620 assert!(matches!(
621 decoder.decode(&[0xFF, 0xFE]),
622 Err(EncodingError::Utf8(_))
623 ));
624 #[cfg(feature = "encoding")]
625 {
626 assert_eq!(
627 decoder.decode(&[0xFF, 0xFE]),
628 Err(EncodingError::Other(Encoding::Utf8))
629 );
630 let mut buf = String::new();
632 assert!(matches!(
633 decoder.decode_into(&[0xFF, 0xFE], &mut buf),
634 Err(EncodingError::Utf8(_))
635 ));
636 }
637 }
638
639 #[test]
640 fn detect() {
641 assert_eq!(
642 detect_encoding(b"\xEF\xBB\xBF<?xml").map(|d| d.bom_len()),
643 Some(3)
644 );
645 assert_eq!(
646 detect_encoding(b"<?xml version").map(|d| d.bom_len()),
647 Some(0)
648 );
649 assert_eq!(detect_encoding(b"plain text"), None);
650 }
651
652 #[cfg(feature = "encoding")]
653 #[test]
654 fn non_utf8_decode() -> Result<(), EncodingError> {
655 let decoder = Decoder {
657 encoding: Encoding::Windows1252,
658 };
659 assert_eq!(decoder.decode(&[b'r', b'o', b's', 0xE9])?, "rosé");
660 Ok(())
661 }
662
663 #[cfg(feature = "encoding")]
682 #[test]
683 fn bare_utf16_label_does_not_override_the_boms_byte_order() {
684 use crate::events::Event;
685 use crate::reader::Reader;
686
687 for big_endian in [true, false] {
688 let mut bytes = if big_endian {
689 vec![0xFE, 0xFF]
690 } else {
691 vec![0xFF, 0xFE]
692 };
693 bytes.extend_from_slice(b"<?xml version=\"1.0\" encoding=\"UTF-16\"?><r>hi</r>");
694
695 let mut reader = Reader::from_reader(bytes.as_slice());
696 let expected = if big_endian {
697 Encoding::Utf16Be
698 } else {
699 Encoding::Utf16Le
700 };
701
702 match reader.read_event() {
703 Ok(Event::Decl(_)) => {}
704 other => panic!("big_endian={big_endian}: expected Decl, got {other:?}"),
705 }
706 assert_eq!(
707 reader.decoder().encoding(),
708 expected,
709 "big_endian={big_endian}: the BOM's byte order must survive the declaration",
710 );
711
712 loop {
714 match reader.read_event() {
715 Ok(Event::Eof) => break,
716 Ok(_) => {}
717 Err(e) => panic!("big_endian={big_endian}: unexpected error {e:?}"),
718 }
719 }
720 assert_eq!(
721 reader.decoder().encoding(),
722 expected,
723 "big_endian={big_endian}"
724 );
725 }
726 }
727}
728
729#[cfg(all(test, feature = "encoding"))]
730mod decoding_reader {
731 use super::*;
732 use std::io::{BufRead, Read};
733
734 struct Chunked<'a> {
737 data: &'a [u8],
738 pos: usize,
739 chunk: usize,
740 }
741
742 impl std::io::Read for Chunked<'_> {
743 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
744 let available = self.fill_buf()?;
745 let len = available.len().min(buf.len());
746 buf[..len].copy_from_slice(&available[..len]);
747 self.pos += len;
748 Ok(len)
749 }
750 }
751
752 impl std::io::BufRead for Chunked<'_> {
753 fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
754 let end = (self.pos + self.chunk).min(self.data.len());
755 Ok(&self.data[self.pos..end])
756 }
757
758 fn consume(&mut self, amount: usize) {
759 self.pos = (self.pos + amount).min(self.data.len());
760 }
761 }
762
763 fn utf16(text: &str, little_endian: bool, bom: bool) -> Vec<u8> {
765 let mut bytes = Vec::new();
766 if bom {
767 bytes.extend_from_slice(if little_endian {
768 UTF16_LE_BOM
769 } else {
770 UTF16_BE_BOM
771 });
772 }
773 for unit in text.encode_utf16() {
774 if little_endian {
775 bytes.extend_from_slice(&unit.to_le_bytes());
776 } else {
777 bytes.extend_from_slice(&unit.to_be_bytes());
778 }
779 }
780 bytes
781 }
782
783 fn decode_all(bytes: &[u8], chunk: usize) -> std::io::Result<String> {
784 let mut reader = DecodingReader::new(Chunked {
785 data: bytes,
786 pos: 0,
787 chunk,
788 });
789 let mut out = Vec::new();
790 reader.read_to_end(&mut out)?;
791 String::from_utf8(out)
792 .map_err(|error| std::io::Error::new(std::io::ErrorKind::InvalidData, error))
793 }
794
795 #[test]
796 fn utf8_passes_through() -> std::io::Result<()> {
797 assert_eq!(decode_all(b"<a>text</a>", 3)?, "<a>text</a>");
798 assert_eq!(decode_all(UTF8_BOM, 1)?, "");
799 Ok(())
800 }
801
802 #[test]
803 fn transcodes_utf16_at_every_chunk_size() -> std::io::Result<()> {
804 let text = "<?xml version='1.0' encoding='UTF-16'?><doc>текст</doc>";
805 for little_endian in [true, false] {
806 for bom in [true, false] {
807 for chunk in [1, 2, 3, 7, 64, 4096] {
808 let bytes = utf16(text, little_endian, bom);
809 assert_eq!(
810 decode_all(&bytes, chunk)?,
811 text,
812 "little_endian={little_endian} bom={bom} chunk={chunk}",
813 );
814 }
815 }
816 }
817 Ok(())
818 }
819
820 #[test]
821 fn reports_malformed_input() {
822 let mut bytes = utf16("<a/>", true, true);
824 bytes.extend_from_slice(&[0x00, 0xD8]);
825 let error = decode_all(&bytes, 4).expect_err("a truncated sequence must fail");
826 assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
827 }
828
829 #[test]
830 fn exposes_and_replaces_the_encoding() -> std::io::Result<()> {
831 let mut bytes = b"<?xml version='1.0' encoding='ISO-8859-1'?><a>".to_vec();
834 bytes.extend_from_slice(&b"-".repeat(32));
835 bytes.extend_from_slice(&[0xE9]);
836 bytes.extend_from_slice(b"</a>");
837
838 let mut reader = DecodingReader::new(&bytes[..]);
839 assert_eq!(reader.encoding(), Encoding::Utf8);
840 assert_eq!(reader.get_ref().len(), bytes.len());
841
842 assert!(!reader.fill_buf()?.is_empty());
844 assert!(reader.set_encoding(Encoding::Latin1));
846 assert_eq!(reader.encoding(), Encoding::Latin1);
847
848 let mut out = String::new();
849 reader.read_to_string(&mut out)?;
850 assert!(out.ends_with("\u{e9}</a>"), "{out}");
851 assert!(reader.get_mut().is_empty());
852 assert!(reader.into_inner().is_empty());
853 Ok(())
854 }
855
856 #[test]
857 fn refuses_to_change_the_encoding_too_late() -> std::io::Result<()> {
858 let bytes =
859 b"<a>0123456789012345678901234567890123456789012345678901234567890123456789</a>";
860 let mut reader = DecodingReader::new(&bytes[..]);
861 let mut out = String::new();
862 reader.read_to_string(&mut out)?;
863 assert_eq!(out, std::str::from_utf8(bytes).expect("utf-8"));
864 assert!(reader.set_encoding(Encoding::Utf8));
866 assert!(!reader.set_encoding(Encoding::Utf16Le));
868 Ok(())
869 }
870
871 #[test]
872 fn parses_a_utf16_document_with_the_reader() -> crate::errors::Result<()> {
873 use crate::events::Event;
874 use crate::reader::Reader;
875
876 let bytes = utf16("<?xml version='1.0'?><doc>hi</doc>", true, true);
880 let mut reader = Reader::from_reader(DecodingReader::new(bytes.as_slice()));
881 let mut buf = Vec::new();
882 let mut texts = Vec::new();
883 loop {
884 buf.clear();
885 match reader.read_event_into(&mut buf)? {
886 Event::Eof => break,
887 Event::Text(text) => texts.push(text.decode()?.into_owned()),
888 _ => continue,
889 }
890 }
891 assert_eq!(texts, vec!["hi".to_owned()]);
892 Ok(())
893 }
894}