Skip to main content

teamcraft/mesh/creo/
tagged.rs

1//! The tagged binary format of Creo sections (`docs/src/research/creo-brep-preview.md`).
2//!
3//! A section is a stream of values whose schema is written on first use:
4//! `e0 TT name 00` defines field number N (in order of appearance) with type
5//! TT, and the value follows. The writer remembers, for every field, which
6//! transition came after it last time – how many struct levels close and
7//! which field comes next – and only writes the tokens that differ
8//! (`f1 f2 f3`/`f4 N` close levels, `f7 NN` names the next field, `f0` is
9//! "zero closes, explicitly"). Structs begin with `e2`/`e3`, `e1` is null,
10//! `f8 N`/`f9 R C`/`fa K d…` start arrays (dimensions are remembered per
11//! field; `fc R` reuses the column count), `fb` precedes the elements of a
12//! struct array, `e4`–`e7 N` repeat the previous array element.
13//!
14//! Integers: `00`–`7f`, `80`–`bf` + 1 byte, `c0`–`df` + 2 bytes, `e8`–`eb`
15//! + 1–4 bytes, `f5 <int>` negated, `f6` = −1. Doubles: see [`double`].
16
17use std::collections::HashMap;
18
19use anyhow::{anyhow, bail, Result};
20
21/// Field types that are read as integers.
22/// Largest number of nodes built from one section (memory guard).
23const MAX_NODES: usize = 20_000_000;
24/// Largest array announced by one header.
25const MAX_ARRAY: usize = 50_000_000;
26
27/// A parsed section.
28#[derive(Debug, Default)]
29pub struct Tree {
30    pub fields: Vec<Field>,
31    pub nodes: Vec<Node>,
32}
33
34#[derive(Debug, Clone)]
35pub struct Field {
36    pub ty: u8,
37    pub name: String,
38    dims: Option<Vec<usize>>,
39}
40
41#[derive(Debug, Clone)]
42pub enum Node {
43    Null,
44    Int(i64),
45    Dbl(f64),
46    Str(String),
47    /// Leaf array (nested lists for 2-D arrays).
48    List(Vec<usize>),
49    /// Struct: (field, child node) in file order; a field may repeat.
50    Struct(Vec<(u32, usize)>),
51}
52
53impl Tree {
54    pub fn root(&self) -> usize {
55        0
56    }
57
58    pub fn node(&self, i: usize) -> &Node {
59        self.nodes.get(i).unwrap_or(&Node::Null)
60    }
61
62    /// The first child `name` of struct `i`.
63    pub fn child(&self, i: usize, name: &str) -> Option<usize> {
64        match self.node(i) {
65            Node::Struct(kids) => kids
66                .iter()
67                .find(|(f, _)| {
68                    self.fields
69                        .get(*f as usize)
70                        .is_some_and(|fd| fd.name == name)
71                })
72                .map(|(_, c)| *c),
73            _ => None,
74        }
75    }
76
77    /// All children of struct `i` whose field name starts with `prefix`,
78    /// with their full field names.
79    pub fn children_with_prefix(&self, i: usize, prefix: &str) -> Vec<(&str, usize)> {
80        match self.node(i) {
81            Node::Struct(kids) => kids
82                .iter()
83                .filter_map(|(f, c)| {
84                    let name = self.fields.get(*f as usize)?.name.as_str();
85                    name.starts_with(prefix).then_some((name, *c))
86                })
87                .collect(),
88            _ => Vec::new(),
89        }
90    }
91
92    /// All children named `name` (repeated fields, flattened lists).
93    pub fn children(&self, i: usize, name: &str) -> Vec<usize> {
94        match self.node(i) {
95            Node::Struct(kids) => kids
96                .iter()
97                .filter(|(f, _)| {
98                    self.fields
99                        .get(*f as usize)
100                        .is_some_and(|fd| fd.name == name)
101                })
102                .map(|(_, c)| *c)
103                .collect(),
104            _ => Vec::new(),
105        }
106    }
107
108    pub fn int(&self, i: usize) -> Option<i64> {
109        match self.node(i) {
110            Node::Int(v) => Some(*v),
111            Node::Dbl(v) if v.fract() == 0.0 => Some(*v as i64),
112            _ => None,
113        }
114    }
115
116    pub fn int_at(&self, i: usize, name: &str) -> Option<i64> {
117        self.child(i, name).and_then(|c| self.int(c))
118    }
119
120    pub fn dbl(&self, i: usize) -> Option<f64> {
121        match self.node(i) {
122            Node::Dbl(v) => Some(*v),
123            Node::Int(v) => Some(*v as f64),
124            _ => None,
125        }
126    }
127
128    pub fn dbl_at(&self, i: usize, name: &str) -> Option<f64> {
129        self.child(i, name).and_then(|c| self.dbl(c))
130    }
131
132    pub fn list(&self, i: usize) -> &[usize] {
133        match self.node(i) {
134            Node::List(v) => v,
135            _ => &[],
136        }
137    }
138
139    /// A flat list of doubles (NaN for anything that is not a number).
140    pub fn dbls(&self, i: usize) -> Vec<f64> {
141        self.list(i)
142            .iter()
143            .map(|&c| self.dbl(c).unwrap_or(f64::NAN))
144            .collect()
145    }
146
147    /// A list of rows of doubles.
148    pub fn rows(&self, i: usize) -> Vec<Vec<f64>> {
149        self.list(i).iter().map(|&r| self.dbls(r)).collect()
150    }
151
152    pub fn str_at(&self, i: usize, name: &str) -> Option<&str> {
153        match self.child(i, name).map(|c| self.node(c)) {
154            Some(Node::Str(s)) => Some(s.as_str()),
155            _ => None,
156        }
157    }
158}
159
160struct Frame {
161    field: u32,
162    node: usize,
163    is_array: bool,
164    /// Elements still expected (struct arrays).
165    left: usize,
166    /// R9: node holding foreign fields written at array level.
167    extra: Option<usize>,
168}
169
170struct Reader<'a> {
171    d: &'a [u8],
172    p: usize,
173    tree: Tree,
174    /// Remembered transition per field: (closes, next field).
175    trans: HashMap<u32, (usize, u32)>,
176    stack: Vec<Frame>,
177    explicit: bool,
178}
179
180/// Parses a tagged section.
181pub fn parse(data: &[u8]) -> Result<Tree> {
182    let mut r = Reader {
183        d: data,
184        p: 0,
185        tree: Tree::default(),
186        trans: HashMap::new(),
187        stack: Vec::new(),
188        explicit: false,
189    };
190    r.run()?;
191    Ok(r.tree)
192}
193
194impl<'a> Reader<'a> {
195    fn byte(&mut self) -> Result<u8> {
196        let b = *self
197            .d
198            .get(self.p)
199            .ok_or_else(|| anyhow!("section ends inside a value"))?;
200        self.p += 1;
201        Ok(b)
202    }
203
204    fn peek(&self) -> Option<u8> {
205        self.d.get(self.p).copied()
206    }
207
208    /// The bytes not read yet.
209    fn rest(&self) -> &'a [u8] {
210        self.d.get(self.p..).unwrap_or_default()
211    }
212
213    fn field(&self, f: u32) -> Result<&Field> {
214        self.tree
215            .fields
216            .get(f as usize)
217            .ok_or_else(|| anyhow!("unknown field {f}"))
218    }
219
220    fn set_dims(&mut self, f: u32, dims: Vec<usize>) {
221        if let Some(fld) = self.tree.fields.get_mut(f as usize) {
222            fld.dims = Some(dims);
223        }
224    }
225
226    fn take(&mut self, n: usize) -> Result<&'a [u8]> {
227        let end = self
228            .p
229            .checked_add(n)
230            .filter(|&e| e <= self.d.len())
231            .ok_or_else(|| anyhow!("section ends inside a value"))?;
232        let s = self.d.get(self.p..end).unwrap_or_default();
233        self.p = end;
234        Ok(s)
235    }
236
237    fn read_int(&mut self) -> Result<i64> {
238        // `f5` negates the integer that follows; counted in a loop, so a run
239        // of them in a damaged file cannot exhaust the stack
240        let mut negate = false;
241        let mut b = self.byte()?;
242        while b == 0xf5 {
243            negate = !negate;
244            b = self.byte()?;
245        }
246        let v = self.read_int_after(b)?;
247        Ok(if negate {
248            v.checked_neg()
249                .ok_or_else(|| anyhow!("integer out of range"))?
250        } else {
251            v
252        })
253    }
254
255    fn read_int_after(&mut self, b: u8) -> Result<i64> {
256        Ok(match b {
257            0x00..=0x7f => i64::from(b),
258            0x80..=0xbf => (i64::from(b & 0x3f) << 8) | i64::from(self.byte()?),
259            0xc0..=0xdf => {
260                let hi = i64::from(b & 0x1f) << 16;
261                let m = i64::from(self.byte()?) << 8;
262                hi | m | i64::from(self.byte()?)
263            }
264            0xe8..=0xeb => {
265                let n = usize::from(b - 0xe7);
266                let s = self.take(n)?;
267                let mut v: i64 = 0;
268                for &x in s {
269                    v = (v << 8) | i64::from(x);
270                }
271                if n == 4 {
272                    v = i64::from(v as u32 as i32);
273                }
274                v
275            }
276            0xf6 => -1,
277            other => bail!("integer token {other:02x} at {}", self.p - 1),
278        })
279    }
280
281    fn read_string(&mut self) -> Result<Node> {
282        if self.peek() == Some(0xe1) {
283            self.p += 1;
284            return Ok(Node::Null);
285        }
286        let rest = self.rest();
287        let end = rest
288            .iter()
289            .position(|&b| b == 0)
290            .ok_or_else(|| anyhow!("unterminated string"))?;
291        let s = String::from_utf8_lossy(rest.get(..end).unwrap_or_default()).into_owned();
292        self.p += end + 1;
293        Ok(Node::Str(s))
294    }
295
296    fn read_scalar(&mut self, ty: u8) -> Result<Node> {
297        Ok(match ty {
298            0x02 => {
299                let (v, n) = double::decode(self.rest())?;
300                self.p += n;
301                Node::Dbl(v)
302            }
303            0x03 | 0x0a => self.read_string()?,
304            0x06 => {
305                let (v, n) = double::decode_float(self.rest())?;
306                self.p += n;
307                Node::Dbl(v)
308            }
309            _ => Node::Int(self.read_int()?),
310        })
311    }
312
313    fn read_values(&mut self, ty: u8, count: usize) -> Result<Vec<usize>> {
314        if count > MAX_ARRAY {
315            bail!("array of {count} elements");
316        }
317        let mut out: Vec<usize> = Vec::new();
318        while out.len() < count {
319            if let (Some(b @ 0xe4..=0xe7), Some(&last)) = (self.peek(), out.last()) {
320                self.p += 1;
321                let n = if b == 0xe7 {
322                    usize::try_from(self.read_int()?).unwrap_or(usize::MAX)
323                } else {
324                    usize::from(b - 0xe3)
325                };
326                if out.len().saturating_add(n) > count {
327                    bail!("array repeat beyond the array");
328                }
329                out.extend(std::iter::repeat_n(last, n));
330                continue;
331            }
332            let v = self.read_scalar(ty)?;
333            out.push(self.push(v)?);
334        }
335        Ok(out)
336    }
337
338    fn push(&mut self, n: Node) -> Result<usize> {
339        if self.tree.nodes.len() >= MAX_NODES {
340            bail!("section with more than {MAX_NODES} values");
341        }
342        self.tree.nodes.push(n);
343        Ok(self.tree.nodes.len() - 1)
344    }
345
346    fn dim(&mut self) -> Result<usize> {
347        let v = self.read_int()?;
348        usize::try_from(v)
349            .ok()
350            .filter(|&n| n <= MAX_ARRAY)
351            .ok_or_else(|| anyhow!("array dimension {v}"))
352    }
353
354    fn shaped(&mut self, ty: u8, dims: &[usize]) -> Result<Node> {
355        let total = dims
356            .iter()
357            .try_fold(1usize, |a, &d| a.checked_mul(d))
358            .filter(|&n| n <= MAX_ARRAY)
359            .ok_or_else(|| anyhow!("array too large"))?;
360        let flat = self.read_values(ty, total)?;
361        if let [r, c] = *dims {
362            if c == 0 {
363                return Ok(Node::List(flat));
364            }
365            let mut rows = Vec::with_capacity(r);
366            for chunk in flat.chunks(c) {
367                rows.push(self.push(Node::List(chunk.to_vec()))?);
368            }
369            return Ok(Node::List(rows));
370        }
371        Ok(Node::List(flat))
372    }
373
374    fn read_leaf(&mut self, field: u32) -> Result<Node> {
375        let ty = self.field(field)?.ty;
376        let dims = self.field(field)?.dims.clone();
377        match self.peek() {
378            Some(0xf8) => {
379                self.p += 1;
380                let n = self.dim()?;
381                self.set_dims(field, vec![n]);
382                self.shaped(ty, &[n])
383            }
384            Some(0xfc) if dims.as_ref().is_some_and(|d| d.len() == 2) => {
385                self.p += 1;
386                let r = self.dim()?;
387                let c = dims.as_ref().and_then(|d| d.get(1)).copied().unwrap_or(0);
388                self.set_dims(field, vec![r, c]);
389                self.shaped(ty, &[r, c])
390            }
391            Some(0xf9) => {
392                self.p += 1;
393                let r = self.dim()?;
394                let c = self.dim()?;
395                self.set_dims(field, vec![r, c]);
396                self.shaped(ty, &[r, c])
397            }
398            Some(0xfa) => {
399                self.p += 1;
400                let k = self.dim()?;
401                if k > 8 {
402                    bail!("{k}-dimensional array");
403                }
404                let mut ds = Vec::with_capacity(k);
405                for _ in 0..k {
406                    ds.push(self.dim()?);
407                }
408                self.set_dims(field, ds.clone());
409                // flat, like the prototype
410                let total = ds
411                    .iter()
412                    .try_fold(1usize, |a, &d| a.checked_mul(d))
413                    .filter(|&n| n <= MAX_ARRAY)
414                    .ok_or_else(|| anyhow!("array too large"))?;
415                Ok(Node::List(self.read_values(ty, total)?))
416            }
417            _ => match dims {
418                Some(ds) => self.shaped(ty, &ds),
419                None => self.read_scalar(ty),
420            },
421        }
422    }
423
424    fn read_def(&mut self) -> Result<u32> {
425        if self.byte()? != 0xe0 {
426            bail!("field definition expected");
427        }
428        let ty = self.byte()?;
429        let Node::Str(name) = self.read_string()? else {
430            bail!("field without name");
431        };
432        if self.tree.fields.len() >= u32::MAX as usize / 2 {
433            bail!("too many fields");
434        }
435        self.tree.fields.push(Field {
436            ty,
437            name,
438            dims: None,
439        });
440        Ok(self.tree.fields.len() as u32 - 1)
441    }
442
443    fn read_closes(&mut self) -> Result<usize> {
444        let mut n = 0usize;
445        self.explicit = false;
446        loop {
447            match self.peek() {
448                Some(b @ 0xf1..=0xf3) => {
449                    self.explicit = true;
450                    self.p += 1;
451                    n += usize::from(b - 0xf0);
452                }
453                Some(0xf4) => {
454                    self.explicit = true;
455                    self.p += 1;
456                    n = n.saturating_add(usize::try_from(self.read_int()?).unwrap_or(usize::MAX));
457                }
458                Some(0xf0) => {
459                    self.explicit = true;
460                    self.p += 1;
461                }
462                _ => return Ok(n),
463            }
464        }
465    }
466
467    fn close(&mut self, n: usize) -> Result<()> {
468        for _ in 0..n {
469            if self.stack.len() <= 1 {
470                bail!("more levels closed than open at {}", self.p);
471            }
472            self.stack.pop();
473        }
474        Ok(())
475    }
476
477    fn run(&mut self) -> Result<()> {
478        let root_field = self.read_def()?;
479        let tok = self.byte()?;
480        if self.field(root_field)?.ty != 0 || !(tok == 0xe2 || tok == 0xe3) {
481            bail!("section does not start with a struct");
482        }
483        let root = self.push(Node::Struct(Vec::new()))?;
484        self.stack.push(Frame {
485            field: root_field,
486            node: root,
487            is_array: false,
488            left: 0,
489            extra: None,
490        });
491        let mut key = root_field;
492        let mut last_p = usize::MAX;
493        let mut stall = 0;
494        while self.p < self.d.len() {
495            if self.p == last_p {
496                stall += 1;
497                if stall > 50 {
498                    bail!("parser stalled at {}", self.p);
499                }
500            } else {
501                stall = 0;
502            }
503            last_p = self.p;
504            let mut closes = self.read_closes()?;
505            let Some(b) = self.peek() else { break };
506            let mut explicit = self.explicit;
507            let next: u32;
508            if b == 0xf7 || b == 0xe0 {
509                if b == 0xf7 {
510                    self.p += 1;
511                    let id = self.read_int()?;
512                    // field numbers in the file count from 1
513                    next = u32::try_from(id)
514                        .ok()
515                        .and_then(|i| i.checked_sub(1))
516                        .filter(|&i| (i as usize) < self.tree.fields.len())
517                        .ok_or_else(|| anyhow!("unknown field {id}"))?;
518                } else {
519                    next = self.read_def()?;
520                }
521                closes += self.read_closes()?;
522                explicit |= self.explicit;
523                if !explicit {
524                    closes = self.trans.get(&key).map_or(0, |m| m.0);
525                }
526                self.trans.insert(key, (closes, next));
527            } else {
528                let m = self.trans.get(&key).copied();
529                match (explicit, m) {
530                    (true, Some((_, n))) => {
531                        next = n;
532                        self.trans.insert(key, (closes, next));
533                    }
534                    (false, Some((c, n))) => {
535                        closes = c;
536                        next = n;
537                    }
538                    (_, None) => bail!("no transition remembered for field {key} at {}", self.p),
539                }
540            }
541            self.close(closes)?;
542            key = self.read_value(next)?;
543        }
544        Ok(())
545    }
546
547    fn store(&mut self, field: u32, child: usize) {
548        let Some(top) = self.stack.last() else { return };
549        let node = top.node;
550        if let Some(Node::Struct(kids)) = self.tree.nodes.get_mut(node) {
551            kids.push((field, child));
552        } else if let Some(Node::List(items)) = self.tree.nodes.get_mut(node) {
553            items.push(child);
554        }
555    }
556
557    fn read_value(&mut self, field: u32) -> Result<u32> {
558        let top_is_array = self.stack.last().is_some_and(|f| f.is_array);
559        let top_field = self.stack.last().map_or(u32::MAX, |f| f.field);
560        if top_is_array && field != top_field {
561            // R9: a foreign field at array level lives in a side struct
562            let extra = match self.stack.last().and_then(|f| f.extra) {
563                Some(e) => e,
564                None => {
565                    let e = self.push(Node::Struct(Vec::new()))?;
566                    if let Some(f) = self.stack.last_mut() {
567                        f.extra = Some(e);
568                    }
569                    e
570                }
571            };
572            let depth = self.stack.len();
573            self.stack.push(Frame {
574                field: top_field,
575                node: extra,
576                is_array: false,
577                left: 0,
578                extra: None,
579            });
580            let r = self.read_value(field);
581            // the pseudo frame is removed again unless a struct was opened under it
582            if self.stack.len() == depth + 1 {
583                self.stack.pop();
584            } else if self.stack.len() > depth {
585                self.stack.remove(depth);
586            }
587            return r;
588        }
589        let ty = self.field(field)?.ty;
590        if ty == 0 {
591            let b = self.byte()?;
592            match b {
593                0xe1 => {
594                    let n = self.push(Node::Null)?;
595                    if let Some(top) = self.stack.last_mut() {
596                        if top.is_array {
597                            top.left = top.left.saturating_sub(1);
598                        }
599                    }
600                    self.store(field, n);
601                    return Ok(field);
602                }
603                0xf8 | 0xf9 if !top_is_array => {
604                    let mut n = self.dim()?;
605                    if b == 0xf9 {
606                        n = n
607                            .checked_mul(self.dim()?)
608                            .filter(|&x| x <= MAX_ARRAY)
609                            .ok_or_else(|| anyhow!("array too large"))?;
610                    }
611                    let list = self.push(Node::List(Vec::new()))?;
612                    self.store(field, list);
613                    if n == 0 {
614                        return Ok(field);
615                    }
616                    self.stack.push(Frame {
617                        field,
618                        node: list,
619                        is_array: true,
620                        left: n,
621                        extra: None,
622                    });
623                    return Ok(field);
624                }
625                0xfb if top_is_array => {
626                    let b2 = self.byte()?;
627                    if b2 == 0xe1 {
628                        let n = self.push(Node::Null)?;
629                        if let Some(top) = self.stack.last_mut() {
630                            top.left = top.left.saturating_sub(1);
631                        }
632                        self.store(field, n);
633                        return Ok(field);
634                    }
635                    if !(b2 == 0xe2 || b2 == 0xe3) {
636                        bail!("struct token {b2:02x} at {}", self.p - 1);
637                    }
638                }
639                0xe2 | 0xe3 => {}
640                other => bail!("struct token {other:02x} at {}", self.p - 1),
641            }
642            let node = self.push(Node::Struct(Vec::new()))?;
643            if let Some(top) = self.stack.last_mut() {
644                if top.is_array {
645                    top.left = top.left.saturating_sub(1);
646                }
647            }
648            self.store(field, node);
649            self.stack.push(Frame {
650                field,
651                node,
652                is_array: false,
653                left: 0,
654                extra: None,
655            });
656            return Ok(field);
657        }
658        let leaf = self.read_leaf(field)?;
659        let n = self.push(leaf)?;
660        self.store(field, n);
661        Ok(field)
662    }
663}
664
665/// Compact doubles of the tagged format.
666pub mod double {
667    use anyhow::{anyhow, bail, Result};
668
669    use std::f64::consts::PI;
670
671    /// Top byte of the ten groups of tokens `19`–`4a`.
672    const GROUP_TOP: [u8; 10] = [0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0];
673
674    /// One-byte constants.
675    fn constant(t: u8) -> Option<f64> {
676        Some(match t {
677            0x03 => 0.125,
678            0x04 => f64::from_bits(0x3FEF_FFFF_FFFF_FFFF),
679            0x0b => 0.06,
680            0x0c => 0.1,
681            0x0d => 0.25,
682            0x0e => 0.5,
683            0x0f => 1.0,
684            0x10 => -1.0,
685            0x11 => PI,
686            0x12 => 0.5 * PI,
687            0x13 => 2.0 * PI,
688            0x14 => 1.5 * PI,
689            0x15 => -PI,
690            0x16 => -0.5 * PI,
691            0x17 => -2.0 * PI,
692            0x18 => 0.0,
693            0x05..=0x0a => f64::NAN, // unknown constants
694            _ => return None,
695        })
696    }
697
698    /// Top 16 bits (sign, exponent, four mantissa bits) of the class tokens
699    /// `4b`–`df`.
700    fn class_top(t: u8) -> Option<u16> {
701        Some(match t {
702            0x4b => 0x3fb0,
703            0x4c => 0x3fb1,
704            0x4d => 0x3fb5,
705            0x4e => 0x3fc0,
706            0x4f..=0x53 => 0x3fc1 + u16::from(t - 0x4f),
707            0x54..=0x57 => 0x3fc7 + u16::from(t - 0x54),
708            0x58 | 0x59 => 0x3fcc + u16::from(t - 0x58),
709            0x5a..=0xa3 => 0x3fcf + u16::from(t - 0x5a),
710            0xa4 => 0xbfc0,
711            0xa5 | 0xa6 => 0xbfd0 + u16::from(t - 0xa5),
712            0xa7..=0xb2 => 0xbfd3 + u16::from(t - 0xa7),
713            0xb3..=0xcf => 0xbfe0 + u16::from(t - 0xb3),
714            0xd0..=0xdc => 0xbffe + u16::from(t - 0xd0),
715            0xdd => 0xc00c,
716            0xde => 0xc010,
717            0xdf => 0xc011,
718            _ => return None,
719        })
720    }
721
722    fn bytes_to_f64(b: [u8; 8]) -> f64 {
723        f64::from_be_bytes(b)
724    }
725
726    /// Decodes one double at the start of `d`: (value, bytes consumed).
727    pub fn decode(d: &[u8]) -> Result<(f64, usize)> {
728        let &t = d
729            .first()
730            .ok_or_else(|| anyhow!("section ends inside a number"))?;
731        let need = |n: usize| -> Result<&[u8]> {
732            d.get(1..1 + n)
733                .ok_or_else(|| anyhow!("section ends inside a number"))
734        };
735        if let Some(c) = constant(t) {
736            return Ok((c, 1));
737        }
738        match t {
739            0x00..=0x02 => {
740                // near-constant: constant `c` with its lowest n bytes replaced
741                let n = usize::from(t) + 1;
742                let s = need(n + 1)?;
743                let (&c, low_bytes) = s
744                    .split_first()
745                    .ok_or_else(|| anyhow!("section ends inside a number"))?;
746                let base = constant(c).unwrap_or(f64::NAN);
747                if base.is_nan() {
748                    return Ok((f64::NAN, n + 2));
749                }
750                let mut bits = base.to_bits();
751                let mask = (1u64 << (8 * n)) - 1;
752                let mut low = 0u64;
753                for &x in low_bytes {
754                    low = (low << 8) | u64::from(x);
755                }
756                bits = (bits & !mask) | low;
757                Ok((f64::from_bits(bits), n + 2))
758            }
759            0x19..=0x4a => {
760                let g = usize::from((t - 0x19) / 5);
761                let k = (t - 0x19) % 5;
762                let top = GROUP_TOP.get(g).copied().unwrap_or(0x3c);
763                let mut raw = [0u8; 8];
764                raw[0] = top;
765                let used = match k {
766                    0 => {
767                        raw[1..8].copy_from_slice(need(7)?);
768                        7
769                    }
770                    1 => {
771                        // two bytes, the last one repeated to the end
772                        let &[a, b] = need(2)? else {
773                            bail!("section ends inside a number")
774                        };
775                        raw = [top, a, b, b, b, b, b, b];
776                        2
777                    }
778                    2 => {
779                        let &[a, b] = need(2)? else {
780                            bail!("section ends inside a number")
781                        };
782                        raw = [top, a, b, 0, 0, 0, 0, 0];
783                        2
784                    }
785                    3 => {
786                        raw[1..4].copy_from_slice(need(3)?);
787                        3
788                    }
789                    _ => {
790                        raw[1..7].copy_from_slice(need(6)?);
791                        6
792                    }
793                };
794                Ok((bytes_to_f64(raw), 1 + used))
795            }
796            0x4b..=0xdf => {
797                let top = class_top(t).unwrap_or(0x7ff8);
798                let s = need(6)?;
799                let mut raw = [0u8; 8];
800                raw[0..2].copy_from_slice(&top.to_be_bytes());
801                raw[2..8].copy_from_slice(s);
802                Ok((bytes_to_f64(raw), 7))
803            }
804            0xe8..=0xef => {
805                let n = match t {
806                    0xe8 => 1,
807                    0xe9 => 2,
808                    0xea => 3,
809                    0xeb => 4,
810                    0xec => 7,
811                    0xed => 8,
812                    0xee => 3,
813                    _ => 2,
814                };
815                let s = need(n)?;
816                let mut raw = [0u8; 8];
817                for (r, &x) in raw.iter_mut().zip(s) {
818                    *r = x;
819                }
820                Ok((bytes_to_f64(raw), 1 + n))
821            }
822            other => bail!("number token {other:02x}"),
823        }
824    }
825
826    /// Type-06 numbers (float32 precision stored with the same token
827    /// families, three payload bytes): (value, bytes consumed).
828    pub fn decode_float(d: &[u8]) -> Result<(f64, usize)> {
829        let &t = d
830            .first()
831            .ok_or_else(|| anyhow!("section ends inside a number"))?;
832        let need = |n: usize| -> Result<&[u8]> {
833            d.get(1..1 + n)
834                .ok_or_else(|| anyhow!("section ends inside a number"))
835        };
836        match t {
837            0x00..=0x32 => Ok((
838                if t == 0 {
839                    0.0
840                } else {
841                    constant(t).unwrap_or(f64::NAN)
842                },
843                1,
844            )),
845            0x50..=0xb7 | 0xbc..=0xdf => {
846                need(2)?;
847                Ok((f64::NAN, 3))
848            }
849            0xe8..=0xeb => {
850                let n = usize::from(t - 0xe7);
851                let s = need(n)?;
852                let mut raw = [0u8; 4];
853                for (r, &x) in raw.iter_mut().zip(s) {
854                    *r = x;
855                }
856                Ok((f64::from(f32::from_be_bytes(raw)), 1 + n))
857            }
858            0x33..=0x4f | 0xb8..=0xbb => {
859                let s = need(3)?;
860                let mut raw = [0u8; 8];
861                if (0x33..=0x4a).contains(&t) {
862                    raw[0] = GROUP_TOP
863                        .get(usize::from((t - 0x19) / 5))
864                        .copied()
865                        .unwrap_or(0x3c);
866                    raw[1..4].copy_from_slice(s);
867                } else {
868                    let top = class_top(t).unwrap_or(0x7ff8);
869                    raw[0..2].copy_from_slice(&top.to_be_bytes());
870                    raw[2..5].copy_from_slice(s);
871                }
872                Ok((bytes_to_f64(raw), 4))
873            }
874            other => bail!("float token {other:02x}"),
875        }
876    }
877}
878
879/// Writes sections in the tagged format, reduced to what the readers use:
880/// every transition is spelled out (`f0`–`f4` closes, then `f7 NN` or a
881/// field definition), integers in four bytes, doubles in eight. For tests
882/// and synthetic files (`asm::sample`); Creo itself writes far more
883/// compactly.
884#[doc(hidden)]
885pub struct Writer {
886    out: Vec<u8>,
887    fields: Vec<(u8, String)>,
888    /// Levels to close before the next value.
889    pending: usize,
890}
891
892/// Field types of the writer.
893const TY_STRUCT: u8 = 0x00;
894const TY_INT: u8 = 0x01;
895const TY_DOUBLE: u8 = 0x02;
896const TY_STRING: u8 = 0x03;
897
898impl Writer {
899    /// A section whose root struct is `root`.
900    pub fn new(root: &str) -> Writer {
901        let mut w = Writer {
902            out: Vec::new(),
903            fields: vec![(TY_STRUCT, root.to_string())],
904            pending: 0,
905        };
906        w.out.extend([0xe0, TY_STRUCT]);
907        w.out.extend(root.as_bytes());
908        w.out.extend([0x00, 0xe2]);
909        w
910    }
911
912    fn int_bytes(&mut self, v: i64) {
913        if v < 0 {
914            self.out.push(0xf5);
915        }
916        let a = v.unsigned_abs();
917        if a < 0x80 {
918            self.out.push(a as u8);
919        } else {
920            self.out.push(0xeb);
921            self.out
922                .extend((a.min(i32::MAX as u64) as u32).to_be_bytes());
923        }
924    }
925
926    /// Closes, then names the field (defining it on first use).
927    fn key(&mut self, name: &str, ty: u8) {
928        match self.pending {
929            0 => self.out.push(0xf0),
930            n @ 1..=3 => self.out.push(0xf0 + n as u8),
931            n => {
932                self.out.push(0xf4);
933                self.int_bytes(n as i64);
934            }
935        }
936        self.pending = 0;
937        match self.fields.iter().position(|(t, n)| *t == ty && n == name) {
938            Some(i) => {
939                self.out.push(0xf7);
940                self.int_bytes(i as i64 + 1);
941            }
942            None => {
943                self.fields.push((ty, name.to_string()));
944                self.out.extend([0xe0, ty]);
945                self.out.extend(name.as_bytes());
946                self.out.push(0x00);
947            }
948        }
949    }
950
951    fn dbl_bytes(&mut self, v: f64) {
952        self.out.push(0xed);
953        self.out.extend(v.to_be_bytes());
954    }
955
956    pub fn int(&mut self, name: &str, v: i64) -> &mut Writer {
957        self.key(name, TY_INT);
958        self.int_bytes(v);
959        self
960    }
961
962    pub fn dbl(&mut self, name: &str, v: f64) -> &mut Writer {
963        self.key(name, TY_DOUBLE);
964        self.dbl_bytes(v);
965        self
966    }
967
968    pub fn string(&mut self, name: &str, s: &str) -> &mut Writer {
969        self.key(name, TY_STRING);
970        self.out.extend(s.as_bytes());
971        self.out.push(0x00);
972        self
973    }
974
975    pub fn dbls(&mut self, name: &str, v: &[f64]) -> &mut Writer {
976        self.key(name, TY_DOUBLE);
977        self.out.push(0xf8);
978        self.int_bytes(v.len() as i64);
979        for &x in v {
980            self.dbl_bytes(x);
981        }
982        self
983    }
984
985    pub fn strings(&mut self, name: &str, v: &[&str]) -> &mut Writer {
986        self.key(name, TY_STRING);
987        self.out.push(0xf8);
988        self.int_bytes(v.len() as i64);
989        for s in v {
990            self.out.extend(s.as_bytes());
991            self.out.push(0x00);
992        }
993        self
994    }
995
996    /// Opens a struct; [`Writer::end`] closes it.
997    pub fn begin(&mut self, name: &str) -> &mut Writer {
998        self.key(name, TY_STRUCT);
999        self.out.push(0xe2);
1000        self
1001    }
1002
1003    /// Opens an array of `n` structs; each element is opened with
1004    /// [`Writer::element`] and closed with [`Writer::end`], the array itself
1005    /// with a last [`Writer::end`] (none for `n` = 0).
1006    pub fn begin_array(&mut self, name: &str, n: usize) -> &mut Writer {
1007        self.key(name, TY_STRUCT);
1008        self.out.push(0xf8);
1009        self.int_bytes(n as i64);
1010        self
1011    }
1012
1013    pub fn element(&mut self, array: &str) -> &mut Writer {
1014        self.key(array, TY_STRUCT);
1015        self.out.extend([0xfb, 0xe2]);
1016        self
1017    }
1018
1019    pub fn end(&mut self) -> &mut Writer {
1020        self.pending += 1;
1021        self
1022    }
1023
1024    pub fn finish(&self) -> Vec<u8> {
1025        self.out.clone()
1026    }
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031    use super::*;
1032
1033    #[test]
1034    fn the_writer_round_trips_through_the_reader() {
1035        let mut w = Writer::new("top");
1036        w.int("count", 2)
1037            .int("neg", -300)
1038            .dbl("pi", std::f64::consts::PI)
1039            .string("name", "MS_TIP")
1040            .dbls("axis", &[1.0, -0.5, 2.0])
1041            .begin_array("items", 2);
1042        for k in 0..2 {
1043            w.element("items")
1044                .int("id", k)
1045                .dbls("o", &[k as f64; 3])
1046                .end();
1047        }
1048        w.end()
1049            .begin("tail")
1050            .strings("words", &["a", "Offset", "in"])
1051            .end();
1052        let t = parse(&w.finish()).unwrap();
1053        let r = t.root();
1054        assert_eq!(t.int_at(r, "count"), Some(2));
1055        assert_eq!(t.int_at(r, "neg"), Some(-300));
1056        assert_eq!(t.dbl_at(r, "pi"), Some(std::f64::consts::PI));
1057        assert_eq!(t.str_at(r, "name"), Some("MS_TIP"));
1058        assert_eq!(t.dbls(t.child(r, "axis").unwrap()), vec![1.0, -0.5, 2.0]);
1059        let items = t.list(t.child(r, "items").unwrap());
1060        assert_eq!(items.len(), 2);
1061        assert_eq!(t.int_at(items[1], "id"), Some(1));
1062        assert_eq!(t.dbls(t.child(items[1], "o").unwrap()), vec![1.0; 3]);
1063        let tail = t.child(r, "tail").unwrap();
1064        let words: Vec<_> = t
1065            .list(t.child(tail, "words").unwrap())
1066            .iter()
1067            .map(|&i| match t.node(i) {
1068                Node::Str(s) => s.clone(),
1069                _ => String::new(),
1070            })
1071            .collect();
1072        assert_eq!(words, ["a", "Offset", "in"]);
1073    }
1074
1075    fn visib_geom() -> Vec<u8> {
1076        let data = std::fs::read(concat!(
1077            env!("CARGO_MANIFEST_DIR"),
1078            "/tests/fixtures/cad/nist/ctc-01/creo-10/nist_ctc_01_asme1_rd_cr10030.prt.5"
1079        ))
1080        .unwrap();
1081        let toc = super::super::container::toc(&data).unwrap();
1082        super::super::container::section(&data, &toc, "VisibGeom")
1083            .unwrap()
1084            .unwrap()
1085    }
1086
1087    #[test]
1088    fn numbers_decode_like_the_prototype() {
1089        // 3π/2 + 2 ulp as a near-constant
1090        let (v, n) = double::decode(&[0x00, 0x14, 0xd4]).unwrap();
1091        assert_eq!(n, 3);
1092        assert_eq!(v.to_bits(), 0x4012_d97c_7f33_21d4);
1093        // group token 2e (top 0x40, two bytes, last byte repeated): 5.05
1094        let (v, n) = double::decode(&[0x2e, 0x14, 0x33]).unwrap();
1095        assert_eq!(n, 3);
1096        assert!((v - 5.05).abs() < 1e-12, "{v}");
1097        // class token d8 with 6 mantissa bytes: -2.8463…
1098        let (v, n) = double::decode(&[0xd8, 0xc5, 0x37, 0x3d, 0x13, 0x16, 0x18]).unwrap();
1099        assert_eq!(n, 7);
1100        assert!((v + 2.8463).abs() < 1e-4, "{v}");
1101        assert_eq!(double::decode(&[0x0f]).unwrap(), (1.0, 1));
1102        assert!(double::decode(&[0xf7]).is_err());
1103        assert!(double::decode(&[0x46, 1]).is_err());
1104    }
1105
1106    #[test]
1107    fn the_visible_geometry_of_ctc_01_parses() {
1108        let sec = visib_geom();
1109        let tree = parse(&sec).unwrap();
1110        let root = tree.root();
1111        let active = tree.child(root, "active_geom").unwrap();
1112        let surfaces = tree.child(active, "srf_array").unwrap();
1113        assert_eq!(tree.list(surfaces).len(), 141);
1114        let curves = tree.child(active, "crv_array").unwrap();
1115        assert_eq!(tree.list(curves).len(), 422);
1116        let first = tree.list(surfaces)[0];
1117        assert_eq!(tree.int_at(first, "geom_id"), Some(106));
1118        assert_eq!(tree.int_at(first, "geom_type"), Some(34));
1119        let prim = tree.child(first, "srf_prim_ptr(plane)").unwrap();
1120        let ls = tree.rows(tree.child(prim, "local_sys").unwrap());
1121        assert_eq!(ls.len(), 4);
1122        assert_eq!(ls[0], [1.0, 0.0, 0.0]);
1123        let body = tree.child(active, "body_array").unwrap();
1124        let b = tree.list(body)[0];
1125        assert_eq!(tree.int_at(b, "body_id"), Some(-5778));
1126        assert_eq!(
1127            tree.str_at(b, "mat_name_ptr"),
1128            Some("PTC_SYSTEM_MTRL_PROPS")
1129        );
1130    }
1131
1132    #[test]
1133    fn cut_and_damaged_sections_are_errors_not_panics() {
1134        let sec = visib_geom();
1135        for cut in (0..sec.len()).step_by(sec.len() / 60) {
1136            let _ = parse(&sec[..cut]);
1137        }
1138        let mut state = 0x9e37_79b9_7f4a_7c15u64;
1139        for _ in 0..30 {
1140            let mut damaged = sec.clone();
1141            for _ in 0..8 {
1142                state ^= state << 13;
1143                state ^= state >> 7;
1144                state ^= state << 17;
1145                let at = (state % damaged.len() as u64) as usize;
1146                damaged[at] = (state >> 32) as u8;
1147            }
1148            let _ = parse(&damaged);
1149        }
1150    }
1151}