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