Skip to main content

abx/decode/
slice.rs

1//! [`AbxParser`] — zero-allocation pull parser over an in-memory `&[u8]` —
2//! and [`AbxParserOwned`], a heap-owning wrapper around it.
3//!
4//! The public surface is intentionally identical to
5//! [`AbxStreamParser`](crate::AbxStreamParser) so the two types are
6//! interchangeable; just swap the constructor.
7
8use std::collections::HashMap;
9
10use nom::{
11    IResult, Parser,
12    bytes::complete::take,
13    number::complete::{be_f32, be_f64, be_i32, be_i64, be_u8, be_u16},
14};
15
16use crate::{
17    AbxError, Attribute, AttributeValue, CMD_ATTRIBUTE, CMD_CDSECT, CMD_COMMENT, CMD_DOCDECL,
18    CMD_END_DOCUMENT, CMD_END_TAG, CMD_ENTITY_REF, CMD_IGNORABLE_WHITESPACE,
19    CMD_PROCESSING_INSTRUCTION, CMD_START_DOCUMENT, CMD_START_TAG, CMD_TEXT, Event, MAGIC, Result,
20    TYPE_BOOLEAN_FALSE, TYPE_BOOLEAN_TRUE, TYPE_BYTES_BASE64, TYPE_BYTES_HEX, TYPE_DOUBLE,
21    TYPE_FLOAT, TYPE_INT, TYPE_INT_HEX, TYPE_LONG, TYPE_LONG_HEX, TYPE_NULL, TYPE_STRING,
22    TYPE_STRING_INTERNED, render_event,
23};
24
25use crate::INTERNED_NEW;
26use crate::InternedStr;
27
28fn parse_utf_string(input: &[u8]) -> IResult<&[u8], String> {
29    let (input, len) = be_u16(input)?;
30    let (input, bytes) = take(len).parse(input)?;
31    let s = std::str::from_utf8(bytes)
32        .map_err(|_| {
33            nom::Err::Failure(nom::error::Error::new(input, nom::error::ErrorKind::Verify))
34        })?
35        .to_owned();
36    Ok((input, s))
37}
38
39fn parse_bytes_blob(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
40    let (input, len) = be_u16(input)?;
41    let (input, bytes) = take(len).parse(input)?;
42    Ok((input, bytes.to_vec()))
43}
44
45/// Zero-allocation pull parser that works on an in-memory `&[u8]`.
46///
47/// Advance through the document with [`next_event`](AbxParser::next_event) or
48/// use one of the higher-level helpers.
49#[derive(Debug)]
50pub struct AbxParser<'a> {
51    rest: &'a [u8],
52    pool: Vec<InternedStr>,
53}
54
55impl<'a> AbxParser<'a> {
56    /// Create a parser, validating the 4-byte magic header.
57    pub fn new(input: &'a [u8]) -> Result<Self> {
58        if input.len() < 4 {
59            return Err(AbxError::UnexpectedEof("magic header"));
60        }
61        let magic: [u8; 4] = input[..4].try_into().unwrap();
62        if magic != MAGIC {
63            return Err(AbxError::InvalidMagic {
64                expected: MAGIC,
65                actual: magic,
66            });
67        }
68        Ok(AbxParser {
69            rest: &input[4..],
70            pool: Vec::with_capacity(32),
71        })
72    }
73
74    /// `true` when no more bytes remain.
75    pub fn is_empty(&self) -> bool {
76        self.rest.is_empty()
77    }
78
79    // -- internal helpers --
80
81    fn run<F, T>(&mut self, f: F) -> Result<T>
82    where
83        F: Fn(&'a [u8]) -> IResult<&'a [u8], T>,
84    {
85        let (rest, val) = f(self.rest).map_err(|e| AbxError::Nom(format!("{e:?}")))?;
86        self.rest = rest;
87        Ok(val)
88    }
89
90    fn read_u8(&mut self) -> Result<u8> {
91        self.run(be_u8)
92    }
93    fn read_u16(&mut self) -> Result<u16> {
94        self.run(be_u16)
95    }
96    fn read_i32(&mut self) -> Result<i32> {
97        self.run(be_i32)
98    }
99    fn read_i64(&mut self) -> Result<i64> {
100        self.run(be_i64)
101    }
102    fn read_f32(&mut self) -> Result<f32> {
103        self.run(be_f32)
104    }
105    fn read_f64(&mut self) -> Result<f64> {
106        self.run(be_f64)
107    }
108
109    fn read_utf(&mut self) -> Result<String> {
110        self.run(parse_utf_string)
111    }
112    fn read_bytes_blob(&mut self) -> Result<Vec<u8>> {
113        self.run(parse_bytes_blob)
114    }
115
116    /// Read an interned string. Every occurrence after the first is a
117    /// back-reference into `pool`, resolved with `InternedStr::clone` (a
118    /// refcount bump) rather than a fresh allocation and copy.
119    fn read_interned(&mut self) -> Result<InternedStr> {
120        let idx = self.read_u16()?;
121        if idx == INTERNED_NEW {
122            let s: InternedStr = self.read_utf()?.into();
123            self.pool.push(s.clone());
124            Ok(s)
125        } else {
126            self.pool
127                .get(idx as usize)
128                .cloned()
129                .ok_or(AbxError::BadInternedIndex(idx))
130        }
131    }
132
133    fn read_attr_value(&mut self, type_nibble: u8) -> Result<AttributeValue> {
134        match type_nibble {
135            TYPE_NULL => Ok(AttributeValue::Null),
136            TYPE_STRING => Ok(AttributeValue::String(self.read_utf()?)),
137            TYPE_STRING_INTERNED => Ok(AttributeValue::String(String::from(self.read_interned()?))),
138            TYPE_BYTES_HEX => Ok(AttributeValue::BytesHex(self.read_bytes_blob()?)),
139            TYPE_BYTES_BASE64 => Ok(AttributeValue::BytesBase64(self.read_bytes_blob()?)),
140            TYPE_INT => Ok(AttributeValue::Int(self.read_i32()?)),
141            TYPE_INT_HEX => Ok(AttributeValue::IntHex(self.read_i32()? as u32)),
142            TYPE_LONG => Ok(AttributeValue::Long(self.read_i64()?)),
143            TYPE_LONG_HEX => Ok(AttributeValue::LongHex(self.read_i64()? as u64)),
144            TYPE_FLOAT => Ok(AttributeValue::Float(self.read_f32()?)),
145            TYPE_DOUBLE => Ok(AttributeValue::Double(self.read_f64()?)),
146            TYPE_BOOLEAN_TRUE => Ok(AttributeValue::Boolean(true)),
147            TYPE_BOOLEAN_FALSE => Ok(AttributeValue::Boolean(false)),
148            other => Err(AbxError::UnknownAttributeType(other)),
149        }
150    }
151
152    // -- public event API --
153
154    /// Pull the next [`Event`].  Returns `None` at end of input.
155    pub fn next_event(&mut self) -> Result<Option<Event>> {
156        if self.rest.is_empty() {
157            return Ok(None);
158        }
159
160        let token = self.read_u8()?;
161        let cmd = token & 0x0F;
162        let type_nibble = token & 0xF0;
163
164        let event = match cmd {
165            CMD_START_DOCUMENT => Event::StartDocument,
166            CMD_END_DOCUMENT => return Ok(Some(Event::EndDocument)),
167
168            CMD_START_TAG => {
169                let name = self.read_interned()?;
170                let mut attributes = Vec::with_capacity(4);
171                loop {
172                    if self.rest.is_empty() {
173                        break;
174                    }
175                    let next = self.rest[0];
176                    if (next & 0x0F) != CMD_ATTRIBUTE {
177                        break;
178                    }
179                    self.rest = &self.rest[1..];
180                    let attr_type = next & 0xF0;
181                    let attr_name = self.read_interned()?;
182                    let attr_value = self.read_attr_value(attr_type)?;
183                    attributes.push(Attribute {
184                        name: attr_name,
185                        value: attr_value,
186                    });
187                }
188                Event::StartTag { name, attributes }
189            }
190
191            CMD_END_TAG => Event::EndTag {
192                name: self.read_interned()?,
193            },
194
195            CMD_TEXT => Event::Text(if type_nibble == TYPE_STRING {
196                self.read_utf()?
197            } else {
198                String::new()
199            }),
200            CMD_CDSECT => Event::CdataSection(if type_nibble == TYPE_STRING {
201                self.read_utf()?
202            } else {
203                String::new()
204            }),
205            CMD_ENTITY_REF => Event::EntityReference(if type_nibble == TYPE_STRING {
206                self.read_utf()?
207            } else {
208                String::new()
209            }),
210            CMD_IGNORABLE_WHITESPACE => Event::IgnorableWhitespace(if type_nibble == TYPE_STRING {
211                self.read_utf()?
212            } else {
213                String::new()
214            }),
215            CMD_PROCESSING_INSTRUCTION => {
216                Event::ProcessingInstruction(if type_nibble == TYPE_STRING {
217                    self.read_utf()?
218                } else {
219                    String::new()
220                })
221            }
222            CMD_COMMENT => Event::Comment(if type_nibble == TYPE_STRING {
223                self.read_utf()?
224            } else {
225                String::new()
226            }),
227            CMD_DOCDECL => Event::DocDecl(if type_nibble == TYPE_STRING {
228                self.read_utf()?
229            } else {
230                String::new()
231            }),
232
233            other => return Err(AbxError::UnknownCommand(other)),
234        };
235
236        Ok(Some(event))
237    }
238
239    // -- convenience API (mirrors AbxStreamParser) --
240
241    /// Drain all remaining events into a `Vec`.
242    pub fn collect_events(&mut self) -> Result<Vec<Event>> {
243        let mut events = Vec::new();
244        while let Some(ev) = self.next_event()? {
245            events.push(ev);
246        }
247        Ok(events)
248    }
249
250    /// Return the value of the first matching `attr_name` inside any
251    /// `<element_name>` in the remaining stream. `Ok(None)` means the
252    /// element/attribute wasn't found before the document ended; `Err`
253    /// means a parse failure interrupted the search. Callers that don't
254    /// care about that distinction can collapse both into `None` with
255    /// `.ok().flatten()`.
256    pub fn find_attribute(&mut self, element: &str, attr: &str) -> Result<Option<AttributeValue>> {
257        loop {
258            match self.next_event()? {
259                Some(Event::StartTag { name, attributes }) if name == element => {
260                    if let Some(a) = attributes.into_iter().find(|a| a.name == attr) {
261                        return Ok(Some(a.value));
262                    }
263                }
264                Some(Event::EndDocument) | None => return Ok(None),
265                _ => {}
266            }
267        }
268    }
269
270    /// All values of `attr_name` found in `<element_name>` tags.
271    pub fn find_all_attributes(
272        &mut self,
273        element: &str,
274        attr: &str,
275    ) -> Result<Vec<AttributeValue>> {
276        let mut out = Vec::new();
277        while let Some(ev) = self.next_event()? {
278            if let Event::StartTag { name, attributes } = ev
279                && name == element
280            {
281                out.extend(
282                    attributes
283                        .into_iter()
284                        .filter(|a| a.name == attr)
285                        .map(|a| a.value),
286                );
287            }
288        }
289        Ok(out)
290    }
291
292    /// Attributes of the first `<element_name>` tag. `Ok(None)` means no
293    /// matching tag was found before the document ended; `Err` means a
294    /// parse failure interrupted the search — see
295    /// [`find_attribute`](Self::find_attribute) for how to collapse both
296    /// into a plain `None`.
297    pub fn attributes_of(&mut self, element: &str) -> Result<Option<Vec<Attribute>>> {
298        loop {
299            match self.next_event()? {
300                Some(Event::StartTag { name, attributes }) if name == element => {
301                    return Ok(Some(attributes));
302                }
303                Some(Event::EndDocument) | None => return Ok(None),
304                _ => {}
305            }
306        }
307    }
308
309    /// Attributes of every `<element_name>` tag.
310    pub fn all_attributes_of(&mut self, element: &str) -> Result<Vec<Vec<Attribute>>> {
311        let mut out = Vec::new();
312        while let Some(ev) = self.next_event()? {
313            if let Event::StartTag { name, attributes } = ev
314                && name == element
315            {
316                out.push(attributes);
317            }
318        }
319        Ok(out)
320    }
321
322    /// Render the rest of the document as an XML string.
323    pub fn to_xml(&mut self) -> Result<String> {
324        let mut buf = String::from(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
325        while let Some(ev) = self.next_event()? {
326            if matches!(ev, Event::EndDocument) {
327                break;
328            }
329            render_event(&ev, &mut buf);
330        }
331        Ok(buf)
332    }
333
334    /// Write the rest of the document as XML into any `std::io::Write` sink.
335    ///
336    /// More memory-efficient than [`to_xml`](AbxParser::to_xml) for very
337    /// large documents because it does not accumulate the whole result in a
338    /// `String`.
339    pub fn write_xml(&mut self, writer: &mut impl std::io::Write) -> Result<()> {
340        writer.write_all(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>")?;
341        // One scratch buffer reused (cleared, not reallocated) across every
342        // event, instead of a fresh allocation per event.
343        let mut tmp = String::new();
344        while let Some(ev) = self.next_event()? {
345            if matches!(ev, Event::EndDocument) {
346                break;
347            }
348            tmp.clear();
349            render_event(&ev, &mut tmp);
350            writer.write_all(tmp.as_bytes())?;
351        }
352        Ok(())
353    }
354
355    /// Find the next `<element>`, deserialize its attributes (and direct
356    /// text content, via a `#[serde(rename = "$text")]` field) into `T`,
357    /// then skip past its matching end tag. `Ok(None)` at end of document.
358    #[cfg(feature = "serialize")]
359    pub fn deserialize_next<T: serde::de::DeserializeOwned>(
360        &mut self,
361        element: &str,
362    ) -> Result<Option<T>> {
363        crate::de::find_and_consume_element(self, element)
364    }
365
366    /// Deserialize every remaining `<element>` into a `Vec<T>`.
367    #[cfg(feature = "serialize")]
368    pub fn deserialize_all<T: serde::de::DeserializeOwned>(
369        &mut self,
370        element: &str,
371    ) -> Result<Vec<T>> {
372        let mut out = Vec::new();
373        while let Some(item) = self.deserialize_next(element)? {
374            out.push(item);
375        }
376        Ok(out)
377    }
378
379    /// Collect the whole document into a `HashMap<element → Vec<HashMap<attr → value_str>>>`.
380    pub fn into_map(mut self) -> Result<HashMap<String, Vec<HashMap<String, String>>>> {
381        let mut map: HashMap<String, Vec<HashMap<String, String>>> = HashMap::new();
382        while let Some(ev) = self.next_event()? {
383            if let Event::StartTag { name, attributes } = ev {
384                let entry = map.entry(name.into()).or_default();
385                let mut attrs = HashMap::new();
386                for attr in attributes {
387                    attrs.insert(attr.name.into(), attr.value.as_str().into_owned());
388                }
389                entry.push(attrs);
390            }
391        }
392        Ok(map)
393    }
394}
395
396/// Heap-owning wrapper. Stores the raw bytes and hands out [`AbxParser`]
397/// borrows without lifetime gymnastics on the call-site.
398///
399/// ```rust,ignore
400/// let owned = AbxParserOwned::new(std::fs::read("foo.abx")?)?;
401/// let xml = owned.parser()?.to_xml()?;
402/// ```
403#[derive(Debug)]
404pub struct AbxParserOwned {
405    data: Vec<u8>,
406}
407
408impl AbxParserOwned {
409    /// Validate the magic header and store the bytes.
410    pub fn new(data: Vec<u8>) -> Result<Self> {
411        if data.len() < 4 {
412            return Err(AbxError::UnexpectedEof("magic header"));
413        }
414        let magic: [u8; 4] = data[..4].try_into().unwrap();
415        if magic != MAGIC {
416            return Err(AbxError::InvalidMagic {
417                expected: MAGIC,
418                actual: magic,
419            });
420        }
421        Ok(Self { data })
422    }
423
424    /// Create a fresh [`AbxParser`] borrowing from the stored bytes.
425    pub fn parser(&self) -> Result<AbxParser<'_>> {
426        AbxParser::new(&self.data)
427    }
428}