Skip to main content

mlt_core/dump/
walker.rs

1//! Shared scaffolding for the tag-specific annotating walkers.
2//!
3//! [`Walker`] records an annotated [`Region`] per field while the per-tag
4//! `walker01` / `walker02` modules mirror their wire layout.
5//! Tile and layer framing is shared - only the layer body differs per tag.
6
7use super::model::{BitField, BlobInfo, DumpTree, Region, RegionKind};
8use crate::codecs::varint::parse_varint;
9use crate::utils::{parse_u8, take};
10use crate::{MltError, MltRefResult, MltResult, Parser};
11
12/// Walk a whole tile buffer, producing an annotated [`DumpTree`].
13///
14/// The returned tree references offsets into `buf`; keep `buf` alive to render it.
15pub fn annotate_tile(buf: &[u8]) -> MltResult<DumpTree> {
16    let mut w = Walker {
17        buf,
18        out: Vec::new(),
19        depth: 0,
20        parser: Parser::default(),
21    };
22    w.walk_tile()?;
23    Ok(DumpTree {
24        buf_len: buf.len(),
25        regions: w.out,
26    })
27}
28
29pub(super) struct Walker<'a> {
30    pub(super) buf: &'a [u8],
31    pub(super) out: Vec<Region>,
32    pub(super) depth: usize,
33    /// Throwaway budget for the authoritative stream-header parsers.
34    pub(super) parser: Parser,
35}
36
37impl<'a> Walker<'a> {
38    /// Absolute offset of `s`, which must be a subslice of the tile buffer.
39    pub(super) fn off(&self, s: &'a [u8]) -> usize {
40        self.buf
41            .subslice_range(s)
42            .expect("annotated slice must come from the tile buffer")
43            .start
44    }
45
46    /// Open a container region spanning children; returns its index for [`Walker::close`].
47    pub(super) fn open(&mut self, at: &'a [u8], label: impl Into<String>) -> usize {
48        let idx = self.out.len();
49        self.out.push(Region {
50            offset: self.off(at),
51            len: 0,
52            depth: self.depth,
53            label: label.into(),
54            value: None,
55            bits: Vec::new(),
56            kind: RegionKind::Meta,
57            container: true,
58            blob: None,
59        });
60        self.depth += 1;
61        idx
62    }
63
64    /// Close the container opened at `idx`, ending it at `after`.
65    pub(super) fn close(&mut self, idx: usize, after: &'a [u8]) {
66        self.depth -= 1;
67        let start = self.out[idx].offset;
68        self.out[idx].len = self.off(after) - start;
69    }
70
71    /// Rename the container opened at `idx`.
72    #[cfg(feature = "unstable-v2")]
73    pub(super) fn relabel(&mut self, idx: usize, label: impl Into<String>) {
74        self.out[idx].label = label.into();
75    }
76
77    pub(super) fn leaf(
78        &mut self,
79        before: &'a [u8],
80        after: &'a [u8],
81        label: impl Into<String>,
82        value: Option<String>,
83    ) {
84        self.out.push(Region {
85            offset: self.off(before),
86            len: before.len() - after.len(),
87            depth: self.depth,
88            label: label.into(),
89            value,
90            bits: Vec::new(),
91            kind: RegionKind::Meta,
92            container: false,
93            blob: None,
94        });
95    }
96
97    /// Record a leaf metadata region carrying a bit-level breakdown.
98    pub(super) fn leaf_bits(
99        &mut self,
100        before: &'a [u8],
101        after: &'a [u8],
102        label: impl Into<String>,
103        value: Option<String>,
104        bits: Vec<BitField>,
105    ) {
106        self.out.push(Region {
107            offset: self.off(before),
108            len: before.len() - after.len(),
109            depth: self.depth,
110            label: label.into(),
111            value,
112            bits,
113            kind: RegionKind::Meta,
114            container: false,
115            blob: None,
116        });
117    }
118
119    /// Parse one field with a real primitive, record a leaf region, return the tail.
120    pub(super) fn field<T>(
121        &mut self,
122        before: &'a [u8],
123        label: &str,
124        parse: impl FnOnce(&'a [u8]) -> MltRefResult<'a, T>,
125        render: impl FnOnce(&T) -> Option<String>,
126    ) -> MltResult<(&'a [u8], T)> {
127        let (after, val) = parse(before)?;
128        let value = render(&val);
129        self.leaf(before, after, label, value);
130        Ok((after, val))
131    }
132
133    /// Parse one packed byte, record it with a bit-level breakdown, return the tail.
134    pub(super) fn byte_field(
135        &mut self,
136        before: &'a [u8],
137        label: &str,
138        value: impl FnOnce(u8) -> String,
139        bits: impl FnOnce(u8) -> Vec<BitField>,
140    ) -> MltResult<(&'a [u8], u8)> {
141        let (after, byte) = parse_u8(before)?;
142        self.leaf_bits(before, after, label, Some(value(byte)), bits(byte));
143        Ok((after, byte))
144    }
145
146    /// Record `len` bytes at `at` as a data blob (no decodable metadata).
147    pub(super) fn raw_blob(&mut self, at: &'a [u8], len: usize, label: impl Into<String>) {
148        self.blob(at, len, label.into(), None);
149    }
150
151    /// Record `len` bytes at `at` as a stream payload.
152    pub(super) fn stream_blob(
153        &mut self,
154        at: &'a [u8],
155        len: usize,
156        label: impl Into<String>,
157        info: BlobInfo,
158    ) {
159        self.blob(at, len, label.into(), Some(info));
160    }
161
162    fn blob(&mut self, at: &'a [u8], len: usize, label: String, blob: Option<BlobInfo>) {
163        self.out.push(Region {
164            offset: self.off(at),
165            len,
166            depth: self.depth,
167            label,
168            value: None,
169            bits: Vec::new(),
170            kind: RegionKind::DataBlob,
171            container: false,
172            blob,
173        });
174    }
175
176    fn walk_tile(&mut self) -> MltResult<()> {
177        let mut input = self.buf;
178        let mut idx = 0;
179        while !input.is_empty() {
180            input = self.walk_layer(input, idx)?;
181            idx += 1;
182        }
183        Ok(())
184    }
185
186    /// Mirror [`crate::decoder::Layer`]`::from_bytes`: `[varint size][u8 tag][value]`.
187    fn walk_layer(&mut self, input: &'a [u8], idx: usize) -> MltResult<&'a [u8]> {
188        let ci = self.open(input, format!("layer[{idx}]"));
189
190        let (input, size) = self.field(
191            input,
192            "size",
193            |i| parse_varint::<u32>(i),
194            |v| Some(format!("{v} (varint) - tag + body")),
195        )?;
196        let (input, tag) = self.field(input, "tag", parse_u8, |t| Some(tag_label(*t)))?;
197
198        let body_len = size.checked_sub(1).ok_or(MltError::ZeroLayerSize)?;
199        let (rest, body) = take(input, body_len)?;
200
201        match tag {
202            1 => self.walk_layer01(body)?,
203            #[cfg(feature = "unstable-v2")]
204            2 => self.walk_layer02(body)?,
205            _ => self.raw_blob(body, body.len(), format!("value (Unknown tag 0x{tag:02X})")),
206        }
207
208        self.close(ci, rest);
209        Ok(rest)
210    }
211}
212
213/// How the parser will read this layer tag.
214fn tag_label(tag: u8) -> String {
215    match tag {
216        1 => "0x01 -> Tag01".into(),
217        #[cfg(feature = "unstable-v2")]
218        2 => "0x02 -> Tag02".into(),
219        other => format!("0x{other:02X} -> Unknown"),
220    }
221}