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 {
648                    protected: flag == 2,
649                    tokens: list(meanings_r.u32()?)?,
650                }))
651            };
652            let slot = self
653                .eqtb
654                .cs
655                .get_mut(i)
656                .ok_or(FormatError::Corrupt("meaning index"))?;
657            *slot = Slot {
658                val: Meaning { cmd, chr, mac },
659                level,
660            };
661        }
662
663        crate::timing::mark("undump: meanings");
664        // Parameters.
665        let r = &mut params_r;
666        for which in 0..2 {
667            let n = r.u()? as usize;
668            let v = if which == 0 {
669                &mut self.eqtb.int_par
670            } else {
671                &mut self.eqtb.dimen_par
672            };
673            if v.len() < n {
674                return Err(FormatError::Corrupt("parameter count"));
675            }
676            for s in v.iter_mut().take(n) {
677                s.val = r.i32()?;
678            }
679        }
680        let n = r.u()? as usize;
681        for s in self.eqtb.glue_par.iter_mut().take(n) {
682            s.val = r.glue()?;
683        }
684        let n = r.u()? as usize;
685        for s in self.eqtb.toks_par.iter_mut().take(n) {
686            s.val = list(r.u32()?)?;
687        }
688        for which in 0..3 {
689            let n = r.u()? as usize;
690            for _ in 0..n {
691                let i = r.u()? as usize;
692                let v = r.i32()?;
693                let target = match which {
694                    0 => &mut self.eqtb.count,
695                    1 => &mut self.eqtb.attr,
696                    _ => &mut self.eqtb.dimen,
697                };
698                target
699                    .get_mut(i)
700                    .ok_or(FormatError::Corrupt("register index"))?
701                    .val = v;
702            }
703        }
704        for which in 0..2 {
705            let n = r.u()? as usize;
706            for _ in 0..n {
707                let i = r.u()? as usize;
708                let g = r.glue()?;
709                let target = if which == 0 {
710                    &mut self.eqtb.skip
711                } else {
712                    &mut self.eqtb.muskip
713                };
714                target
715                    .get_mut(i)
716                    .ok_or(FormatError::Corrupt("register index"))?
717                    .val = g;
718            }
719        }
720        let n = r.u()? as usize;
721        for _ in 0..n {
722            let i = r.u()? as usize;
723            let l = list(r.u32()?)?;
724            self.eqtb
725                .toks
726                .get_mut(i)
727                .ok_or(FormatError::Corrupt("register index"))?
728                .val = l;
729        }
730        let n = r.u()? as usize;
731        for _ in 0..n {
732            let i = r.u()? as usize;
733            let b = self.undump_node_list(r, &lists)?;
734            self.eqtb
735                .boxes
736                .get_mut(i)
737                .ok_or(FormatError::Corrupt("box index"))?
738                .val = b;
739        }
740        self.eqtb.cur_font.val = r.u32()?;
741        let n = r.u()? as usize;
742        for _ in 0..n {
743            let i = r.u()? as usize;
744            let f = r.u32()?;
745            self.eqtb
746                .math_font
747                .get_mut(i)
748                .ok_or(FormatError::Corrupt("math font index"))?
749                .val = f;
750        }
751        let read_shape = |r: &mut Reader| -> Result<Option<Rc<[i32]>>, FormatError> {
752            let n = r.u()? as usize;
753            if n == 0 {
754                return Ok(None);
755            }
756            let mut v = Vec::with_capacity(n - 1);
757            for _ in 0..n - 1 {
758                v.push(r.i32()?);
759            }
760            Ok(Some(Rc::from(v)))
761        };
762        self.eqtb.par_shape.val = read_shape(r)?;
763        let n = r.u()? as usize;
764        for i in 0..n {
765            let s = read_shape(r)?;
766            if let Some(slot) = self.eqtb.etex_shape.get_mut(i) {
767                slot.val = s;
768            }
769        }
770        let n = r.u()? as usize;
771        let mut page_pool: Vec<Rc<crate::eqtb::Page>> = Vec::with_capacity(n);
772        for _ in 0..n {
773            let raw = r.bytes()?;
774            if raw.len() != 256 * 8 {
775                return Err(FormatError::Corrupt("code table page"));
776            }
777            page_pool.push(Rc::new(std::array::from_fn(|i| {
778                let b: [u8; 8] = raw[i * 8..i * 8 + 8].try_into().unwrap_or_default();
779                Slot {
780                    val: i64::from_le_bytes(b),
781                    level: crate::eqtb::LEVEL_ONE,
782                }
783            })));
784        }
785        let read_table = |r: &mut Reader, t: &mut CodeTable| -> Result<(), FormatError> {
786            let n = r.u()? as usize;
787            for _ in 0..n {
788                let k = r.u()? as usize;
789                let i = r.u()? as usize;
790                let page = page_pool
791                    .get(i)
792                    .ok_or(FormatError::Corrupt("code table page"))?;
793                if k > 0x11_0000 / 256 {
794                    return Err(FormatError::Corrupt("code table page"));
795                }
796                t.load_page(k, page.clone());
797            }
798            Ok(())
799        };
800        crate::timing::mark("undump: registers (before code tables)");
801        let n = r.u()? as usize;
802        self.eqtb.catcode_tables.clear();
803        for _ in 0..n {
804            let k = r.u32()?;
805            let mut t = CodeTable::new(crate::eqtb::initex_catcode);
806            read_table(r, &mut t)?;
807            self.eqtb.catcode_tables.insert(k, t);
808        }
809        crate::timing::mark(&format!("undump: {n} catcode tables"));
810        read_table(r, &mut self.eqtb.lc)?;
811        read_table(r, &mut self.eqtb.uc)?;
812        read_table(r, &mut self.eqtb.sf)?;
813        read_table(r, &mut self.eqtb.math)?;
814        read_table(r, &mut self.eqtb.del)?;
815        let n = r.u()? as usize;
816        for _ in 0..n {
817            let k = r.u32()?;
818            let v = r.i32()?;
819            self.eqtb.math_param.insert(
820                k,
821                Slot {
822                    val: v,
823                    level: crate::eqtb::LEVEL_ONE,
824                },
825            );
826        }
827
828        crate::timing::mark("undump: parameters and registers");
829        // Fonts.
830        let r = &mut fonts_r;
831        let n = r.u()? as usize;
832        self.fonts.fonts.clear();
833        self.fonts.id_cs.clear();
834        for _ in 0..n {
835            let mut fi = FontInfo::null();
836            fi.name = r.bytes()?.to_vec();
837            fi.size = r.i32()?;
838            fi.design_size = r.i32()?;
839            fi.checksum = r.u32()?;
840            let np = r.u()? as usize;
841            fi.params = (0..np).map(|_| r.i32()).collect::<Result<_, _>>()?;
842            let nc = r.u()? as usize;
843            fi.chars = HashMap::with_capacity_and_hasher(nc, crate::hash::FastHash::default());
844            for _ in 0..nc {
845                let c = r.u32()?;
846                let m = CharMetrics {
847                    width: r.i32()?,
848                    height: r.i32()?,
849                    depth: r.i32()?,
850                    italic: r.i32()?,
851                    index: r.u32()?,
852                };
853                fi.chars.insert(c, m);
854            }
855            // Fonts from the format are not reused by \font (observed).
856            fi.hyphen_char = r.i32()?;
857            fi.skew_char = r.i32()?;
858            fi.kind = match r.byte()? {
859                0 => FontKind::Null,
860                1 => FontKind::Tfm,
861                _ => FontKind::OpenType,
862            };
863            fi.handle = r.u()?;
864            let cs = CsId(r.u32()?);
865            let raw = r.bytes()?;
866            if !raw.is_empty() {
867                let tfm = retex_font::tfm::Tfm::parse(raw)
868                    .map_err(|_| FormatError::Corrupt("tfm font data"))?;
869                // The ligature/kern program is rebuilt from the TFM data.
870                let scaled = tfm.scaled(fi.size);
871                fi.lig_kern = crate::fonts::lig_kern_table(&tfm, &scaled.kerns);
872                fi.right_boundary = tfm.bchar.map(u32::from);
873                fi.data = Some(crate::fonts::tfm_font_data(tfm, raw.to_vec()));
874            }
875            let nm = r.u()? as usize;
876            if nm > 0 {
877                let mut m = crate::fonts::MathConstants::new();
878                for _ in 1..nm {
879                    let k = String::from_utf8_lossy(r.bytes()?).into_owned();
880                    m.insert(k, r.i32()?);
881                }
882                fi.math_constants = Some(std::rc::Rc::new(m));
883            }
884            self.fonts.fonts.push(fi);
885            self.fonts.id_cs.push(cs);
886        }
887
888        crate::timing::mark("undump: fonts");
889        // Miscellaneous.
890        let r = &mut misc_r;
891        let mut ids = [CsId(0); 15];
892        for id in ids.iter_mut() {
893            *id = CsId(r.u32()?);
894        }
895        self.cs = crate::init::KnownCs {
896            par: ids[0],
897            frozen_protection: ids[1],
898            frozen_cr: ids[2],
899            frozen_end_group: ids[3],
900            frozen_right: ids[4],
901            frozen_fi: ids[5],
902            frozen_end_template: ids[6],
903            frozen_endv: ids[7],
904            frozen_relax: ids[8],
905            end_write: ids[9],
906            frozen_dont_expand: ids[10],
907            frozen_special: ids[11],
908            frozen_null_font: ids[12],
909            end_local: ids[13],
910        };
911        self.par_token_cs = ids[14];
912        self.format_ident = String::from_utf8_lossy(r.bytes()?).into_owned();
913        self.err.interaction = crate::error::Interaction::from_i32(i32::from(r.byte()?));
914        let n = r.u()? as usize;
915        self.frozen_prims.clear();
916        for _ in 0..n {
917            let name = r.bytes()?.to_vec();
918            let c = CsId(r.u32()?);
919            self.frozen_prims.insert(name, c);
920        }
921        let n = r.u()? as usize;
922        self.backend_cs.clear();
923        for _ in 0..n {
924            let name = r.bytes()?.to_vec();
925            let c = CsId(r.u32()?);
926            if let Some((static_name, _, _)) = crate::backend::PDF_VARIABLES
927                .iter()
928                .find(|(s, _, _)| s.as_bytes() == name.as_slice())
929            {
930                self.backend_cs.insert(static_name, c);
931            }
932        }
933        for i in 0..8 {
934            let v = r.i32()?;
935            if let Some(d) = self.eqtb.dir_par.get_mut(i) {
936                d.val = v;
937            }
938        }
939        let n = r.u()? as usize;
940        self.math_glues = (0..n).map(|_| r.glue()).collect::<Result<_, _>>()?;
941        self.printer.new_line_char = self
942            .eqtb
943            .int(crate::primitives_gen::assign_int::NEWLINECHAR);
944        crate::timing::mark("undump: misc");
945        // Languages.
946        let n = langs_r.u()? as usize;
947        self.languages.clear();
948        for _ in 0..n {
949            let i = langs_r.u()? as usize;
950            let l = retex_hyph::Language::from_bytes(langs_r.bytes()?)
951                .map_err(|_| FormatError::Corrupt("hyphenation language"))?;
952            if self.languages.len() <= i {
953                self.languages.resize_with(i + 1, || None);
954            }
955            self.languages[i] = Some(l);
956        }
957        Ok(())
958    }
959
960    fn undump_node_list(
961        &mut self,
962        r: &mut Reader,
963        lists: &[TokenList],
964    ) -> Result<NodeRef, FormatError> {
965        let n = r.u()? as usize;
966        let mut head = NodeRef::NULL;
967        let mut tail = NodeRef::NULL;
968        for _ in 0..n {
969            let t = r.byte()?;
970            let sub = r.u()? as u16;
971            let data = match t {
972                id::HLIST | id::VLIST => {
973                    let width = r.i32()?;
974                    let depth = r.i32()?;
975                    let height = r.i32()?;
976                    let shift = r.i32()?;
977                    let dir = r.byte()? as i8;
978                    let glue_order = r.byte()?;
979                    let glue_sign = r.byte()?;
980                    let glue_set = f64::from_bits(r.u()?);
981                    let head = self.undump_node_list(r, lists)?;
982                    NodeData::Box(Box::new(crate::nodes::BoxData {
983                        width,
984                        depth,
985                        height,
986                        dir,
987                        shift,
988                        glue_order,
989                        glue_sign,
990                        glue_set,
991                        head,
992                    }))
993                }
994                id::GLUE => NodeData::Glue {
995                    spec: r.glue()?,
996                    leader: NodeRef::NULL,
997                },
998                id::KERN => NodeData::Kern {
999                    kern: r.i32()?,
1000                    expansion_factor: 0,
1001                },
1002                id::PENALTY => NodeData::Penalty { penalty: r.i32()? },
1003                id::RULE => NodeData::Rule {
1004                    width: r.i32()?,
1005                    depth: r.i32()?,
1006                    height: r.i32()?,
1007                    dir: 0,
1008                    index: 0,
1009                    left: 0,
1010                    right: 0,
1011                },
1012                id::GLYPH => {
1013                    let chr = r.u32()?;
1014                    let font = r.u32()?;
1015                    NodeData::Glyph(crate::nodes::GlyphData {
1016                        chr,
1017                        font,
1018                        ..Default::default()
1019                    })
1020                }
1021                id::MARK => {
1022                    let class = r.u32()?;
1023                    let i = r.u32()? as usize;
1024                    NodeData::Mark {
1025                        class,
1026                        mark: lists
1027                            .get(i)
1028                            .cloned()
1029                            .ok_or(FormatError::Corrupt("bad mark list"))?,
1030                    }
1031                }
1032                _ => NodeData::Temp,
1033            };
1034            let p = self.nodes.alloc(t, sub, data);
1035            if head.is_null() {
1036                head = p;
1037            } else {
1038                self.nodes.link(tail, p);
1039            }
1040            tail = p;
1041        }
1042        Ok(head)
1043    }
1044}
1045
1046/// Builds a complete format file from sections.
1047pub fn assemble(sections: &[(&str, &[u8])]) -> Vec<u8> {
1048    let mut w = Writer::default();
1049    w.buf.extend_from_slice(MAGIC);
1050    w.u(FORMAT_VERSION.into());
1051    w.bytes(env!("CARGO_PKG_VERSION").as_bytes());
1052    w.u(sections.len() as u64);
1053    for (name, data) in sections {
1054        w.bytes(name.as_bytes());
1055        w.bytes(data);
1056    }
1057    w.buf
1058}
1059
1060/// Splits a format file into its sections.
1061pub fn disassemble(data: &[u8]) -> Result<Vec<(&str, &[u8])>, FormatError> {
1062    if !data.starts_with(MAGIC) {
1063        return Err(FormatError::BadHeader);
1064    }
1065    let mut r = Reader::new(&data[MAGIC.len()..]);
1066    if r.u32()? != FORMAT_VERSION {
1067        return Err(FormatError::BadHeader);
1068    }
1069    if r.bytes()? != env!("CARGO_PKG_VERSION").as_bytes() {
1070        return Err(FormatError::BadHeader);
1071    }
1072    let n = r.u()? as usize;
1073    let mut out = Vec::with_capacity(n);
1074    for _ in 0..n {
1075        let name =
1076            std::str::from_utf8(r.bytes()?).map_err(|_| FormatError::Corrupt("section name"))?;
1077        let d = r.bytes()?;
1078        out.push((name, d));
1079    }
1080    Ok(out)
1081}
1082
1083#[cfg(test)]
1084mod tests {
1085    use super::*;
1086
1087    #[test]
1088    fn varints_round_trip() {
1089        let mut w = Writer::default();
1090        for v in [
1091            0i64,
1092            1,
1093            -1,
1094            63,
1095            -64,
1096            1 << 40,
1097            i64::from(i32::MIN),
1098            i64::from(i32::MAX),
1099        ] {
1100            w.i(v);
1101        }
1102        let mut r = Reader::new(&w.buf);
1103        for v in [
1104            0i64,
1105            1,
1106            -1,
1107            63,
1108            -64,
1109            1 << 40,
1110            i64::from(i32::MIN),
1111            i64::from(i32::MAX),
1112        ] {
1113            assert_eq!(r.i().unwrap(), v);
1114        }
1115        assert!(r.at_end());
1116    }
1117
1118    #[test]
1119    fn corrupt_data_is_an_error() {
1120        assert!(disassemble(b"nonsense").is_err());
1121        let mut r = Reader::new(&[0xFF, 0xFF]);
1122        assert!(r.u().is_err());
1123    }
1124}