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}