Skip to main content

retex_engine/
nodes.rs

1//! Node memory: an arena of typesetting nodes addressed by [`NodeRef`].
2//!
3//! The node model mirrors LuaTeX's. Node type ids, subtypes and field sets
4//! are those that LuaTeX's `node` library reports (see
5//! `data/luatex-node-types.txt`). Lists are doubly linked through `next` and
6//! `prev`. List heads in the semantic nest are `temp` nodes, as in TeX.
7
8use std::rc::Rc;
9
10use crate::glue::Glue;
11use crate::token::TokenList;
12
13/// LuaTeX's node types, fields and subtypes, as `node.types()`,
14/// `node.fields()` and `node.subtypes()` report them
15/// (`data/luatex-node-types.txt`, see `data/README.md`).
16pub const LUATEX_NODE_TYPES: &str = include_str!("../data/luatex-node-types.txt");
17
18/// The Lua name of node type `t` (`glue`), from [`LUATEX_NODE_TYPES`].
19pub fn type_name(t: u8) -> String {
20    LUATEX_NODE_TYPES
21        .lines()
22        .filter_map(|l| l.strip_prefix("type "))
23        .find_map(|l| {
24            let mut it = l.split_whitespace();
25            let n: u8 = it.next()?.parse().ok()?;
26            (n == t).then(|| it.next().unwrap_or("").to_owned())
27        })
28        .unwrap_or_default()
29}
30
31/// A reference to a node in the node arena; `NodeRef::NULL` is the empty
32/// list.
33#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
34pub struct NodeRef(pub u32);
35
36impl NodeRef {
37    /// The null pointer.
38    pub const NULL: NodeRef = NodeRef(0);
39
40    /// Whether this is the null pointer.
41    #[inline]
42    pub const fn is_null(self) -> bool {
43        self.0 == 0
44    }
45}
46
47/// A "running" rule dimension (TeX's `null_flag`, −2³⁰).
48pub const RUNNING: i32 = -0o10000000000;
49
50/// Node type ids, numbered as LuaTeX's `node.types()`.
51#[allow(missing_docs)]
52pub mod id {
53    pub const HLIST: u8 = 0;
54    pub const VLIST: u8 = 1;
55    pub const RULE: u8 = 2;
56    pub const INS: u8 = 3;
57    pub const MARK: u8 = 4;
58    pub const ADJUST: u8 = 5;
59    pub const BOUNDARY: u8 = 6;
60    pub const DISC: u8 = 7;
61    pub const WHATSIT: u8 = 8;
62    pub const LOCAL_PAR: u8 = 9;
63    pub const DIR: u8 = 10;
64    pub const MATH: u8 = 11;
65    pub const GLUE: u8 = 12;
66    pub const KERN: u8 = 13;
67    pub const PENALTY: u8 = 14;
68    pub const UNSET: u8 = 15;
69    pub const STYLE: u8 = 16;
70    pub const CHOICE: u8 = 17;
71    pub const NOAD: u8 = 18;
72    pub const RADICAL: u8 = 19;
73    pub const FRACTION: u8 = 20;
74    pub const ACCENT: u8 = 21;
75    pub const FENCE: u8 = 22;
76    pub const MATH_CHAR: u8 = 23;
77    pub const SUB_BOX: u8 = 24;
78    pub const SUB_MLIST: u8 = 25;
79    pub const MATH_TEXT_CHAR: u8 = 26;
80    pub const DELIM: u8 = 27;
81    pub const MARGIN_KERN: u8 = 28;
82    pub const GLYPH: u8 = 29;
83    pub const ALIGN_RECORD: u8 = 30;
84    pub const PAGE_INSERT: u8 = 33;
85    pub const SPLIT_INSERT: u8 = 34;
86    /// A glue specification made by Lua code (`node.new("glue_spec")`);
87    /// its data is that of a glue node.
88    pub const GLUE_SPEC: u8 = 39;
89    pub const TEMP: u8 = 41;
90    /// A freed node (in the arena's free list).
91    pub const FREE: u8 = 255;
92}
93
94/// The deepest nesting of boxes that is copied and shipped out (TeX
95/// builds at most 1000 levels at a time, LuaTeX ships out 20000 boxes
96/// nested in each other, and Lua code can make a box contain itself).
97pub const MAX_NESTING: u32 = 50_000;
98
99/// Glue subtypes (LuaTeX numbering).
100#[allow(missing_docs)]
101pub mod glue_sub {
102    pub const USER: u16 = 0;
103    pub const LINE_SKIP: u16 = 1;
104    pub const BASELINE_SKIP: u16 = 2;
105    pub const PAR_SKIP: u16 = 3;
106    pub const ABOVE_DISPLAY: u16 = 4;
107    pub const BELOW_DISPLAY: u16 = 5;
108    pub const ABOVE_DISPLAY_SHORT: u16 = 6;
109    pub const BELOW_DISPLAY_SHORT: u16 = 7;
110    pub const LEFT_SKIP: u16 = 8;
111    pub const RIGHT_SKIP: u16 = 9;
112    pub const TOP_SKIP: u16 = 10;
113    pub const SPLIT_TOP_SKIP: u16 = 11;
114    pub const TAB_SKIP: u16 = 12;
115    pub const SPACE_SKIP: u16 = 13;
116    pub const XSPACE_SKIP: u16 = 14;
117    pub const PAR_FILL_SKIP: u16 = 15;
118    pub const MATH_SKIP: u16 = 16;
119    pub const THIN_MU_SKIP: u16 = 17;
120    pub const MED_MU_SKIP: u16 = 18;
121    pub const THICK_MU_SKIP: u16 = 19;
122    pub const COND_MATH_GLUE: u16 = 98;
123    pub const MU_GLUE: u16 = 99;
124    pub const A_LEADERS: u16 = 100;
125    pub const C_LEADERS: u16 = 101;
126    pub const X_LEADERS: u16 = 102;
127    pub const G_LEADERS: u16 = 103;
128}
129
130/// Subtypes of hlist and vlist nodes that math typesetting gives the boxes
131/// it builds (LuaTeX's `mathchar`, `vextensible`, `vdelimiter`).
132pub mod list_sub {
133    /// A box holding one math character.
134    pub const MATH_CHAR: u16 = 9;
135    /// A horizontal extensible delimiter.
136    pub const H_EXTENSIBLE: u16 = 10;
137    /// A vertical extensible delimiter.
138    pub const V_EXTENSIBLE: u16 = 11;
139    /// A null (empty) delimiter.
140    pub const V_DELIMITER: u16 = 13;
141    /// A delimiter above its nucleus (`\Uoverdelimiter`, `\Udelimiterover`).
142    pub const OVER_DELIMITER: u16 = 14;
143    /// A delimiter below its nucleus.
144    pub const UNDER_DELIMITER: u16 = 15;
145    /// The numerator of a fraction.
146    pub const NUMERATOR: u16 = 16;
147    /// The denominator of a fraction.
148    pub const DENOMINATOR: u16 = 17;
149    /// An operator with limits.
150    pub const LIMITS: u16 = 18;
151    /// A fraction.
152    pub const FRACTION: u16 = 19;
153    /// The nucleus of a noad.
154    pub const NUCLEUS: u16 = 20;
155    /// A superscript.
156    pub const SUP: u16 = 21;
157    /// A subscript.
158    pub const SUB: u16 = 22;
159    /// The degree of a radical.
160    pub const DEGREE: u16 = 23;
161    /// A superscript and a subscript.
162    pub const SCRIPTS: u16 = 24;
163    /// An overline.
164    pub const OVER: u16 = 25;
165    /// An underline.
166    pub const UNDER: u16 = 26;
167    /// An accented nucleus.
168    pub const ACCENT: u16 = 27;
169    /// A radical.
170    pub const RADICAL: u16 = 28;
171}
172
173/// Kern subtypes.
174#[allow(missing_docs)]
175pub mod kern_sub {
176    pub const FONT: u16 = 0;
177    pub const USER: u16 = 1;
178    pub const ACCENT: u16 = 2;
179    pub const ITALIC: u16 = 3;
180}
181
182/// Glyph subtypes (bit flags).
183#[allow(missing_docs)]
184pub mod glyph_sub {
185    pub const CHARACTER: u16 = 1;
186    pub const LIGATURE: u16 = 2;
187    pub const GHOST: u16 = 4;
188    pub const LEFT: u16 = 8;
189    pub const RIGHT: u16 = 16;
190}
191
192/// Whatsit subtypes.
193#[allow(missing_docs)]
194pub mod whatsit_sub {
195    pub const OPEN: u16 = 0;
196    pub const WRITE: u16 = 1;
197    pub const CLOSE: u16 = 2;
198    pub const SPECIAL: u16 = 3;
199    pub const LATE_SPECIAL: u16 = 4;
200    pub const SAVE_POS: u16 = 7;
201    pub const LATE_LUA: u16 = 8;
202    pub const USER_DEFINED: u16 = 9;
203    pub const PDF_LITERAL: u16 = 16;
204    pub const PDF_LATE_LITERAL: u16 = 17;
205    pub const PDF_REFOBJ: u16 = 18;
206    pub const PDF_ANNOT: u16 = 19;
207    pub const PDF_START_LINK: u16 = 20;
208    pub const PDF_END_LINK: u16 = 21;
209    pub const PDF_DEST: u16 = 22;
210    pub const PDF_ACTION: u16 = 23;
211    pub const PDF_THREAD: u16 = 24;
212    pub const PDF_START_THREAD: u16 = 25;
213    pub const PDF_END_THREAD: u16 = 26;
214    pub const PDF_COLORSTACK: u16 = 29;
215    pub const PDF_SETMATRIX: u16 = 30;
216    pub const PDF_SAVE: u16 = 31;
217    pub const PDF_RESTORE: u16 = 32;
218    pub const PDF_LINK_STATE: u16 = 33;
219}
220
221/// A list of attribute (number, value) pairs, sorted by number.
222pub type AttrList = Rc<[(u32, i32)]>;
223
224/// Box fields shared by hlist and vlist nodes.
225#[derive(Clone, Debug, Default, PartialEq)]
226pub struct BoxData {
227    /// Width.
228    pub width: i32,
229    /// Depth.
230    pub depth: i32,
231    /// Height.
232    pub height: i32,
233    /// Direction (0 = TLT).
234    pub dir: i8,
235    /// Shift amount.
236    pub shift: i32,
237    /// Glue order of the setting.
238    pub glue_order: u8,
239    /// Glue sign: 0 normal, 1 stretching, 2 shrinking.
240    pub glue_sign: u8,
241    /// Glue set ratio. LuaTeX keeps it in single precision, so computed
242    /// ratios are rounded to `f32` (see [`glue_ratio`]).
243    pub glue_set: f64,
244    /// The list.
245    pub head: NodeRef,
246}
247
248/// Fields of a glyph node.
249#[derive(Clone, Debug, Default, PartialEq)]
250pub struct GlyphData {
251    /// The character.
252    pub chr: u32,
253    /// The font.
254    pub font: u32,
255    /// The language.
256    pub lang: u16,
257    /// `\lefthyphenmin` for this glyph.
258    pub left: u8,
259    /// `\righthyphenmin` for this glyph.
260    pub right: u8,
261    /// `\uchyph`.
262    pub uchyph: bool,
263    /// Ligature components.
264    pub components: NodeRef,
265    /// Horizontal offset.
266    pub xoffset: i32,
267    /// Vertical offset.
268    pub yoffset: i32,
269    /// Font expansion factor.
270    pub expansion_factor: i32,
271    /// User data.
272    pub data: i32,
273}
274
275/// Type-specific node data.
276#[derive(Clone, Debug, PartialEq)]
277pub enum NodeData {
278    /// A list head or other temporary node.
279    Temp,
280    /// An hlist or vlist (`id` tells which).
281    Box(Box<BoxData>),
282    /// A rule.
283    Rule {
284        /// Width (or "running").
285        width: i32,
286        /// Depth.
287        depth: i32,
288        /// Height.
289        height: i32,
290        /// Direction.
291        dir: i8,
292        /// Resource index (box/image rules).
293        index: i32,
294        /// Left offset.
295        left: i32,
296        /// Right offset.
297        right: i32,
298    },
299    /// An insertion.
300    Ins {
301        /// `\floatingpenalty`.
302        cost: i32,
303        /// Depth.
304        depth: i32,
305        /// Natural height plus depth.
306        height: i32,
307        /// `\splittopskip`.
308        spec: Glue,
309        /// The vertical list.
310        head: NodeRef,
311    },
312    /// A mark.
313    Mark {
314        /// The marks class.
315        class: u32,
316        /// The token list.
317        mark: TokenList,
318    },
319    /// A `\vadjust`.
320    Adjust {
321        /// The vertical list.
322        head: NodeRef,
323    },
324    /// A boundary.
325    Boundary {
326        /// The value.
327        value: i32,
328    },
329    /// A discretionary.
330    Disc {
331        /// Pre-break list.
332        pre: NodeRef,
333        /// Post-break list.
334        post: NodeRef,
335        /// Replacement list.
336        replace: NodeRef,
337        /// Penalty.
338        penalty: i32,
339    },
340    /// A whatsit.
341    Whatsit(Box<Whatsit>),
342    /// A local paragraph node.
343    LocalPar {
344        /// `\localinterlinepenalty`.
345        pen_inter: i32,
346        /// `\localbrokenpenalty`.
347        pen_broken: i32,
348        /// Paragraph direction.
349        dir: i8,
350        /// `\localleftbox`.
351        box_left: NodeRef,
352        /// Its width.
353        box_left_width: i32,
354        /// `\localrightbox`.
355        box_right: NodeRef,
356        /// Its width.
357        box_right_width: i32,
358    },
359    /// A direction change.
360    Dir {
361        /// The direction.
362        dir: i8,
363        /// The group level.
364        level: i32,
365    },
366    /// Math on/off.
367    Math {
368        /// `\mathsurround`.
369        surround: i32,
370        /// `\mathsurroundskip` as glue.
371        glue: Glue,
372    },
373    /// Glue.
374    Glue {
375        /// The specification.
376        spec: Glue,
377        /// Leader box or rule.
378        leader: NodeRef,
379    },
380    /// A kern.
381    Kern {
382        /// The amount.
383        kern: i32,
384        /// Font expansion factor.
385        expansion_factor: i32,
386    },
387    /// A penalty.
388    Penalty {
389        /// The penalty.
390        penalty: i32,
391    },
392    /// An unset box in an alignment.
393    Unset {
394        /// Box fields (glue_set unused).
395        b: Box<BoxData>,
396        /// Number of spanned columns minus one.
397        span_count: u16,
398        /// Total stretch.
399        stretch: i32,
400        /// Total shrink.
401        shrink: i32,
402        /// Stretch order.
403        stretch_order: u8,
404        /// Shrink order.
405        shrink_order: u8,
406    },
407    /// A glyph (stored inline: glyphs are the most frequent nodes).
408    Glyph(GlyphData),
409    /// A margin kern.
410    MarginKern {
411        /// Width.
412        width: i32,
413        /// The glyph it belongs to.
414        glyph: NodeRef,
415    },
416    /// A noad (`noad`, `radical` and `accent` ids).
417    Noad(Box<NoadData>),
418    /// A generalized fraction noad.
419    Fraction(Box<FractionData>),
420    /// A `\left`, `\middle` or `\right` noad.
421    Fence(Box<FenceData>),
422    /// A style node (`\displaystyle` …); the style is 0–7 as in TeX.
423    Style {
424        /// The style.
425        style: u8,
426    },
427    /// A `\mathchoice` node: display, text, script and scriptscript lists.
428    Choice(Box<[NodeRef; 4]>),
429    /// A `math_char` or `math_text_char` kernel.
430    MathChar {
431        /// The family.
432        fam: u32,
433        /// The character.
434        chr: u32,
435    },
436    /// A `sub_box` or `sub_mlist` kernel.
437    SubList {
438        /// The box (sub_box) or the mlist (sub_mlist).
439        head: NodeRef,
440    },
441    /// A delimiter specification (`delim` node).
442    Delim {
443        /// Family of the small variant.
444        small_fam: u32,
445        /// Character of the small variant.
446        small_char: u32,
447        /// Family of the large variant.
448        large_fam: u32,
449        /// Character of the large variant.
450        large_char: u32,
451    },
452    /// Nodes not modelled in detail.
453    Other(Vec<i64>),
454}
455
456/// Fields of a noad (LuaTeX's `noad`, `radical` and `accent` nodes). The
457/// nucleus and the scripts are kernel nodes (`math_char`,
458/// `math_text_char`, `sub_box`, `sub_mlist`), or null when empty.
459#[derive(Clone, Debug, Default, PartialEq)]
460pub struct NoadData {
461    /// The nucleus.
462    pub nucleus: NodeRef,
463    /// The subscript.
464    pub sub: NodeRef,
465    /// The superscript.
466    pub sup: NodeRef,
467    /// Rendering options (LuaTeX's option bits).
468    pub options: u32,
469    /// Radicals: the delimiter (a `delim` node).
470    pub left: NodeRef,
471    /// Radicals: the degree of `\Uroot`.
472    pub degree: NodeRef,
473    /// Radicals: the requested width (`\Uhextensible`, `width` keyword).
474    pub width: i32,
475    /// Accents: the top accent (a `math_char` kernel).
476    pub accent: NodeRef,
477    /// Accents: the bottom accent.
478    pub bot_accent: NodeRef,
479    /// Accents: the `fraction` criterion (0 when not given).
480    pub fraction: i32,
481    /// The translation into an hlist (`new_hlist`), during
482    /// `mlist_to_hlist`.
483    pub new_hlist: NodeRef,
484}
485
486/// Fields of a generalized fraction noad.
487#[derive(Clone, Debug, PartialEq)]
488pub struct FractionData {
489    /// Rule thickness, or [`DEFAULT_CODE`].
490    pub thickness: i32,
491    /// The numerator (a `sub_mlist` kernel).
492    pub num: NodeRef,
493    /// The denominator (a `sub_mlist` kernel).
494    pub denom: NodeRef,
495    /// Left delimiter (`delim` node) or null.
496    pub left: NodeRef,
497    /// Right delimiter or null.
498    pub right: NodeRef,
499    /// Middle delimiter (`\Uskewed`) or null.
500    pub middle: NodeRef,
501    /// Rendering options.
502    pub options: u32,
503    /// The translation (`new_hlist`).
504    pub new_hlist: NodeRef,
505}
506
507/// Fields of a fence noad (`\left`, `\middle`, `\right`).
508#[derive(Clone, Debug, Default, PartialEq)]
509pub struct FenceData {
510    /// The delimiter (a `delim` node).
511    pub delim: NodeRef,
512    /// Italic correction.
513    pub italic: i32,
514    /// Requested height (0 when not given).
515    pub height: i32,
516    /// Requested depth.
517    pub depth: i32,
518    /// Rendering options.
519    pub options: u32,
520    /// Spacing class (−1 when not given).
521    pub class: i32,
522    /// The translation (`new_hlist`).
523    pub new_hlist: NodeRef,
524}
525
526/// The fraction thickness meaning "the default rule thickness"
527/// (`default_code`).
528pub const DEFAULT_CODE: i32 = 0o10000000000;
529
530/// Noad subtypes (LuaTeX numbering).
531#[allow(missing_docs)]
532pub mod noad_sub {
533    pub const ORD: u16 = 0;
534    pub const OP_DISPLAY_LIMITS: u16 = 1;
535    pub const OP_LIMITS: u16 = 2;
536    pub const OP_NO_LIMITS: u16 = 3;
537    pub const BIN: u16 = 4;
538    pub const REL: u16 = 5;
539    pub const OPEN: u16 = 6;
540    pub const CLOSE: u16 = 7;
541    pub const PUNCT: u16 = 8;
542    pub const INNER: u16 = 9;
543    pub const UNDER: u16 = 10;
544    pub const OVER: u16 = 11;
545    pub const VCENTER: u16 = 12;
546}
547
548/// Radical subtypes.
549#[allow(missing_docs)]
550pub mod radical_sub {
551    pub const RADICAL: u16 = 0;
552    pub const URADICAL: u16 = 1;
553    pub const UROOT: u16 = 2;
554    pub const UUNDERDELIMITER: u16 = 3;
555    pub const UOVERDELIMITER: u16 = 4;
556    pub const UDELIMITERUNDER: u16 = 5;
557    pub const UDELIMITEROVER: u16 = 6;
558    pub const UHEXTENSIBLE: u16 = 7;
559}
560
561/// Fence subtypes.
562#[allow(missing_docs)]
563pub mod fence_sub {
564    pub const UNSET: u16 = 0;
565    pub const LEFT: u16 = 1;
566    pub const MIDDLE: u16 = 2;
567    pub const RIGHT: u16 = 3;
568    pub const NO: u16 = 4;
569}
570
571/// Accent subtypes.
572#[allow(missing_docs)]
573pub mod accent_sub {
574    pub const BOTH_FLEXIBLE: u16 = 0;
575    pub const FIXED_TOP: u16 = 1;
576    pub const FIXED_BOTTOM: u16 = 2;
577    pub const FIXED_BOTH: u16 = 3;
578}
579
580/// Noad option bits (LuaTeX manual, "Math core nodes").
581#[allow(missing_docs)]
582pub mod noad_opt {
583    pub const SET: u32 = 0x08;
584    pub const AXIS: u32 = 0x02 | SET;
585    pub const NO_AXIS: u32 = 0x04 | SET;
586    pub const EXACT: u32 = 0x10 | SET;
587    pub const LEFT: u32 = 0x11 | SET;
588    pub const MIDDLE: u32 = 0x12 | SET;
589    pub const RIGHT: u32 = 0x14 | SET;
590    pub const NO_SUB_SCRIPT: u32 = 0x21 | SET;
591    pub const NO_SUPER_SCRIPT: u32 = 0x22 | SET;
592    /// retex: an overlay accent (`\Umathaccent overlay`).
593    pub const OVERLAY: u32 = 0x100;
594}
595
596/// Whatsit payloads.
597#[derive(Clone, Debug, PartialEq)]
598pub enum Whatsit {
599    /// `\openout`.
600    Open {
601        /// Stream number.
602        stream: u32,
603        /// File name.
604        name: Vec<u8>,
605    },
606    /// `\write`.
607    Write {
608        /// Stream number.
609        stream: i32,
610        /// Unexpanded token list.
611        data: TokenList,
612    },
613    /// `\closeout`.
614    Close {
615        /// Stream number.
616        stream: u32,
617    },
618    /// `\special`.
619    Special {
620        /// The text.
621        data: Vec<u8>,
622    },
623    /// `\latelua`.
624    LateLua {
625        /// Token list or string.
626        data: LateData,
627        /// Chunk name.
628        name: Vec<u8>,
629    },
630    /// `\pdfextension literal` and friends.
631    PdfLiteral {
632        /// Mode (0 origin, 1 page, 2 direct, …).
633        mode: u8,
634        /// Data.
635        data: LateData,
636        /// Whether this is a late literal.
637        late: bool,
638    },
639    /// `\savepos`.
640    SavePos,
641    /// `\pdfextension colorstack`.
642    PdfColorstack {
643        /// Stack number.
644        stack: i32,
645        /// Command (set, push, pop, current).
646        cmd: u8,
647        /// Data.
648        data: Vec<u8>,
649    },
650    /// `\pdfextension setmatrix`.
651    PdfSetmatrix {
652        /// The matrix text.
653        data: Vec<u8>,
654    },
655    /// `\pdfextension save`.
656    PdfSave,
657    /// `\pdfextension restore`.
658    PdfRestore,
659    /// `\pdfextension annot`.
660    PdfAnnot {
661        /// `width`, `height`, `depth` ([`RUNNING`] when not given).
662        dims: [i32; 3],
663        /// The annotation's object number.
664        objnum: i32,
665        /// The annotation's text.
666        data: Vec<u8>,
667    },
668    /// `\pdfextension startlink`.
669    PdfStartLink {
670        /// `width`, `height`, `depth` ([`RUNNING`] when not given).
671        dims: [i32; 3],
672        /// The object number of the link's first annotation.
673        objnum: i32,
674        /// `attr {…}`.
675        attr: Vec<u8>,
676        /// The action.
677        action: crate::pdfext::PdfAction,
678    },
679    /// `\pdfextension endlink`.
680    PdfEndLink,
681    /// `\pdfextension dest`.
682    PdfDest {
683        /// The destination's identifier.
684        id: crate::pdfext::DestId,
685        /// The view.
686        view: crate::pdfext::DestView,
687        /// `struct n`: a structure destination for structure element
688        /// object `n`.
689        structure: Option<i32>,
690    },
691    /// `\pdfextension refobj`.
692    PdfRefobj {
693        /// The object number.
694        objnum: i32,
695    },
696    /// `\pdfextension linkstate`.
697    PdfLinkState {
698        /// The state (0 links on, 1 links off).
699        state: i32,
700    },
701    /// A `user_defined` whatsit (made from Lua).
702    UserDefined {
703        /// `user_id`.
704        user_id: i32,
705        /// The value, by type.
706        value: UserValue,
707    },
708    /// Other whatsits, kept opaque for now.
709    Other {
710        /// Raw fields.
711        fields: Vec<i64>,
712        /// Raw data.
713        data: Vec<u8>,
714    },
715}
716
717/// Data of late whatsits: an unexpanded token list or a string.
718#[derive(Clone, Debug, PartialEq)]
719pub enum LateData {
720    /// Tokens, expanded at shipout.
721    Tokens(TokenList),
722    /// An already expanded string.
723    Bytes(Vec<u8>),
724    /// A string set from Lua outside IniTeX, which LuaTeX keeps as a Lua
725    /// reference (observed: shown as `<lua data reference N>`).
726    Lua {
727        /// The string.
728        data: Vec<u8>,
729        /// The reference number shown.
730        reference: i32,
731    },
732    /// A Lua function (`\latelua` data set from Lua), called at shipout
733    /// with the whatsit; the host keeps it under its reference number
734    /// (observed: shown as `<function reference N>`).
735    Function {
736        /// The reference number.
737        reference: i32,
738    },
739}
740
741impl LateData {
742    /// The string of a [`LateData::Bytes`] or [`LateData::Lua`] value.
743    pub fn bytes(&self) -> Option<&[u8]> {
744        match self {
745            LateData::Bytes(b) | LateData::Lua { data: b, .. } => Some(b),
746            LateData::Tokens(_) | LateData::Function { .. } => None,
747        }
748    }
749}
750
751/// The value of a `user_defined` whatsit, by its type (LuaTeX manual:
752/// `a`, `d`, `l`, `n`, `s`, `t`).
753#[derive(Clone, Debug, PartialEq)]
754pub enum UserValue {
755    /// `d`: a number.
756    Int(i32),
757    /// `s`: a string.
758    Str(Vec<u8>),
759    /// `t`: a token list.
760    Tokens(TokenList),
761    /// `n`: a node list.
762    Nodes(NodeRef),
763    /// `l`: a Lua value, by its reference number.
764    Lua(i32),
765    /// `a`: an attribute list (not modelled).
766    Attr,
767}
768
769/// A node.
770#[derive(Clone, Debug, PartialEq)]
771pub struct Node {
772    /// The type id.
773    pub id: u8,
774    /// The subtype.
775    pub subtype: u16,
776    /// Next node in the list.
777    pub next: NodeRef,
778    /// Previous node in the list.
779    pub prev: NodeRef,
780    /// Attributes.
781    pub attr: Option<AttrList>,
782    /// Type-specific data.
783    pub data: NodeData,
784}
785
786impl Node {
787    /// A temporary node, like the null node.
788    fn temp() -> Node {
789        Node {
790            id: id::TEMP,
791            subtype: 0,
792            next: NodeRef::NULL,
793            prev: NodeRef::NULL,
794            attr: None,
795            data: NodeData::Temp,
796        }
797    }
798}
799
800/// What happened to a node with a Lua property (`node.setproperty`),
801/// for the Lua side to update its property table.
802#[derive(Debug, Clone, Copy, PartialEq, Eq)]
803pub enum PropEvent {
804    /// The node was freed: its property goes.
805    Flush(u32),
806    /// The node (first) was copied (second).
807    Copy(u32, u32),
808}
809
810/// LuaTeX's node properties as the engine tracks them: which nodes have
811/// one, the mode set by `node.set_properties_mode(flush, copy)`, and the
812/// frees and copies of such nodes since the Lua side last looked.
813#[derive(Debug, Clone, Default)]
814pub struct Properties {
815    /// Nodes with a property (only tracked when `flush` or `copy` is on).
816    pub nodes: std::collections::HashSet<u32>,
817    /// Properties go when their node is freed.
818    pub flush: bool,
819    /// Copies of a node get a property that inherits from the original.
820    pub copy: bool,
821    /// Pending events, oldest first.
822    pub events: Vec<PropEvent>,
823}
824
825/// The node arena.
826#[derive(Debug, Clone)]
827pub struct Nodes {
828    nodes: Vec<Node>,
829    /// The nesting depth of [`Nodes::copy_list`].
830    copy_depth: u32,
831    /// Where writes to nodes outside the arena go: Lua code can name any
832    /// number as a direct node.
833    sink: Node,
834    free: Vec<u32>,
835    /// Number of nodes in use.
836    pub in_use: usize,
837    /// Lua node properties.
838    pub props: Properties,
839    /// SyncTeX fields of the nodes, while SyncTeX is on (see
840    /// [`SyncFields`]).
841    pub sync: Option<Box<SyncFields>>,
842    /// The first node freed twice, with its type before the first free:
843    /// a fatal error in LuaTeX, reported by the engine (see
844    /// `Engine::check_double_free`).
845    pub double_free: Option<(u8, u32)>,
846    /// The temporary head of a paragraph during its node processing
847    /// callbacks: LuaTeX's is not a node that can be freed.
848    pub par_temp: NodeRef,
849    /// The numbers of the Lua values nodes keep by reference.
850    pub lua_refs: LuaRefs,
851}
852
853/// The numbers LuaTeX gives the Lua values it keeps by reference (shown
854/// as `<lua data reference N>`), modelled as observed: a new reference
855/// takes the most recently released number, else the next one; freeing
856/// or overwriting a node's value releases its number, copying the node
857/// takes a new one. Fonts made from Lua tables and luaotfload's set-up
858/// hold references too.
859#[derive(Debug, Clone, Default)]
860pub struct LuaRefs {
861    /// The next number never used (780 at the start of a plain run).
862    pub next: i32,
863    /// The released numbers, the last released last.
864    pub free: Vec<i32>,
865    /// References made by copying nodes: (copy, original), for the Lua
866    /// side to duplicate the value.
867    pub copies: Vec<(i32, i32)>,
868}
869
870impl LuaRefs {
871    /// A new reference number.
872    pub fn take(&mut self) -> i32 {
873        if let Some(r) = self.free.pop() {
874            return r;
875        }
876        if self.next == 0 {
877            self.next = 780;
878        }
879        let r = self.next;
880        self.next += 1;
881        r
882    }
883
884    /// Releases reference `r` (none if it is not positive).
885    pub fn release(&mut self, r: i32) {
886        if r > 0 {
887            self.free.push(r);
888        }
889    }
890
891    /// The references held by a font that luaotfload defines: one while
892    /// it is made, released after, and one kept (observed).
893    pub fn font_defined(&mut self, temporary: bool) {
894        if temporary {
895            let t = self.take();
896            self.take();
897            self.release(t);
898        } else {
899            self.take();
900        }
901    }
902}
903
904/// The reference number of the Lua value node data keeps, if any.
905fn lua_ref_mut(data: &mut NodeData) -> Option<&mut i32> {
906    match data {
907        NodeData::Whatsit(w) => match &mut **w {
908            Whatsit::LateLua {
909                data: LateData::Lua { reference, .. } | LateData::Function { reference },
910                ..
911            }
912            | Whatsit::PdfLiteral {
913                data: LateData::Lua { reference, .. },
914                ..
915            } => Some(reference),
916            Whatsit::UserDefined {
917                value: UserValue::Lua(r),
918                ..
919            } if *r > 0 => Some(r),
920            _ => None,
921        },
922        _ => None,
923    }
924}
925
926/// The SyncTeX fields (input tag and line) of the nodes, kept beside the
927/// arena while SyncTeX is on, so that runs without it pay nothing.
928///
929/// As LuaTeX's `node.direct.get_synctex_fields` shows, a node gets the
930/// current tag and line when it is made, except glyphs and kerns, which get
931/// none; a copy keeps the fields of its original, except kerns and math
932/// nodes, which get the current ones (observed).
933#[derive(Debug, Clone, Default)]
934pub struct SyncFields {
935    /// The tag and line of the input being read.
936    pub cur: (u32, i32),
937    fields: Vec<(u32, i32)>,
938}
939
940impl SyncFields {
941    #[inline]
942    fn set(&mut self, i: u32, f: (u32, i32)) {
943        let i = i as usize;
944        if i >= self.fields.len() {
945            self.fields.resize(i + 1, (0, 0));
946        }
947        self.fields[i] = f;
948    }
949}
950
951impl Default for Nodes {
952    fn default() -> Self {
953        Self::new()
954    }
955}
956
957impl Nodes {
958    /// An arena with only the null node.
959    pub fn new() -> Self {
960        Nodes {
961            nodes: vec![Node::temp()],
962            copy_depth: 0,
963            sink: Node::temp(),
964            free: Vec::new(),
965            in_use: 0,
966            props: Properties::default(),
967            sync: None,
968            lua_refs: LuaRefs::default(),
969            double_free: None,
970            par_temp: NodeRef::NULL,
971        }
972    }
973
974    /// Starts keeping SyncTeX fields (for nodes made from now on).
975    pub fn enable_sync(&mut self) {
976        if self.sync.is_none() {
977            self.sync = Some(Box::default());
978        }
979    }
980
981    /// The SyncTeX tag and line of `p`, (0, 0) if it has none.
982    #[inline]
983    pub fn sync_fields(&self, p: NodeRef) -> (u32, i32) {
984        self.sync
985            .as_ref()
986            .and_then(|s| s.fields.get(p.0 as usize).copied())
987            .unwrap_or((0, 0))
988    }
989
990    /// Sets the SyncTeX tag and line of `p` (when SyncTeX is on).
991    pub fn set_sync_fields(&mut self, p: NodeRef, f: (u32, i32)) {
992        if p.0 as usize >= self.nodes.len() {
993            return;
994        }
995        if let Some(s) = self.sync.as_deref_mut() {
996            s.set(p.0, f);
997        }
998    }
999
1000    /// Sets the SyncTeX fields of `p`, a node just made by font
1001    /// processing that LuaTeX's Lua code makes by copying (kerns from
1002    /// OpenType positioning): the current tag and line.
1003    pub fn sync_as_copied(&mut self, p: NodeRef) {
1004        if let Some(s) = self.sync.as_deref_mut() {
1005            let cur = s.cur;
1006            s.set(p.0, cur);
1007        }
1008    }
1009
1010    /// The node at `p`; the null node if `p` is outside the arena.
1011    #[inline]
1012    fn at(&self, p: NodeRef) -> &Node {
1013        self.nodes.get(p.0 as usize).unwrap_or(&self.nodes[0])
1014    }
1015
1016    /// The node at `p`, mutably; a scratch node if `p` is outside the
1017    /// arena.
1018    #[inline]
1019    fn at_mut(&mut self, p: NodeRef) -> &mut Node {
1020        match self.nodes.get_mut(p.0 as usize) {
1021            Some(n) => n,
1022            None => {
1023                self.sink = Node::temp();
1024                &mut self.sink
1025            }
1026        }
1027    }
1028
1029    /// The number of node slots in the arena.
1030    pub fn capacity(&self) -> usize {
1031        self.nodes.len()
1032    }
1033
1034    /// Allocates a node.
1035    pub fn alloc(&mut self, id: u8, subtype: u16, data: NodeData) -> NodeRef {
1036        let node = Node {
1037            id,
1038            subtype,
1039            next: NodeRef::NULL,
1040            prev: NodeRef::NULL,
1041            attr: None,
1042            data,
1043        };
1044        self.in_use += 1;
1045        let p = if let Some(i) = self.free.pop() {
1046            self.nodes[i as usize] = node;
1047            NodeRef(i)
1048        } else {
1049            self.nodes.push(node);
1050            NodeRef(self.nodes.len() as u32 - 1)
1051        };
1052        if let Some(s) = self.sync.as_deref_mut() {
1053            let f = if id == id::KERN || id == id::GLYPH {
1054                (0, 0)
1055            } else {
1056                s.cur
1057            };
1058            s.set(p.0, f);
1059        }
1060        p
1061    }
1062
1063    /// Frees a single node (not its sublists).
1064    pub fn free(&mut self, p: NodeRef) {
1065        // Lua code can free a node twice (LuaTeX corrupts its memory);
1066        // a node in the free list twice would be allocated twice.
1067        if p.is_null() || p.0 as usize >= self.nodes.len() {
1068            return;
1069        }
1070        if self.is_free(p) {
1071            if self.double_free.is_none() {
1072                self.double_free = Some((self.at(p).subtype as u8, p.0));
1073            }
1074            return;
1075        }
1076        // The subtype of a free node keeps its type, for the message.
1077        let t = self.at(p).id;
1078        self.at_mut(p).subtype = u16::from(t);
1079        if let Some(r) = lua_ref_mut(&mut self.at_mut(p).data) {
1080            let r = *r;
1081            self.lua_refs.release(r);
1082        }
1083        self.at_mut(p).id = id::FREE;
1084        self.at_mut(p).data = NodeData::Temp;
1085        self.at_mut(p).attr = None;
1086        self.free.push(p.0);
1087        self.in_use -= 1;
1088        if !self.props.nodes.is_empty() && self.props.nodes.remove(&p.0) && self.props.flush {
1089            self.props.events.push(PropEvent::Flush(p.0));
1090        }
1091    }
1092
1093    /// The reference number of the Lua value node `p` keeps, if any.
1094    pub fn lua_ref_of(&mut self, p: NodeRef) -> Option<i32> {
1095        lua_ref_mut(&mut self.at_mut(p).data).map(|r| *r)
1096    }
1097
1098    /// Whether `p` was freed (and not allocated again).
1099    #[inline]
1100    pub fn is_free(&self, p: NodeRef) -> bool {
1101        self.at(p).id == id::FREE
1102    }
1103
1104    /// The node at `p`.
1105    #[inline]
1106    pub fn get(&self, p: NodeRef) -> &Node {
1107        self.at(p)
1108    }
1109
1110    /// The node at `p`, mutably.
1111    #[inline]
1112    pub fn get_mut(&mut self, p: NodeRef) -> &mut Node {
1113        self.at_mut(p)
1114    }
1115
1116    /// The type id of `p`.
1117    #[inline]
1118    pub fn id(&self, p: NodeRef) -> u8 {
1119        self.at(p).id
1120    }
1121
1122    /// The `next` link of `p`.
1123    #[inline]
1124    pub fn next(&self, p: NodeRef) -> NodeRef {
1125        self.at(p).next
1126    }
1127
1128    /// Links `q` after `p` (sets `p.next` and `q.prev`).
1129    #[inline]
1130    pub fn link(&mut self, p: NodeRef, q: NodeRef) {
1131        self.at_mut(p).next = q;
1132        if !q.is_null() {
1133            self.at_mut(q).prev = p;
1134        }
1135    }
1136
1137    /// The box data of an hlist/vlist node.
1138    pub fn box_data(&self, p: NodeRef) -> Option<&BoxData> {
1139        match &self.at(p).data {
1140            NodeData::Box(b) => Some(b),
1141            NodeData::Unset { b, .. } => Some(b),
1142            _ => None,
1143        }
1144    }
1145
1146    /// The box data of an hlist/vlist node, mutably.
1147    pub fn box_data_mut(&mut self, p: NodeRef) -> Option<&mut BoxData> {
1148        match &mut self.at_mut(p).data {
1149            NodeData::Box(b) => Some(b),
1150            NodeData::Unset { b, .. } => Some(b),
1151            _ => None,
1152        }
1153    }
1154
1155    /// The last node of the list starting at `p`.
1156    pub fn tail_of(&self, mut p: NodeRef) -> NodeRef {
1157        if p.is_null() {
1158            return p;
1159        }
1160        loop {
1161            let n = self.next(p);
1162            if n.is_null() {
1163                return p;
1164            }
1165            p = n;
1166        }
1167    }
1168
1169    /// The last node of the list starting at `p`, or `None` if the list
1170    /// is circular (Lua code can link nodes into a loop).
1171    pub fn bounded_tail(&self, mut p: NodeRef) -> Option<NodeRef> {
1172        if p.is_null() {
1173            return Some(p);
1174        }
1175        for _ in 0..self.nodes.len() {
1176            let n = self.next(p);
1177            if n.is_null() {
1178                return Some(p);
1179            }
1180            p = n;
1181        }
1182        None
1183    }
1184
1185    /// Frees a whole node list, including sublists (`flush_node_list`).
1186    pub fn flush_list(&mut self, p: NodeRef) {
1187        let mut stack: Vec<NodeRef> = Vec::new();
1188        let mut p = p;
1189        loop {
1190            while !p.is_null() {
1191                if self.is_free(p) {
1192                    // Freed already: a list that Lua code made circular,
1193                    // or a node in two lists.
1194                    self.free(p);
1195                    break;
1196                }
1197                let next = self.next(p);
1198                match &self.at(p).data {
1199                    NodeData::Box(b) => stack.push(b.head),
1200                    NodeData::Unset { b, .. } => stack.push(b.head),
1201                    NodeData::Ins { head, .. } | NodeData::Adjust { head } => stack.push(*head),
1202                    NodeData::Disc {
1203                        pre, post, replace, ..
1204                    } => {
1205                        stack.push(*pre);
1206                        stack.push(*post);
1207                        stack.push(*replace);
1208                    }
1209                    NodeData::Glue { leader, .. } => stack.push(*leader),
1210                    NodeData::Glyph(g) => {
1211                        if !g.components.is_null() {
1212                            stack.push(g.components);
1213                        }
1214                    }
1215                    NodeData::LocalPar {
1216                        box_left,
1217                        box_right,
1218                        ..
1219                    } => {
1220                        stack.push(*box_left);
1221                        stack.push(*box_right);
1222                    }
1223                    NodeData::Noad(n) => stack.extend([
1224                        n.nucleus,
1225                        n.sub,
1226                        n.sup,
1227                        n.left,
1228                        n.degree,
1229                        n.accent,
1230                        n.bot_accent,
1231                        n.new_hlist,
1232                    ]),
1233                    NodeData::Fraction(f) => {
1234                        stack.extend([f.num, f.denom, f.left, f.right, f.middle, f.new_hlist])
1235                    }
1236                    NodeData::Fence(f) => stack.extend([f.delim, f.new_hlist]),
1237                    NodeData::Choice(c) => stack.extend(c.iter().copied()),
1238                    NodeData::SubList { head } => stack.push(*head),
1239                    _ => {}
1240                }
1241                self.free(p);
1242                p = next;
1243            }
1244            match stack.pop() {
1245                Some(q) => p = q,
1246                None => return,
1247            }
1248        }
1249    }
1250
1251    /// Copies a whole node list, including sublists (`copy_node_list`).
1252    pub fn copy_list(&mut self, mut p: NodeRef) -> NodeRef {
1253        let mut head = NodeRef::NULL;
1254        let mut tail = NodeRef::NULL;
1255        // Lists nested deeper than this are cut off: only Lua code makes
1256        // them, with a box that contains itself (LuaTeX crashes).
1257        if self.copy_depth >= MAX_NESTING {
1258            return head;
1259        }
1260        self.copy_depth += 1;
1261        // A list longer than the arena is circular (made by Lua code;
1262        // LuaTeX copies until memory runs out): the copy stops.
1263        let mut left = self.nodes.len();
1264        while !p.is_null() && left > 0 {
1265            left -= 1;
1266            let q = self.copy_node(p);
1267            if head.is_null() {
1268                head = q;
1269            } else {
1270                self.link(tail, q);
1271            }
1272            tail = q;
1273            p = self.next(p);
1274        }
1275        self.copy_depth -= 1;
1276        head
1277    }
1278
1279    /// Copies one node with its sublists; the copy's links are null.
1280    pub fn copy_node(&mut self, p: NodeRef) -> NodeRef {
1281        let mut node = self.at(p).clone();
1282        node.next = NodeRef::NULL;
1283        node.prev = NodeRef::NULL;
1284        match &mut node.data {
1285            NodeData::Box(b) => b.head = self.copy_list(b.head),
1286            NodeData::Unset { b, .. } => b.head = self.copy_list(b.head),
1287            NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head = self.copy_list(*head),
1288            NodeData::Disc {
1289                pre, post, replace, ..
1290            } => {
1291                *pre = self.copy_list(*pre);
1292                *post = self.copy_list(*post);
1293                *replace = self.copy_list(*replace);
1294            }
1295            NodeData::Glue { leader, .. } => *leader = self.copy_list(*leader),
1296            NodeData::Glyph(g) => g.components = self.copy_list(g.components),
1297            NodeData::LocalPar {
1298                box_left,
1299                box_right,
1300                ..
1301            } => {
1302                *box_left = self.copy_list(*box_left);
1303                *box_right = self.copy_list(*box_right);
1304            }
1305            NodeData::Noad(n) => {
1306                for f in [
1307                    &mut n.nucleus,
1308                    &mut n.sub,
1309                    &mut n.sup,
1310                    &mut n.left,
1311                    &mut n.degree,
1312                    &mut n.accent,
1313                    &mut n.bot_accent,
1314                    &mut n.new_hlist,
1315                ] {
1316                    *f = self.copy_list(*f);
1317                }
1318            }
1319            NodeData::Fraction(fr) => {
1320                for f in [
1321                    &mut fr.num,
1322                    &mut fr.denom,
1323                    &mut fr.left,
1324                    &mut fr.right,
1325                    &mut fr.middle,
1326                    &mut fr.new_hlist,
1327                ] {
1328                    *f = self.copy_list(*f);
1329                }
1330            }
1331            NodeData::Fence(fe) => {
1332                fe.delim = self.copy_list(fe.delim);
1333                fe.new_hlist = self.copy_list(fe.new_hlist);
1334            }
1335            NodeData::Choice(c) => {
1336                for f in c.iter_mut() {
1337                    *f = self.copy_list(*f);
1338                }
1339            }
1340            NodeData::SubList { head } => *head = self.copy_list(*head),
1341            _ => {}
1342        }
1343        if let Some(r) = lua_ref_mut(&mut node.data) {
1344            let old = *r;
1345            let new = self.lua_refs.take();
1346            *r = new;
1347            self.lua_refs.copies.push((new, old));
1348        }
1349        self.in_use += 1;
1350        let q = if let Some(i) = self.free.pop() {
1351            self.nodes[i as usize] = node;
1352            NodeRef(i)
1353        } else {
1354            self.nodes.push(node);
1355            NodeRef(self.nodes.len() as u32 - 1)
1356        };
1357        if self.props.copy && self.props.nodes.contains(&p.0) {
1358            self.props.nodes.insert(q.0);
1359            self.props.events.push(PropEvent::Copy(p.0, q.0));
1360        }
1361        if self.sync.is_some() {
1362            let id = self.at(q).id;
1363            let f = if id == id::KERN || id == id::MATH {
1364                self.sync.as_ref().map_or((0, 0), |s| s.cur)
1365            } else {
1366                self.sync_fields(p)
1367            };
1368            self.set_sync_fields(q, f);
1369        }
1370        q
1371    }
1372
1373    /// A new empty box of the given type (`new_null_box`).
1374    pub fn new_null_box(&mut self, hlist: bool) -> NodeRef {
1375        self.alloc(
1376            if hlist { id::HLIST } else { id::VLIST },
1377            0,
1378            NodeData::Box(Box::default()),
1379        )
1380    }
1381
1382    /// A new glue node.
1383    pub fn new_glue(&mut self, spec: Glue, subtype: u16) -> NodeRef {
1384        self.alloc(
1385            id::GLUE,
1386            subtype,
1387            NodeData::Glue {
1388                spec,
1389                leader: NodeRef::NULL,
1390            },
1391        )
1392    }
1393
1394    /// A new kern node.
1395    pub fn new_kern(&mut self, w: i32, subtype: u16) -> NodeRef {
1396        self.alloc(
1397            id::KERN,
1398            subtype,
1399            NodeData::Kern {
1400                kern: w,
1401                expansion_factor: 0,
1402            },
1403        )
1404    }
1405
1406    /// A new penalty node.
1407    pub fn new_penalty(&mut self, m: i32, subtype: u16) -> NodeRef {
1408        self.alloc(id::PENALTY, subtype, NodeData::Penalty { penalty: m })
1409    }
1410
1411    /// A new temporary list head.
1412    pub fn new_temp(&mut self) -> NodeRef {
1413        self.alloc(id::TEMP, 0, NodeData::Temp)
1414    }
1415
1416    /// A new glyph node.
1417    pub fn new_glyph(&mut self, font: u32, chr: u32) -> NodeRef {
1418        self.alloc(
1419            id::GLYPH,
1420            0,
1421            NodeData::Glyph(GlyphData {
1422                chr,
1423                font,
1424                ..GlyphData::default()
1425            }),
1426        )
1427    }
1428
1429    /// A new rule node with the given dimensions.
1430    pub fn new_rule(&mut self, width: i32, height: i32, depth: i32) -> NodeRef {
1431        self.alloc(
1432            id::RULE,
1433            0,
1434            NodeData::Rule {
1435                width,
1436                depth,
1437                height,
1438                dir: 0,
1439                index: 0,
1440                left: 0,
1441                right: 0,
1442            },
1443        )
1444    }
1445
1446    /// A new empty discretionary node.
1447    pub fn new_disc(&mut self, subtype: u16, penalty: i32) -> NodeRef {
1448        self.alloc(
1449            id::DISC,
1450            subtype,
1451            NodeData::Disc {
1452                pre: NodeRef::NULL,
1453                post: NodeRef::NULL,
1454                replace: NodeRef::NULL,
1455                penalty,
1456            },
1457        )
1458    }
1459
1460    /// A new math node.
1461    pub fn new_math(&mut self, surround: i32, subtype: u16) -> NodeRef {
1462        self.alloc(
1463            id::MATH,
1464            subtype,
1465            NodeData::Math {
1466                surround,
1467                glue: Glue::ZERO,
1468            },
1469        )
1470    }
1471
1472    /// The type-specific data of `p`.
1473    #[inline]
1474    pub fn data(&self, p: NodeRef) -> &NodeData {
1475        &self.at(p).data
1476    }
1477
1478    /// The type-specific data of `p`, mutably.
1479    #[inline]
1480    pub fn data_mut(&mut self, p: NodeRef) -> &mut NodeData {
1481        &mut self.at_mut(p).data
1482    }
1483
1484    /// The subtype of `p`.
1485    #[inline]
1486    pub fn subtype(&self, p: NodeRef) -> u16 {
1487        self.at(p).subtype
1488    }
1489
1490    /// Sets the subtype of `p`.
1491    #[inline]
1492    pub fn set_subtype(&mut self, p: NodeRef, s: u16) {
1493        self.at_mut(p).subtype = s;
1494    }
1495
1496    /// The `prev` link of `p`.
1497    #[inline]
1498    pub fn prev(&self, p: NodeRef) -> NodeRef {
1499        self.at(p).prev
1500    }
1501
1502    /// Sets only the `next` link of `p`.
1503    #[inline]
1504    pub fn set_next(&mut self, p: NodeRef, q: NodeRef) {
1505        self.at_mut(p).next = q;
1506    }
1507
1508    /// Sets only the `prev` link of `p`.
1509    #[inline]
1510    pub fn set_prev(&mut self, p: NodeRef, q: NodeRef) {
1511        self.at_mut(p).prev = q;
1512    }
1513
1514    /// Whether `p` is a glyph node.
1515    #[inline]
1516    pub fn is_glyph(&self, p: NodeRef) -> bool {
1517        self.at(p).id == id::GLYPH
1518    }
1519
1520    /// The glyph data of a glyph node.
1521    #[inline]
1522    pub fn glyph(&self, p: NodeRef) -> Option<&GlyphData> {
1523        match &self.at(p).data {
1524            NodeData::Glyph(g) => Some(g),
1525            _ => None,
1526        }
1527    }
1528
1529    /// The glyph data of a glyph node, mutably.
1530    #[inline]
1531    pub fn glyph_mut(&mut self, p: NodeRef) -> Option<&mut GlyphData> {
1532        match &mut self.at_mut(p).data {
1533            NodeData::Glyph(g) => Some(g),
1534            _ => None,
1535        }
1536    }
1537
1538    /// The glue specification of a glue node.
1539    #[inline]
1540    pub fn glue_spec(&self, p: NodeRef) -> Glue {
1541        match &self.at(p).data {
1542            NodeData::Glue { spec, .. } => *spec,
1543            NodeData::Math { glue, .. } => *glue,
1544            _ => Glue::ZERO,
1545        }
1546    }
1547
1548    /// The width of a node that has one (boxes, rules, glue, kerns, math,
1549    /// margin kerns, unset nodes); zero otherwise. Glyph widths need the
1550    /// font and are not handled here.
1551    pub fn width(&self, p: NodeRef) -> i32 {
1552        match &self.at(p).data {
1553            NodeData::Box(b) => b.width,
1554            NodeData::Unset { b, .. } => b.width,
1555            NodeData::Rule { width, .. } => *width,
1556            NodeData::Glue { spec, .. } => spec.width,
1557            NodeData::Kern {
1558                kern,
1559                expansion_factor,
1560            } => *kern + *expansion_factor,
1561            NodeData::Math { surround, glue } => {
1562                if glue.width != 0 || glue.stretch != 0 || glue.shrink != 0 {
1563                    glue.width
1564                } else {
1565                    *surround
1566                }
1567            }
1568            NodeData::MarginKern { width, .. } => *width,
1569            _ => 0,
1570        }
1571    }
1572
1573    /// The penalty of a penalty node (0 for other nodes).
1574    pub fn penalty(&self, p: NodeRef) -> i32 {
1575        match &self.at(p).data {
1576            NodeData::Penalty { penalty } => *penalty,
1577            NodeData::Disc { penalty, .. } => *penalty,
1578            _ => 0,
1579        }
1580    }
1581
1582    /// The (pre, post, replace) lists of a discretionary.
1583    pub fn disc_lists(&self, p: NodeRef) -> (NodeRef, NodeRef, NodeRef) {
1584        match &self.at(p).data {
1585            NodeData::Disc {
1586                pre, post, replace, ..
1587            } => (*pre, *post, *replace),
1588            _ => (NodeRef::NULL, NodeRef::NULL, NodeRef::NULL),
1589        }
1590    }
1591
1592    /// Sets the lists of a discretionary.
1593    pub fn set_disc_lists(&mut self, p: NodeRef, l: (NodeRef, NodeRef, NodeRef)) {
1594        if let NodeData::Disc {
1595            pre, post, replace, ..
1596        } = &mut self.at_mut(p).data
1597        {
1598            *pre = l.0;
1599            *post = l.1;
1600            *replace = l.2;
1601        }
1602        for q in [l.0, l.1, l.2] {
1603            if !q.is_null() {
1604                self.at_mut(q).prev = NodeRef::NULL;
1605            }
1606        }
1607    }
1608
1609    /// The list of a box, unset node, insertion or adjust node.
1610    pub fn list_ptr(&self, p: NodeRef) -> NodeRef {
1611        match &self.at(p).data {
1612            NodeData::Box(b) => b.head,
1613            NodeData::Unset { b, .. } => b.head,
1614            NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head,
1615            _ => NodeRef::NULL,
1616        }
1617    }
1618
1619    /// Sets the list of a box, unset node, insertion or adjust node.
1620    pub fn set_list_ptr(&mut self, p: NodeRef, l: NodeRef) {
1621        match &mut self.at_mut(p).data {
1622            NodeData::Box(b) => b.head = l,
1623            NodeData::Unset { b, .. } => b.head = l,
1624            NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head = l,
1625            _ => {}
1626        }
1627    }
1628
1629    /// The attribute list of `p`.
1630    #[inline]
1631    pub fn attr(&self, p: NodeRef) -> Option<&AttrList> {
1632        self.at(p).attr.as_ref()
1633    }
1634
1635    /// Sets the attribute list of `p`.
1636    #[inline]
1637    pub fn set_attr(&mut self, p: NodeRef, a: Option<AttrList>) {
1638        self.at_mut(p).attr = a;
1639    }
1640
1641    /// The noad fields of a noad, radical or accent node.
1642    #[inline]
1643    pub fn noad(&self, p: NodeRef) -> Option<&NoadData> {
1644        match &self.at(p).data {
1645            NodeData::Noad(n) => Some(n),
1646            _ => None,
1647        }
1648    }
1649
1650    /// The noad fields of a noad, radical or accent node, mutably.
1651    #[inline]
1652    pub fn noad_mut(&mut self, p: NodeRef) -> Option<&mut NoadData> {
1653        match &mut self.at_mut(p).data {
1654            NodeData::Noad(n) => Some(n),
1655            _ => None,
1656        }
1657    }
1658
1659    /// The list of a `sub_box` or `sub_mlist` kernel (null otherwise).
1660    pub fn list_ptr_kernel(&self, p: NodeRef) -> NodeRef {
1661        if p.is_null() {
1662            return NodeRef::NULL;
1663        }
1664        match &self.at(p).data {
1665            NodeData::SubList { head } => *head,
1666            _ => NodeRef::NULL,
1667        }
1668    }
1669
1670    /// Sets the list of a `sub_box` or `sub_mlist` kernel.
1671    pub fn set_kernel_list(&mut self, p: NodeRef, l: NodeRef) {
1672        if p.is_null() {
1673            return;
1674        }
1675        if let NodeData::SubList { head } = &mut self.at_mut(p).data {
1676            *head = l;
1677        }
1678    }
1679
1680    /// Whether `p` is a noad that can carry scripts (`scripts_allowed`).
1681    pub fn scripts_allowed(&self, p: NodeRef) -> bool {
1682        matches!(self.at(p).data, NodeData::Noad(_))
1683            && matches!(self.id(p), id::NOAD | id::RADICAL | id::ACCENT)
1684    }
1685
1686    /// The number of nodes in the list starting at `p`.
1687    pub fn list_len(&self, mut p: NodeRef) -> usize {
1688        let mut n = 0;
1689        while !p.is_null() {
1690            n += 1;
1691            p = self.next(p);
1692        }
1693        n
1694    }
1695}
1696
1697/// The glue set ratio `num / den` as LuaTeX stores it: the quotient is
1698/// computed in double precision and kept as a C `float` (observed through
1699/// `tex.box[n].glue_set`). The rounding shows at shipout, where the ratio
1700/// multiplies the accumulated stretch or shrink (§625).
1701pub fn glue_ratio(num: i32, den: i32) -> f64 {
1702    f64::from((f64::from(num) / f64::from(den)) as f32)
1703}
1704
1705#[cfg(test)]
1706mod tests {
1707    use super::*;
1708
1709    #[test]
1710    fn glue_ratio_is_single_precision() {
1711        // `\hbox to 1021537sp{\hskip 0pt plus 873808sp}` in LuaTeX.
1712        assert_eq!(glue_ratio(1021537, 873808), 1.1690634489059448);
1713    }
1714
1715    #[test]
1716    fn alloc_link_copy_flush() {
1717        let mut n = Nodes::new();
1718        let b = n.new_null_box(true);
1719        let k = n.new_kern(5, kern_sub::USER);
1720        let g = n.new_glue(Glue::fixed(7), glue_sub::USER);
1721        n.link(k, g);
1722        n.box_data_mut(b).unwrap().head = k;
1723        assert_eq!(n.in_use, 3);
1724        let c = n.copy_node(b);
1725        assert_eq!(n.in_use, 6);
1726        let ch = n.box_data(c).unwrap().head;
1727        assert_ne!(ch, k);
1728        assert_eq!(n.get(n.next(ch)).data, n.get(g).data);
1729        n.flush_list(c);
1730        n.flush_list(b);
1731        assert_eq!(n.in_use, 0);
1732    }
1733
1734    /// A list that Lua code linked into a loop: walks that are bounded
1735    /// anyway end, and flushing it reports the node freed twice.
1736    #[test]
1737    fn circular_list() {
1738        let mut n = Nodes::new();
1739        let a = n.new_kern(1, kern_sub::USER);
1740        let b = n.new_glue(Glue::fixed(2), glue_sub::USER);
1741        n.link(a, b);
1742        n.link(b, a);
1743        assert_eq!(n.bounded_tail(a), None);
1744        let c = n.copy_list(a);
1745        assert!(!c.is_null());
1746        assert_eq!(n.double_free, None);
1747        n.flush_list(a);
1748        assert_eq!(n.double_free, Some((id::KERN, a.0)));
1749        assert_eq!(type_name(id::KERN), "kern");
1750    }
1751}