aster_forge_xml/
document.rs

1//! Source-backed, non-recursive XML document tree.
2
3use std::borrow::Cow;
4use std::io::{Read, Write};
5use std::num::NonZeroU32;
6use std::ops::Range;
7use std::sync::Arc;
8
9use aster_forge_utils::numbers::{u32_to_usize, u64_to_usize, usize_to_u32, usize_to_u64};
10use quick_xml::Reader;
11use quick_xml::XmlVersion;
12use quick_xml::escape::unescape;
13use quick_xml::events::{BytesStart, Event};
14
15use crate::syntax::{
16    XML_NAMESPACE_URI, map_quick_xml_error_at, split_qualified_name, validate_namespace_binding,
17    validate_qualified_name,
18};
19use crate::{Error, ParseOptions, XmlSafetyError, XmlSafetyPolicy};
20
21const OWNED_VALUE_OFFSET: u64 = u64::MAX;
22
23/// Stable identifier for a node in an [`XmlDocument`].
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub struct NodeId(NonZeroU32);
26
27impl NodeId {
28    fn from_index(index: usize) -> Result<Self, Error> {
29        let value = usize_to_u32(index, "XML node index")
30            .ok()
31            .and_then(|index| index.checked_add(1))
32            .and_then(NonZeroU32::new)
33            .ok_or(XmlSafetyError::TooManyElements)?;
34        Ok(Self(value))
35    }
36
37    fn index(self) -> usize {
38        stored_index(self.0.get() - 1, "XML node index")
39    }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43struct ScopeId(NonZeroU32);
44
45impl ScopeId {
46    fn from_index(index: usize) -> Result<Self, Error> {
47        let value = usize_to_u32(index, "XML namespace scope index")
48            .ok()
49            .and_then(|index| index.checked_add(1))
50            .and_then(NonZeroU32::new)
51            .ok_or_else(|| Error::InvalidXml("too many namespace scopes".into()))?;
52        Ok(Self(value))
53    }
54
55    fn index(self) -> usize {
56        stored_index(self.0.get() - 1, "XML namespace scope index")
57    }
58}
59
60/// A byte range in the original XML source.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub struct SourceSpan {
63    pub start: u64,
64    pub end: u64,
65}
66
67impl SourceSpan {
68    fn as_range(self, source_len: usize) -> Option<Range<usize>> {
69        let start = u64_to_usize(self.start, "XML source span start").ok()?;
70        let end = u64_to_usize(self.end, "XML source span end").ok()?;
71        (start <= end && end <= source_len).then_some(start..end)
72    }
73}
74
75#[derive(Debug, Clone, Copy)]
76struct ValueRef {
77    offset: u64,
78    length: u32,
79    owned_index: u32,
80}
81
82impl ValueRef {
83    fn source(offset: u64, length: u32) -> Self {
84        Self {
85            offset,
86            length,
87            owned_index: 0,
88        }
89    }
90
91    fn owned(index: u32, length: u32) -> Self {
92        Self {
93            offset: OWNED_VALUE_OFFSET,
94            length,
95            owned_index: index,
96        }
97    }
98}
99
100#[derive(Debug)]
101struct ArenaNode {
102    parent: Option<NodeId>,
103    first_child: Option<NodeId>,
104    last_child: Option<NodeId>,
105    next_sibling: Option<NodeId>,
106    kind: NodeKind,
107}
108
109#[derive(Debug)]
110enum NodeKind {
111    Element(ElementData),
112    Text(ValueRef),
113    CData(ValueRef),
114    Comment(ValueRef),
115    ProcessingInstruction {
116        target: ValueRef,
117        content: Option<ValueRef>,
118    },
119}
120
121#[derive(Debug)]
122struct ElementData {
123    qualified_name: ValueRef,
124    attributes: Range<u32>,
125    namespace_scope: Option<ScopeId>,
126    source: SourceSpan,
127}
128
129#[derive(Debug)]
130struct AttributeData {
131    qualified_name: ValueRef,
132    value: ValueRef,
133}
134
135#[derive(Debug)]
136struct NamespaceScope {
137    parent: Option<ScopeId>,
138    bindings: Range<u32>,
139}
140
141#[derive(Debug)]
142struct NamespaceBinding {
143    prefix: ValueRef,
144    uri: Option<ValueRef>,
145}
146
147#[derive(Clone, Copy)]
148struct ArenaView<'a> {
149    source: &'a [u8],
150    namespace_scopes: &'a [NamespaceScope],
151    namespace_bindings: &'a [NamespaceBinding],
152    owned_values: &'a [Box<str>],
153}
154
155impl<'a> ArenaView<'a> {
156    fn value(self, value: ValueRef) -> &'a str {
157        let resolved = self.checked_value(value);
158        debug_assert!(resolved.is_some(), "invalid internal XML value reference");
159        resolved.unwrap_or("")
160    }
161
162    fn checked_value(self, value: ValueRef) -> Option<&'a str> {
163        let length = u32_to_usize(value.length, "XML value length").ok()?;
164        if value.offset == OWNED_VALUE_OFFSET {
165            let index = u32_to_usize(value.owned_index, "owned XML value index").ok()?;
166            let value = self.owned_values.get(index)?.as_ref();
167            return (value.len() == length).then_some(value);
168        }
169
170        let start = u64_to_usize(value.offset, "XML value offset").ok()?;
171        let end = start.checked_add(length)?;
172        std::str::from_utf8(self.source.get(start..end)?).ok()
173    }
174
175    fn resolve_namespace(self, scope: Option<ScopeId>, prefix: &str) -> Option<&'a str> {
176        if let Ok(namespace) = self.checked_resolve_namespace(scope, prefix) {
177            namespace
178        } else {
179            debug_assert!(false, "invalid internal XML namespace reference");
180            None
181        }
182    }
183
184    fn checked_resolve_namespace(
185        self,
186        mut scope: Option<ScopeId>,
187        prefix: &str,
188    ) -> Result<Option<&'a str>, ()> {
189        if prefix == "xml" {
190            return Ok(Some(XML_NAMESPACE_URI));
191        }
192        while let Some(scope_id) = scope {
193            let scope_data = self.namespace_scopes.get(scope_id.index()).ok_or(())?;
194            for binding_index in scope_data.bindings.clone().rev() {
195                let binding_index =
196                    u32_to_usize(binding_index, "XML namespace binding index").map_err(|_| ())?;
197                let binding = self.namespace_bindings.get(binding_index).ok_or(())?;
198                if self.checked_value(binding.prefix).ok_or(())? == prefix {
199                    return binding
200                        .uri
201                        .map(|uri| self.checked_value(uri).ok_or(()))
202                        .transpose();
203                }
204            }
205            scope = scope_data.parent;
206        }
207        Ok(None)
208    }
209}
210
211/// An immutable XML tree whose nodes reference ranges in `source` whenever possible.
212///
213/// `S` may be `&[u8]`, `Arc<[u8]>`, `Vec<u8>`, or another byte container.
214#[derive(Debug)]
215pub struct XmlDocument<S> {
216    source: S,
217    nodes: Box<[ArenaNode]>,
218    attributes: Box<[AttributeData]>,
219    namespace_scopes: Box<[NamespaceScope]>,
220    namespace_bindings: Box<[NamespaceBinding]>,
221    owned_values: Box<[Box<str>]>,
222    root: NodeId,
223}
224
225/// A document borrowing its complete source buffer.
226pub type BorrowedDocument<'a> = XmlDocument<&'a [u8]>;
227
228/// A document sharing ownership of its source buffer.
229pub type OwnedDocument = XmlDocument<Arc<[u8]>>;
230
231impl<S: AsRef<[u8]>> XmlDocument<S> {
232    /// Parses a complete XML document with the default bounded policy.
233    ///
234    /// # Errors
235    ///
236    /// Returns an error when the source violates the default safety policy, is malformed, or
237    /// contains invalid XML data or encoding.
238    pub fn parse(source: S) -> Result<Self, Error> {
239        Self::parse_with_options(source, &ParseOptions::default())
240    }
241
242    /// Parses a complete XML document into a flat arena.
243    ///
244    /// # Errors
245    ///
246    /// Returns an error when `options` is invalid, the source exceeds a configured limit, or the
247    /// XML is malformed or has invalid data or encoding.
248    pub fn parse_with_options(source: S, options: &ParseOptions) -> Result<Self, Error> {
249        options.safety.validate()?;
250        if source.as_ref().len() > options.safety.max_input_bytes {
251            return Err(XmlSafetyError::InputTooLarge.into());
252        }
253
254        let (nodes, attributes, namespace_scopes, namespace_bindings, owned_values, root) = {
255            let mut builder = DocumentBuilder::new(source.as_ref(), options);
256            builder.parse()?;
257            let root = builder.root.ok_or(XmlSafetyError::Malformed)?;
258            (
259                builder.nodes.into_boxed_slice(),
260                builder.attributes.into_boxed_slice(),
261                builder.namespace_scopes.into_boxed_slice(),
262                builder.namespace_bindings.into_boxed_slice(),
263                builder.owned_values.into_boxed_slice(),
264                root,
265            )
266        };
267        Ok(Self {
268            source,
269            nodes,
270            attributes,
271            namespace_scopes,
272            namespace_bindings,
273            owned_values,
274            root,
275        })
276    }
277
278    pub fn source(&self) -> &[u8] {
279        self.source.as_ref()
280    }
281
282    pub fn into_source(self) -> S {
283        self.source
284    }
285
286    pub fn root(&self) -> ElementRef<'_, S> {
287        ElementRef {
288            document: self,
289            id: self.root,
290        }
291    }
292
293    pub fn node(&self, id: NodeId) -> Option<NodeRef<'_, S>> {
294        let node = self.nodes.get(id.index())?;
295        Some(match &node.kind {
296            NodeKind::Element(_) => NodeRef::Element(ElementRef { document: self, id }),
297            NodeKind::Text(value) => NodeRef::Text(self.value(*value)),
298            NodeKind::CData(value) => NodeRef::CData(self.value(*value)),
299            NodeKind::Comment(value) => NodeRef::Comment(self.value(*value)),
300            NodeKind::ProcessingInstruction { target, content } => {
301                NodeRef::ProcessingInstruction(ProcessingInstructionRef {
302                    target: self.value(*target),
303                    content: content.map(|value| self.value(value)),
304                })
305            }
306        })
307    }
308
309    pub fn node_count(&self) -> usize {
310        self.nodes.len()
311    }
312
313    pub fn allocated_value_count(&self) -> usize {
314        self.owned_values.len()
315    }
316
317    /// Writes the exact original document bytes to `writer`.
318    ///
319    /// # Errors
320    ///
321    /// Returns an I/O error when the destination rejects the write.
322    pub fn write_original<W: Write>(&self, mut writer: W) -> Result<(), Error> {
323        writer.write_all(self.source())?;
324        Ok(())
325    }
326
327    fn value(&self, value: ValueRef) -> &str {
328        self.arena_view().value(value)
329    }
330
331    fn element_data(&self, id: NodeId) -> Option<&ElementData> {
332        match &self.nodes.get(id.index())?.kind {
333            NodeKind::Element(element) => Some(element),
334            _ => None,
335        }
336    }
337
338    fn resolve_namespace(&self, scope: Option<ScopeId>, prefix: &str) -> Option<&str> {
339        self.arena_view().resolve_namespace(scope, prefix)
340    }
341
342    fn arena_view(&self) -> ArenaView<'_> {
343        ArenaView {
344            source: self.source.as_ref(),
345            namespace_scopes: &self.namespace_scopes,
346            namespace_bindings: &self.namespace_bindings,
347            owned_values: &self.owned_values,
348        }
349    }
350}
351
352impl XmlDocument<Arc<[u8]>> {
353    /// Reads and parses a complete document with the default bounded policy.
354    ///
355    /// # Errors
356    ///
357    /// Returns an error when reading fails or the input violates the default XML safety and
358    /// well-formedness contract.
359    pub fn from_reader<R: Read>(reader: R) -> Result<Self, Error> {
360        Self::from_reader_with_options(reader, &ParseOptions::default())
361    }
362
363    /// Reads at most one byte beyond the configured limit before parsing an owned document.
364    ///
365    /// # Errors
366    ///
367    /// Returns an error when `options` is invalid, reading fails, the input exceeds the byte limit,
368    /// or the XML violates a configured safety or well-formedness rule.
369    pub fn from_reader_with_options<R: Read>(
370        reader: R,
371        options: &ParseOptions,
372    ) -> Result<Self, Error> {
373        options.safety.validate()?;
374        let read_limit = options.safety.max_input_bytes.saturating_add(1);
375        let read_limit = usize_to_u64(read_limit, "XML reader byte limit").unwrap_or(u64::MAX);
376        let mut reader = reader.take(read_limit);
377        let mut source = Vec::new();
378        reader.read_to_end(&mut source)?;
379        if source.len() > options.safety.max_input_bytes {
380            return Err(XmlSafetyError::InputTooLarge.into());
381        }
382        Self::parse_with_options(Arc::from(source), options)
383    }
384}
385
386/// A cheap-to-clone, validated XML document retaining the exact original bytes.
387#[derive(Debug, Clone)]
388pub struct ValidatedXml(Arc<OwnedDocument>);
389
390impl ValidatedXml {
391    /// Validates and owns XML bytes under the default untrusted-input policy.
392    ///
393    /// # Errors
394    ///
395    /// Returns an error when the bytes violate the default XML safety or well-formedness contract.
396    pub fn new(bytes: impl Into<Arc<[u8]>>) -> Result<Self, Error> {
397        Self::with_policy(bytes, XmlSafetyPolicy::untrusted())
398    }
399
400    /// Validates and owns XML bytes under `policy`.
401    ///
402    /// # Errors
403    ///
404    /// Returns an error when `policy` is invalid or the bytes violate a configured safety or
405    /// well-formedness rule.
406    pub fn with_policy(
407        bytes: impl Into<Arc<[u8]>>,
408        policy: XmlSafetyPolicy,
409    ) -> Result<Self, Error> {
410        let source = bytes.into();
411        let document =
412            XmlDocument::parse_with_options(source, &ParseOptions::new().safety_policy(policy))?;
413        Ok(Self(Arc::new(document)))
414    }
415
416    /// Reads, validates, and owns XML under the default untrusted-input policy.
417    ///
418    /// # Errors
419    ///
420    /// Returns an error when reading fails or the input violates the default XML safety or
421    /// well-formedness contract.
422    pub fn from_reader<R: Read>(reader: R) -> Result<Self, Error> {
423        let document = OwnedDocument::from_reader(reader)?;
424        Ok(Self(Arc::new(document)))
425    }
426
427    #[must_use]
428    pub fn as_bytes(&self) -> &[u8] {
429        self.0.source()
430    }
431
432    #[must_use]
433    pub fn document(&self) -> &OwnedDocument {
434        &self.0
435    }
436}
437
438/// A borrowed view of an element node.
439pub struct ElementRef<'document, S> {
440    document: &'document XmlDocument<S>,
441    id: NodeId,
442}
443
444impl<S> Copy for ElementRef<'_, S> {}
445
446impl<S> Clone for ElementRef<'_, S> {
447    fn clone(&self) -> Self {
448        *self
449    }
450}
451
452impl<'document, S: AsRef<[u8]>> ElementRef<'document, S> {
453    #[must_use]
454    pub fn id(self) -> NodeId {
455        self.id
456    }
457
458    #[must_use]
459    pub fn parent(self) -> Option<ElementRef<'document, S>> {
460        self.document.nodes[self.id.index()]
461            .parent
462            .map(|id| ElementRef {
463                document: self.document,
464                id,
465            })
466    }
467
468    #[must_use]
469    pub fn qualified_name(self) -> &'document str {
470        let Some(data) = self.document.element_data(self.id) else {
471            return "";
472        };
473        self.document.value(data.qualified_name)
474    }
475
476    #[must_use]
477    pub fn prefix(self) -> Option<&'document str> {
478        split_qualified_name(self.qualified_name()).0
479    }
480
481    #[must_use]
482    pub fn name(self) -> &'document str {
483        split_qualified_name(self.qualified_name()).1
484    }
485
486    #[must_use]
487    pub fn namespace(self) -> Option<&'document str> {
488        let data = self.document.element_data(self.id)?;
489        self.document
490            .resolve_namespace(data.namespace_scope, self.prefix().unwrap_or(""))
491    }
492
493    #[must_use]
494    pub fn raw_xml(self) -> &'document [u8] {
495        let Some(data) = self.document.element_data(self.id) else {
496            return &[];
497        };
498        let Some(range) = data.source.as_range(self.document.source().len()) else {
499            return &[];
500        };
501        &self.document.source()[range]
502    }
503
504    #[must_use]
505    pub fn attributes(self) -> Attributes<'document, S> {
506        let range = self
507            .document
508            .element_data(self.id)
509            .map(|data| data.attributes.clone())
510            .unwrap_or(0..0);
511        Attributes {
512            element: self,
513            next: range.start,
514            end: range.end,
515        }
516    }
517
518    pub fn attribute(self, qualified_name: &str) -> Option<&'document str> {
519        self.attributes()
520            .find(|attribute| attribute.qualified_name() == qualified_name)
521            .map(AttributeRef::value)
522    }
523
524    pub fn attribute_ns(self, name: &str, namespace: Option<&str>) -> Option<&'document str> {
525        self.attributes()
526            .find(|attribute| attribute.name() == name && attribute.namespace() == namespace)
527            .map(AttributeRef::value)
528    }
529
530    #[must_use]
531    pub fn children(self) -> Children<'document, S> {
532        Children {
533            document: self.document,
534            next: self.document.nodes[self.id.index()].first_child,
535        }
536    }
537
538    #[must_use]
539    pub fn child_elements(self) -> ChildElements<'document, S> {
540        ChildElements {
541            children: self.children(),
542        }
543    }
544
545    #[must_use]
546    pub fn get_child(self, name: &str) -> Option<ElementRef<'document, S>> {
547        self.child_elements().find(|element| element.name() == name)
548    }
549
550    #[must_use]
551    pub fn get_child_ns(self, name: &str, namespace: &str) -> Option<ElementRef<'document, S>> {
552        self.child_elements()
553            .find(|element| element.name() == name && element.namespace() == Some(namespace))
554    }
555
556    #[must_use]
557    pub fn descendants(self) -> DescendantElements<'document, S> {
558        DescendantElements { stack: vec![self] }
559    }
560
561    #[must_use]
562    pub fn text(self) -> Option<Cow<'document, str>> {
563        let mut values = self.children().filter_map(|node| match node {
564            NodeRef::Text(text) | NodeRef::CData(text) => Some(text),
565            _ => None,
566        });
567        let first = values.next()?;
568        match values.next() {
569            None => Some(Cow::Borrowed(first)),
570            Some(second) => {
571                let mut output = String::with_capacity(first.len() + second.len());
572                output.push_str(first);
573                output.push_str(second);
574                values.for_each(|value| output.push_str(value));
575                Some(Cow::Owned(output))
576            }
577        }
578    }
579}
580
581/// A borrowed XML node view.
582pub enum NodeRef<'document, S> {
583    Element(ElementRef<'document, S>),
584    Text(&'document str),
585    CData(&'document str),
586    Comment(&'document str),
587    ProcessingInstruction(ProcessingInstructionRef<'document>),
588}
589
590impl<S> Copy for NodeRef<'_, S> {}
591
592impl<S> Clone for NodeRef<'_, S> {
593    fn clone(&self) -> Self {
594        *self
595    }
596}
597
598#[derive(Debug, Clone, Copy, PartialEq, Eq)]
599pub struct ProcessingInstructionRef<'a> {
600    pub target: &'a str,
601    pub content: Option<&'a str>,
602}
603
604pub struct Children<'document, S> {
605    document: &'document XmlDocument<S>,
606    next: Option<NodeId>,
607}
608
609impl<'document, S: AsRef<[u8]>> Iterator for Children<'document, S> {
610    type Item = NodeRef<'document, S>;
611
612    fn next(&mut self) -> Option<Self::Item> {
613        let id = self.next?;
614        self.next = self.document.nodes[id.index()].next_sibling;
615        self.document.node(id)
616    }
617}
618
619pub struct ChildElements<'document, S> {
620    children: Children<'document, S>,
621}
622
623impl<'document, S: AsRef<[u8]>> Iterator for ChildElements<'document, S> {
624    type Item = ElementRef<'document, S>;
625
626    fn next(&mut self) -> Option<Self::Item> {
627        self.children.find_map(|node| match node {
628            NodeRef::Element(element) => Some(element),
629            _ => None,
630        })
631    }
632}
633
634pub struct DescendantElements<'document, S> {
635    stack: Vec<ElementRef<'document, S>>,
636}
637
638impl<'document, S: AsRef<[u8]>> Iterator for DescendantElements<'document, S> {
639    type Item = ElementRef<'document, S>;
640
641    fn next(&mut self) -> Option<Self::Item> {
642        let element = self.stack.pop()?;
643        let child_start = self.stack.len();
644        self.stack.extend(element.child_elements());
645        self.stack[child_start..].reverse();
646        Some(element)
647    }
648}
649
650pub struct Attributes<'document, S> {
651    element: ElementRef<'document, S>,
652    next: u32,
653    end: u32,
654}
655
656impl<'document, S: AsRef<[u8]>> Iterator for Attributes<'document, S> {
657    type Item = AttributeRef<'document, S>;
658
659    fn next(&mut self) -> Option<Self::Item> {
660        if self.next >= self.end {
661            return None;
662        }
663        let index = self.next;
664        self.next += 1;
665        Some(AttributeRef {
666            element: self.element,
667            index,
668        })
669    }
670}
671
672pub struct AttributeRef<'document, S> {
673    element: ElementRef<'document, S>,
674    index: u32,
675}
676
677impl<S> Copy for AttributeRef<'_, S> {}
678
679impl<S> Clone for AttributeRef<'_, S> {
680    fn clone(&self) -> Self {
681        *self
682    }
683}
684
685impl<'document, S: AsRef<[u8]>> AttributeRef<'document, S> {
686    fn data(self) -> Option<&'document AttributeData> {
687        let data = self.checked_data();
688        debug_assert!(data.is_some(), "invalid internal XML attribute index");
689        data
690    }
691
692    fn checked_data(self) -> Option<&'document AttributeData> {
693        self.document()
694            .attributes
695            .get(stored_index(self.index, "XML attribute index"))
696    }
697
698    fn document(self) -> &'document XmlDocument<S> {
699        self.element.document
700    }
701
702    #[must_use]
703    pub fn qualified_name(self) -> &'document str {
704        self.data()
705            .map_or("", |data| self.document().value(data.qualified_name))
706    }
707
708    #[must_use]
709    pub fn prefix(self) -> Option<&'document str> {
710        split_qualified_name(self.qualified_name()).0
711    }
712
713    #[must_use]
714    pub fn name(self) -> &'document str {
715        split_qualified_name(self.qualified_name()).1
716    }
717
718    #[must_use]
719    pub fn namespace(self) -> Option<&'document str> {
720        let prefix = self.prefix()?;
721        let scope = self
722            .document()
723            .element_data(self.element.id)
724            .and_then(|element| element.namespace_scope);
725        self.document().resolve_namespace(scope, prefix)
726    }
727
728    #[must_use]
729    pub fn value(self) -> &'document str {
730        self.data()
731            .map_or("", |data| self.document().value(data.value))
732    }
733}
734
735struct DocumentBuilder<'a> {
736    source: &'a [u8],
737    options: &'a ParseOptions,
738    nodes: Vec<ArenaNode>,
739    attributes: Vec<AttributeData>,
740    namespace_scopes: Vec<NamespaceScope>,
741    namespace_bindings: Vec<NamespaceBinding>,
742    owned_values: Vec<Box<str>>,
743    open: Vec<NodeId>,
744    root: Option<NodeId>,
745    root_complete: bool,
746    element_count: usize,
747    text_bytes: usize,
748    event_count: usize,
749}
750
751impl<'a> DocumentBuilder<'a> {
752    fn new(source: &'a [u8], options: &'a ParseOptions) -> Self {
753        Self {
754            source,
755            options,
756            nodes: Vec::new(),
757            attributes: Vec::new(),
758            namespace_scopes: Vec::new(),
759            namespace_bindings: Vec::new(),
760            owned_values: Vec::new(),
761            open: Vec::new(),
762            root: None,
763            root_complete: false,
764            element_count: 0,
765            text_bytes: 0,
766            event_count: 0,
767        }
768    }
769
770    fn parse(&mut self) -> Result<(), Error> {
771        let mut reader = Reader::from_reader(self.source);
772        reader.config_mut().trim_text(false);
773        reader.config_mut().check_end_names = true;
774        loop {
775            let event_start = reader.buffer_position();
776            let event = reader.read_event().map_err(|error| {
777                let error_position =
778                    usize::try_from(reader.error_position()).unwrap_or(self.source.len());
779                map_quick_xml_error_at(
780                    error,
781                    error_position,
782                    self.source,
783                    self.options.safety.reject_doctype,
784                )
785            })?;
786            let event_end = reader.buffer_position();
787            if !matches!(event, Event::Eof) {
788                self.count_event()?;
789            }
790            match event {
791                Event::Start(start) => {
792                    self.start_element(&reader, &start, event_start, event_end)?;
793                }
794                Event::Empty(start) => {
795                    self.empty_element(&reader, &start, event_start, event_end)?;
796                }
797                Event::End(_) => self.end_element(event_end)?,
798                Event::Text(text) => {
799                    let raw = text.as_ref();
800                    let value =
801                        unescape(raw).map_err(|error| Error::InvalidXml(error.to_string()))?;
802                    self.text_node(value, false)?;
803                }
804                Event::CData(text) => self.text_node(Cow::Borrowed(text.as_ref()), true)?,
805                Event::Comment(comment) => {
806                    let value = self.source_value(comment.as_ref())?;
807                    self.push_content(NodeKind::Comment(value))?;
808                }
809                Event::PI(pi) => {
810                    let target = self.source_value(pi.target())?;
811                    let content = pi
812                        .content()
813                        .trim_start_matches(|character: char| character.is_ascii_whitespace());
814                    let content = (!content.is_empty())
815                        .then(|| self.source_value(content))
816                        .transpose()?;
817                    self.push_content(NodeKind::ProcessingInstruction { target, content })?;
818                }
819                Event::GeneralRef(reference) => {
820                    let value = if let Some(character) = reference
821                        .resolve_char_ref()
822                        .map_err(|error| Error::InvalidXml(error.to_string()))?
823                    {
824                        Cow::Owned(character.to_string())
825                    } else {
826                        Cow::Owned(
827                            match reference.as_ref() {
828                                "amp" => "&",
829                                "lt" => "<",
830                                "gt" => ">",
831                                "apos" => "'",
832                                "quot" => "\"",
833                                _ => return Err(XmlSafetyError::ExternalEntity.into()),
834                            }
835                            .to_owned(),
836                        )
837                    };
838                    self.text_node(value, false)?;
839                }
840                Event::Decl(_) => {
841                    if self.root.is_some() || !self.open.is_empty() || self.root_complete {
842                        return Err(XmlSafetyError::Malformed.into());
843                    }
844                }
845                Event::DocType(_) => {
846                    if self.options.safety.reject_doctype {
847                        return Err(XmlSafetyError::ExternalEntity.into());
848                    }
849                    if self.root.is_some() || !self.open.is_empty() || self.root_complete {
850                        return Err(XmlSafetyError::Malformed.into());
851                    }
852                }
853                Event::Eof => {
854                    if !self.open.is_empty() || !self.root_complete {
855                        return Err(XmlSafetyError::Malformed.into());
856                    }
857                    return Ok(());
858                }
859            }
860        }
861    }
862
863    fn start_element(
864        &mut self,
865        reader: &Reader<&[u8]>,
866        start: &BytesStart<'a>,
867        source_start: u64,
868        source_end: u64,
869    ) -> Result<(), Error> {
870        self.check_element()?;
871        let id = self.build_element(reader, start, source_start, source_end)?;
872        self.open.push(id);
873        Ok(())
874    }
875
876    fn empty_element(
877        &mut self,
878        reader: &Reader<&[u8]>,
879        start: &BytesStart<'a>,
880        source_start: u64,
881        source_end: u64,
882    ) -> Result<(), Error> {
883        self.check_element()?;
884        self.build_element(reader, start, source_start, source_end)?;
885        if self.open.is_empty() {
886            self.root_complete = true;
887        }
888        Ok(())
889    }
890
891    fn end_element(&mut self, source_end: u64) -> Result<(), Error> {
892        let id = self.open.pop().ok_or(XmlSafetyError::Malformed)?;
893        let NodeKind::Element(element) = &mut self.nodes[id.index()].kind else {
894            return Err(XmlSafetyError::Malformed.into());
895        };
896        element.source.end = source_end;
897        if self.open.is_empty() {
898            self.root_complete = true;
899        }
900        Ok(())
901    }
902
903    #[expect(
904        clippy::too_many_lines,
905        reason = "Element construction keeps namespace scopes, attributes, source spans, and arena links atomic."
906    )]
907    fn build_element(
908        &mut self,
909        _reader: &Reader<&[u8]>,
910        start: &BytesStart<'a>,
911        source_start: u64,
912        source_end: u64,
913    ) -> Result<NodeId, Error> {
914        if self.open.is_empty() && self.root_complete {
915            return Err(XmlSafetyError::Malformed.into());
916        }
917        let start_name = start.name();
918        let qualified_name = start_name.as_ref();
919        let (prefix, _) = validate_qualified_name(qualified_name)?;
920        let parent_scope = self.open.last().and_then(|id| {
921            let NodeKind::Element(element) = &self.nodes[id.index()].kind else {
922                return None;
923            };
924            element.namespace_scope
925        });
926        let binding_start = arena_len(self.namespace_bindings.len(), "namespace bindings")?;
927        let mut attribute_count = 0usize;
928        for attribute in start.attributes() {
929            attribute_count = attribute_count
930                .checked_add(1)
931                .ok_or(XmlSafetyError::TooManyAttributes)?;
932            if attribute_count > self.options.safety.max_attributes_per_element {
933                return Err(XmlSafetyError::TooManyAttributes.into());
934            }
935            let attribute = attribute.map_err(|error| Error::InvalidXml(error.to_string()))?;
936            let name = attribute.key.as_ref();
937            validate_qualified_name(name)?;
938            if name == "xmlns" || name.starts_with("xmlns:") {
939                let namespace_prefix = name.strip_prefix("xmlns:").unwrap_or("");
940                let uri = attribute
941                    .normalized_value(XmlVersion::Explicit1_0)
942                    .map_err(|error| Error::InvalidXml(error.to_string()))?;
943                validate_namespace_binding(namespace_prefix, &uri)?;
944                let prefix_value = if namespace_prefix.is_empty() {
945                    ValueRef::source(0, 0)
946                } else {
947                    self.source_value(namespace_prefix)?
948                };
949                let uri_value = if uri.is_empty() {
950                    None
951                } else {
952                    Some(self.cow_value(uri)?)
953                };
954                self.namespace_bindings.push(NamespaceBinding {
955                    prefix: prefix_value,
956                    uri: uri_value,
957                });
958            }
959        }
960        let binding_end = arena_len(self.namespace_bindings.len(), "namespace bindings")?;
961        let namespace_scope = if binding_start == binding_end {
962            parent_scope
963        } else {
964            let id = ScopeId::from_index(self.namespace_scopes.len())?;
965            self.namespace_scopes.push(NamespaceScope {
966                parent: parent_scope,
967                bindings: binding_start..binding_end,
968            });
969            Some(id)
970        };
971        if let Some(prefix) = prefix
972            && self
973                .arena_view()
974                .resolve_namespace(namespace_scope, prefix)
975                .is_none()
976        {
977            return Err(XmlSafetyError::Malformed.into());
978        }
979
980        let attribute_start = arena_len(self.attributes.len(), "attributes")?;
981        for attribute in start.attributes() {
982            let attribute = attribute.map_err(|error| Error::InvalidXml(error.to_string()))?;
983            let name = attribute.key.as_ref();
984            if name == "xmlns" || name.starts_with("xmlns:") {
985                continue;
986            }
987            let (prefix, _) = split_qualified_name(name);
988            if let Some(prefix) = prefix
989                && prefix != "xml"
990                && self
991                    .arena_view()
992                    .resolve_namespace(namespace_scope, prefix)
993                    .is_none()
994            {
995                return Err(XmlSafetyError::Malformed.into());
996            }
997            let value = attribute
998                .normalized_value(XmlVersion::Explicit1_0)
999                .map_err(|error| Error::InvalidXml(error.to_string()))?;
1000            let qualified_name = self.source_value(name)?;
1001            let value = self.cow_value(value)?;
1002            self.attributes.push(AttributeData {
1003                qualified_name,
1004                value,
1005            });
1006        }
1007        let attribute_end = arena_len(self.attributes.len(), "attributes")?;
1008        let qualified_name = self.source_value(qualified_name)?;
1009        let id = self.push_node(NodeKind::Element(ElementData {
1010            qualified_name,
1011            attributes: attribute_start..attribute_end,
1012            namespace_scope,
1013            source: SourceSpan {
1014                start: source_start,
1015                end: source_end,
1016            },
1017        }))?;
1018        if self.root.is_none() {
1019            self.root = Some(id);
1020        }
1021        Ok(id)
1022    }
1023
1024    fn check_element(&mut self) -> Result<(), Error> {
1025        let depth = self
1026            .open
1027            .len()
1028            .checked_add(1)
1029            .ok_or(XmlSafetyError::TooDeep)?;
1030        if depth > self.options.safety.max_depth {
1031            return Err(XmlSafetyError::TooDeep.into());
1032        }
1033        self.element_count = self
1034            .element_count
1035            .checked_add(1)
1036            .ok_or(XmlSafetyError::TooManyElements)?;
1037        if self.element_count > self.options.safety.max_elements {
1038            return Err(XmlSafetyError::TooManyElements.into());
1039        }
1040        Ok(())
1041    }
1042
1043    fn count_event(&mut self) -> Result<(), Error> {
1044        self.event_count = self
1045            .event_count
1046            .checked_add(1)
1047            .ok_or(XmlSafetyError::TooManyEvents)?;
1048        if self.event_count > self.options.safety.max_events {
1049            return Err(XmlSafetyError::TooManyEvents.into());
1050        }
1051        Ok(())
1052    }
1053
1054    fn text_node(&mut self, value: Cow<'_, str>, cdata: bool) -> Result<(), Error> {
1055        self.text_bytes = self
1056            .text_bytes
1057            .checked_add(value.len())
1058            .ok_or(XmlSafetyError::TextTooLarge)?;
1059        if self.text_bytes > self.options.safety.max_text_bytes {
1060            return Err(XmlSafetyError::TextTooLarge.into());
1061        }
1062        let value = if self.options.trim_whitespace {
1063            match value {
1064                Cow::Borrowed(value) => Cow::Borrowed(value.trim()),
1065                Cow::Owned(value) => Cow::Owned(value.trim().to_owned()),
1066            }
1067        } else {
1068            value
1069        };
1070        if value.is_empty() {
1071            return Ok(());
1072        }
1073        if self.open.is_empty() {
1074            if value.chars().all(char::is_whitespace) {
1075                return Ok(());
1076            }
1077            return Err(XmlSafetyError::Malformed.into());
1078        }
1079        let value = self.cow_value(value)?;
1080        self.push_content(if cdata {
1081            NodeKind::CData(value)
1082        } else {
1083            NodeKind::Text(value)
1084        })?;
1085        Ok(())
1086    }
1087
1088    fn push_content(&mut self, kind: NodeKind) -> Result<(), Error> {
1089        if self.open.is_empty() {
1090            return Ok(());
1091        }
1092        self.push_node(kind).map(|_| ())
1093    }
1094
1095    fn push_node(&mut self, kind: NodeKind) -> Result<NodeId, Error> {
1096        let id = NodeId::from_index(self.nodes.len())?;
1097        let parent = self.open.last().copied();
1098        self.nodes.push(ArenaNode {
1099            parent,
1100            first_child: None,
1101            last_child: None,
1102            next_sibling: None,
1103            kind,
1104        });
1105        if let Some(parent) = parent {
1106            if let Some(previous) = self.nodes[parent.index()].last_child {
1107                self.nodes[previous.index()].next_sibling = Some(id);
1108            } else {
1109                self.nodes[parent.index()].first_child = Some(id);
1110            }
1111            self.nodes[parent.index()].last_child = Some(id);
1112        }
1113        Ok(id)
1114    }
1115
1116    fn source_value(&self, value: &str) -> Result<ValueRef, Error> {
1117        let source_start = self.source.as_ptr() as usize;
1118        let value_start = value.as_ptr() as usize;
1119        let offset = value_start
1120            .checked_sub(source_start)
1121            .filter(|offset| offset.saturating_add(value.len()) <= self.source.len())
1122            .ok_or_else(|| Error::InvalidXml("borrowed value is outside XML source".into()))?;
1123        Ok(ValueRef::source(
1124            usize_to_u64(offset, "XML value offset").map_err(|_| XmlSafetyError::InputTooLarge)?,
1125            usize_to_u32(value.len(), "XML value length")
1126                .map_err(|_| XmlSafetyError::InputTooLarge)?,
1127        ))
1128    }
1129
1130    fn cow_value(&mut self, value: Cow<'_, str>) -> Result<ValueRef, Error> {
1131        match value {
1132            Cow::Borrowed(value) => self.source_value(value),
1133            Cow::Owned(value) => {
1134                let index = arena_len(self.owned_values.len(), "owned XML values")?;
1135                let length = usize_to_u32(value.len(), "owned XML value length")
1136                    .map_err(|_| XmlSafetyError::InputTooLarge)?;
1137                self.owned_values.push(value.into_boxed_str());
1138                Ok(ValueRef::owned(index, length))
1139            }
1140        }
1141    }
1142
1143    fn arena_view(&self) -> ArenaView<'_> {
1144        ArenaView {
1145            source: self.source,
1146            namespace_scopes: &self.namespace_scopes,
1147            namespace_bindings: &self.namespace_bindings,
1148            owned_values: &self.owned_values,
1149        }
1150    }
1151}
1152
1153fn arena_len(length: usize, label: &str) -> Result<u32, Error> {
1154    usize_to_u32(length, label).map_err(|_| Error::InvalidXml(format!("too many {label}")))
1155}
1156
1157fn stored_index(value: u32, label: &str) -> usize {
1158    // Rust's supported platforms can represent every u32 as usize. Keep the checked Forge
1159    // conversion at the representation boundary and make malformed internal state fail indexing.
1160    u32_to_usize(value, label).unwrap_or(usize::MAX)
1161}
1162
1163#[cfg(test)]
1164mod layout_tests {
1165    use std::fmt::Write as _;
1166    use std::mem::size_of_val;
1167
1168    use super::*;
1169
1170    fn arena_payload_bytes<S>(document: &XmlDocument<S>) -> usize {
1171        size_of_val(document.nodes.as_ref())
1172            + size_of_val(document.attributes.as_ref())
1173            + size_of_val(document.namespace_scopes.as_ref())
1174            + size_of_val(document.namespace_bindings.as_ref())
1175            + size_of_val(document.owned_values.as_ref())
1176            + document
1177                .owned_values
1178                .iter()
1179                .map(|value| value.len())
1180                .sum::<usize>()
1181    }
1182
1183    #[test]
1184    fn arena_view_rejects_invalid_value_ranges_and_namespace_indices() {
1185        let source = b"value\xFF";
1186        let owned_values = [Box::<str>::from("owned")];
1187        let scope = ScopeId::from_index(0).expect("scope id");
1188        let namespace_scopes = [NamespaceScope {
1189            parent: None,
1190            bindings: 0..1,
1191        }];
1192        let view = ArenaView {
1193            source,
1194            namespace_scopes: &namespace_scopes,
1195            namespace_bindings: &[],
1196            owned_values: &owned_values,
1197        };
1198
1199        assert_eq!(view.checked_value(ValueRef::source(0, 5)), Some("value"));
1200        assert_eq!(view.checked_value(ValueRef::owned(0, 5)), Some("owned"));
1201        assert_eq!(view.checked_value(ValueRef::source(5, 1)), None);
1202        assert_eq!(view.checked_value(ValueRef::source(u64::MAX, 2)), None);
1203        assert_eq!(view.checked_value(ValueRef::owned(1, 5)), None);
1204        assert_eq!(view.checked_value(ValueRef::owned(0, 4)), None);
1205        assert_eq!(view.checked_resolve_namespace(Some(scope), "p"), Err(()));
1206    }
1207
1208    #[test]
1209    fn attribute_lookup_reports_invalid_internal_indices_without_indexing() {
1210        let document = BorrowedDocument::parse(br#"<root id="7"/>"#.as_slice())
1211            .expect("document should parse");
1212        let attribute = AttributeRef {
1213            element: document.root(),
1214            index: u32::MAX,
1215        };
1216
1217        assert!(attribute.checked_data().is_none());
1218    }
1219
1220    #[test]
1221    fn large_owned_document_payload_stays_below_six_times_input() {
1222        const RESPONSES: usize = 10_000;
1223        let mut source = String::from("<D:multistatus xmlns:D=\"DAV:\">");
1224        for index in 0..RESPONSES {
1225            let _ = write!(
1226                source,
1227                "<D:response><D:href>/files/{index}</D:href><D:propstat><D:prop><D:displayname>file-{index}</D:displayname><D:getcontentlength>{}</D:getcontentlength><D:getetag>&quot;etag-{index}&quot;</D:getetag></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>",
1228                index * 1024
1229            );
1230        }
1231        source.push_str("</D:multistatus>");
1232        let input_bytes = source.len();
1233        let options = ParseOptions::new()
1234            .max_size(input_bytes)
1235            .max_elements(RESPONSES * 8 + 1);
1236        let document = XmlDocument::parse_with_options(Arc::from(source.into_bytes()), &options)
1237            .expect("large document");
1238        let retained_payload = input_bytes + arena_payload_bytes(&document);
1239
1240        assert!(
1241            retained_payload <= input_bytes * 6,
1242            "retained payload {retained_payload} exceeds 6x input {input_bytes}"
1243        );
1244    }
1245}