Skip to main content

oxixml_xml/reader/
buffered_reader.rs

1//! Implementation of [`Reader`] for reading from a [`BufRead`] as underlying
2//! byte stream.
3//!
4//! Events read through this path borrow from a caller-provided `Vec<u8>` buffer:
5//! bytes are copied out of the underlying reader into that buffer and the events
6//! borrow from it, so the same allocation can be reused across reads.
7
8use std::fs::File;
9use std::io::{self, BufRead, BufReader};
10use std::path::Path;
11
12use crate::encoding;
13use crate::errors::{Error, Result};
14use crate::events::{BytesText, Event};
15use crate::name::QName;
16use crate::parser::Parser;
17use crate::reader::{BangType, ReadRefResult, ReadTextResult, Reader, Span, XmlSource};
18use crate::utils::is_whitespace;
19
20/// Generates the body of [`XmlSource`] for a buffered byte source, either
21/// synchronous ([`BufRead`]) or, with the `async-tokio` feature, asynchronous
22/// (`tokio`'s `AsyncBufRead`).
23///
24/// This single definition is reused by both the synchronous implementation
25/// below and the asynchronous implementation in [`async_tokio`](super::async_tokio),
26/// so the byte-level scanning logic (searching for `<`, `&`, `>`, comment/CDATA
27/// terminators, …) lives in exactly one place.
28///
29/// # Parameters
30/// - `$lf`: lifetime used on methods that need one only in the async form
31///   (the sync trait methods already have it in scope via the `impl` block)
32/// - `$reader`: optional field access (e.g. `0`) used to reach the wrapped
33///   reader when `Self` is an adapter struct instead of the reader itself
34/// - `$async`/`$await`: optional `async`/`.await` tokens, present only for the
35///   asynchronous instantiation
36macro_rules! impl_buffered_source {
37    ($($lf:lifetime, $reader:tt, $async:ident, $await:ident)?) => {
38        #[cfg(not(feature = "encoding"))]
39        #[inline]
40        $($async)? fn remove_utf8_bom(&mut self) -> io::Result<()> {
41            loop {
42                break match self $(.$reader)? .fill_buf() $(.$await)? {
43                    Ok(n) => {
44                        if n.starts_with(encoding::UTF8_BOM) {
45                            self $(.$reader)? .consume(encoding::UTF8_BOM.len());
46                        }
47                        Ok(())
48                    }
49                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
50                    Err(e) => Err(e),
51                };
52            }
53        }
54
55        #[cfg(feature = "encoding")]
56        #[inline]
57        $($async)? fn detect_encoding(&mut self) -> io::Result<Option<encoding::DetectedEncoding>> {
58            loop {
59                break match self $(.$reader)? .fill_buf() $(.$await)? {
60                    Ok(n) => {
61                        if let Some(detected) = encoding::detect_encoding(n) {
62                            self $(.$reader)? .consume(detected.bom_len());
63                            Ok(Some(detected))
64                        } else {
65                            Ok(None)
66                        }
67                    }
68                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
69                    Err(e) => Err(e),
70                };
71            }
72        }
73
74        #[inline]
75        $($async)? fn read_text $(<$lf>)? (
76            &mut self,
77            buf: &'b mut Vec<u8>,
78            position: &mut u64,
79            trim_whitespace_only: bool,
80        ) -> ReadTextResult<'b, &'b mut Vec<u8>> {
81            let mut read = 0;
82            let start = buf.len();
83            loop {
84                let available = match self $(.$reader)? .fill_buf() $(.$await)? {
85                    Ok(n) if n.is_empty() => break,
86                    Ok(n) => n,
87                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
88                    Err(e) => {
89                        *position += read;
90                        return ReadTextResult::Err(e);
91                    }
92                };
93
94                // Search for start of markup or an entity or character reference
95                match oxixml_support::bytes::memchr2(b'<', b'&', available) {
96                    // Special handling is needed only on the first iteration.
97                    // On next iterations we already read something and should emit Text event
98                    Some(0) if read == 0 && available[0] == b'<' => return ReadTextResult::Markup(buf),
99                    // Do not consume `&` because it may be lone and we would be need to
100                    // return it as part of Text event
101                    Some(0) if read == 0 => return ReadTextResult::Ref(buf),
102                    Some(i) if available[i] == b'<' => {
103                        buf.extend_from_slice(&available[..i]);
104
105                        self $(.$reader)? .consume(i);
106                        read += i as u64;
107
108                        *position += read;
109                        if trim_whitespace_only
110                            && buf[start..].iter().all(|b| crate::utils::is_whitespace(*b))
111                        {
112                            // Trimming would leave nothing, so report that markup
113                            // starts here rather than an empty text run.
114                            buf.truncate(start);
115                            return ReadTextResult::Markup(buf);
116                        }
117                        return ReadTextResult::UpToMarkup(&buf[start..]);
118                    }
119                    Some(i) => {
120                        buf.extend_from_slice(&available[..i]);
121
122                        self $(.$reader)? .consume(i);
123                        read += i as u64;
124
125                        *position += read;
126                        if trim_whitespace_only
127                            && buf[start..].iter().all(|b| crate::utils::is_whitespace(*b))
128                        {
129                            // Trimming would leave nothing, so report that a
130                            // reference starts here rather than an empty text run.
131                            buf.truncate(start);
132                            return ReadTextResult::Ref(buf);
133                        }
134                        return ReadTextResult::UpToRef(&buf[start..]);
135                    }
136                    None => {
137                        buf.extend_from_slice(available);
138
139                        let used = available.len();
140                        self $(.$reader)? .consume(used);
141                        read += used as u64;
142                    }
143                }
144            }
145
146            *position += read;
147            ReadTextResult::UpToEof(&buf[start..])
148        }
149
150        #[inline]
151        $($async)? fn read_ref $(<$lf>)? (
152            &mut self,
153            buf: &'b mut Vec<u8>,
154            position: &mut u64,
155        ) -> ReadRefResult<'b> {
156            let mut read = 0;
157            let start = buf.len();
158            loop {
159                let available = match self $(.$reader)? .fill_buf() $(.$await)? {
160                    Ok(n) if n.is_empty() => break,
161                    Ok(n) => n,
162                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
163                    Err(e) => {
164                        *position += read;
165                        return ReadRefResult::Err(e);
166                    }
167                };
168                // `read_ref` called when the first character is `&`, so we
169                // should explicitly skip it at first iteration lest we confuse
170                // it with the end
171                if read == 0 {
172                    debug_assert!(
173                        available.starts_with(b"&"),
174                        "`read_ref` must be called at `&`:\n{:?}",
175                        crate::utils::Bytes(available)
176                    );
177                    // If that ampersand is lone, then it will be part of text
178                    // and we should keep it
179                    buf.push(b'&');
180                    self $(.$reader)? .consume(1);
181                    read += 1;
182                    continue;
183                }
184
185                match oxixml_support::bytes::memchr3(b';', b'&', b'<', available) {
186                    Some(i) if available[i] == b';' => {
187                        // +1 -- skip the end `;`
188                        let used = i + 1;
189
190                        buf.extend_from_slice(&available[..used]);
191                        self $(.$reader)? .consume(used);
192                        read += used as u64;
193
194                        *position += read;
195
196                        return ReadRefResult::Ref(&buf[start..]);
197                    }
198                    // Do not consume `&` because it may be lone and we would be need to
199                    // return it as part of Text event
200                    Some(i) => {
201                        let is_amp = available[i] == b'&';
202                        buf.extend_from_slice(&available[..i]);
203
204                        self $(.$reader)? .consume(i);
205                        read += i as u64;
206
207                        *position += read;
208
209                        return if is_amp {
210                            ReadRefResult::UpToRef(&buf[start..])
211                        } else {
212                            ReadRefResult::UpToMarkup(&buf[start..])
213                        };
214                    }
215                    None => {
216                        buf.extend_from_slice(available);
217
218                        let used = available.len();
219                        self $(.$reader)? .consume(used);
220                        read += used as u64;
221                    }
222                }
223            }
224
225            *position += read;
226            ReadRefResult::UpToEof(&buf[start..])
227        }
228
229        #[inline]
230        $($async)? fn read_with<$($lf,)? P: Parser>(
231            &mut self,
232            mut parser: P,
233            buf: &'b mut Vec<u8>,
234            position: &mut u64,
235        ) -> Result<&'b [u8]> {
236            let mut read = 1;
237            let start = buf.len();
238            // '<' was consumed in peek_one(), but not placed in buf
239            buf.push(b'<');
240            loop {
241                let available = match self $(.$reader)? .fill_buf() $(.$await)? {
242                    Ok(n) if n.is_empty() => break,
243                    Ok(n) => n,
244                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
245                    Err(e) => {
246                        *position += read;
247                        return Err(Error::from(e));
248                    }
249                };
250
251                if let Some(i) = parser.feed(available) {
252                    let used = i + 1; // +1 for `>`
253                    buf.extend_from_slice(&available[..used]);
254
255                    self $(.$reader)? .consume(used);
256                    read += used as u64;
257
258                    *position += read;
259                    return Ok(&buf[start..]);
260                }
261
262                // The `>` symbol not yet found, continue reading
263                buf.extend_from_slice(available);
264
265                let used = available.len();
266                self $(.$reader)? .consume(used);
267                read += used as u64;
268            }
269
270            *position += read;
271            Err(Error::Syntax(parser.eof_error(&buf[start..])))
272        }
273
274        #[inline]
275        $($async)? fn read_bang_element $(<$lf>)? (
276            &mut self,
277            buf: &'b mut Vec<u8>,
278            position: &mut u64,
279        ) -> Result<(BangType, &'b [u8])> {
280            // Peeked '<!' before being called, so it's guaranteed to start with it.
281            let start = buf.len();
282            let mut read = 2;
283            // '<' was consumed in peek_one(), but not placed in buf
284            buf.push(b'<');
285            buf.push(b'!');
286            self $(.$reader)? .consume(1);
287
288            let mut bang_type = loop {
289                break match self $(.$reader)? .fill_buf() $(.$await)? {
290                    Ok(n) => BangType::new(n.first().copied())?,
291                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
292                    Err(e) => return Err(Error::from(e)),
293                };
294            };
295
296            loop {
297                let available = match self $(.$reader)? .fill_buf() $(.$await)? {
298                    Ok(n) if n.is_empty() => break,
299                    Ok(n) => n,
300                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
301                    Err(e) => {
302                        *position += read;
303                        return Err(Error::from(e));
304                    }
305                };
306                // We only parse from start because we don't want to consider
307                // whatever is in the buffer before the bang element
308                if let Some(i) = bang_type.feed(&buf[start..], available) {
309                    let consumed = i + 1; // +1 for `>`
310                    buf.extend_from_slice(&available[..consumed]);
311
312                    self $(.$reader)? .consume(consumed);
313                    read += consumed as u64;
314
315                    *position += read;
316                    return Ok((bang_type, &buf[start..]));
317                }
318
319                // The `>` symbol not yet found, continue reading
320                buf.extend_from_slice(available);
321
322                let used = available.len();
323                self $(.$reader)? .consume(used);
324                read += used as u64;
325            }
326
327            *position += read;
328            Err(Error::Syntax(bang_type.to_err()))
329        }
330
331        #[inline]
332        $($async)? fn skip_whitespace(&mut self, position: &mut u64) -> io::Result<()> {
333            loop {
334                break match self $(.$reader)? .fill_buf() $(.$await)? {
335                    Ok(n) => {
336                        let count = n.iter().position(|b| !is_whitespace(*b)).unwrap_or(n.len());
337                        if count > 0 {
338                            self $(.$reader)? .consume(count);
339                            *position += count as u64;
340                            continue;
341                        } else {
342                            Ok(())
343                        }
344                    }
345                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
346                    Err(e) => Err(e),
347                };
348            }
349        }
350
351        #[inline]
352        $($async)? fn peek_one(&mut self) -> io::Result<Option<u8>> {
353            // That method is called only when available buffer starts from '<'
354            // We need to consume it
355            self $(.$reader)? .consume(1);
356            let available = loop {
357                break match self $(.$reader)? .fill_buf() $(.$await)? {
358                    Ok(n) => n,
359                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
360                    Err(e) => return Err(e),
361                };
362            };
363            Ok(available.first().copied())
364        }
365    };
366}
367
368// Made available to the async implementation, which lives in a sibling module
369// and reuses the exact same scanning logic via `TokioAdapter`.
370#[cfg(feature = "async-tokio")]
371pub(super) use impl_buffered_source;
372
373/// Implementation of `XmlSource` for any `BufRead` reader using a user-given
374/// `Vec<u8>` as buffer that will be borrowed by events.
375impl<'b, R: BufRead> XmlSource<'b, &'b mut Vec<u8>> for R {
376    impl_buffered_source!();
377}
378
379////////////////////////////////////////////////////////////////////////////////////////////////////
380
381/// This is an implementation for reading from a [`BufRead`] as underlying byte stream.
382impl<R: BufRead> Reader<R> {
383    /// Reads the next `Event`.
384    ///
385    /// This is the main entry point for reading XML `Event`s when the input is a
386    /// generic [`BufRead`].
387    ///
388    /// `Event`s borrow `buf` and can be converted to own their data if needed
389    /// (uses `Cow` internally).
390    ///
391    /// Having the possibility to control the internal buffers gives you some
392    /// additional benefits such as reducing the number of allocations by reusing
393    /// the same buffer (call `buf.clear()` once you are done with the event).
394    ///
395    /// # Examples
396    ///
397    /// ```
398    /// use oxixml_xml::events::Event;
399    /// use oxixml_xml::reader::Reader;
400    ///
401    /// let xml = r#"<tag1 att1 = "test">
402    ///                 <tag2><!--Test comment-->Test</tag2>
403    ///                 <tag2>Test 2</tag2>
404    ///              </tag1>"#;
405    /// let mut reader = Reader::from_reader(xml.as_bytes());
406    /// reader.config_mut().trim_text(true);
407    /// let mut count = 0;
408    /// let mut buf = Vec::new();
409    /// let mut txt = Vec::new();
410    /// loop {
411    ///     match reader.read_event_into(&mut buf) {
412    ///         Ok(Event::Start(_)) => count += 1,
413    ///         Ok(Event::Text(e)) => txt.push(e.decode().expect("utf-8").into_owned()),
414    ///         Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e),
415    ///         Ok(Event::Eof) => break,
416    ///         _ => (),
417    ///     }
418    ///     buf.clear();
419    /// }
420    /// assert_eq!(count, 3);
421    /// assert_eq!(txt, vec!["Test".to_string(), "Test 2".to_string()]);
422    /// ```
423    #[inline]
424    pub fn read_event_into<'b>(&mut self, buf: &'b mut Vec<u8>) -> Result<Event<'b>> {
425        self.read_event_impl(buf)
426    }
427
428    /// Reads until end element is found using provided buffer as intermediate
429    /// storage for events content. This function is supposed to be called after
430    /// you already read a [`Start`] event.
431    ///
432    /// Returns a span that cover content between `>` of an opening tag and `<` of
433    /// a closing tag or an empty slice, if [`expand_empty_elements`] is set and
434    /// this method was called after reading expanded [`Start`] event.
435    ///
436    /// Manages nested cases where parent and child elements have the _literally_
437    /// same name.
438    ///
439    /// If a corresponding [`End`] event is not found, an error of type
440    /// [`Error::IllFormed`] will be returned.
441    ///
442    /// The `end` parameter should contain name of the end element _in the reader
443    /// encoding_. It is good practice to always get that parameter using
444    /// [`BytesStart::to_end()`] method.
445    ///
446    /// [`Start`]: Event::Start
447    /// [`End`]: Event::End
448    /// [`BytesStart::to_end()`]: crate::events::BytesStart::to_end
449    /// [`Error::IllFormed`]: crate::errors::Error::IllFormed
450    /// [`expand_empty_elements`]: crate::reader::Config::expand_empty_elements
451    pub fn read_to_end_into(&mut self, end: QName, buf: &mut Vec<u8>) -> Result<Span> {
452        Ok(read_to_end!(self, end, buf, read_event_impl, {
453            buf.clear();
454        }))
455    }
456
457    /// Reads content between start and end tags, including any markup, using the
458    /// provided buffer as intermediate storage for events content. This function
459    /// is supposed to be called after you already read a [`Start`] event.
460    ///
461    /// Manages nested cases where parent and child elements have the _literally_
462    /// same name.
463    ///
464    /// This method does not unescape read data, instead it returns content
465    /// "as is" of the XML document.
466    ///
467    /// [`Start`]: Event::Start
468    pub fn read_text_into<'b>(
469        &mut self,
470        end: QName,
471        buf: &'b mut Vec<u8>,
472    ) -> Result<BytesText<'b>> {
473        let start = buf.len();
474        let span = read_to_end!(self, end, buf, read_event_impl, {});
475
476        let len = span.end - span.start;
477        // `buf` may contain not more than isize::MAX bytes and because it is
478        // not cleared when reading event, length of the returned span should fit into
479        // usize (because otherwise we panic at appending to the buffer before that point)
480        let end = start + len as usize;
481
482        Ok(BytesText::wrap(&buf[start..end], self.decoder()))
483    }
484}
485
486impl Reader<BufReader<File>> {
487    /// Creates an XML reader from a file path.
488    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
489        let file = File::open(path)?;
490        let reader = BufReader::new(file);
491        Ok(Self::from_reader(reader))
492    }
493}
494
495#[cfg(test)]
496mod test {
497    use crate::reader::XmlSource;
498    use crate::reader::check_tests::check;
499
500    /// Default buffer constructor just pass the byte array from the test
501    fn identity<T>(input: T) -> T {
502        input
503    }
504
505    check!(
506        #[test]
507        read_event_impl,
508        identity,
509        1,
510        &mut Vec::new()
511    );
512}