Skip to main content

retex_engine/
format.rs

1//! Format files: dumping the engine state after an IniTeX run (`\dump`) and
2//! loading it at the start of a normal run.
3//!
4//! The format is retex's own binary layout, not LuaTeX's. It is a sequence
5//! of sections (engine, then host-specific sections such as Lua bytecode),
6//! each a length-prefixed blob, behind a header that identifies the retex
7//! build. Integers are LEB128 varints (zigzag for signed values). Only
8//! non-default entries of the large tables are stored, so loading is fast.
9
10use std::collections::HashMap;
11use std::rc::Rc;
12
13use crate::Engine;
14use crate::cmd::Cmd;
15use crate::eqtb::{CodeTable, Meaning, Slot};
16use crate::fonts::{CharMetrics, FontInfo, FontKind};
17use crate::glue::Glue;
18use crate::hash::{CsId, CsKind, CsTable};
19use crate::nodes::{NodeData, NodeRef, id};
20use crate::token::{Macro, Token, TokenList};
21
22/// The magic bytes at the start of every format file.
23pub const MAGIC: &[u8; 8] = b"RETEXFMT";
24/// The format layout version; bump on any incompatible change.
25pub const FORMAT_VERSION: u32 = 7;
26
27/// Errors while loading a format.
28#[derive(Debug, thiserror::Error)]
29pub enum FormatError {
30    /// Not a retex format, or from another version.
31    #[error("not a format file for this version of retex")]
32    BadHeader,
33    /// The file ends early or contains invalid data.
34    #[error("the format file is corrupt ({0})")]
35    Corrupt(&'static str),
36}
37
38/// A byte writer with varint helpers.
39#[derive(Default)]
40pub struct Writer {
41    /// The bytes written so far.
42    pub buf: Vec<u8>,
43}
44
45impl Writer {
46    /// Writes an unsigned varint.
47    pub fn u(&mut self, mut v: u64) {
48        loop {
49            let b = (v & 0x7F) as u8;
50            v >>= 7;
51            if v == 0 {
52                self.buf.push(b);
53                return;
54            }
55            self.buf.push(b | 0x80);
56        }
57    }
58
59    /// Writes a signed varint (zigzag).
60    pub fn i(&mut self, v: i64) {
61        self.u(((v << 1) ^ (v >> 63)) as u64);
62    }
63
64    /// Writes a length-prefixed byte string.
65    pub fn bytes(&mut self, b: &[u8]) {
66        self.u(b.len() as u64);
67        self.buf.extend_from_slice(b);
68    }
69
70    /// Writes a glue specification.
71    pub fn glue(&mut self, g: &Glue) {
72        self.i(g.width.into());
73        self.i(g.stretch.into());
74        self.i(g.shrink.into());
75        self.buf.push(g.stretch_order);
76        self.buf.push(g.shrink_order);
77    }
78}
79
80/// A byte reader matching [`Writer`].
81pub struct Reader<'a> {
82    data: &'a [u8],
83    pos: usize,
84}
85
86impl<'a> Reader<'a> {
87    /// A reader over `data`.
88    pub fn new(data: &'a [u8]) -> Self {
89        Reader { data, pos: 0 }
90    }
91
92    /// Whether all data has been read.
93    pub fn at_end(&self) -> bool {
94        self.pos >= self.data.len()
95    }
96
97    /// Reads an unsigned varint.
98    pub fn u(&mut self) -> Result<u64, FormatError> {
99        let mut v: u64 = 0;
100        let mut shift = 0;
101        loop {
102            let b = *self
103                .data
104                .get(self.pos)
105                .ok_or(FormatError::Corrupt("unexpected end"))?;
106            self.pos += 1;
107            if shift >= 64 {
108                return Err(FormatError::Corrupt("varint too long"));
109            }
110            v |= u64::from(b & 0x7F) << shift;
111            if b & 0x80 == 0 {
112                return Ok(v);
113            }
114            shift += 7;
115        }
116    }
117
118    /// Reads an unsigned varint as `u32`.
119    pub fn u32(&mut self) -> Result<u32, FormatError> {
120        u32::try_from(self.u()?).map_err(|_| FormatError::Corrupt("value out of range"))
121    }
122
123    /// Reads a signed varint.
124    pub fn i(&mut self) -> Result<i64, FormatError> {
125        let v = self.u()?;
126        Ok(((v >> 1) as i64) ^ -((v & 1) as i64))
127    }
128
129    /// Reads a signed varint as `i32`.
130    pub fn i32(&mut self) -> Result<i32, FormatError> {
131        i32::try_from(self.i()?).map_err(|_| FormatError::Corrupt("value out of range"))
132    }
133
134    /// Reads one byte.
135    pub fn byte(&mut self) -> Result<u8, FormatError> {
136        let b = *self
137            .data
138            .get(self.pos)
139            .ok_or(FormatError::Corrupt("unexpected end"))?;
140        self.pos += 1;
141        Ok(b)
142    }
143
144    /// Reads a length-prefixed byte string.
145    pub fn bytes(&mut self) -> Result<&'a [u8], FormatError> {
146        let n = self.u()? as usize;
147        let end = self
148            .pos
149            .checked_add(n)
150            .filter(|&e| e <= self.data.len())
151            .ok_or(FormatError::Corrupt("string too long"))?;
152        let s = &self.data[self.pos..end];
153        self.pos = end;
154        Ok(s)
155    }
156
157    /// Reads a glue specification.
158    pub fn glue(&mut self) -> Result<Glue, FormatError> {
159        Ok(Glue {
160            width: self.i32()?,
161            stretch: self.i32()?,
162            shrink: self.i32()?,
163            stretch_order: self.byte()?,
164            shrink_order: self.byte()?,
165        })
166    }
167}
168
169fn cmd_of(b: u8) -> Result<Cmd, FormatError> {
170    Cmd::ALL
171        .get(b as usize)
172        .copied()
173        .ok_or(FormatError::Corrupt("bad command code"))
174}
175
176fn kind_code(k: &CsKind) -> (u8, u32) {
177    match k {
178        CsKind::Active(c) => (0, *c),
179        CsKind::Null => (1, 0),
180        CsKind::Single(c) => (2, *c),
181        CsKind::Multi => (3, 0),
182        CsKind::Frozen => (4, 0),
183        CsKind::Undefined => (5, 0),
184        CsKind::Internal => (6, 0),
185    }
186}
187
188fn kind_from(code: u8, c: u32) -> Result<CsKind, FormatError> {
189    Ok(match code {
190        0 => CsKind::Active(c),
191        1 => CsKind::Null,
192        2 => CsKind::Single(c),
193        3 => CsKind::Multi,
194        4 => CsKind::Frozen,
195        5 => CsKind::Undefined,
196        6 => CsKind::Internal,
197        _ => return Err(FormatError::Corrupt("bad control sequence kind")),
198    })
199}
200
201/// Collects macros and token lists so that shared lists are stored once.
202#[derive(Default)]
203struct ListPool {
204    index: HashMap<*const Token, u32>,
205    lists: Vec<TokenList>,
206}
207
208impl ListPool {
209    fn id(&mut self, l: &TokenList) -> u32 {
210        let key = l.as_ptr();
211        if l.is_empty() {
212            return 0;
213        }
214        *self.index.entry(key).or_insert_with(|| {
215            self.lists.push(l.clone());
216            self.lists.len() as u32
217        })
218    }
219}
220
221impl Engine {
222    /// Serializes the engine state into the engine section of a format.
223    pub fn dump_engine(&self) -> Vec<u8> {
224        let mut w = Writer::default();
225        let mut pool = ListPool::default();
226        // Control-sequence names.
227        w.u(self.hash.len() as u64);
228        for (name, kind) in self.hash.entries() {
229            let (k, c) = kind_code(kind);
230            w.buf.push(k);
231            w.u(c.into());
232            w.bytes(name);
233        }
234        // Meanings: (id, cmd, chr, macro flags, list id) for defined ones.
235        let mut defined: Vec<(u32, &Slot<Meaning>)> = Vec::new();
236        for (i, slot) in self.eqtb.cs.iter().enumerate() {
237            if slot.val.cmd != Cmd::UndefinedCs || slot.level != 0 {
238                defined.push((i as u32, slot));
239            }
240        }
241        let mut meanings = Writer::default();
242        meanings.u(defined.len() as u64);
243        for (i, slot) in defined {
244            meanings.u(i.into());
245            meanings.buf.push(slot.val.cmd as u8);
246            meanings.u(slot.val.chr.into());
247            meanings.u(slot.level.into());
248            match &slot.val.mac {
249                Some(m) => {
250                    meanings.buf.push(if m.protected { 2 } else { 1 });
251                    let id = pool.id(&m.tokens);
252                    meanings.u(id.into());
253                }
254                None => meanings.buf.push(0),
255            }
256        }
257        // Parameters.
258        let mut params = Writer::default();
259        for v in [&self.eqtb.int_par, &self.eqtb.dimen_par] {
260            params.u(v.len() as u64);
261            for s in v {
262                params.i(s.val.into());
263            }
264        }
265        params.u(self.eqtb.glue_par.len() as u64);
266        for s in &self.eqtb.glue_par {
267            params.glue(&s.val);
268        }
269        params.u(self.eqtb.toks_par.len() as u64);
270        for s in &self.eqtb.toks_par {
271            params.u(pool.id(&s.val).into());
272        }
273        // Registers (sparse).
274        let sparse_int = |w: &mut Writer, v: &[Slot<i32>], default: i32| {
275            let n = v.iter().filter(|s| s.val != default).count();
276            w.u(n as u64);
277            for (i, s) in v.iter().enumerate() {
278                if s.val != default {
279                    w.u(i as u64);
280                    w.i(s.val.into());
281                }
282            }
283        };
284        sparse_int(&mut params, &self.eqtb.count, 0);
285        sparse_int(&mut params, &self.eqtb.attr, crate::eqtb::UNUSED_ATTRIBUTE);
286        sparse_int(&mut params, &self.eqtb.dimen, 0);
287        for v in [&self.eqtb.skip, &self.eqtb.muskip] {
288            let n = v.iter().filter(|s| s.val != Glue::ZERO).count();
289            params.u(n as u64);
290            for (i, s) in v.iter().enumerate() {
291                if s.val != Glue::ZERO {
292                    params.u(i as u64);
293                    params.glue(&s.val);
294                }
295            }
296        }
297        let n = self.eqtb.toks.iter().filter(|s| !s.val.is_empty()).count();
298        params.u(n as u64);
299        for (i, s) in self.eqtb.toks.iter().enumerate() {
300            if !s.val.is_empty() {
301                params.u(i as u64);
302                params.u(pool.id(&s.val).into());
303            }
304        }
305        // Boxes.
306        let n = self.eqtb.boxes.iter().filter(|s| !s.val.is_null()).count();
307        params.u(n as u64);
308        for (i, s) in self.eqtb.boxes.iter().enumerate() {
309            if !s.val.is_null() {
310                params.u(i as u64);
311                self.dump_node_list(&mut params, s.val, &mut pool);
312            }
313        }
314        params.u(self.eqtb.cur_font.val.into());
315        let n = self.eqtb.math_font.iter().filter(|s| s.val != 0).count();
316        params.u(n as u64);
317        for (i, s) in self.eqtb.math_font.iter().enumerate() {
318            if s.val != 0 {
319                params.u(i as u64);
320                params.u(s.val.into());
321            }
322        }
323        let shape = |w: &mut Writer, s: &Option<Rc<[i32]>>| match s {
324            None => w.u(0),
325            Some(v) => {
326                w.u(v.len() as u64 + 1);
327                for x in v.iter() {
328                    w.i((*x).into());
329                }
330            }
331        };
332        shape(&mut params, &self.eqtb.par_shape.val);
333        params.u(self.eqtb.etex_shape.len() as u64);
334        for s in &self.eqtb.etex_shape {
335            shape(&mut params, &s.val);
336        }
337        // Code tables.
338        // Code tables are stored as whole pages of fixed-width
339        // little-endian values in a pool of distinct pages (catcode tables
340        // share most of theirs), and each table lists (page number, pool
341        // index) pairs.
342        let mut tables: Vec<(&u32, &CodeTable)> = self.eqtb.catcode_tables.iter().collect();
343        tables.sort_by_key(|(k, _)| **k);
344        let all: Vec<&CodeTable> = tables
345            .iter()
346            .map(|(_, t)| *t)
347            .chain([
348                &self.eqtb.lc,
349                &self.eqtb.uc,
350                &self.eqtb.sf,
351                &self.eqtb.math,
352                &self.eqtb.del,
353            ])
354            .collect();
355        let mut page_pool: Vec<Vec<u8>> = Vec::new();
356        let mut page_index: std::collections::HashMap<Vec<u8>, usize> =
357            std::collections::HashMap::new();
358        let mut refs: Vec<Vec<(usize, usize)>> = Vec::new();
359        for t in &all {
360            let mut r = Vec::new();
361            for (k, vals) in t.dump_pages() {
362                let mut raw = Vec::with_capacity(vals.len() * 8);
363                for v in vals {
364                    raw.extend_from_slice(&v.to_le_bytes());
365                }
366                let next = page_pool.len();
367                let i = *page_index.entry(raw.clone()).or_insert(next);
368                if i == next {
369                    page_pool.push(raw);
370                }
371                r.push((k, i));
372            }
373            refs.push(r);
374        }
375        params.u(page_pool.len() as u64);
376        for raw in &page_pool {
377            params.bytes(raw);
378        }
379        let code_table = |w: &mut Writer, r: &[(usize, usize)]| {
380            w.u(r.len() as u64);
381            for &(k, i) in r {
382                w.u(k as u64);
383                w.u(i as u64);
384            }
385        };
386        params.u(tables.len() as u64);
387        for ((k, _), r) in tables.iter().zip(&refs) {
388            params.u((**k).into());
389            code_table(&mut params, r);
390        }
391        for r in &refs[tables.len()..] {
392            code_table(&mut params, r);
393        }
394        let mut mp: Vec<(&u32, &Slot<i32>)> = self.eqtb.math_param.iter().collect();
395        mp.sort_by_key(|(k, _)| **k);
396        params.u(mp.len() as u64);
397        for (k, s) in mp {
398            params.u((*k).into());
399            params.i(s.val.into());
400        }
401        // Fonts.
402        let mut fonts = Writer::default();
403        fonts.u(self.fonts.fonts.len() as u64);
404        for (f, fi) in self.fonts.fonts.iter().enumerate() {
405            fonts.bytes(&fi.name);
406            fonts.i(fi.size.into());
407            fonts.i(fi.design_size.into());
408            fonts.u(fi.checksum.into());
409            fonts.u(fi.params.len() as u64);
410            for p in &fi.params {
411                fonts.i((*p).into());
412            }
413            let mut chars: Vec<(&u32, &CharMetrics)> = fi.chars.iter().collect();
414            chars.sort_by_key(|(c, _)| **c);
415            fonts.u(chars.len() as u64);
416            for (c, m) in chars {
417                fonts.u((*c).into());
418                fonts.i(m.width.into());
419                fonts.i(m.height.into());
420                fonts.i(m.depth.into());
421                fonts.i(m.italic.into());
422                fonts.u(m.index.into());
423            }
424            fonts.i(fi.hyphen_char.into());
425            fonts.i(fi.skew_char.into());
426            fonts.buf.push(match fi.kind {
427                FontKind::Null => 0,
428                FontKind::Tfm => 1,
429                FontKind::OpenType => 2,
430            });
431            fonts.u(fi.handle);
432            fonts.u(self.fonts.id_cs.get(f).map_or(0, |c| c.0).into());
433            let raw = fi
434                .data
435                .as_ref()
436                .and_then(|d| d.0.downcast_ref::<crate::fonts::TfmData>())
437                .map(|t| t.raw.clone())
438                .unwrap_or_default();
439            fonts.bytes(&raw);
440            match &fi.math_constants {
441                None => fonts.u(0),
442                Some(m) => {
443                    let mut kv: Vec<(&String, &i32)> = m.iter().collect();
444                    kv.sort();
445                    fonts.u(kv.len() as u64 + 1);
446                    for (k, v) in kv {
447                        fonts.bytes(k.as_bytes());
448                        fonts.i((*v).into());
449                    }
450                }
451            }
452        }
453        // Miscellaneous state.
454        let mut misc = Writer::default();
455        let cs = &self.cs;
456        for id in [
457            cs.par,
458            cs.frozen_protection,
459            cs.frozen_cr,
460            cs.frozen_end_group,
461            cs.frozen_right,
462            cs.frozen_fi,
463            cs.frozen_end_template,
464            cs.frozen_endv,
465            cs.frozen_relax,
466            cs.end_write,
467            cs.frozen_dont_expand,
468            cs.frozen_special,
469            cs.frozen_null_font,
470            cs.end_local,
471            self.par_token_cs,
472        ] {
473            misc.u(id.0.into());
474        }
475        misc.bytes(self.format_ident.as_bytes());
476        misc.buf.push(self.err.interaction as u8);
477        let mut fp: Vec<(&Vec<u8>, &CsId)> = self.frozen_prims.iter().collect();
478        fp.sort();
479        misc.u(fp.len() as u64);
480        for (n, c) in fp {
481            misc.bytes(n);
482            misc.u(c.0.into());
483        }
484        let mut bc: Vec<(&&str, &CsId)> = self.backend_cs.iter().collect();
485        bc.sort();
486        misc.u(bc.len() as u64);
487        for (n, c) in bc {
488            misc.bytes(n.as_bytes());
489            misc.u(c.0.into());
490        }
491        // Eight slots (the layout of earlier formats).
492        for i in 0..8 {
493            misc.i(self.eqtb.dir_par.get(i).map_or(0, |d| d.val).into());
494        }
495        misc.u(self.math_glues.len() as u64);
496        for g in &self.math_glues {
497            misc.glue(g);
498        }
499        // Token lists last: everything above may have added to the pool.
500        let mut lists = Writer::default();
501        lists.u(pool.lists.len() as u64);
502        for l in &pool.lists {
503            lists.u(l.len() as u64);
504            for t in l.iter() {
505                lists.u(t.0.into());
506            }
507        }
508        // Hyphenation languages.
509        let mut langs = Writer::default();
510        let present: Vec<(usize, &retex_hyph::Language)> = self
511            .languages
512            .iter()
513            .enumerate()
514            .filter_map(|(i, l)| l.as_ref().map(|l| (i, l)))
515            .collect();
516        langs.u(present.len() as u64);
517        for (i, l) in present {
518            langs.u(i as u64);
519            langs.bytes(&l.to_bytes());
520        }
521        // Assemble.
522        let mut out = Writer::default();
523        let parts = [
524            ("names", w),
525            ("token lists", lists),
526            ("meanings", meanings),
527            ("parameters and registers", params),
528            ("fonts", fonts),
529            ("misc", misc),
530            ("hyphenation", langs),
531        ];
532        for (name, part) in parts {
533            if crate::timing::enabled() {
534                eprintln!("[format] {:>10} bytes  {name}", part.buf.len());
535            }
536            out.bytes(&part.buf);
537        }
538        out.buf
539    }
540
541    fn dump_node_list(&self, w: &mut Writer, mut p: NodeRef, pool: &mut ListPool) {
542        let mut items: Vec<NodeRef> = Vec::new();
543        while !p.is_null() {
544            items.push(p);
545            p = self.nodes.next(p);
546        }
547        w.u(items.len() as u64);
548        for p in items {
549            let n = self.nodes.get(p);
550            w.buf.push(n.id);
551            w.u(n.subtype.into());
552            match &n.data {
553                NodeData::Box(b) => {
554                    for v in [b.width, b.depth, b.height, b.shift] {
555                        w.i(v.into());
556                    }
557                    w.buf.push(b.dir as u8);
558                    w.buf.push(b.glue_order);
559                    w.buf.push(b.glue_sign);
560                    w.u(b.glue_set.to_bits());
561                    self.dump_node_list(w, b.head, pool);
562                }
563                NodeData::Glue { spec, .. } => w.glue(spec),
564                NodeData::Kern { kern, .. } => w.i((*kern).into()),
565                NodeData::Penalty { penalty } => w.i((*penalty).into()),
566                NodeData::Rule {
567                    width,
568                    depth,
569                    height,
570                    ..
571                } => {
572                    for v in [*width, *depth, *height] {
573                        w.i(v.into());
574                    }
575                }
576                NodeData::Glyph(g) => {
577                    w.u(g.chr.into());
578                    w.u(g.font.into());
579                }
580                NodeData::Mark { class, mark } => {
581                    w.u((*class).into());
582                    w.u(pool.id(mark).into());
583                }
584                _ => {}
585            }
586        }
587    }
588
589    /// Restores the engine state from an engine section produced by
590    /// [`dump_engine`](Self::dump_engine). The engine must be freshly
591    /// created (not IniTeX).
592    pub fn undump_engine(&mut self, data: &[u8]) -> Result<(), FormatError> {
593        let mut top = Reader::new(data);
594        let mut hash_r = Reader::new(top.bytes()?);
595        let mut lists_r = Reader::new(top.bytes()?);
596        let mut meanings_r = Reader::new(top.bytes()?);
597        let mut params_r = Reader::new(top.bytes()?);
598        let mut fonts_r = Reader::new(top.bytes()?);
599        let mut misc_r = Reader::new(top.bytes()?);
600        let mut langs_r = Reader::new(top.bytes()?);
601
602        // Hash.
603        let n = hash_r.u()? as usize;
604        let mut entries = Vec::with_capacity(n);
605        for _ in 0..n {
606            let k = hash_r.byte()?;
607            let c = hash_r.u32()?;
608            let name: Box<[u8]> = Box::from(hash_r.bytes()?);
609            entries.push((name, kind_from(k, c)?));
610        }
611        self.hash = CsTable::from_entries(entries);
612
613        crate::timing::mark("undump: hash");
614        // Token lists.
615        let n = lists_r.u()? as usize;
616        let mut lists: Vec<TokenList> = Vec::with_capacity(n + 1);
617        lists.push(crate::token::empty_list());
618        for _ in 0..n {
619            let len = lists_r.u()? as usize;
620            let mut v = Vec::with_capacity(len);
621            for _ in 0..len {
622                v.push(Token(lists_r.u32()?));
623            }
624            lists.push(Rc::from(v));
625        }
626        let list = |i: u32| -> Result<TokenList, FormatError> {
627            lists
628                .get(i as usize)
629                .cloned()
630                .ok_or(FormatError::Corrupt("bad token list index"))
631        };
632
633        crate::timing::mark("undump: token lists");
634        // Meanings.
635        self.eqtb.cs.clear();
636        self.eqtb.ensure_cs(CsId(self.hash.len() as u32 - 1));
637        let n = meanings_r.u()? as usize;
638        for _ in 0..n {
639            let i = meanings_r.u32()? as usize;
640            let cmd = cmd_of(meanings_r.byte()?)?;
641            let chr = meanings_r.u32()?;
642            let level = meanings_r.u()? as u16;
643            let flag = meanings_r.byte()?;
644            let mac = if flag == 0 {
645                None
646            } else {
647                Some(Rc::new(Macro::new(flag == 2, list(meanings_r.u32()?)?)))
648            };
649            let slot = self
650                .eqtb
651                .cs
652                .get_mut(i)
653                .ok_or(FormatError::Corrupt("meaning index"))?;
654            *slot = Slot {
655                val: Meaning { cmd, chr, mac },
656                level,
657            };
658        }
659
660        crate::timing::mark("undump: meanings");
661        // Parameters.
662        let r = &mut params_r;
663        for which in 0..2 {
664            let n = r.u()? as usize;
665            let v = if which == 0 {
666                &mut self.eqtb.int_par
667            } else {
668                &mut self.eqtb.dimen_par
669            };
670            if v.len() < n {
671                return Err(FormatError::Corrupt("parameter count"));
672            }
673            for s in v.iter_mut().take(n) {
674                s.val = r.i32()?;
675            }
676        }
677        let n = r.u()? as usize;
678        for s in self.eqtb.glue_par.iter_mut().take(n) {
679            s.val = r.glue()?;
680        }
681        let n = r.u()? as usize;
682        for s in self.eqtb.toks_par.iter_mut().take(n) {
683            s.val = list(r.u32()?)?;
684        }
685        for which in 0..3 {
686            let n = r.u()? as usize;
687            for _ in 0..n {
688                let i = r.u()? as usize;
689                let v = r.i32()?;
690                let target = match which {
691                    0 => &mut self.eqtb.count,
692                    1 => &mut self.eqtb.attr,
693                    _ => &mut self.eqtb.dimen,
694                };
695                target
696                    .get_mut(i)
697                    .ok_or(FormatError::Corrupt("register index"))?
698                    .val = v;
699            }
700        }
701        for which in 0..2 {
702            let n = r.u()? as usize;
703            for _ in 0..n {
704                let i = r.u()? as usize;
705                let g = r.glue()?;
706                let target = if which == 0 {
707                    &mut self.eqtb.skip
708                } else {
709                    &mut self.eqtb.muskip
710                };
711                target
712                    .get_mut(i)
713                    .ok_or(FormatError::Corrupt("register index"))?
714                    .val = g;
715            }
716        }
717        let n = r.u()? as usize;
718        for _ in 0..n {
719            let i = r.u()? as usize;
720            let l = list(r.u32()?)?;
721            self.eqtb
722                .toks
723                .get_mut(i)
724                .ok_or(FormatError::Corrupt("register index"))?
725                .val = l;
726        }
727        let n = r.u()? as usize;
728        for _ in 0..n {
729            let i = r.u()? as usize;
730            let b = self.undump_node_list(r, &lists)?;
731            self.eqtb
732                .boxes
733                .get_mut(i)
734                .ok_or(FormatError::Corrupt("box index"))?
735                .val = b;
736        }
737        self.eqtb.cur_font.val = r.u32()?;
738        let n = r.u()? as usize;
739        for _ in 0..n {
740            let i = r.u()? as usize;
741            let f = r.u32()?;
742            self.eqtb
743                .math_font
744                .get_mut(i)
745                .ok_or(FormatError::Corrupt("math font index"))?
746                .val = f;
747        }
748        let read_shape = |r: &mut Reader| -> Result<Option<Rc<[i32]>>, FormatError> {
749            let n = r.u()? as usize;
750            if n == 0 {
751                return Ok(None);
752            }
753            let mut v = Vec::with_capacity(n - 1);
754            for _ in 0..n - 1 {
755                v.push(r.i32()?);
756            }
757            Ok(Some(Rc::from(v)))
758        };
759        self.eqtb.par_shape.val = read_shape(r)?;
760        let n = r.u()? as usize;
761        for i in 0..n {
762            let s = read_shape(r)?;
763            if let Some(slot) = self.eqtb.etex_shape.get_mut(i) {
764                slot.val = s;
765            }
766        }
767        let n = r.u()? as usize;
768        let mut page_pool: Vec<Rc<crate::eqtb::Page>> = Vec::with_capacity(n);
769        for _ in 0..n {
770            let raw = r.bytes()?;
771            if raw.len() != 256 * 8 {
772                return Err(FormatError::Corrupt("code table page"));
773            }
774            page_pool.push(Rc::new(std::array::from_fn(|i| {
775                let b: [u8; 8] = raw[i * 8..i * 8 + 8].try_into().unwrap_or_default();
776                Slot {
777                    val: i64::from_le_bytes(b),
778                    level: crate::eqtb::LEVEL_ONE,
779                }
780            })));
781        }
782        let read_table = |r: &mut Reader, t: &mut CodeTable| -> Result<(), FormatError> {
783            let n = r.u()? as usize;
784            for _ in 0..n {
785                let k = r.u()? as usize;
786                let i = r.u()? as usize;
787                let page = page_pool
788                    .get(i)
789                    .ok_or(FormatError::Corrupt("code table page"))?;
790                if k > 0x11_0000 / 256 {
791                    return Err(FormatError::Corrupt("code table page"));
792                }
793                t.load_page(k, page.clone());
794            }
795            Ok(())
796        };
797        crate::timing::mark("undump: registers (before code tables)");
798        let n = r.u()? as usize;
799        self.eqtb.catcode_tables.clear();
800        for _ in 0..n {
801            let k = r.u32()?;
802            let mut t = CodeTable::new(crate::eqtb::initex_catcode);
803            read_table(r, &mut t)?;
804            self.eqtb.catcode_tables.insert(k, t);
805        }
806        crate::timing::mark(&format!("undump: {n} catcode tables"));
807        read_table(r, &mut self.eqtb.lc)?;
808        read_table(r, &mut self.eqtb.uc)?;
809        read_table(r, &mut self.eqtb.sf)?;
810        read_table(r, &mut self.eqtb.math)?;
811        read_table(r, &mut self.eqtb.del)?;
812        let n = r.u()? as usize;
813        for _ in 0..n {
814            let k = r.u32()?;
815            let v = r.i32()?;
816            self.eqtb.math_param.insert(
817                k,
818                Slot {
819                    val: v,
820                    level: crate::eqtb::LEVEL_ONE,
821                },
822            );
823        }
824
825        crate::timing::mark("undump: parameters and registers");
826        // Fonts.
827        let r = &mut fonts_r;
828        let n = r.u()? as usize;
829        self.fonts.fonts.clear();
830        self.fonts.id_cs.clear();
831        for _ in 0..n {
832            let mut fi = FontInfo::null();
833            fi.name = r.bytes()?.to_vec();
834            fi.size = r.i32()?;
835            fi.design_size = r.i32()?;
836            fi.checksum = r.u32()?;
837            let np = r.u()? as usize;
838            fi.params = (0..np).map(|_| r.i32()).collect::<Result<_, _>>()?;
839            let nc = r.u()? as usize;
840            fi.chars = HashMap::with_capacity_and_hasher(nc, crate::hash::FastHash::default());
841            for _ in 0..nc {
842                let c = r.u32()?;
843                let m = CharMetrics {
844                    width: r.i32()?,
845                    height: r.i32()?,
846                    depth: r.i32()?,
847                    italic: r.i32()?,
848                    index: r.u32()?,
849                };
850                fi.chars.insert(c, m);
851            }
852            // Fonts from the format are not reused by \font (observed).
853            fi.hyphen_char = r.i32()?;
854            fi.skew_char = r.i32()?;
855            fi.kind = match r.byte()? {
856                0 => FontKind::Null,
857                1 => FontKind::Tfm,
858                _ => FontKind::OpenType,
859            };
860            fi.handle = r.u()?;
861            let cs = CsId(r.u32()?);
862            let raw = r.bytes()?;
863            if !raw.is_empty() {
864                let tfm = retex_font::tfm::Tfm::parse(raw)
865                    .map_err(|_| FormatError::Corrupt("tfm font data"))?;
866                // The ligature/kern program is rebuilt from the TFM data.
867                let scaled = tfm.scaled(fi.size);
868                fi.lig_kern = crate::fonts::lig_kern_table(&tfm, &scaled.kerns);
869                fi.right_boundary = tfm.bchar.map(u32::from);
870                fi.data = Some(crate::fonts::tfm_font_data(tfm, raw.to_vec()));
871            }
872            let nm = r.u()? as usize;
873            if nm > 0 {
874                let mut m = crate::fonts::MathConstants::new();
875                for _ in 1..nm {
876                    let k = String::from_utf8_lossy(r.bytes()?).into_owned();
877                    m.insert(k, r.i32()?);
878                }
879                fi.math_constants = Some(std::rc::Rc::new(m));
880            }
881            self.fonts.fonts.push(fi);
882            self.fonts.id_cs.push(cs);
883        }
884
885        crate::timing::mark("undump: fonts");
886        // Miscellaneous.
887        let r = &mut misc_r;
888        let mut ids = [CsId(0); 15];
889        for id in ids.iter_mut() {
890            *id = CsId(r.u32()?);
891        }
892        self.cs = crate::init::KnownCs {
893            par: ids[0],
894            frozen_protection: ids[1],
895            frozen_cr: ids[2],
896            frozen_end_group: ids[3],
897            frozen_right: ids[4],
898            frozen_fi: ids[5],
899            frozen_end_template: ids[6],
900            frozen_endv: ids[7],
901            frozen_relax: ids[8],
902            end_write: ids[9],
903            frozen_dont_expand: ids[10],
904            frozen_special: ids[11],
905            frozen_null_font: ids[12],
906            end_local: ids[13],
907        };
908        self.par_token_cs = ids[14];
909        self.format_ident = String::from_utf8_lossy(r.bytes()?).into_owned();
910        self.err.interaction = crate::error::Interaction::from_i32(i32::from(r.byte()?));
911        let n = r.u()? as usize;
912        self.frozen_prims.clear();
913        for _ in 0..n {
914            let name = r.bytes()?.to_vec();
915            let c = CsId(r.u32()?);
916            self.frozen_prims.insert(name, c);
917        }
918        let n = r.u()? as usize;
919        self.backend_cs.clear();
920        for _ in 0..n {
921            let name = r.bytes()?.to_vec();
922            let c = CsId(r.u32()?);
923            if let Some((static_name, _, _)) = crate::backend::PDF_VARIABLES
924                .iter()
925                .find(|(s, _, _)| s.as_bytes() == name.as_slice())
926            {
927                self.backend_cs.insert(static_name, c);
928            }
929        }
930        for i in 0..8 {
931            let v = r.i32()?;
932            if let Some(d) = self.eqtb.dir_par.get_mut(i) {
933                d.val = v;
934            }
935        }
936        let n = r.u()? as usize;
937        self.math_glues = (0..n).map(|_| r.glue()).collect::<Result<_, _>>()?;
938        self.printer.new_line_char = self
939            .eqtb
940            .int(crate::primitives_gen::assign_int::NEWLINECHAR);
941        crate::timing::mark("undump: misc");
942        // Languages.
943        let n = langs_r.u()? as usize;
944        self.languages.clear();
945        for _ in 0..n {
946            let i = langs_r.u()? as usize;
947            let l = retex_hyph::Language::from_bytes(langs_r.bytes()?)
948                .map_err(|_| FormatError::Corrupt("hyphenation language"))?;
949            if self.languages.len() <= i {
950                self.languages.resize_with(i + 1, || None);
951            }
952            self.languages[i] = Some(l);
953        }
954        Ok(())
955    }
956
957    fn undump_node_list(
958        &mut self,
959        r: &mut Reader,
960        lists: &[TokenList],
961    ) -> Result<NodeRef, FormatError> {
962        let n = r.u()? as usize;
963        let mut head = NodeRef::NULL;
964        let mut tail = NodeRef::NULL;
965        for _ in 0..n {
966            let t = r.byte()?;
967            let sub = r.u()? as u16;
968            let data = match t {
969                id::HLIST | id::VLIST => {
970                    let width = r.i32()?;
971                    let depth = r.i32()?;
972                    let height = r.i32()?;
973                    let shift = r.i32()?;
974                    let dir = r.byte()? as i8;
975                    let glue_order = r.byte()?;
976                    let glue_sign = r.byte()?;
977                    let glue_set = f64::from_bits(r.u()?);
978                    let head = self.undump_node_list(r, lists)?;
979                    NodeData::Box(Box::new(crate::nodes::BoxData {
980                        width,
981                        depth,
982                        height,
983                        dir,
984                        shift,
985                        glue_order,
986                        glue_sign,
987                        glue_set,
988                        head,
989                    }))
990                }
991                id::GLUE => NodeData::Glue {
992                    spec: r.glue()?,
993                    leader: NodeRef::NULL,
994                },
995                id::KERN => NodeData::Kern {
996                    kern: r.i32()?,
997                    expansion_factor: 0,
998                },
999                id::PENALTY => NodeData::Penalty { penalty: r.i32()? },
1000                id::RULE => NodeData::Rule {
1001                    width: r.i32()?,
1002                    depth: r.i32()?,
1003                    height: r.i32()?,
1004                    dir: 0,
1005                    index: 0,
1006                    left: 0,
1007                    right: 0,
1008                },
1009                id::GLYPH => {
1010                    let chr = r.u32()?;
1011                    let font = r.u32()?;
1012                    NodeData::Glyph(crate::nodes::GlyphData {
1013                        chr,
1014                        font,
1015                        ..Default::default()
1016                    })
1017                }
1018                id::MARK => {
1019                    let class = r.u32()?;
1020                    let i = r.u32()? as usize;
1021                    NodeData::Mark {
1022                        class,
1023                        mark: lists
1024                            .get(i)
1025                            .cloned()
1026                            .ok_or(FormatError::Corrupt("bad mark list"))?,
1027                    }
1028                }
1029                _ => NodeData::Temp,
1030            };
1031            let p = self.nodes.alloc(t, sub, data);
1032            if head.is_null() {
1033                head = p;
1034            } else {
1035                self.nodes.link(tail, p);
1036            }
1037            tail = p;
1038        }
1039        Ok(head)
1040    }
1041}
1042
1043/// Builds a complete format file from sections.
1044pub fn assemble(sections: &[(&str, &[u8])]) -> Vec<u8> {
1045    let mut w = Writer::default();
1046    w.buf.extend_from_slice(MAGIC);
1047    w.u(FORMAT_VERSION.into());
1048    w.bytes(env!("CARGO_PKG_VERSION").as_bytes());
1049    w.u(sections.len() as u64);
1050    for (name, data) in sections {
1051        w.bytes(name.as_bytes());
1052        w.bytes(data);
1053    }
1054    w.buf
1055}
1056
1057/// Splits a format file into its sections.
1058pub fn disassemble(data: &[u8]) -> Result<Vec<(&str, &[u8])>, FormatError> {
1059    if !data.starts_with(MAGIC) {
1060        return Err(FormatError::BadHeader);
1061    }
1062    let mut r = Reader::new(&data[MAGIC.len()..]);
1063    if r.u32()? != FORMAT_VERSION {
1064        return Err(FormatError::BadHeader);
1065    }
1066    if r.bytes()? != env!("CARGO_PKG_VERSION").as_bytes() {
1067        return Err(FormatError::BadHeader);
1068    }
1069    let n = r.u()? as usize;
1070    let mut out = Vec::with_capacity(n);
1071    for _ in 0..n {
1072        let name =
1073            std::str::from_utf8(r.bytes()?).map_err(|_| FormatError::Corrupt("section name"))?;
1074        let d = r.bytes()?;
1075        out.push((name, d));
1076    }
1077    Ok(out)
1078}
1079
1080#[cfg(test)]
1081mod tests {
1082    use super::*;
1083
1084    #[test]
1085    fn varints_round_trip() {
1086        let mut w = Writer::default();
1087        for v in [
1088            0i64,
1089            1,
1090            -1,
1091            63,
1092            -64,
1093            1 << 40,
1094            i64::from(i32::MIN),
1095            i64::from(i32::MAX),
1096        ] {
1097            w.i(v);
1098        }
1099        let mut r = Reader::new(&w.buf);
1100        for v in [
1101            0i64,
1102            1,
1103            -1,
1104            63,
1105            -64,
1106            1 << 40,
1107            i64::from(i32::MIN),
1108            i64::from(i32::MAX),
1109        ] {
1110            assert_eq!(r.i().unwrap(), v);
1111        }
1112        assert!(r.at_end());
1113    }
1114
1115    #[test]
1116    fn corrupt_data_is_an_error() {
1117        assert!(disassemble(b"nonsense").is_err());
1118        let mut r = Reader::new(&[0xFF, 0xFF]);
1119        assert!(r.u().is_err());
1120    }
1121}