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 #[cfg(feature = "unstable-v2")]
89 pub(super) fn relabel(&mut self, idx: usize, label: impl Into<String>) {
90 self.out[idx].label = label.into();
91 }
92
93 pub(super) fn leaf(
94 &mut self,
95 before: &'a [u8],
96 after: &'a [u8],
97 label: impl Into<String>,
98 value: Option<String>,
99 ) {
100 self.out.push(Region {
101 offset: self.off(before),
102 len: before.len() - after.len(),
103 depth: self.depth,
104 label: label.into(),
105 value,
106 bits: Vec::new(),
107 kind: RegionKind::Meta,
108 container: false,
109 blob: None,
110 });
111 }
112
113 pub(super) fn leaf_bits(
115 &mut self,
116 before: &'a [u8],
117 after: &'a [u8],
118 label: impl Into<String>,
119 value: Option<String>,
120 bits: Vec<BitField>,
121 ) {
122 self.out.push(Region {
123 offset: self.off(before),
124 len: before.len() - after.len(),
125 depth: self.depth,
126 label: label.into(),
127 value,
128 bits,
129 kind: RegionKind::Meta,
130 container: false,
131 blob: None,
132 });
133 }
134
135 pub(super) fn field<T>(
137 &mut self,
138 before: &'a [u8],
139 label: &str,
140 parse: impl FnOnce(&'a [u8]) -> MltRefResult<'a, T>,
141 render: impl FnOnce(&T) -> Option<String>,
142 ) -> MltResult<(&'a [u8], T)> {
143 let (after, val) = parse(before)?;
144 let value = render(&val);
145 self.leaf(before, after, label, value);
146 Ok((after, val))
147 }
148
149 pub(super) fn byte_field(
151 &mut self,
152 before: &'a [u8],
153 label: &str,
154 value: impl FnOnce(u8) -> String,
155 bits: impl FnOnce(u8) -> Vec<BitField>,
156 ) -> MltResult<(&'a [u8], u8)> {
157 let (after, byte) = parse_u8(before)?;
158 self.leaf_bits(before, after, label, Some(value(byte)), bits(byte));
159 Ok((after, byte))
160 }
161
162 pub(super) fn raw_blob(&mut self, at: &'a [u8], len: usize, label: impl Into<String>) {
164 self.blob(at, len, label.into(), None);
165 }
166
167 pub(super) fn stream_blob(
169 &mut self,
170 at: &'a [u8],
171 len: usize,
172 label: impl Into<String>,
173 info: BlobInfo,
174 ) {
175 self.blob(at, len, label.into(), Some(info));
176 }
177
178 fn blob(&mut self, at: &'a [u8], len: usize, label: String, blob: Option<BlobInfo>) {
179 self.out.push(Region {
180 offset: self.off(at),
181 len,
182 depth: self.depth,
183 label,
184 value: None,
185 bits: Vec::new(),
186 kind: RegionKind::DataBlob,
187 container: false,
188 blob,
189 });
190 }
191
192 fn seal_partial(&mut self) {
198 let end = self
199 .out
200 .iter()
201 .filter(|r| !r.container)
202 .map(|r| r.offset + r.len)
203 .max()
204 .unwrap_or(0);
205
206 for idx in self.open_containers.drain(..) {
207 let offset = self.out[idx].offset;
208 debug_assert!(
209 offset <= end,
210 "a container opens at the cursor, never past it"
211 );
212 self.out[idx].len = end.saturating_sub(offset);
213 }
214
215 if end < self.buf.len() {
216 self.out.push(Region {
217 offset: end,
218 len: self.buf.len() - end,
219 depth: 0,
220 label: UNANNOTATED.to_string(),
221 value: None,
222 bits: Vec::new(),
223 kind: RegionKind::DataBlob,
224 container: false,
225 blob: None,
226 });
227 }
228 }
229
230 fn walk_tile(&mut self) -> MltResult<()> {
231 let mut input = self.buf;
232 let mut idx = 0;
233 while !input.is_empty() {
234 input = self.walk_layer(input, idx)?;
235 idx += 1;
236 }
237 Ok(())
238 }
239
240 fn walk_layer(&mut self, input: &'a [u8], idx: usize) -> MltResult<&'a [u8]> {
242 let ci = self.open(input, format!("layer[{idx}]"));
243
244 let (input, size) = self.field(
245 input,
246 "size",
247 |i| parse_varint::<u32>(i),
248 |v| Some(format!("{v} (varint) - tag + body")),
249 )?;
250 let (input, tag) = self.field(input, "tag", parse_u8, |t| Some(tag_label(*t)))?;
251
252 let body_len = size.checked_sub(1).ok_or(MltError::ZeroLayerSize)?;
253 let (rest, body) = take(input, body_len)?;
254
255 match tag {
256 1 => self.walk_layer01(body)?,
257 #[cfg(feature = "unstable-v2")]
258 2 => self.walk_layer02(body)?,
259 _ => self.raw_blob(body, body.len(), format!("value (Unknown tag 0x{tag:02X})")),
260 }
261
262 self.close(ci, rest);
263 Ok(rest)
264 }
265}
266
267fn tag_label(tag: u8) -> String {
269 match tag {
270 1 => "0x01 -> Tag01".into(),
271 #[cfg(feature = "unstable-v2")]
272 2 => "0x02 -> Tag02".into(),
273 other => format!("0x{other:02X} -> Unknown"),
274 }
275}