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}