1use super::UNANNOTATED;
8use super::model::{BitField, BlobInfo, DumpTree, Region, RegionKind};
9use crate::codecs::varint::parse_varint;
10use crate::utils::{parse_u8, take};
11use crate::{MltError, MltRefResult, MltResult, Parser};
12
13#[must_use]
19pub fn annotate_tile(buf: &[u8]) -> (DumpTree, Option<MltError>) {
20 let mut w = Walker {
21 buf,
22 out: Vec::new(),
23 depth: 0,
24 open_containers: Vec::new(),
25 parser: Parser::default(),
26 };
27 let err = w.walk_tile().err();
28 if err.is_some() {
29 w.seal_partial();
30 }
31 let tree = DumpTree {
32 buf_len: buf.len(),
33 regions: w.out,
34 };
35 (tree, err)
36}
37
38pub(super) struct Walker<'a> {
39 pub(super) buf: &'a [u8],
40 pub(super) out: Vec<Region>,
41 pub(super) depth: usize,
42 open_containers: Vec<usize>,
44 pub(super) parser: Parser,
46}
47
48impl<'a> Walker<'a> {
49 pub(super) fn off(&self, s: &'a [u8]) -> usize {
51 self.buf
52 .subslice_range(s)
53 .expect("annotated slice must come from the tile buffer")
54 .start
55 }
56
57 pub(super) fn open(&mut self, at: &'a [u8], label: impl Into<String>) -> usize {
59 let idx = self.out.len();
60 self.out.push(Region {
61 offset: self.off(at),
62 len: 0,
63 depth: self.depth,
64 label: label.into(),
65 value: None,
66 bits: Vec::new(),
67 kind: RegionKind::Meta,
68 container: true,
69 blob: None,
70 });
71 self.depth += 1;
72 self.open_containers.push(idx);
73 idx
74 }
75
76 pub(super) fn close(&mut self, idx: usize, after: &'a [u8]) {
78 self.depth -= 1;
79 let closed = self.open_containers.pop();
82 debug_assert_eq!(closed, Some(idx), "containers must close innermost first");
83 let start = self.out[idx].offset;
84 self.out[idx].len = self.off(after) - start;
85 }
86
87 pub(super) fn relabel(&mut self, idx: usize, label: impl Into<String>) {
89 self.out[idx].label = label.into();
90 }
91
92 pub(super) fn leaf(
93 &mut self,
94 before: &'a [u8],
95 after: &'a [u8],
96 label: impl Into<String>,
97 value: Option<String>,
98 ) {
99 self.out.push(Region {
100 offset: self.off(before),
101 len: before.len() - after.len(),
102 depth: self.depth,
103 label: label.into(),
104 value,
105 bits: Vec::new(),
106 kind: RegionKind::Meta,
107 container: false,
108 blob: None,
109 });
110 }
111
112 pub(super) fn leaf_bits(
114 &mut self,
115 before: &'a [u8],
116 after: &'a [u8],
117 label: impl Into<String>,
118 value: Option<String>,
119 bits: Vec<BitField>,
120 ) {
121 self.out.push(Region {
122 offset: self.off(before),
123 len: before.len() - after.len(),
124 depth: self.depth,
125 label: label.into(),
126 value,
127 bits,
128 kind: RegionKind::Meta,
129 container: false,
130 blob: None,
131 });
132 }
133
134 pub(super) fn field<T>(
136 &mut self,
137 before: &'a [u8],
138 label: &str,
139 parse: impl FnOnce(&'a [u8]) -> MltRefResult<'a, T>,
140 render: impl FnOnce(&T) -> Option<String>,
141 ) -> MltResult<(&'a [u8], T)> {
142 let (after, val) = parse(before)?;
143 let value = render(&val);
144 self.leaf(before, after, label, value);
145 Ok((after, val))
146 }
147
148 pub(super) fn byte_field(
150 &mut self,
151 before: &'a [u8],
152 label: &str,
153 value: impl FnOnce(u8) -> String,
154 bits: impl FnOnce(u8) -> Vec<BitField>,
155 ) -> MltResult<(&'a [u8], u8)> {
156 let (after, byte) = parse_u8(before)?;
157 self.leaf_bits(before, after, label, Some(value(byte)), bits(byte));
158 Ok((after, byte))
159 }
160
161 pub(super) fn raw_blob(&mut self, at: &'a [u8], len: usize, label: impl Into<String>) {
163 self.blob(at, len, label.into(), None);
164 }
165
166 pub(super) fn stream_blob(
168 &mut self,
169 at: &'a [u8],
170 len: usize,
171 label: impl Into<String>,
172 info: BlobInfo,
173 ) {
174 self.blob(at, len, label.into(), Some(info));
175 }
176
177 fn blob(&mut self, at: &'a [u8], len: usize, label: String, blob: Option<BlobInfo>) {
178 self.out.push(Region {
179 offset: self.off(at),
180 len,
181 depth: self.depth,
182 label,
183 value: None,
184 bits: Vec::new(),
185 kind: RegionKind::DataBlob,
186 container: false,
187 blob,
188 });
189 }
190
191 fn seal_partial(&mut self) {
197 let end = self
198 .out
199 .iter()
200 .filter(|r| !r.container)
201 .map(|r| r.offset + r.len)
202 .max()
203 .unwrap_or(0);
204
205 for idx in self.open_containers.drain(..) {
206 let offset = self.out[idx].offset;
207 debug_assert!(
208 offset <= end,
209 "a container opens at the cursor, never past it"
210 );
211 self.out[idx].len = end.saturating_sub(offset);
212 }
213
214 if end < self.buf.len() {
215 self.out.push(Region {
216 offset: end,
217 len: self.buf.len() - end,
218 depth: 0,
219 label: UNANNOTATED.to_string(),
220 value: None,
221 bits: Vec::new(),
222 kind: RegionKind::DataBlob,
223 container: false,
224 blob: None,
225 });
226 }
227 }
228
229 fn walk_tile(&mut self) -> MltResult<()> {
230 let mut input = self.buf;
231 let mut idx = 0;
232 while !input.is_empty() {
233 input = self.walk_layer(input, idx)?;
234 idx += 1;
235 }
236 Ok(())
237 }
238
239 fn walk_layer(&mut self, input: &'a [u8], idx: usize) -> MltResult<&'a [u8]> {
241 let ci = self.open(input, format!("layer[{idx}]"));
242
243 let (input, size) = self.field(
244 input,
245 "size",
246 |i| parse_varint::<u32>(i),
247 |v| Some(format!("{v} (varint) - tag + body")),
248 )?;
249 let (input, tag) = self.field(input, "tag", parse_u8, |t| Some(tag_label(*t)))?;
250
251 let body_len = size.checked_sub(1).ok_or(MltError::ZeroLayerSize)?;
252 let (rest, body) = take(input, body_len)?;
253
254 match tag {
257 1 => {
258 let name = self.walk_layer01(body)?;
259 self.relabel(ci, format!("layer[{idx}] {name:?}"));
260 }
261 #[cfg(feature = "unstable-v2")]
262 2 => {
263 let name = self.walk_layer02(body)?;
264 self.relabel(ci, format!("layer[{idx}] {name:?}"));
265 }
266 _ => self.raw_blob(body, body.len(), format!("value (Unknown tag 0x{tag:02X})")),
267 }
268
269 self.close(ci, rest);
270 Ok(rest)
271 }
272}
273
274fn tag_label(tag: u8) -> String {
276 match tag {
277 1 => "0x01 -> Tag01".into(),
278 #[cfg(feature = "unstable-v2")]
279 2 => "0x02 -> Tag02".into(),
280 other => format!("0x{other:02X} -> Unknown"),
281 }
282}