Skip to main content

oxixml_xml/reader/
mod.rs

1//! High-level interface for the pull-based XML parser.
2//!
3//! The central type is [`Reader`], a low-level, encoding-agnostic, pull-based
4//! XML event reader that mirrors the quick-xml 0.41 `Reader`. It consumes bytes
5//! and streams XML [`Event`]s, borrowing their payload
6//! either from the input itself (for the `&[u8]`/`&str` sources, giving a
7//! zero-copy path) or from a caller-provided `Vec<u8>` buffer (for arbitrary
8//! [`BufRead`](std::io::BufRead) sources).
9//!
10//! Reading is configured through [`Config`], retrieved with [`Reader::config()`]
11//! and mutated through [`Reader::config_mut()`].
12//!
13//! This reader does not manage namespace declarations and is not able to resolve
14//! prefixes. For namespace-aware reading, use [`NsReader`], which wraps this
15//! [`Reader`] and resolves prefixes to namespaces.
16//!
17//! # Example
18//!
19//! ```
20//! use oxixml_xml::events::Event;
21//! use oxixml_xml::reader::Reader;
22//!
23//! let mut reader = Reader::from_str("<tag>text</tag>");
24//! reader.config_mut().trim_text(true);
25//!
26//! assert!(matches!(reader.read_event().expect("start"), Event::Start(_)));
27//! assert!(matches!(reader.read_event().expect("text"), Event::Text(_)));
28//! assert!(matches!(reader.read_event().expect("end"), Event::End(_)));
29//! assert!(matches!(reader.read_event().expect("eof"), Event::Eof));
30//! ```
31
32use std::io;
33use std::ops::Range;
34
35use crate::encoding::Decoder;
36#[cfg(feature = "encoding")]
37use crate::encoding::DetectedEncoding;
38use crate::errors::{Error, IllFormedError, SyntaxError};
39use crate::events::{BytesRef, Event};
40use crate::parser::{DtdParser, ElementParser, Parser, PiParser};
41use crate::reader::state::ReaderState;
42#[cfg(feature = "encoding")]
43use oxixml_encoding::Encoding;
44
45/// A parser configuration.
46///
47/// The current configuration can be retrieved with [`Reader::config()`] and
48/// changed through [`Reader::config_mut()`].
49#[derive(Debug, Clone, PartialEq, Eq)]
50#[cfg_attr(feature = "serde-types", derive(serde::Deserialize, serde::Serialize))]
51#[non_exhaustive]
52pub struct Config {
53    /// Whether a lone ampersand (without a paired semicolon) is allowed in text
54    /// content. Unless enabled, a dangling ampersand yields
55    /// [`Error::IllFormed(UnclosedReference)`].
56    ///
57    /// Default: `false`.
58    ///
59    /// [`Error::IllFormed(UnclosedReference)`]: crate::errors::IllFormedError::UnclosedReference
60    pub allow_dangling_amp: bool,
61
62    /// Whether unmatched closing tag names are allowed. Unless enabled, a dangling
63    /// end tag yields [`Error::IllFormed(UnmatchedEndTag)`].
64    ///
65    /// When set to `true`, it won't check if a closing tag has a corresponding
66    /// opening tag at all. For example, `<a></a></b>` will be permitted.
67    ///
68    /// Default: `false`.
69    ///
70    /// [`Error::IllFormed(UnmatchedEndTag)`]: crate::errors::IllFormedError::UnmatchedEndTag
71    pub allow_unmatched_ends: bool,
72
73    /// Whether comments are validated for the forbidden `--` sequence. If enabled,
74    /// an invalid comment yields [`Error::IllFormed(DoubleHyphenInComment)`].
75    ///
76    /// Default: `false`.
77    ///
78    /// [`Error::IllFormed(DoubleHyphenInComment)`]: crate::errors::IllFormedError::DoubleHyphenInComment
79    pub check_comments: bool,
80
81    /// Whether mismatched closing tag names are detected. If enabled, a mismatch
82    /// yields [`Error::IllFormed(MismatchedEndTag)`].
83    ///
84    /// When set to `false`, it won't check if a closing tag matches the
85    /// corresponding opening tag. For example, `<mytag></different_tag>` will be
86    /// permitted.
87    ///
88    /// Default: `true`.
89    ///
90    /// [`Error::IllFormed(MismatchedEndTag)`]: crate::errors::IllFormedError::MismatchedEndTag
91    pub check_end_names: bool,
92
93    /// Whether the full set of XML [well-formedness constraints] is enforced.
94    ///
95    /// The default parsing path of this crate is deliberately as permissive as
96    /// `quick-xml` 0.41, which only detects a handful of problems. Setting this
97    /// flag turns the reader into a conforming **non-validating** XML processor:
98    /// the `Char` and `Name` productions, the document structure of section 2.1,
99    /// the grammar of the XML declaration and of the (internal) document type
100    /// declaration, and the entity-related constraints are all enforced, and
101    /// violations are reported as [`Error::NotWellFormed`].
102    ///
103    /// *Validity* constraints are never enforced — that would require reading the
104    /// external DTD subset, which this crate does not do.
105    ///
106    /// This check assumes that no white space is trimmed away, so it should not
107    /// be combined with [`trim_text_start`](Self::trim_text_start).
108    ///
109    /// Default: `false`.
110    ///
111    /// [well-formedness constraints]: https://www.w3.org/TR/xml/#dt-wellformed
112    /// [`Error::NotWellFormed`]: crate::errors::Error::NotWellFormed
113    pub check_well_formedness: bool,
114
115    /// Whether empty elements are split into a [`Start`] and an [`End`] event.
116    ///
117    /// When `true`, every [`Empty`] event from a self-closing tag like `<tag/>` is
118    /// expanded into a [`Start`] followed by an [`End`].
119    ///
120    /// Default: `false`.
121    ///
122    /// [`Empty`]: crate::events::Event::Empty
123    /// [`Start`]: crate::events::Event::Start
124    /// [`End`]: crate::events::Event::End
125    pub expand_empty_elements: bool,
126
127    /// Whether trailing white space after the markup name is trimmed in closing
128    /// tags (`</a >`).
129    ///
130    /// Default: `true`.
131    pub trim_markup_names_in_closing_tags: bool,
132
133    /// Whether white space before character data is removed. If, after trimming, a
134    /// [`Text`](crate::events::Event::Text) event is empty it is not emitted.
135    ///
136    /// Default: `false`.
137    pub trim_text_start: bool,
138
139    /// Whether white space after character data is removed. If, after trimming, a
140    /// [`Text`](crate::events::Event::Text) event is empty it is not emitted.
141    ///
142    /// Default: `false`.
143    pub trim_text_end: bool,
144
145    /// Maximum number of open (unclosed) elements a
146    /// [`NsReader`] will accept while nesting namespace
147    /// scopes, before returning
148    /// [`Error::Namespace(NamespaceError::TooDeeplyNested)`](crate::errors::Error::Namespace)
149    /// instead of pushing another scope.
150    ///
151    /// This guards [`NamespaceResolver`](crate::name::NamespaceResolver)'s
152    /// scope stack against unbounded growth from pathologically (or
153    /// maliciously) deep documents. Only [`NsReader`]
154    /// enforces it; the namespace-agnostic [`Reader`] does not track nesting
155    /// depth and is unaffected by this setting.
156    ///
157    /// Default: [`DEFAULT_MAX_ELEMENT_DEPTH`]. Raise it if you need to read
158    /// documents nested deeper than that (there is no arithmetic-overflow
159    /// reason to keep it low — the underlying counter is a `usize` — only a
160    /// resource-usage one), or set it to `usize::MAX` to disable the check
161    /// entirely.
162    pub max_element_depth: usize,
163}
164
165/// Default value of [`Config::max_element_depth`].
166pub const DEFAULT_MAX_ELEMENT_DEPTH: usize = 1024;
167
168impl Config {
169    /// Sets both [`trim_text_start`](Self::trim_text_start) and
170    /// [`trim_text_end`](Self::trim_text_end) to the same value.
171    #[inline]
172    pub fn trim_text(&mut self, trim: bool) {
173        self.trim_text_start = trim;
174        self.trim_text_end = trim;
175    }
176
177    /// Turns on or off all well-formedness checks, namely
178    /// [`check_comments`](Self::check_comments),
179    /// [`check_end_names`](Self::check_end_names) and
180    /// [`check_well_formedness`](Self::check_well_formedness).
181    #[inline]
182    pub fn enable_all_checks(&mut self, enable: bool) {
183        self.check_comments = enable;
184        self.check_end_names = enable;
185        self.check_well_formedness = enable;
186    }
187}
188
189impl Default for Config {
190    fn default() -> Self {
191        Self {
192            allow_dangling_amp: false,
193            allow_unmatched_ends: false,
194            check_comments: false,
195            check_end_names: true,
196            check_well_formedness: false,
197            expand_empty_elements: false,
198            trim_markup_names_in_closing_tags: true,
199            trim_text_start: false,
200            trim_text_end: false,
201            max_element_depth: DEFAULT_MAX_ELEMENT_DEPTH,
202        }
203    }
204}
205
206////////////////////////////////////////////////////////////////////////////////////////////////////
207
208macro_rules! read_event_impl {
209    (
210        $self:ident, $buf:ident,
211        $reader:expr,
212        $read_until_close:ident
213        $(, $await:ident)?
214    ) => {{
215        let event = loop {
216            break match $self.state.state {
217                ParseState::Init => {
218                    // If encoding set explicitly, we not need to detect it. For example,
219                    // explicit UTF-8 set automatically if Reader was created using `from_str`.
220                    // But we still need to remove BOM for consistency with no encoding
221                    // feature enabled path
222                    #[cfg(feature = "encoding")]
223                    if let Some(encoding) = $reader.detect_encoding() $(.$await)? ? {
224                        if $self.state.encoding.can_be_refined() {
225                            $self.state.encoding =
226                                crate::reader::EncodingRef::BomDetected(encoding.encoding());
227                        }
228                    }
229
230                    // Removes UTF-8 BOM if it is present
231                    #[cfg(not(feature = "encoding"))]
232                    $reader.remove_utf8_bom() $(.$await)? ?;
233
234                    $self.state.state = ParseState::InsideText;
235                    continue;
236                }
237                ParseState::InsideRef => {
238                    let start = $self.state.offset;
239                    match $reader.read_ref($buf, &mut $self.state.offset) $(.$await)? {
240                        // Emit reference, go to InsideText state
241                        ReadRefResult::Ref(bytes) => {
242                            $self.state.state = ParseState::InsideText;
243                            // +1 to skip start `&`
244                            // -1 to skip end `;`
245                            Ok(Event::GeneralRef(BytesRef::wrap(
246                                &bytes[1..bytes.len() - 1],
247                                $self.decoder(),
248                            )))
249                        }
250                        // Go to Done state
251                        ReadRefResult::UpToEof(bytes) if $self.state.config.allow_dangling_amp => {
252                            $self.state.state = ParseState::Done;
253                            Ok(Event::Text($self.state.emit_text(bytes)))
254                        }
255                        ReadRefResult::UpToEof(_) => {
256                            $self.state.state = ParseState::Done;
257                            $self.state.last_error_offset = start;
258                            Err(Error::IllFormed(IllFormedError::UnclosedReference))
259                        }
260                        // Do not change state, stay in InsideRef
261                        ReadRefResult::UpToRef(bytes) if $self.state.config.allow_dangling_amp => {
262                            Ok(Event::Text($self.state.emit_text(bytes)))
263                        }
264                        ReadRefResult::UpToRef(_) => {
265                            $self.state.last_error_offset = start;
266                            Err(Error::IllFormed(IllFormedError::UnclosedReference))
267                        }
268                        // Go to InsideMarkup state
269                        ReadRefResult::UpToMarkup(bytes)
270                            if $self.state.config.allow_dangling_amp =>
271                        {
272                            $self.state.state = ParseState::InsideMarkup;
273                            Ok(Event::Text($self.state.emit_text(bytes)))
274                        }
275                        ReadRefResult::UpToMarkup(_) => {
276                            $self.state.state = ParseState::InsideMarkup;
277                            $self.state.last_error_offset = start;
278                            Err(Error::IllFormed(IllFormedError::UnclosedReference))
279                        }
280                        ReadRefResult::Err(e) => Err(Error::from(e)),
281                    }
282                }
283                ParseState::InsideText => {
284                    if $self.state.config.trim_text_start {
285                        $reader.skip_whitespace(&mut $self.state.offset) $(.$await)? ?;
286                    }
287
288                    match $reader.read_text(
289                        $buf,
290                        &mut $self.state.offset,
291                        $self.state.config.trim_text_end,
292                    ) $(.$await)? {
293                        ReadTextResult::Markup(buf) => {
294                            $self.state.state = ParseState::InsideMarkup;
295                            // Pass `buf` to the next iteration of parsing loop
296                            $buf = buf;
297                            continue;
298                        }
299                        ReadTextResult::Ref(buf) => {
300                            $self.state.state = ParseState::InsideRef;
301                            // Pass `buf` to the next iteration of parsing loop
302                            $buf = buf;
303                            continue;
304                        }
305                        ReadTextResult::UpToMarkup(bytes) => {
306                            $self.state.state = ParseState::InsideMarkup;
307                            // An all-white-space run before markup cannot reach
308                            // this arm while `trim_text_end` is set: `read_text`
309                            // reports it as `Markup` instead, so no empty `Text`
310                            // event is ever produced here — as
311                            // `Config::trim_text_end` documents, and unlike
312                            // quick-xml 0.41, which leaves this case open.
313                            debug_assert!(
314                                !$self.state.config.trim_text_end
315                                    || !bytes.iter().all(|b| crate::utils::is_whitespace(*b)),
316                                "an all-white-space run must be reported as `Markup`",
317                            );
318                            Ok(Event::Text($self.state.emit_text(bytes)))
319                        }
320                        ReadTextResult::UpToRef(bytes) => {
321                            $self.state.state = ParseState::InsideRef;
322                            // As for `UpToMarkup`, an all-white-space run cannot
323                            // reach this arm while `trim_text_end` is set, so the
324                            // emitted event is never empty.
325                            debug_assert!(
326                                !$self.state.config.trim_text_end
327                                    || !bytes.iter().all(|b| crate::utils::is_whitespace(*b)),
328                                "an all-white-space run must be reported as `Ref`",
329                            );
330                            Ok(Event::Text($self.state.emit_text(bytes)))
331                        }
332                        ReadTextResult::UpToEof(bytes) => {
333                            $self.state.state = ParseState::Done;
334                            // Trim bytes from end if required
335                            let event = $self.state.emit_text(bytes);
336                            if event.is_empty() {
337                                Ok(Event::Eof)
338                            } else {
339                                Ok(Event::Text(event))
340                            }
341                        }
342                        ReadTextResult::Err(e) => Err(Error::from(e)),
343                    }
344                }
345                // Go to InsideText state in next two arms
346                ParseState::InsideMarkup => $self.$read_until_close($buf) $(.$await)?,
347                ParseState::InsideEmpty => Ok(Event::End($self.state.close_expanded_empty())),
348                ParseState::Done => Ok(Event::Eof),
349            };
350        };
351        let event = match event {
352            Ok(ev) if $self.state.config.check_well_formedness => {
353                match $self.state.check_well_formedness(&ev) {
354                    Ok(()) => Ok(ev),
355                    Err(err) => Err(err),
356                }
357            }
358            other => other,
359        };
360        match event {
361            // #513: In case of ill-formed errors we already consume the wrong data
362            // and change the state. We can continue parsing if we wish
363            Err(Error::IllFormed(_)) => {}
364            Err(_) | Ok(Event::Eof) => $self.state.state = ParseState::Done,
365            _ => {}
366        }
367        event
368    }};
369}
370
371/// Read bytes up to the `>` and skip it. This macro is expected to be used after
372/// seeing the `<` symbol and skipping it. It inspects the next (current) symbol
373/// and returns an appropriate [`Event`]:
374///
375/// |Symbol |Event
376/// |-------|-------------------------------------
377/// |`!`    |`Comment`, `CData` or `DocType`
378/// |`/`    |`End`
379/// |`?`    |`PI`
380/// |_other_|`Start` or `Empty`
381///
382/// Moves parser to the `InsideText` state.
383macro_rules! read_until_close {
384    (
385        $self:ident, $buf:ident,
386        $reader:expr
387        $(, $await:ident)?
388    ) => {{
389        $self.state.state = ParseState::InsideText;
390
391        let start = $self.state.offset;
392        match $reader.peek_one() $(.$await)? {
393            // `<!` - comment, CDATA or DOCTYPE declaration
394            Ok(Some(b'!')) => {
395                match $reader.read_bang_element($buf, &mut $self.state.offset) $(.$await)? {
396                    Ok((bang_type, bytes)) => $self.state.emit_bang(bang_type, bytes),
397                    Err(e) => {
398                        // We want to report error at `<`
399                        $self.state.last_error_offset = start;
400                        Err(e)
401                    }
402                }
403            }
404            // `</` - closing tag
405            // #776: We parse using ElementParser which allows us to have attributes
406            // in close tags. While such tags are not allowed by the specification,
407            // we anyway allow to parse them because:
408            // - we do not check constraints during parsing. This is performed by the
409            //   optional validate step which user should call manually
410            // - if we just look for `>` we will parse `</tag attr=">" >` as end tag
411            //   `</tag attr=">` and text `" >` which probably no one existing parser
412            //   does. This is malformed XML, however it is tolerated by some parsers
413            //   (e.g. the one used by Adobe Flash) and such documents do exist in the wild.
414            Ok(Some(b'/')) => {
415                match $reader.read_with(ElementParser::Outside, $buf, &mut $self.state.offset) $(.$await)? {
416                    Ok(bytes) => $self.state.emit_end(bytes),
417                    Err(e) => {
418                        // We want to report error at `<`
419                        $self.state.last_error_offset = start;
420                        Err(e)
421                    }
422                }
423            }
424            // `<?` - processing instruction
425            Ok(Some(b'?')) => {
426                match $reader.read_with(PiParser(false), $buf, &mut $self.state.offset) $(.$await)? {
427                    Ok(bytes) => $self.state.emit_question_mark(bytes),
428                    Err(e) => {
429                        // We want to report error at `<`
430                        $self.state.last_error_offset = start;
431                        Err(e)
432                    }
433                }
434            }
435            // `<...` - opening or self-closed tag
436            Ok(Some(_)) => {
437                match $reader.read_with(ElementParser::Outside, $buf, &mut $self.state.offset) $(.$await)? {
438                    Ok(bytes) => Ok($self.state.emit_start(bytes)),
439                    Err(e) => {
440                        // We want to report error at `<`
441                        $self.state.last_error_offset = start;
442                        Err(e)
443                    }
444                }
445            }
446            // `<` - syntax error, tag not closed
447            Ok(None) => {
448                // We want to report error at `<`
449                $self.state.last_error_offset = start;
450                Err(Error::Syntax(SyntaxError::UnclosedTag))
451            }
452            Err(e) => Err(Error::from(e)),
453        }
454    }};
455}
456
457/// Generalization of `read_to_end` method for buffered and borrowed readers
458macro_rules! read_to_end {
459    (
460        // $self: &mut Reader
461        $self:expr, $end:expr, $buf:expr,
462        $read_event:ident,
463        // Code block that performs clearing of internal buffer after read of each event
464        $clear:block
465        $(, $await:ident)?
466    ) => {{
467        // Because we take position after the event before the End event,
468        // it is important that this position indicates beginning of the End event.
469        // If between last event and the End event would be only spaces, then we
470        // take position before the spaces, but spaces would be skipped without
471        // generating event if `trim_text_start` is set to `true`. To prevent that
472        // we temporary disable start text trimming.
473        //
474        // We also cannot take position after getting End event, because if
475        // `trim_markup_names_in_closing_tags` is set to `true` (which is the default),
476        // we do not known the real size of the End event that it is occupies in
477        // the source and cannot correct the position after the End event.
478        // So, we in any case should tweak parser configuration.
479        let config = $self.config_mut();
480        let trim = config.trim_text_start;
481        config.trim_text_start = false;
482
483        let start = $self.buffer_position();
484        let mut depth = 0;
485        loop {
486            $clear
487            let end = $self.buffer_position();
488            match $self.$read_event($buf) $(.$await)? {
489                Err(e) => {
490                    $self.config_mut().trim_text_start = trim;
491                    return Err(e);
492                }
493
494                Ok(Event::Start(e)) if e.name() == $end => depth += 1,
495                Ok(Event::End(e)) if e.name() == $end => {
496                    if depth == 0 {
497                        $self.config_mut().trim_text_start = trim;
498                        break start..end;
499                    }
500                    depth -= 1;
501                }
502                Ok(Event::Eof) => {
503                    $self.config_mut().trim_text_start = trim;
504                    return Err(Error::missed_end($end, $self.decoder()));
505                }
506                _ => (),
507            }
508        }
509    }};
510}
511
512#[cfg(feature = "async-tokio")]
513mod async_tokio;
514mod buffered_reader;
515#[cfg(test)]
516mod check_tests;
517mod ns_reader;
518mod slice_reader;
519mod state;
520
521pub use ns_reader::NsReader;
522
523/// Range of input in bytes, that corresponds to some piece of XML
524pub type Span = Range<u64>;
525
526////////////////////////////////////////////////////////////////////////////////////////////////////
527
528/// Possible reader states. The state transition diagram (`true` and `false` shows
529/// value of [`Config::expand_empty_elements`] option):
530///
531/// ```mermaid
532/// flowchart LR
533///   subgraph _
534///     direction LR
535///
536///     Init         -- "(no event)"\n                                       --> InsideMarkup
537///     InsideMarkup -- Decl, DocType, PI\nComment, CData\nStart, Empty, End --> InsideText
538///     InsideText   -- "#lt;false#gt;\n(no event)"\nText                    --> InsideMarkup
539///     InsideRef    -- "(no event)"\nGeneralRef                             --> InsideText
540///   end
541///   InsideText     -- "#lt;true#gt;"\nStart --> InsideEmpty
542///   InsideEmpty    -- End                   --> InsideText
543///   _ -. Eof .-> Done
544/// ```
545#[derive(Clone, Debug)]
546pub(crate) enum ParseState {
547    /// Initial state in which reader stay after creation. Transition from that
548    /// state could produce a `Text`, `Decl`, `Comment` or `Start` event. The next
549    /// state is always `InsideText`. The reader will never return to this state.
550    Init,
551    /// State after seeing the `&` symbol in textual content. Depending on the next
552    /// symbol all other events could be generated.
553    ///
554    /// After generating one event the reader moves to the `InsideText` state.
555    InsideRef,
556    /// State after seeing the `<` symbol. Depending on the next symbol all other
557    /// events could be generated.
558    ///
559    /// After generating one event the reader moves to the `InsideText` state.
560    InsideMarkup,
561    /// State in which reader searches the `<` symbol of a markup. All bytes before
562    /// that symbol will be returned in the [`Event::Text`]
563    /// event. After that the reader moves to the `InsideMarkup` state.
564    InsideText,
565    /// This state is used only if option [`expand_empty_elements`] is set to `true`.
566    /// Reader enters to this state when it is in a `InsideText` state and emits an
567    /// [`Event::Start`](crate::events::Event::Start) event. The next event emitted
568    /// will be an [`Event::End`](crate::events::Event::End), after which reader
569    /// returned to the `InsideText` state.
570    ///
571    /// [`expand_empty_elements`]: Config::expand_empty_elements
572    InsideEmpty,
573    /// Reader enters this state when `Eof` event generated or an error occurred.
574    /// This is the last state, the reader stay in it forever.
575    Done,
576}
577
578/// A reference to an encoding together with information about how it was retrieved.
579///
580/// The state transition diagram:
581///
582/// ```mermaid
583/// flowchart LR
584///   Implicit    -- from_str       --> Explicit
585///   Implicit    -- BOM            --> BomDetected
586///   Implicit    -- "encoding=..." --> XmlDetected
587///   BomDetected -- "encoding=..." --> XmlDetected
588/// ```
589#[cfg(feature = "encoding")]
590#[derive(Clone, Copy, Debug)]
591pub(crate) enum EncodingRef {
592    /// Encoding was implicitly assumed to have a specified value. It can be refined
593    /// using BOM or by the XML declaration event (`<?xml encoding=... ?>`)
594    Implicit(Encoding),
595    /// Encoding was explicitly set to the desired value. It cannot be changed
596    /// nor by BOM, nor by parsing XML declaration (`<?xml encoding=... ?>`)
597    Explicit(Encoding),
598    /// Encoding was detected from a byte order mark (BOM) or by the first bytes
599    /// of the content. It can be refined by the XML declaration event
600    /// (`<?xml encoding=... ?>`)
601    BomDetected(Encoding),
602    /// Encoding was detected using XML declaration event (`<?xml encoding=... ?>`).
603    /// It can no longer change.
604    XmlDetected(Encoding),
605}
606
607#[cfg(feature = "encoding")]
608impl EncodingRef {
609    #[inline]
610    const fn encoding(&self) -> Encoding {
611        match self {
612            Self::Implicit(e) | Self::Explicit(e) | Self::BomDetected(e) | Self::XmlDetected(e) => {
613                *e
614            }
615        }
616    }
617    #[inline]
618    const fn can_be_refined(&self) -> bool {
619        match self {
620            Self::Implicit(_) | Self::BomDetected(_) => true,
621            Self::Explicit(_) | Self::XmlDetected(_) => false,
622        }
623    }
624}
625
626////////////////////////////////////////////////////////////////////////////////////////////////////
627
628/// A direct stream to the underlying [`Reader`]s reader which updates
629/// [`Reader::buffer_position()`] when read from it.
630#[derive(Debug)]
631#[must_use = "streams do nothing unless read or polled"]
632pub struct BinaryStream<'r, R> {
633    inner: &'r mut R,
634    offset: &'r mut u64,
635}
636
637impl<'r, R> BinaryStream<'r, R> {
638    /// Returns current position in bytes in the original source.
639    #[inline]
640    pub const fn offset(&self) -> u64 {
641        *self.offset
642    }
643
644    /// Gets a reference to the underlying reader.
645    #[inline]
646    pub const fn get_ref(&self) -> &R {
647        self.inner
648    }
649
650    /// Gets a mutable reference to the underlying reader.
651    ///
652    /// Avoid read from this reader because this will not update reader's position
653    /// and will lead to incorrect positions of errors. Read from this stream instead.
654    #[inline]
655    pub fn get_mut(&mut self) -> &mut R {
656        self.inner
657    }
658}
659
660impl<R> io::Read for BinaryStream<'_, R>
661where
662    R: io::Read,
663{
664    #[inline]
665    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
666        let amt = self.inner.read(buf)?;
667        *self.offset += amt as u64;
668        Ok(amt)
669    }
670}
671
672impl<R> io::BufRead for BinaryStream<'_, R>
673where
674    R: io::BufRead,
675{
676    #[inline]
677    fn fill_buf(&mut self) -> io::Result<&[u8]> {
678        self.inner.fill_buf()
679    }
680
681    #[inline]
682    fn consume(&mut self, amt: usize) {
683        self.inner.consume(amt);
684        *self.offset += amt as u64;
685    }
686}
687
688////////////////////////////////////////////////////////////////////////////////////////////////////
689
690/// A low level encoding-agnostic XML event reader.
691///
692/// Consumes bytes and streams XML [`Event`]s.
693///
694/// This reader does not manage namespace declarations and is not able to resolve
695/// prefixes. For namespace-aware reading, use [`NsReader`].
696///
697/// # Examples
698///
699/// ```
700/// use oxixml_xml::events::Event;
701/// use oxixml_xml::reader::Reader;
702///
703/// let xml = r#"<tag1 att1 = "test">
704///                 <tag2><!--Test comment-->Test</tag2>
705///                 <tag2>Test 2</tag2>
706///              </tag1>"#;
707/// let mut reader = Reader::from_str(xml);
708/// reader.config_mut().trim_text(true);
709///
710/// let mut count = 0;
711/// let mut txt = Vec::new();
712/// let mut buf = Vec::new();
713///
714/// // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s)
715/// loop {
716///     // NOTE: this is the generic case when we don't know about the input BufRead.
717///     // when the input is a &str or a &[u8], we don't actually need to use another
718///     // buffer, we could directly call `reader.read_event()`
719///     match reader.read_event_into(&mut buf) {
720///         Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e),
721///         // exits the loop when reaching end of file
722///         Ok(Event::Eof) => break,
723///
724///         Ok(Event::Start(e)) => {
725///             match e.name().as_ref() {
726///                 b"tag2" => count += 1,
727///                 _ => (),
728///             }
729///         }
730///         Ok(Event::Text(e)) => txt.push(e.decode().expect("utf-8").into_owned()),
731///
732///         // There are several other `Event`s we do not consider here
733///         _ => (),
734///     }
735///     // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low
736///     buf.clear();
737/// }
738/// assert_eq!(count, 2);
739/// ```
740#[derive(Debug, Clone)]
741pub struct Reader<R> {
742    /// Source of data for parse
743    reader: R,
744    /// Configuration and current parse state
745    state: ReaderState,
746}
747
748/// Builder methods
749impl<R> Reader<R> {
750    /// Creates a `Reader` that reads from a given reader.
751    pub fn from_reader(reader: R) -> Self {
752        Self {
753            reader,
754            state: ReaderState::default(),
755        }
756    }
757
758    /// Returns reference to the parser configuration
759    pub const fn config(&self) -> &Config {
760        &self.state.config
761    }
762
763    /// Returns mutable reference to the parser configuration
764    pub fn config_mut(&mut self) -> &mut Config {
765        &mut self.state.config
766    }
767
768    /// Returns the replacement text of an internal general entity declared by the
769    /// document type declaration of the document being read. Only populated while
770    /// [`Config::check_well_formedness`] is enabled.
771    #[inline]
772    pub(super) fn declared_entity(&self, name: &str) -> Option<&str> {
773        self.state.declared_entity(name)
774    }
775
776    /// Tells the optional well-formedness checker that this reader is driven by
777    /// an [`NsReader`], so that the extra name constraints of Namespaces in XML
778    /// apply.
779    #[inline]
780    pub(super) fn enable_namespace_constraints(&mut self) {
781        self.state.enable_namespace_constraints();
782    }
783}
784
785/// Getters
786impl<R> Reader<R> {
787    /// Consumes `Reader` returning the underlying reader.
788    ///
789    /// Can be used to compute line and column of a parsing error position.
790    pub fn into_inner(self) -> R {
791        self.reader
792    }
793
794    /// Gets a reference to the underlying reader.
795    pub const fn get_ref(&self) -> &R {
796        &self.reader
797    }
798
799    /// Gets a mutable reference to the underlying reader.
800    ///
801    /// Avoid read from this reader because this will not update reader's position
802    /// and will lead to incorrect positions of errors. If you want to read, use
803    /// [`stream()`] instead.
804    ///
805    /// [`stream()`]: Self::stream
806    pub fn get_mut(&mut self) -> &mut R {
807        &mut self.reader
808    }
809
810    /// Gets the byte position in the input data just after the last emitted event
811    /// (i.e. this is position where data of last event ends).
812    ///
813    /// Note, that for text events which is originally ended with whitespace characters
814    /// (` `, `\t`, `\r`, and `\n`) if [`Config::trim_text_end`] is set this is position
815    /// before trim, not the position of the last byte of the
816    /// [`Event::Text`] content.
817    pub const fn buffer_position(&self) -> u64 {
818        self.state.offset
819    }
820
821    /// Gets the last error byte position in the input data. If there is no errors
822    /// yet, returns `0`.
823    ///
824    /// Unlike `buffer_position` it will point to the place where it is rational
825    /// to report error to the end user. For example, all
826    /// [`SyntaxError`]s are reported when the parser
827    /// sees EOF inside of some kind of markup. The `buffer_position()` will point
828    /// to the last byte of input which is not very useful. `error_position()` will
829    /// point to the start of corresponding markup element (i. e. to the `<` character).
830    ///
831    /// This position is always `<= buffer_position()`.
832    pub const fn error_position(&self) -> u64 {
833        self.state.last_error_offset
834    }
835
836    /// Get the decoder, used to decode bytes, read by this reader, to the strings.
837    ///
838    /// If [`encoding`] feature is enabled, the used encoding may change after
839    /// parsing the XML declaration, otherwise encoding is fixed to UTF-8.
840    ///
841    /// If [`encoding`] feature is enabled and no encoding is specified in declaration,
842    /// defaults to UTF-8.
843    ///
844    /// [`encoding`]: ../index.html#encoding
845    #[inline]
846    pub const fn decoder(&self) -> Decoder {
847        self.state.decoder()
848    }
849
850    /// Get the direct access to the underlying reader, but tracks the amount of
851    /// read data and update [`Reader::buffer_position()`] accordingly.
852    ///
853    /// Note, that this method gives you access to the internal reader and read
854    /// data will not be returned in any subsequent events read by `read_event`
855    /// family of methods.
856    ///
857    /// # Example
858    ///
859    /// This example demonstrates how to read stream raw bytes from an XML document.
860    ///
861    /// ```
862    /// use std::io::Read;
863    /// use oxixml_xml::events::{BytesEnd, BytesStart, Event};
864    /// use oxixml_xml::reader::Reader;
865    ///
866    /// let mut reader = Reader::from_str("<tag>binary << data&></tag>");
867    /// //                                 ^    ^               ^     ^
868    /// //                                 0    5              21    27
869    ///
870    /// assert_eq!(
871    ///     (reader.read_event().expect("start"), reader.buffer_position()),
872    ///     (Event::Start(BytesStart::new("tag")), 5)
873    /// );
874    ///
875    /// // Reading from the stream() advances position
876    /// let mut inner = reader.stream();
877    ///
878    /// // Read binary data. We must know its size
879    /// let mut binary = [0u8; 16];
880    /// inner.read_exact(&mut binary).expect("16 bytes");
881    /// assert_eq!(&binary, b"binary << data&>");
882    /// assert_eq!(inner.offset(), 21);
883    /// assert_eq!(reader.buffer_position(), 21);
884    ///
885    /// assert_eq!(
886    ///     (reader.read_event().expect("end"), reader.buffer_position()),
887    ///     (Event::End(BytesEnd::new("tag")), 27)
888    /// );
889    ///
890    /// assert_eq!(reader.read_event().expect("eof"), Event::Eof);
891    /// ```
892    #[inline]
893    pub fn stream(&mut self) -> BinaryStream<'_, R> {
894        BinaryStream {
895            inner: &mut self.reader,
896            offset: &mut self.state.offset,
897        }
898    }
899}
900
901/// Private sync reading methods
902impl<R> Reader<R> {
903    /// Read text into the given buffer, and return an event that borrows from
904    /// either that buffer or from the input itself, based on the type of the
905    /// reader.
906    fn read_event_impl<'i, B>(&mut self, mut buf: B) -> Result<Event<'i>, Error>
907    where
908        R: XmlSource<'i, B>,
909    {
910        read_event_impl!(self, buf, self.reader, read_until_close)
911    }
912
913    /// Private function to read until `>` is found. This function expects that
914    /// it was called just after encounter a `<` symbol.
915    fn read_until_close<'i, B>(&mut self, buf: B) -> Result<Event<'i>, Error>
916    where
917        R: XmlSource<'i, B>,
918    {
919        read_until_close!(self, buf, self.reader)
920    }
921}
922
923////////////////////////////////////////////////////////////////////////////////////////////////////
924
925/// Result of an attempt to read XML textual data from the source.
926#[derive(Debug)]
927pub(crate) enum ReadTextResult<'r, B> {
928    /// Start of markup (`<` character) was found in the first byte. `<` was consumed.
929    /// Contains buffer that should be returned back to the next iteration cycle
930    /// to satisfy borrow checker requirements.
931    Markup(B),
932    /// Start of reference (`&` character) was found in the first byte.
933    /// `&` was not consumed.
934    /// Contains buffer that should be returned back to the next iteration cycle
935    /// to satisfy borrow checker requirements.
936    Ref(B),
937    /// Contains text block up to start of markup (`<` character). `<` was consumed.
938    UpToMarkup(&'r [u8]),
939    /// Contains text block up to start of reference (`&` character).
940    /// `&` was not consumed.
941    UpToRef(&'r [u8]),
942    /// Contains text block up to EOF, neither start of markup (`<` character)
943    /// or start of reference (`&` character) was found.
944    UpToEof(&'r [u8]),
945    /// IO error occurred.
946    Err(io::Error),
947}
948
949/// Result of an attempt to read general reference from the reader.
950#[derive(Debug)]
951pub(crate) enum ReadRefResult<'r> {
952    /// Contains text block up to end of reference (`;` character).
953    /// Result includes start `&`, but not end `;`.
954    Ref(&'r [u8]),
955    /// Contains text block up to EOF. Neither end of reference (`;`), start of
956    /// another reference (`&`) or start of markup (`<`) characters was found.
957    /// Result includes start `&`.
958    UpToEof(&'r [u8]),
959    /// Contains text block up to next possible reference (`&` character).
960    /// Result includes start `&`.
961    UpToRef(&'r [u8]),
962    /// Contains text block up to start of markup (`<` character).
963    /// Result includes start `&`.
964    UpToMarkup(&'r [u8]),
965    /// IO error occurred.
966    Err(io::Error),
967}
968
969/// Represents an input for a reader that can return borrowed data.
970///
971/// There are two implementors of this trait: a generic one that reads data from
972/// `Self`, copies some part of it into a provided buffer of type `B` and then
973/// returns data that borrows from that buffer.
974///
975/// The other implementor is for `&[u8]` and instead of copying data returns
976/// borrowed data from `Self` instead. This implementation allows zero-copy
977/// deserialization.
978///
979/// # Parameters
980/// - `'r`: lifetime of a buffer from which events will borrow
981/// - `B`: a type of a buffer that can be used to store data read from `Self` and
982///   from which events can borrow
983pub(crate) trait XmlSource<'r, B> {
984    /// Removes UTF-8 BOM if it is present
985    #[cfg(not(feature = "encoding"))]
986    fn remove_utf8_bom(&mut self) -> io::Result<()>;
987
988    /// Determines encoding from the start of input and removes BOM if it is present
989    #[cfg(feature = "encoding")]
990    fn detect_encoding(&mut self) -> io::Result<Option<DetectedEncoding>>;
991
992    /// Read input until start of markup (the `<`) is found, start of general entity
993    /// reference (the `&`) is found or end of input is reached.
994    ///
995    /// # Parameters
996    /// - `buf`: Buffer that could be filled from an input (`Self`) and
997    ///   from which events could borrow their data
998    /// - `position`: Will be increased by amount of bytes consumed
999    /// - `trim_whitespace_only`: When set, a text run that consists solely of
1000    ///   white space and is followed by markup is dropped and
1001    ///   [`ReadTextResult::Markup`] — respectively [`ReadTextResult::Ref`] —
1002    ///   is returned instead of an empty [`ReadTextResult::UpToMarkup`] or
1003    ///   [`ReadTextResult::UpToRef`]. Set from
1004    ///   [`Config::trim_text_end`](crate::reader::Config::trim_text_end), which
1005    ///   would trim such a run to nothing anyway.
1006    fn read_text(
1007        &mut self,
1008        buf: B,
1009        position: &mut u64,
1010        trim_whitespace_only: bool,
1011    ) -> ReadTextResult<'r, B>;
1012
1013    /// Read input until end of general reference (the `;`) is found, start of
1014    /// another general reference (the `&`) is found or end of input is reached.
1015    ///
1016    /// This method must be called when current character is `&`.
1017    ///
1018    /// # Parameters
1019    /// - `buf`: Buffer that could be filled from an input (`Self`) and
1020    ///   from which events could borrow their data
1021    /// - `position`: Will be increased by amount of bytes consumed
1022    fn read_ref(&mut self, buf: B, position: &mut u64) -> ReadRefResult<'r>;
1023
1024    /// Read input until processing instruction is finished.
1025    ///
1026    /// This method expect that start sequence of a parser already was read.
1027    ///
1028    /// Returns a slice of data read up to the end of the thing being parsed.
1029    /// The end of thing and the returned content is determined by the used parser.
1030    ///
1031    /// If input (`Self`) is exhausted and no bytes was read, or if the specified
1032    /// parser could not find the ending sequence of the thing, returns `SyntaxError`.
1033    ///
1034    /// # Parameters
1035    /// - `buf`: Buffer that could be filled from an input (`Self`) and
1036    ///   from which events could borrow their data
1037    /// - `position`: Will be increased by amount of bytes consumed
1038    ///
1039    /// A `P` type parameter is used to preserve state between calls to the underlying
1040    /// reader which provides bytes fed into the parser.
1041    fn read_with<P>(&mut self, parser: P, buf: B, position: &mut u64) -> Result<&'r [u8], Error>
1042    where
1043        P: Parser;
1044
1045    /// Read input until comment or CDATA is finished.
1046    ///
1047    /// This method expect that `<` already was read.
1048    ///
1049    /// Returns a slice of data read up to end of comment or CDATA (`>`),
1050    /// which does not include into result.
1051    ///
1052    /// If input (`Self`) is exhausted and nothing was read, returns `None`.
1053    ///
1054    /// # Parameters
1055    /// - `buf`: Buffer that could be filled from an input (`Self`) and
1056    ///   from which events could borrow their data
1057    /// - `position`: Will be increased by amount of bytes consumed
1058    fn read_bang_element(
1059        &mut self,
1060        buf: B,
1061        position: &mut u64,
1062    ) -> Result<(BangType, &'r [u8]), Error>;
1063
1064    /// Consume and discard all the whitespace until the next non-whitespace
1065    /// character or EOF.
1066    ///
1067    /// # Parameters
1068    /// - `position`: Will be increased by amount of bytes consumed
1069    fn skip_whitespace(&mut self, position: &mut u64) -> io::Result<()>;
1070
1071    /// Return one character without consuming it, so that future `read_*` calls
1072    /// will still include it. On EOF, return `None`.
1073    fn peek_one(&mut self) -> io::Result<Option<u8>>;
1074}
1075
1076/// Possible elements started with `<!`
1077#[derive(Debug, PartialEq)]
1078pub(crate) enum BangType {
1079    /// `<![CDATA[...]]>`
1080    CData,
1081    /// `<!--...-->`
1082    Comment,
1083    /// `<!DOCTYPE...>`.
1084    DocType(DtdParser),
1085}
1086impl BangType {
1087    #[inline(always)]
1088    const fn new(byte: Option<u8>) -> Result<Self, SyntaxError> {
1089        Ok(match byte {
1090            Some(b'[') => Self::CData,
1091            Some(b'-') => Self::Comment,
1092            Some(b'D') | Some(b'd') => Self::DocType(DtdParser::BeforeInternalSubset(0)),
1093            _ => return Err(SyntaxError::InvalidBangMarkup),
1094        })
1095    }
1096
1097    /// If element is finished, returns its content up to `>` symbol and
1098    /// an index of this symbol, otherwise returns `None`
1099    ///
1100    /// # Parameters
1101    /// - `buf`: buffer with data consumed on previous iterations
1102    /// - `chunk`: data read on current iteration and not yet consumed from reader
1103    #[inline(always)]
1104    fn feed(&mut self, buf: &[u8], chunk: &[u8]) -> Option<usize> {
1105        match self {
1106            Self::Comment => {
1107                for i in crate::utils::memchr_iter(b'>', chunk) {
1108                    // Need to read at least 6 symbols (`!---->`) for properly finished comment
1109                    // <!----> - XML comment
1110                    // 0123456 - i
1111                    if buf.len() + i > 5 {
1112                        if chunk[..i].ends_with(b"--") {
1113                            // We cannot strip last `--` from the buffer because we need it in case of
1114                            // check_comments enabled option. XML standard requires that comment
1115                            // will not end with `--->` sequence because this is a special case of
1116                            // `--` in the comment (https://www.w3.org/TR/xml11/#sec-comments)
1117                            return Some(i);
1118                        }
1119                        // End sequence `-|->` was splitted at |
1120                        //        buf --/   \-- chunk
1121                        if i == 1 && buf.ends_with(b"-") && chunk[0] == b'-' {
1122                            return Some(i);
1123                        }
1124                        // End sequence `--|>` was splitted at |
1125                        //         buf --/   \-- chunk
1126                        if i == 0 && buf.ends_with(b"--") {
1127                            return Some(i);
1128                        }
1129                    }
1130                }
1131            }
1132            Self::CData => {
1133                for i in crate::utils::memchr_iter(b'>', chunk) {
1134                    if chunk[..i].ends_with(b"]]") {
1135                        return Some(i);
1136                    }
1137                    // End sequence `]|]>` was splitted at |
1138                    //        buf --/   \-- chunk
1139                    if i == 1 && buf.ends_with(b"]") && chunk[0] == b']' {
1140                        return Some(i);
1141                    }
1142                    // End sequence `]]|>` was splitted at |
1143                    //         buf --/   \-- chunk
1144                    if i == 0 && buf.ends_with(b"]]") {
1145                        return Some(i);
1146                    }
1147                }
1148            }
1149            Self::DocType(parser) => return parser.feed(buf, chunk),
1150        }
1151        None
1152    }
1153    #[inline]
1154    const fn to_err(&self) -> SyntaxError {
1155        match self {
1156            Self::CData => SyntaxError::UnclosedCData,
1157            Self::Comment => SyntaxError::UnclosedComment,
1158            Self::DocType(_) => SyntaxError::UnclosedDoctype,
1159        }
1160    }
1161}
1162
1163#[cfg(test)]
1164mod tests {
1165    use super::*;
1166
1167    #[test]
1168    fn defaults_match_spec() {
1169        let config = Config::default();
1170        assert!(!config.allow_dangling_amp);
1171        assert!(!config.allow_unmatched_ends);
1172        assert!(!config.check_comments);
1173        assert!(config.check_end_names);
1174        assert!(!config.expand_empty_elements);
1175        assert!(config.trim_markup_names_in_closing_tags);
1176        assert!(!config.trim_text_start);
1177        assert!(!config.trim_text_end);
1178        assert_eq!(config.max_element_depth, DEFAULT_MAX_ELEMENT_DEPTH);
1179    }
1180
1181    #[test]
1182    fn trim_text_sets_both() {
1183        let mut config = Config::default();
1184        config.trim_text(true);
1185        assert!(config.trim_text_start);
1186        assert!(config.trim_text_end);
1187    }
1188
1189    #[test]
1190    fn enable_all_checks_toggles() {
1191        let mut config = Config::default();
1192        config.enable_all_checks(true);
1193        assert!(config.check_comments);
1194        assert!(config.check_end_names);
1195        config.enable_all_checks(false);
1196        assert!(!config.check_comments);
1197        assert!(!config.check_end_names);
1198    }
1199}