Skip to main content

oxixml_xml/reader/
slice_reader.rs

1//! Implementation of [`Reader`] for reading from a `&[u8]` as underlying byte
2//! stream.
3//!
4//! This implementation supports not using an intermediate buffer as the byte
5//! slice itself can be used to borrow from, providing a zero-copy path.
6
7use std::io;
8
9use oxixml_support::bytes::{memchr2, memchr3};
10
11#[cfg(feature = "encoding")]
12use crate::encoding::DetectedEncoding;
13use crate::errors::{Error, Result};
14use crate::events::{BytesText, Event};
15use crate::name::QName;
16use crate::parser::Parser;
17#[cfg(feature = "encoding")]
18use crate::reader::EncodingRef;
19use crate::reader::{BangType, ReadRefResult, ReadTextResult, Reader, Span, XmlSource};
20use crate::utils::is_whitespace;
21#[cfg(feature = "encoding")]
22use oxixml_encoding::Encoding;
23
24/// This is an implementation for reading from a `&[u8]` as underlying byte stream.
25/// This implementation supports not using an intermediate buffer as the byte slice
26/// itself can be used to borrow from.
27impl<'a> Reader<&'a [u8]> {
28    /// Creates an XML reader from a string slice.
29    #[allow(clippy::should_implement_trait)]
30    pub fn from_str(s: &'a str) -> Self {
31        // Rust strings are guaranteed to be UTF-8, so lock the encoding
32        #[cfg(feature = "encoding")]
33        {
34            let mut reader = Self::from_reader(s.as_bytes());
35            reader.state.encoding = EncodingRef::Explicit(Encoding::Utf8);
36            reader
37        }
38
39        #[cfg(not(feature = "encoding"))]
40        Self::from_reader(s.as_bytes())
41    }
42
43    /// Read an event that borrows from the input rather than a buffer.
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use oxixml_xml::events::Event;
49    /// use oxixml_xml::reader::Reader;
50    ///
51    /// let mut reader = Reader::from_str(r#"
52    ///     <tag1 att1 = "test">
53    ///        <tag2><!--Test comment-->Test</tag2>
54    ///        <tag2>Test 2</tag2>
55    ///     </tag1>
56    /// "#);
57    /// reader.config_mut().trim_text(true);
58    ///
59    /// let mut count = 0;
60    /// let mut txt = Vec::new();
61    /// loop {
62    ///     match reader.read_event().expect("valid event") {
63    ///         Event::Start(_) => count += 1,
64    ///         Event::Text(e) => txt.push(e.decode().expect("utf-8").into_owned()),
65    ///         Event::Eof => break,
66    ///         _ => (),
67    ///     }
68    /// }
69    /// assert_eq!(count, 3);
70    /// assert_eq!(txt, vec!["Test".to_string(), "Test 2".to_string()]);
71    /// ```
72    #[inline]
73    pub fn read_event(&mut self) -> Result<Event<'a>> {
74        self.read_event_impl(())
75    }
76
77    /// Reads until end element is found. This function is supposed to be called
78    /// after you already read a [`Start`] event.
79    ///
80    /// Returns a span that cover content between `>` of an opening tag and `<` of
81    /// a closing tag or an empty slice, if [`expand_empty_elements`] is set and
82    /// this method was called after reading expanded [`Start`] event.
83    ///
84    /// Manages nested cases where parent and child elements have the _literally_
85    /// same name.
86    ///
87    /// If a corresponding [`End`] event is not found, an error of type
88    /// [`Error::IllFormed`] will be returned.
89    ///
90    /// The `end` parameter should contain name of the end element _in the reader
91    /// encoding_. It is good practice to always get that parameter using
92    /// [`BytesStart::to_end()`] method.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use oxixml_xml::events::{BytesStart, Event};
98    /// use oxixml_xml::reader::Reader;
99    ///
100    /// let mut reader = Reader::from_str(r#"
101    ///     <outer>
102    ///         <inner>
103    ///             <inner></inner>
104    ///             <inner/>
105    ///             <outer></outer>
106    ///             <outer/>
107    ///         </inner>
108    ///     </outer>
109    /// "#);
110    /// reader.config_mut().trim_text(true);
111    ///
112    /// let start = BytesStart::new("outer");
113    /// let end   = start.to_end().into_owned();
114    ///
115    /// // First, we read a start event...
116    /// assert_eq!(reader.read_event().expect("start"), Event::Start(start));
117    ///
118    /// // ...then, we could skip all events to the corresponding end event.
119    /// // This call will correctly handle nested <outer> elements.
120    /// reader.read_to_end(end.name()).expect("end");
121    ///
122    /// // At the end we should get an Eof event, because we ate the whole XML
123    /// assert_eq!(reader.read_event().expect("eof"), Event::Eof);
124    /// ```
125    ///
126    /// [`Start`]: Event::Start
127    /// [`End`]: Event::End
128    /// [`BytesStart::to_end()`]: crate::events::BytesStart::to_end
129    /// [`Error::IllFormed`]: crate::errors::Error::IllFormed
130    /// [`expand_empty_elements`]: crate::reader::Config::expand_empty_elements
131    pub fn read_to_end(&mut self, end: QName) -> Result<Span> {
132        Ok(read_to_end!(self, end, (), read_event_impl, {}))
133    }
134
135    /// Reads content between start and end tags, including any markup. This
136    /// function is supposed to be called after you already read a [`Start`] event.
137    ///
138    /// Manages nested cases where parent and child elements have the _literally_
139    /// same name.
140    ///
141    /// This method does not unescape read data, instead it returns content
142    /// "as is" of the XML document. Any text will be decoded using the XML current
143    /// [`decoder()`].
144    ///
145    /// [`Start`]: Event::Start
146    /// [`decoder()`]: Self::decoder()
147    pub fn read_text(&mut self, end: QName) -> Result<BytesText<'a>> {
148        // self.reader will be changed, so store original reference
149        let buffer = self.reader;
150        let span = self.read_to_end(end)?;
151
152        let len = span.end - span.start;
153        // `span` can only contain indexes up to usize::MAX because it
154        // was created from offsets from a single &[u8] slice
155        Ok(BytesText::wrap(&buffer[0..len as usize], self.decoder()))
156    }
157}
158
159////////////////////////////////////////////////////////////////////////////////////////////////////
160
161/// Implementation of `XmlSource` for `&[u8]` reader using a `Self` as buffer
162/// that will be borrowed by events. This implementation provides a zero-copy
163/// deserialization.
164impl<'a> XmlSource<'a, ()> for &'a [u8] {
165    #[cfg(not(feature = "encoding"))]
166    #[inline]
167    fn remove_utf8_bom(&mut self) -> io::Result<()> {
168        if self.starts_with(crate::encoding::UTF8_BOM) {
169            *self = &self[crate::encoding::UTF8_BOM.len()..];
170        }
171        Ok(())
172    }
173
174    #[cfg(feature = "encoding")]
175    #[inline]
176    fn detect_encoding(&mut self) -> io::Result<Option<DetectedEncoding>> {
177        if let Some(detected) = crate::encoding::detect_encoding(self) {
178            *self = &self[detected.bom_len()..];
179            return Ok(Some(detected));
180        }
181        Ok(None)
182    }
183
184    #[inline]
185    fn read_text(
186        &mut self,
187        _buf: (),
188        position: &mut u64,
189        trim_whitespace_only: bool,
190    ) -> ReadTextResult<'a, ()> {
191        // Search for start of markup or an entity or character reference
192        match memchr2(b'<', b'&', self) {
193            Some(0) if self[0] == b'<' => ReadTextResult::Markup(()),
194            // Do not consume `&` because it may be lone and we would be need to
195            // return it as part of Text event
196            Some(0) => ReadTextResult::Ref(()),
197            Some(i) if self[i] == b'<' => {
198                let (bytes, rest) = self.split_at(i);
199                *self = rest;
200                *position += i as u64;
201                if trim_whitespace_only && bytes.iter().all(|b| is_whitespace(*b)) {
202                    // Trimming would leave nothing, so report that markup starts
203                    // here rather than an empty text run.
204                    ReadTextResult::Markup(())
205                } else {
206                    ReadTextResult::UpToMarkup(bytes)
207                }
208            }
209            Some(i) => {
210                let (bytes, rest) = self.split_at(i);
211                *self = rest;
212                *position += i as u64;
213                if trim_whitespace_only && bytes.iter().all(|b| is_whitespace(*b)) {
214                    // Trimming would leave nothing, so report that a reference
215                    // starts here rather than an empty text run.
216                    ReadTextResult::Ref(())
217                } else {
218                    ReadTextResult::UpToRef(bytes)
219                }
220            }
221            None => {
222                let bytes = &self[..];
223                *self = &[];
224                *position += bytes.len() as u64;
225                ReadTextResult::UpToEof(bytes)
226            }
227        }
228    }
229
230    #[inline]
231    fn read_ref(&mut self, _buf: (), position: &mut u64) -> ReadRefResult<'a> {
232        debug_assert!(
233            self.starts_with(b"&"),
234            "`read_ref` must be called at `&`:\n{:?}",
235            crate::utils::Bytes(self)
236        );
237        // Search for the end of reference or a start of another reference or a markup
238        match memchr3(b';', b'&', b'<', &self[1..]) {
239            Some(i) if self[i + 1] == b';' => {
240                // +1 for the start `&`
241                // +1 for the end `;`
242                let end = i + 2;
243                let (bytes, rest) = self.split_at(end);
244                *self = rest;
245                *position += end as u64;
246
247                ReadRefResult::Ref(bytes)
248            }
249            // Do not consume `&` because it may be lone and we would be need to
250            // return it as part of Text event
251            Some(i) => {
252                let is_amp = self[i + 1] == b'&';
253                let (bytes, rest) = self.split_at(i + 1);
254                *self = rest;
255                *position += i as u64 + 1;
256
257                if is_amp {
258                    ReadRefResult::UpToRef(bytes)
259                } else {
260                    ReadRefResult::UpToMarkup(bytes)
261                }
262            }
263            None => {
264                let bytes = &self[..];
265                *self = &[];
266                *position += bytes.len() as u64;
267
268                ReadRefResult::UpToEof(bytes)
269            }
270        }
271    }
272
273    #[inline]
274    fn read_with<P>(&mut self, mut parser: P, _buf: (), position: &mut u64) -> Result<&'a [u8]>
275    where
276        P: Parser,
277    {
278        if let Some(i) = parser.feed(self) {
279            let used = i + 1; // +1 for `>`
280            *position += used as u64;
281            let (bytes, rest) = self.split_at(used);
282            *self = rest;
283            return Ok(bytes);
284        }
285
286        *position += self.len() as u64;
287        Err(Error::Syntax(parser.eof_error(self)))
288    }
289
290    #[inline]
291    fn read_bang_element(&mut self, _buf: (), position: &mut u64) -> Result<(BangType, &'a [u8])> {
292        // Peeked one bang ('!') before being called, so it's guaranteed to
293        // start with it.
294        debug_assert!(
295            self.starts_with(b"<!"),
296            "`read_bang_element` must be called at `<!`:\n{:?}",
297            crate::utils::Bytes(self)
298        );
299
300        let mut bang_type = BangType::new(self.get(2).copied())?;
301
302        if let Some(i) = bang_type.feed(&[], self) {
303            let consumed = i + 1; // +1 for `>`
304            *position += consumed as u64;
305            let (bytes, rest) = self.split_at(consumed);
306            *self = rest;
307            return Ok((bang_type, bytes));
308        }
309
310        *position += self.len() as u64;
311        Err(Error::Syntax(bang_type.to_err()))
312    }
313
314    #[inline]
315    fn skip_whitespace(&mut self, position: &mut u64) -> io::Result<()> {
316        let whitespaces = self
317            .iter()
318            .position(|b| !is_whitespace(*b))
319            .unwrap_or(self.len());
320        *position += whitespaces as u64;
321        *self = &self[whitespaces..];
322        Ok(())
323    }
324
325    #[inline]
326    fn peek_one(&mut self) -> io::Result<Option<u8>> {
327        debug_assert!(
328            self.starts_with(b"<"),
329            "markup must start from '<':\n{:?}",
330            crate::utils::Bytes(self)
331        );
332        Ok(self.get(1).copied())
333    }
334}
335
336#[cfg(test)]
337mod test {
338    use crate::reader::XmlSource;
339    use crate::reader::check_tests::check;
340
341    /// Default buffer constructor just pass the byte array from the test
342    fn identity<T>(input: T) -> T {
343        input
344    }
345
346    check!(
347        #[test]
348        read_event_impl,
349        identity,
350        0,
351        ()
352    );
353}