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}