1use usize_cast::IntoUsize as _;
12
13use super::model::{BitField, BlobInfo, DecodeHint, DumpTree, Region, RegionKind};
14use crate::codecs::varint::parse_varint;
15use crate::decoder::{Column, ColumnType, DictionaryType, StreamType};
16use crate::utils::{parse_string, parse_u8, take};
17use crate::wire::{LogicalEncoding, LogicalTechnique, PhysicalEncoding, StreamMeta};
18use crate::{MltError, MltRefResult, MltResult, Parser};
19
20pub fn annotate_tile(buf: &[u8]) -> MltResult<DumpTree> {
24 let mut w = Walker {
25 buf,
26 out: Vec::new(),
27 depth: 0,
28 parser: Parser::default(),
29 };
30 w.walk_tile()?;
31 Ok(DumpTree {
32 buf_len: buf.len(),
33 regions: w.out,
34 })
35}
36
37struct Walker<'a> {
38 buf: &'a [u8],
39 out: Vec<Region>,
40 depth: usize,
41 parser: Parser,
43}
44
45impl<'a> Walker<'a> {
46 fn off(&self, s: &'a [u8]) -> usize {
48 (s.as_ptr() as usize) - (self.buf.as_ptr() as usize)
49 }
50
51 fn open(&mut self, at: &'a [u8], label: String) -> usize {
53 let idx = self.out.len();
54 self.out.push(Region {
55 offset: self.off(at),
56 len: 0,
57 depth: self.depth,
58 label,
59 value: None,
60 bits: Vec::new(),
61 kind: RegionKind::Meta,
62 container: true,
63 blob: None,
64 });
65 self.depth += 1;
66 idx
67 }
68
69 fn close(&mut self, idx: usize, after: &'a [u8]) {
71 self.depth -= 1;
72 let start = self.out[idx].offset;
73 self.out[idx].len = self.off(after) - start;
74 }
75
76 fn leaf(&mut self, before: &'a [u8], after: &'a [u8], label: String, value: Option<String>) {
77 self.out.push(Region {
78 offset: self.off(before),
79 len: before.len() - after.len(),
80 depth: self.depth,
81 label,
82 value,
83 bits: Vec::new(),
84 kind: RegionKind::Meta,
85 container: false,
86 blob: None,
87 });
88 }
89
90 fn leaf_bits(
92 &mut self,
93 before: &'a [u8],
94 after: &'a [u8],
95 label: String,
96 value: Option<String>,
97 bits: Vec<BitField>,
98 ) {
99 self.out.push(Region {
100 offset: self.off(before),
101 len: before.len() - after.len(),
102 depth: self.depth,
103 label,
104 value,
105 bits,
106 kind: RegionKind::Meta,
107 container: false,
108 blob: None,
109 });
110 }
111
112 fn field<T>(
114 &mut self,
115 before: &'a [u8],
116 label: &str,
117 parse: impl FnOnce(&'a [u8]) -> MltRefResult<'a, T>,
118 render: impl FnOnce(&T) -> Option<String>,
119 ) -> MltResult<(&'a [u8], T)> {
120 let (after, val) = parse(before)?;
121 let value = render(&val);
122 self.leaf(before, after, label.to_string(), value);
123 Ok((after, val))
124 }
125
126 fn raw_blob(&mut self, before: &'a [u8], after: &'a [u8], label: String) {
128 self.out.push(Region {
129 offset: self.off(before),
130 len: before.len() - after.len(),
131 depth: self.depth,
132 label,
133 value: None,
134 bits: Vec::new(),
135 kind: RegionKind::DataBlob,
136 container: false,
137 blob: None,
138 });
139 }
140
141 fn walk_tile(&mut self) -> MltResult<()> {
142 let mut input = self.buf;
143 let mut idx = 0;
144 while !input.is_empty() {
145 input = self.walk_layer(input, idx)?;
146 idx += 1;
147 }
148 Ok(())
149 }
150
151 fn walk_layer(&mut self, input: &'a [u8], idx: usize) -> MltResult<&'a [u8]> {
153 let start = input;
154 let ci = self.open(start, format!("layer[{idx}]"));
155
156 let (input, size) = self.field(
157 input,
158 "size",
159 |i| parse_varint::<u32>(i),
160 |v| Some(format!("{v} (varint) — tag + body")),
161 )?;
162 let (input, tag) = self.field(input, "tag", parse_u8, |t| {
163 Some(match t {
164 1 => "0x01 → Tag01".to_string(),
165 other => format!("0x{other:02X} → Unknown"),
166 })
167 })?;
168
169 let body_len = size.checked_sub(1).ok_or(MltError::ZeroLayerSize)?;
170 let (rest, body) = take(input, body_len)?;
171
172 if tag == 1 {
173 self.walk_layer01(body)?;
174 } else {
175 let end = &body[body.len()..];
176 self.raw_blob(body, end, format!("value (Unknown tag 0x{tag:02X})"));
177 }
178
179 self.close(ci, rest);
180 Ok(rest)
181 }
182
183 fn walk_layer01(&mut self, input: &'a [u8]) -> MltResult<()> {
186 let (input, _name) = self.field(input, "name", parse_string, |s| Some(format!("{s:?}")))?;
187 let (input, _extent) = self.field(
188 input,
189 "extent",
190 |i| parse_varint::<u32>(i),
191 |v| Some(v.to_string()),
192 )?;
193 let (input, column_count) = self.field(
194 input,
195 "column_count",
196 |i| parse_varint::<u32>(i),
197 |v| Some(v.to_string()),
198 )?;
199
200 let (mut input, columns) = self.walk_schema(input, column_count)?;
201
202 if !columns.is_empty() {
203 let di = self.open(input, "column data".to_string());
204 for (ci, col) in columns.iter().enumerate() {
205 input = self.walk_column_data(input, ci, col)?;
206 }
207 self.close(di, input);
208 }
209
210 if !input.is_empty() {
212 let end = &input[input.len()..];
213 self.raw_blob(input, end, "trailing bytes".to_string());
214 }
215 Ok(())
216 }
217
218 fn walk_schema(
220 &mut self,
221 mut input: &'a [u8],
222 column_count: u32,
223 ) -> MltResult<(&'a [u8], Vec<Column<'a>>)> {
224 let si = self.open(input, "schema".to_string());
225 if input.len() < column_count.into_usize() {
226 return Err(MltError::BufferUnderflow(column_count, input.len()));
227 }
228 let mut cols = Vec::with_capacity(column_count.into_usize());
229 for i in 0..column_count {
230 let (rest, col) = self.walk_column_def(input, i)?;
231 input = rest;
232 cols.push(col);
233 }
234 self.close(si, input);
235 Ok((input, cols))
236 }
237
238 fn walk_column_def(&mut self, input: &'a [u8], i: u32) -> MltResult<(&'a [u8], Column<'a>)> {
241 let ci = self.open(input, format!("column[{i}]"));
242
243 let (after_ty, typ) = ColumnType::from_bytes(input)?;
245 let byte = typ as u8;
246 let bits = vec![
247 BitField {
248 hi: 7,
249 lo: 1,
250 raw: u64::from(byte >> 1),
251 meaning: format!("base type = {typ:?}"),
252 },
253 BitField {
254 hi: 0,
255 lo: 0,
256 raw: u64::from(byte & 1),
257 meaning: format!("optional = {}", typ.is_optional()),
258 },
259 ];
260 self.leaf_bits(
261 input,
262 after_ty,
263 "type".to_string(),
264 Some(format!("0x{byte:02X} {typ:?}")),
265 bits,
266 );
267 let mut input = after_ty;
268
269 let name = if typ.has_name() {
270 let (rest, name) =
271 self.field(input, "name", parse_string, |s| Some(format!("{s:?}")))?;
272 input = rest;
273 Some(name)
274 } else {
275 None
276 };
277
278 let mut children = Vec::new();
279 if typ == ColumnType::SharedDict {
280 let (rest, child_count) = self.field(
281 input,
282 "child_count",
283 |i| parse_varint::<u32>(i),
284 |v| Some(v.to_string()),
285 )?;
286 input = rest;
287 if input.len() < child_count.into_usize() {
288 return Err(MltError::BufferUnderflow(child_count, input.len()));
289 }
290 children.reserve(child_count.into_usize());
291 for j in 0..child_count {
292 let (rest, child) = self.walk_column_def(input, j)?;
293 input = rest;
294 children.push(child);
295 }
296 }
297
298 self.close(ci, input);
299 Ok((
300 input,
301 Column {
302 typ,
303 name,
304 children,
305 },
306 ))
307 }
308
309 fn walk_column_data(
310 &mut self,
311 input: &'a [u8],
312 ci: usize,
313 col: &Column<'a>,
314 ) -> MltResult<&'a [u8]> {
315 use ColumnType as C;
316 let typ = col.typ;
317 let name_suffix = col.name.map(|n| format!(" {n:?}")).unwrap_or_default();
318 let gi = self.open(input, format!("column[{ci}] {typ:?}{name_suffix}"));
319
320 let mut input = input;
321 match typ {
322 C::Id | C::OptId => {
323 input = self.walk_optional(input, typ)?;
324 input = self.walk_stream(input, false, "id", |_| DecodeHint::U32)?.0;
325 }
326 C::LongId | C::OptLongId => {
327 input = self.walk_optional(input, typ)?;
328 input = self.walk_stream(input, false, "id", |_| DecodeHint::U64)?.0;
329 }
330 C::Geometry => {
331 input = self.walk_geometry(input)?;
332 }
333 C::Bool | C::OptBool => {
334 input = self.walk_optional(input, typ)?;
335 input = self
336 .walk_stream(input, true, "data", |_| DecodeHint::Bool)?
337 .0;
338 }
339 C::I8 | C::OptI8 | C::I32 | C::OptI32 => {
340 input = self.walk_optional(input, typ)?;
341 input = self
342 .walk_stream(input, false, "data", |_| DecodeHint::I32)?
343 .0;
344 }
345 C::U8 | C::OptU8 | C::U32 | C::OptU32 => {
346 input = self.walk_optional(input, typ)?;
347 input = self
348 .walk_stream(input, false, "data", |_| DecodeHint::U32)?
349 .0;
350 }
351 C::I64 | C::OptI64 => {
352 input = self.walk_optional(input, typ)?;
353 input = self
354 .walk_stream(input, false, "data", |_| DecodeHint::I64)?
355 .0;
356 }
357 C::U64 | C::OptU64 => {
358 input = self.walk_optional(input, typ)?;
359 input = self
360 .walk_stream(input, false, "data", |_| DecodeHint::U64)?
361 .0;
362 }
363 C::F32 | C::OptF32 => {
364 input = self.walk_optional(input, typ)?;
365 input = self
366 .walk_stream(input, false, "data", |_| DecodeHint::F32)?
367 .0;
368 }
369 C::F64 | C::OptF64 => {
370 input = self.walk_optional(input, typ)?;
371 input = self
372 .walk_stream(input, false, "data", |_| DecodeHint::F64)?
373 .0;
374 }
375 C::Str | C::OptStr => {
376 input = self.walk_str(input, typ)?;
377 }
378 C::SharedDict => {
379 input = self.walk_shared_dict(input, col)?;
380 }
381 }
382
383 self.close(gi, input);
384 Ok(input)
385 }
386
387 fn walk_optional(&mut self, input: &'a [u8], typ: ColumnType) -> MltResult<&'a [u8]> {
389 if typ.is_optional() {
390 Ok(self
391 .walk_stream(input, true, "present", |_| DecodeHint::Presence)?
392 .0)
393 } else {
394 Ok(input)
395 }
396 }
397
398 fn walk_geometry(&mut self, input: &'a [u8]) -> MltResult<&'a [u8]> {
400 let (mut input, stream_count) = self.field(
401 input,
402 "stream_count",
403 |i| parse_varint::<u32>(i),
404 |v| Some(v.to_string()),
405 )?;
406 if stream_count == 0 {
407 return Err(MltError::GeometryWithoutStreams);
408 }
409 input = self.walk_stream(input, false, "meta", geom_hint)?.0;
410 for j in 0..stream_count - 1 {
411 input = self
412 .walk_stream(input, false, &format!("stream[{j}]"), geom_hint)?
413 .0;
414 }
415 Ok(input)
416 }
417
418 fn walk_str(&mut self, input: &'a [u8], typ: ColumnType) -> MltResult<&'a [u8]> {
421 let (mut input, stream_count) = self.field(
422 input,
423 "stream_count",
424 |i| parse_varint::<u32>(i),
425 |v| Some(v.to_string()),
426 )?;
427 let mut remaining = stream_count.into_usize();
428 if typ.is_optional() {
429 if remaining == 0 {
430 return Err(MltError::UnsupportedStringStreamCount(remaining));
431 }
432 input = self
433 .walk_stream(input, true, "present", |_| DecodeHint::Presence)?
434 .0;
435 remaining -= 1;
436 }
437 for j in 0..remaining {
438 input = self
439 .walk_stream(input, false, &format!("stream[{j}]"), auto_hint)?
440 .0;
441 }
442 Ok(input)
443 }
444
445 fn walk_shared_dict(&mut self, input: &'a [u8], col: &Column<'a>) -> MltResult<&'a [u8]> {
447 let (mut input, _stream_count) = self.field(
448 input,
449 "stream_count",
450 |i| parse_varint::<u32>(i),
451 |v| Some(v.to_string()),
452 )?;
453
454 let mut taken = 0usize;
456 loop {
457 let (rest, meta) =
458 self.walk_stream(input, false, &format!("dict_stream[{taken}]"), auto_hint)?;
459 input = rest;
460 taken += 1;
461 if matches!(
462 meta.stream_type,
463 StreamType::Data(DictionaryType::Single | DictionaryType::Shared)
464 ) {
465 break;
466 }
467 if taken >= 5 {
468 return Err(MltError::UnsupportedStringStreamCount(taken + 1));
469 }
470 }
471
472 for (j, child) in col.children.iter().enumerate() {
474 let cci = self.open(input, format!("child[{j}] {:?}", child.typ));
475 let (rest, _sc) = self.field(
476 input,
477 "stream_count",
478 |i| parse_varint::<u32>(i),
479 |v| Some(v.to_string()),
480 )?;
481 input = rest;
482 if child.typ.is_optional() {
483 input = self
484 .walk_stream(input, true, "present", |_| DecodeHint::Presence)?
485 .0;
486 }
487 input = self.walk_stream(input, false, "data", auto_hint)?.0;
488 self.close(cci, input);
489 }
490 Ok(input)
491 }
492
493 fn walk_stream(
496 &mut self,
497 input: &'a [u8],
498 is_bool: bool,
499 label: &str,
500 hint: impl FnOnce(StreamType) -> DecodeHint,
501 ) -> MltResult<(&'a [u8], StreamMeta)> {
502 let si = self.open(input, label.to_string());
503
504 let (after_hdr, (meta, byte_length)) =
506 StreamMeta::from_bytes(input, is_bool, &mut self.parser)?;
507
508 let hi = self.open(input, "header".to_string());
510 let mut c = input;
511
512 let (c1, st_byte) = parse_u8(c)?;
513 self.leaf_bits(
514 c,
515 c1,
516 "stream_type".to_string(),
517 Some(format!("0x{st_byte:02X} {:?}", meta.stream_type)),
518 stream_type_bits(meta.stream_type, st_byte),
519 );
520 c = c1;
521
522 let (c2, enc_byte) = parse_u8(c)?;
523 self.leaf_bits(
524 c,
525 c2,
526 "encoding".to_string(),
527 Some(format!(
528 "0x{enc_byte:02X} logical={:?} physical={:?}",
529 meta.encoding.logical, meta.encoding.physical
530 )),
531 encoding_bits(enc_byte),
532 );
533 c = c2;
534
535 (c, _) = self.field(
536 c,
537 "num_values",
538 |i| parse_varint::<u32>(i),
539 |v| Some(v.to_string()),
540 )?;
541 (c, _) = self.field(
542 c,
543 "byte_length",
544 |i| parse_varint::<u32>(i),
545 |v| Some(v.to_string()),
546 )?;
547
548 match meta.encoding.logical {
549 LogicalEncoding::Rle(_) | LogicalEncoding::DeltaRle(_) if !is_bool => {
550 (c, _) = self.field(
551 c,
552 "runs",
553 |i| parse_varint::<u32>(i),
554 |v| Some(v.to_string()),
555 )?;
556 (c, _) = self.field(
557 c,
558 "num_rle_values",
559 |i| parse_varint::<u32>(i),
560 |v| Some(v.to_string()),
561 )?;
562 }
563 LogicalEncoding::Morton(_)
564 | LogicalEncoding::MortonDelta(_)
565 | LogicalEncoding::MortonRle(_) => {
566 (c, _) = self.field(
567 c,
568 "bits",
569 |i| parse_varint::<u32>(i),
570 |v| Some(v.to_string()),
571 )?;
572 (c, _) = self.field(
573 c,
574 "shift",
575 |i| parse_varint::<u32>(i),
576 |v| Some(v.to_string()),
577 )?;
578 }
579 _ => {}
580 }
581 self.close(hi, c);
582
583 if self.off(c) != self.off(after_hdr) {
585 return Err(MltError::NotImplemented("stream header re-walk desync"));
586 }
587
588 let (rest, _payload) = take(after_hdr, byte_length)?;
589 self.out.push(Region {
590 offset: self.off(after_hdr),
591 len: byte_length.into_usize(),
592 depth: self.depth,
593 label: "data".to_string(),
594 value: None,
595 bits: Vec::new(),
596 kind: RegionKind::DataBlob,
597 container: false,
598 blob: Some(BlobInfo {
599 meta,
600 hint: hint(meta.stream_type),
601 }),
602 });
603
604 self.close(si, rest);
605 Ok((rest, meta))
606 }
607}
608
609fn auto_hint(st: StreamType) -> DecodeHint {
611 match st {
612 StreamType::Present => DecodeHint::Presence,
613 StreamType::Offset(_) | StreamType::Length(_) => DecodeHint::U32,
614 StreamType::Data(_) => DecodeHint::Bytes,
615 }
616}
617
618fn geom_hint(st: StreamType) -> DecodeHint {
621 match st {
622 StreamType::Present => DecodeHint::Presence,
623 StreamType::Offset(_) | StreamType::Length(_) => DecodeHint::U32,
624 StreamType::Data(_) => DecodeHint::I32,
625 }
626}
627
628fn stream_type_bits(st: StreamType, byte: u8) -> Vec<BitField> {
630 let category = match st {
631 StreamType::Present => "Present",
632 StreamType::Data(_) => "Data",
633 StreamType::Offset(_) => "Offset",
634 StreamType::Length(_) => "Length",
635 };
636 let subtype = match st {
637 StreamType::Present => "—".to_string(),
638 StreamType::Data(d) => format!("{d:?}"),
639 StreamType::Offset(o) => format!("{o:?}"),
640 StreamType::Length(l) => format!("{l:?}"),
641 };
642 vec![
643 BitField {
644 hi: 7,
645 lo: 4,
646 raw: u64::from(byte >> 4),
647 meaning: format!("category = {category}"),
648 },
649 BitField {
650 hi: 3,
651 lo: 0,
652 raw: u64::from(byte & 0x0F),
653 meaning: format!("subtype = {subtype}"),
654 },
655 ]
656}
657
658fn encoding_bits(byte: u8) -> Vec<BitField> {
660 let l1 = byte >> 5;
661 let l2 = (byte >> 2) & 0x7;
662 let ph = byte & 0x3;
663 let name_lt = |v: u8| {
664 LogicalTechnique::try_from(v).map_or_else(|_| format!("invalid({v})"), |t| format!("{t:?}"))
665 };
666 let name_ph = PhysicalEncoding::try_from(ph)
667 .map_or_else(|_| format!("invalid({ph})"), |p| format!("{p:?}"));
668 vec![
669 BitField {
670 hi: 7,
671 lo: 5,
672 raw: u64::from(l1),
673 meaning: format!("logical1 = {}", name_lt(l1)),
674 },
675 BitField {
676 hi: 4,
677 lo: 2,
678 raw: u64::from(l2),
679 meaning: format!("logical2 = {}", name_lt(l2)),
680 },
681 BitField {
682 hi: 1,
683 lo: 0,
684 raw: u64::from(ph),
685 meaning: format!("physical = {name_ph}"),
686 },
687 ]
688}