use std::rc::Rc;
use crate::glue::Glue;
use crate::token::TokenList;
pub const LUATEX_NODE_TYPES: &str = include_str!("../data/luatex-node-types.txt");
pub fn type_name(t: u8) -> String {
LUATEX_NODE_TYPES
.lines()
.filter_map(|l| l.strip_prefix("type "))
.find_map(|l| {
let mut it = l.split_whitespace();
let n: u8 = it.next()?.parse().ok()?;
(n == t).then(|| it.next().unwrap_or("").to_owned())
})
.unwrap_or_default()
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
pub struct NodeRef(pub u32);
impl NodeRef {
pub const NULL: NodeRef = NodeRef(0);
#[inline]
pub const fn is_null(self) -> bool {
self.0 == 0
}
}
pub const RUNNING: i32 = -0o10000000000;
#[allow(missing_docs)]
pub mod id {
pub const HLIST: u8 = 0;
pub const VLIST: u8 = 1;
pub const RULE: u8 = 2;
pub const INS: u8 = 3;
pub const MARK: u8 = 4;
pub const ADJUST: u8 = 5;
pub const BOUNDARY: u8 = 6;
pub const DISC: u8 = 7;
pub const WHATSIT: u8 = 8;
pub const LOCAL_PAR: u8 = 9;
pub const DIR: u8 = 10;
pub const MATH: u8 = 11;
pub const GLUE: u8 = 12;
pub const KERN: u8 = 13;
pub const PENALTY: u8 = 14;
pub const UNSET: u8 = 15;
pub const STYLE: u8 = 16;
pub const CHOICE: u8 = 17;
pub const NOAD: u8 = 18;
pub const RADICAL: u8 = 19;
pub const FRACTION: u8 = 20;
pub const ACCENT: u8 = 21;
pub const FENCE: u8 = 22;
pub const MATH_CHAR: u8 = 23;
pub const SUB_BOX: u8 = 24;
pub const SUB_MLIST: u8 = 25;
pub const MATH_TEXT_CHAR: u8 = 26;
pub const DELIM: u8 = 27;
pub const MARGIN_KERN: u8 = 28;
pub const GLYPH: u8 = 29;
pub const ALIGN_RECORD: u8 = 30;
pub const PAGE_INSERT: u8 = 33;
pub const SPLIT_INSERT: u8 = 34;
pub const GLUE_SPEC: u8 = 39;
pub const TEMP: u8 = 41;
pub const FREE: u8 = 255;
}
pub const MAX_NESTING: u32 = 50_000;
#[allow(missing_docs)]
pub mod glue_sub {
pub const USER: u16 = 0;
pub const LINE_SKIP: u16 = 1;
pub const BASELINE_SKIP: u16 = 2;
pub const PAR_SKIP: u16 = 3;
pub const ABOVE_DISPLAY: u16 = 4;
pub const BELOW_DISPLAY: u16 = 5;
pub const ABOVE_DISPLAY_SHORT: u16 = 6;
pub const BELOW_DISPLAY_SHORT: u16 = 7;
pub const LEFT_SKIP: u16 = 8;
pub const RIGHT_SKIP: u16 = 9;
pub const TOP_SKIP: u16 = 10;
pub const SPLIT_TOP_SKIP: u16 = 11;
pub const TAB_SKIP: u16 = 12;
pub const SPACE_SKIP: u16 = 13;
pub const XSPACE_SKIP: u16 = 14;
pub const PAR_FILL_SKIP: u16 = 15;
pub const MATH_SKIP: u16 = 16;
pub const THIN_MU_SKIP: u16 = 17;
pub const MED_MU_SKIP: u16 = 18;
pub const THICK_MU_SKIP: u16 = 19;
pub const COND_MATH_GLUE: u16 = 98;
pub const MU_GLUE: u16 = 99;
pub const A_LEADERS: u16 = 100;
pub const C_LEADERS: u16 = 101;
pub const X_LEADERS: u16 = 102;
pub const G_LEADERS: u16 = 103;
}
pub mod list_sub {
pub const MATH_CHAR: u16 = 9;
pub const H_EXTENSIBLE: u16 = 10;
pub const V_EXTENSIBLE: u16 = 11;
pub const V_DELIMITER: u16 = 13;
pub const OVER_DELIMITER: u16 = 14;
pub const UNDER_DELIMITER: u16 = 15;
pub const NUMERATOR: u16 = 16;
pub const DENOMINATOR: u16 = 17;
pub const LIMITS: u16 = 18;
pub const FRACTION: u16 = 19;
pub const NUCLEUS: u16 = 20;
pub const SUP: u16 = 21;
pub const SUB: u16 = 22;
pub const DEGREE: u16 = 23;
pub const SCRIPTS: u16 = 24;
pub const OVER: u16 = 25;
pub const UNDER: u16 = 26;
pub const ACCENT: u16 = 27;
pub const RADICAL: u16 = 28;
}
#[allow(missing_docs)]
pub mod kern_sub {
pub const FONT: u16 = 0;
pub const USER: u16 = 1;
pub const ACCENT: u16 = 2;
pub const ITALIC: u16 = 3;
}
#[allow(missing_docs)]
pub mod glyph_sub {
pub const CHARACTER: u16 = 1;
pub const LIGATURE: u16 = 2;
pub const GHOST: u16 = 4;
pub const LEFT: u16 = 8;
pub const RIGHT: u16 = 16;
}
#[allow(missing_docs)]
pub mod whatsit_sub {
pub const OPEN: u16 = 0;
pub const WRITE: u16 = 1;
pub const CLOSE: u16 = 2;
pub const SPECIAL: u16 = 3;
pub const LATE_SPECIAL: u16 = 4;
pub const SAVE_POS: u16 = 7;
pub const LATE_LUA: u16 = 8;
pub const USER_DEFINED: u16 = 9;
pub const PDF_LITERAL: u16 = 16;
pub const PDF_LATE_LITERAL: u16 = 17;
pub const PDF_REFOBJ: u16 = 18;
pub const PDF_ANNOT: u16 = 19;
pub const PDF_START_LINK: u16 = 20;
pub const PDF_END_LINK: u16 = 21;
pub const PDF_DEST: u16 = 22;
pub const PDF_ACTION: u16 = 23;
pub const PDF_THREAD: u16 = 24;
pub const PDF_START_THREAD: u16 = 25;
pub const PDF_END_THREAD: u16 = 26;
pub const PDF_COLORSTACK: u16 = 29;
pub const PDF_SETMATRIX: u16 = 30;
pub const PDF_SAVE: u16 = 31;
pub const PDF_RESTORE: u16 = 32;
pub const PDF_LINK_STATE: u16 = 33;
}
pub type AttrList = Rc<[(u32, i32)]>;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct BoxData {
pub width: i32,
pub depth: i32,
pub height: i32,
pub dir: i8,
pub shift: i32,
pub glue_order: u8,
pub glue_sign: u8,
pub glue_set: f64,
pub head: NodeRef,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct GlyphData {
pub chr: u32,
pub font: u32,
pub lang: u16,
pub left: u8,
pub right: u8,
pub uchyph: bool,
pub components: NodeRef,
pub xoffset: i32,
pub yoffset: i32,
pub expansion_factor: i32,
pub data: i32,
}
#[derive(Clone, Debug, PartialEq)]
pub enum NodeData {
Temp,
Box(Box<BoxData>),
Rule {
width: i32,
depth: i32,
height: i32,
dir: i8,
index: i32,
left: i32,
right: i32,
},
Ins {
cost: i32,
depth: i32,
height: i32,
spec: Glue,
head: NodeRef,
},
Mark {
class: u32,
mark: TokenList,
},
Adjust {
head: NodeRef,
},
Boundary {
value: i32,
},
Disc {
pre: NodeRef,
post: NodeRef,
replace: NodeRef,
penalty: i32,
},
Whatsit(Box<Whatsit>),
LocalPar {
pen_inter: i32,
pen_broken: i32,
dir: i8,
box_left: NodeRef,
box_left_width: i32,
box_right: NodeRef,
box_right_width: i32,
},
Dir {
dir: i8,
level: i32,
},
Math {
surround: i32,
glue: Glue,
},
Glue {
spec: Glue,
leader: NodeRef,
},
Kern {
kern: i32,
expansion_factor: i32,
},
Penalty {
penalty: i32,
},
Unset {
b: Box<BoxData>,
span_count: u16,
stretch: i32,
shrink: i32,
stretch_order: u8,
shrink_order: u8,
},
Glyph(GlyphData),
MarginKern {
width: i32,
glyph: NodeRef,
},
Noad(Box<NoadData>),
Fraction(Box<FractionData>),
Fence(Box<FenceData>),
Style {
style: u8,
},
Choice(Box<[NodeRef; 4]>),
MathChar {
fam: u32,
chr: u32,
},
SubList {
head: NodeRef,
},
Delim {
small_fam: u32,
small_char: u32,
large_fam: u32,
large_char: u32,
},
Other(Vec<i64>),
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct NoadData {
pub nucleus: NodeRef,
pub sub: NodeRef,
pub sup: NodeRef,
pub options: u32,
pub left: NodeRef,
pub degree: NodeRef,
pub width: i32,
pub accent: NodeRef,
pub bot_accent: NodeRef,
pub fraction: i32,
pub new_hlist: NodeRef,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FractionData {
pub thickness: i32,
pub num: NodeRef,
pub denom: NodeRef,
pub left: NodeRef,
pub right: NodeRef,
pub middle: NodeRef,
pub options: u32,
pub new_hlist: NodeRef,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FenceData {
pub delim: NodeRef,
pub italic: i32,
pub height: i32,
pub depth: i32,
pub options: u32,
pub class: i32,
pub new_hlist: NodeRef,
}
pub const DEFAULT_CODE: i32 = 0o10000000000;
#[allow(missing_docs)]
pub mod noad_sub {
pub const ORD: u16 = 0;
pub const OP_DISPLAY_LIMITS: u16 = 1;
pub const OP_LIMITS: u16 = 2;
pub const OP_NO_LIMITS: u16 = 3;
pub const BIN: u16 = 4;
pub const REL: u16 = 5;
pub const OPEN: u16 = 6;
pub const CLOSE: u16 = 7;
pub const PUNCT: u16 = 8;
pub const INNER: u16 = 9;
pub const UNDER: u16 = 10;
pub const OVER: u16 = 11;
pub const VCENTER: u16 = 12;
}
#[allow(missing_docs)]
pub mod radical_sub {
pub const RADICAL: u16 = 0;
pub const URADICAL: u16 = 1;
pub const UROOT: u16 = 2;
pub const UUNDERDELIMITER: u16 = 3;
pub const UOVERDELIMITER: u16 = 4;
pub const UDELIMITERUNDER: u16 = 5;
pub const UDELIMITEROVER: u16 = 6;
pub const UHEXTENSIBLE: u16 = 7;
}
#[allow(missing_docs)]
pub mod fence_sub {
pub const UNSET: u16 = 0;
pub const LEFT: u16 = 1;
pub const MIDDLE: u16 = 2;
pub const RIGHT: u16 = 3;
pub const NO: u16 = 4;
}
#[allow(missing_docs)]
pub mod accent_sub {
pub const BOTH_FLEXIBLE: u16 = 0;
pub const FIXED_TOP: u16 = 1;
pub const FIXED_BOTTOM: u16 = 2;
pub const FIXED_BOTH: u16 = 3;
}
#[allow(missing_docs)]
pub mod noad_opt {
pub const SET: u32 = 0x08;
pub const AXIS: u32 = 0x02 | SET;
pub const NO_AXIS: u32 = 0x04 | SET;
pub const EXACT: u32 = 0x10 | SET;
pub const LEFT: u32 = 0x11 | SET;
pub const MIDDLE: u32 = 0x12 | SET;
pub const RIGHT: u32 = 0x14 | SET;
pub const NO_SUB_SCRIPT: u32 = 0x21 | SET;
pub const NO_SUPER_SCRIPT: u32 = 0x22 | SET;
pub const OVERLAY: u32 = 0x100;
}
#[derive(Clone, Debug, PartialEq)]
pub enum Whatsit {
Open {
stream: u32,
name: Vec<u8>,
},
Write {
stream: i32,
data: TokenList,
},
Close {
stream: u32,
},
Special {
data: Vec<u8>,
},
LateLua {
data: LateData,
name: Vec<u8>,
},
PdfLiteral {
mode: u8,
data: LateData,
late: bool,
},
SavePos,
PdfColorstack {
stack: i32,
cmd: u8,
data: Vec<u8>,
},
PdfSetmatrix {
data: Vec<u8>,
},
PdfSave,
PdfRestore,
PdfAnnot {
dims: [i32; 3],
objnum: i32,
data: Vec<u8>,
},
PdfStartLink {
dims: [i32; 3],
objnum: i32,
attr: Vec<u8>,
action: crate::pdfext::PdfAction,
},
PdfEndLink,
PdfDest {
id: crate::pdfext::DestId,
view: crate::pdfext::DestView,
structure: Option<i32>,
},
PdfRefobj {
objnum: i32,
},
PdfLinkState {
state: i32,
},
UserDefined {
user_id: i32,
value: UserValue,
},
Other {
fields: Vec<i64>,
data: Vec<u8>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub enum LateData {
Tokens(TokenList),
Bytes(Vec<u8>),
Lua {
data: Vec<u8>,
reference: i32,
},
Function {
reference: i32,
},
}
impl LateData {
pub fn bytes(&self) -> Option<&[u8]> {
match self {
LateData::Bytes(b) | LateData::Lua { data: b, .. } => Some(b),
LateData::Tokens(_) | LateData::Function { .. } => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum UserValue {
Int(i32),
Str(Vec<u8>),
Tokens(TokenList),
Nodes(NodeRef),
Lua(i32),
Attr,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Node {
pub id: u8,
pub subtype: u16,
pub next: NodeRef,
pub prev: NodeRef,
pub attr: Option<AttrList>,
pub data: NodeData,
}
impl Node {
fn temp() -> Node {
Node {
id: id::TEMP,
subtype: 0,
next: NodeRef::NULL,
prev: NodeRef::NULL,
attr: None,
data: NodeData::Temp,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PropEvent {
Flush(u32),
Copy(u32, u32),
}
#[derive(Debug, Clone, Default)]
pub struct Properties {
pub nodes: std::collections::HashSet<u32>,
pub flush: bool,
pub copy: bool,
pub events: Vec<PropEvent>,
}
#[derive(Debug, Clone)]
pub struct Nodes {
nodes: Vec<Node>,
copy_depth: u32,
sink: Node,
free: Vec<u32>,
pub in_use: usize,
pub props: Properties,
pub sync: Option<Box<SyncFields>>,
pub double_free: Option<(u8, u32)>,
pub par_temp: NodeRef,
pub lua_refs: LuaRefs,
}
#[derive(Debug, Clone, Default)]
pub struct LuaRefs {
pub next: i32,
pub free: Vec<i32>,
pub copies: Vec<(i32, i32)>,
}
impl LuaRefs {
pub fn take(&mut self) -> i32 {
if let Some(r) = self.free.pop() {
return r;
}
if self.next == 0 {
self.next = 780;
}
let r = self.next;
self.next += 1;
r
}
pub fn release(&mut self, r: i32) {
if r > 0 {
self.free.push(r);
}
}
pub fn font_defined(&mut self, temporary: bool) {
if temporary {
let t = self.take();
self.take();
self.release(t);
} else {
self.take();
}
}
}
fn lua_ref_mut(data: &mut NodeData) -> Option<&mut i32> {
match data {
NodeData::Whatsit(w) => match &mut **w {
Whatsit::LateLua {
data: LateData::Lua { reference, .. } | LateData::Function { reference },
..
}
| Whatsit::PdfLiteral {
data: LateData::Lua { reference, .. },
..
} => Some(reference),
Whatsit::UserDefined {
value: UserValue::Lua(r),
..
} if *r > 0 => Some(r),
_ => None,
},
_ => None,
}
}
#[derive(Debug, Clone, Default)]
pub struct SyncFields {
pub cur: (u32, i32),
fields: Vec<(u32, i32)>,
}
impl SyncFields {
#[inline]
fn set(&mut self, i: u32, f: (u32, i32)) {
let i = i as usize;
if i >= self.fields.len() {
self.fields.resize(i + 1, (0, 0));
}
self.fields[i] = f;
}
}
impl Default for Nodes {
fn default() -> Self {
Self::new()
}
}
impl Nodes {
pub fn new() -> Self {
Nodes {
nodes: vec![Node::temp()],
copy_depth: 0,
sink: Node::temp(),
free: Vec::new(),
in_use: 0,
props: Properties::default(),
sync: None,
lua_refs: LuaRefs::default(),
double_free: None,
par_temp: NodeRef::NULL,
}
}
pub fn enable_sync(&mut self) {
if self.sync.is_none() {
self.sync = Some(Box::default());
}
}
#[inline]
pub fn sync_fields(&self, p: NodeRef) -> (u32, i32) {
self.sync
.as_ref()
.and_then(|s| s.fields.get(p.0 as usize).copied())
.unwrap_or((0, 0))
}
pub fn set_sync_fields(&mut self, p: NodeRef, f: (u32, i32)) {
if p.0 as usize >= self.nodes.len() {
return;
}
if let Some(s) = self.sync.as_deref_mut() {
s.set(p.0, f);
}
}
pub fn sync_as_copied(&mut self, p: NodeRef) {
if let Some(s) = self.sync.as_deref_mut() {
let cur = s.cur;
s.set(p.0, cur);
}
}
#[inline]
fn at(&self, p: NodeRef) -> &Node {
self.nodes.get(p.0 as usize).unwrap_or(&self.nodes[0])
}
#[inline]
fn at_mut(&mut self, p: NodeRef) -> &mut Node {
match self.nodes.get_mut(p.0 as usize) {
Some(n) => n,
None => {
self.sink = Node::temp();
&mut self.sink
}
}
}
pub fn capacity(&self) -> usize {
self.nodes.len()
}
pub fn alloc(&mut self, id: u8, subtype: u16, data: NodeData) -> NodeRef {
let node = Node {
id,
subtype,
next: NodeRef::NULL,
prev: NodeRef::NULL,
attr: None,
data,
};
self.in_use += 1;
let p = if let Some(i) = self.free.pop() {
self.nodes[i as usize] = node;
NodeRef(i)
} else {
self.nodes.push(node);
NodeRef(self.nodes.len() as u32 - 1)
};
if let Some(s) = self.sync.as_deref_mut() {
let f = if id == id::KERN || id == id::GLYPH {
(0, 0)
} else {
s.cur
};
s.set(p.0, f);
}
p
}
pub fn free(&mut self, p: NodeRef) {
if p.is_null() || p.0 as usize >= self.nodes.len() {
return;
}
if self.is_free(p) {
if self.double_free.is_none() {
self.double_free = Some((self.at(p).subtype as u8, p.0));
}
return;
}
let t = self.at(p).id;
self.at_mut(p).subtype = u16::from(t);
if let Some(r) = lua_ref_mut(&mut self.at_mut(p).data) {
let r = *r;
self.lua_refs.release(r);
}
self.at_mut(p).id = id::FREE;
self.at_mut(p).data = NodeData::Temp;
self.at_mut(p).attr = None;
self.free.push(p.0);
self.in_use -= 1;
if !self.props.nodes.is_empty() && self.props.nodes.remove(&p.0) && self.props.flush {
self.props.events.push(PropEvent::Flush(p.0));
}
}
pub fn lua_ref_of(&mut self, p: NodeRef) -> Option<i32> {
lua_ref_mut(&mut self.at_mut(p).data).map(|r| *r)
}
#[inline]
pub fn is_free(&self, p: NodeRef) -> bool {
self.at(p).id == id::FREE
}
#[inline]
pub fn get(&self, p: NodeRef) -> &Node {
self.at(p)
}
#[inline]
pub fn get_mut(&mut self, p: NodeRef) -> &mut Node {
self.at_mut(p)
}
#[inline]
pub fn id(&self, p: NodeRef) -> u8 {
self.at(p).id
}
#[inline]
pub fn next(&self, p: NodeRef) -> NodeRef {
self.at(p).next
}
#[inline]
pub fn link(&mut self, p: NodeRef, q: NodeRef) {
self.at_mut(p).next = q;
if !q.is_null() {
self.at_mut(q).prev = p;
}
}
pub fn box_data(&self, p: NodeRef) -> Option<&BoxData> {
match &self.at(p).data {
NodeData::Box(b) => Some(b),
NodeData::Unset { b, .. } => Some(b),
_ => None,
}
}
pub fn box_data_mut(&mut self, p: NodeRef) -> Option<&mut BoxData> {
match &mut self.at_mut(p).data {
NodeData::Box(b) => Some(b),
NodeData::Unset { b, .. } => Some(b),
_ => None,
}
}
pub fn tail_of(&self, mut p: NodeRef) -> NodeRef {
if p.is_null() {
return p;
}
loop {
let n = self.next(p);
if n.is_null() {
return p;
}
p = n;
}
}
pub fn bounded_tail(&self, mut p: NodeRef) -> Option<NodeRef> {
if p.is_null() {
return Some(p);
}
for _ in 0..self.nodes.len() {
let n = self.next(p);
if n.is_null() {
return Some(p);
}
p = n;
}
None
}
pub fn flush_list(&mut self, p: NodeRef) {
let mut stack: Vec<NodeRef> = Vec::new();
let mut p = p;
loop {
while !p.is_null() {
if self.is_free(p) {
self.free(p);
break;
}
let next = self.next(p);
match &self.at(p).data {
NodeData::Box(b) => stack.push(b.head),
NodeData::Unset { b, .. } => stack.push(b.head),
NodeData::Ins { head, .. } | NodeData::Adjust { head } => stack.push(*head),
NodeData::Disc {
pre, post, replace, ..
} => {
stack.push(*pre);
stack.push(*post);
stack.push(*replace);
}
NodeData::Glue { leader, .. } => stack.push(*leader),
NodeData::Glyph(g) => {
if !g.components.is_null() {
stack.push(g.components);
}
}
NodeData::LocalPar {
box_left,
box_right,
..
} => {
stack.push(*box_left);
stack.push(*box_right);
}
NodeData::Noad(n) => stack.extend([
n.nucleus,
n.sub,
n.sup,
n.left,
n.degree,
n.accent,
n.bot_accent,
n.new_hlist,
]),
NodeData::Fraction(f) => {
stack.extend([f.num, f.denom, f.left, f.right, f.middle, f.new_hlist])
}
NodeData::Fence(f) => stack.extend([f.delim, f.new_hlist]),
NodeData::Choice(c) => stack.extend(c.iter().copied()),
NodeData::SubList { head } => stack.push(*head),
_ => {}
}
self.free(p);
p = next;
}
match stack.pop() {
Some(q) => p = q,
None => return,
}
}
}
pub fn copy_list(&mut self, mut p: NodeRef) -> NodeRef {
let mut head = NodeRef::NULL;
let mut tail = NodeRef::NULL;
if self.copy_depth >= MAX_NESTING {
return head;
}
self.copy_depth += 1;
let mut left = self.nodes.len();
while !p.is_null() && left > 0 {
left -= 1;
let q = self.copy_node(p);
if head.is_null() {
head = q;
} else {
self.link(tail, q);
}
tail = q;
p = self.next(p);
}
self.copy_depth -= 1;
head
}
pub fn copy_node(&mut self, p: NodeRef) -> NodeRef {
let mut node = self.at(p).clone();
node.next = NodeRef::NULL;
node.prev = NodeRef::NULL;
match &mut node.data {
NodeData::Box(b) => b.head = self.copy_list(b.head),
NodeData::Unset { b, .. } => b.head = self.copy_list(b.head),
NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head = self.copy_list(*head),
NodeData::Disc {
pre, post, replace, ..
} => {
*pre = self.copy_list(*pre);
*post = self.copy_list(*post);
*replace = self.copy_list(*replace);
}
NodeData::Glue { leader, .. } => *leader = self.copy_list(*leader),
NodeData::Glyph(g) => g.components = self.copy_list(g.components),
NodeData::LocalPar {
box_left,
box_right,
..
} => {
*box_left = self.copy_list(*box_left);
*box_right = self.copy_list(*box_right);
}
NodeData::Noad(n) => {
for f in [
&mut n.nucleus,
&mut n.sub,
&mut n.sup,
&mut n.left,
&mut n.degree,
&mut n.accent,
&mut n.bot_accent,
&mut n.new_hlist,
] {
*f = self.copy_list(*f);
}
}
NodeData::Fraction(fr) => {
for f in [
&mut fr.num,
&mut fr.denom,
&mut fr.left,
&mut fr.right,
&mut fr.middle,
&mut fr.new_hlist,
] {
*f = self.copy_list(*f);
}
}
NodeData::Fence(fe) => {
fe.delim = self.copy_list(fe.delim);
fe.new_hlist = self.copy_list(fe.new_hlist);
}
NodeData::Choice(c) => {
for f in c.iter_mut() {
*f = self.copy_list(*f);
}
}
NodeData::SubList { head } => *head = self.copy_list(*head),
_ => {}
}
if let Some(r) = lua_ref_mut(&mut node.data) {
let old = *r;
let new = self.lua_refs.take();
*r = new;
self.lua_refs.copies.push((new, old));
}
self.in_use += 1;
let q = if let Some(i) = self.free.pop() {
self.nodes[i as usize] = node;
NodeRef(i)
} else {
self.nodes.push(node);
NodeRef(self.nodes.len() as u32 - 1)
};
if self.props.copy && self.props.nodes.contains(&p.0) {
self.props.nodes.insert(q.0);
self.props.events.push(PropEvent::Copy(p.0, q.0));
}
if self.sync.is_some() {
let id = self.at(q).id;
let f = if id == id::KERN || id == id::MATH {
self.sync.as_ref().map_or((0, 0), |s| s.cur)
} else {
self.sync_fields(p)
};
self.set_sync_fields(q, f);
}
q
}
pub fn new_null_box(&mut self, hlist: bool) -> NodeRef {
self.alloc(
if hlist { id::HLIST } else { id::VLIST },
0,
NodeData::Box(Box::default()),
)
}
pub fn new_glue(&mut self, spec: Glue, subtype: u16) -> NodeRef {
self.alloc(
id::GLUE,
subtype,
NodeData::Glue {
spec,
leader: NodeRef::NULL,
},
)
}
pub fn new_kern(&mut self, w: i32, subtype: u16) -> NodeRef {
self.alloc(
id::KERN,
subtype,
NodeData::Kern {
kern: w,
expansion_factor: 0,
},
)
}
pub fn new_penalty(&mut self, m: i32, subtype: u16) -> NodeRef {
self.alloc(id::PENALTY, subtype, NodeData::Penalty { penalty: m })
}
pub fn new_temp(&mut self) -> NodeRef {
self.alloc(id::TEMP, 0, NodeData::Temp)
}
pub fn new_glyph(&mut self, font: u32, chr: u32) -> NodeRef {
self.alloc(
id::GLYPH,
0,
NodeData::Glyph(GlyphData {
chr,
font,
..GlyphData::default()
}),
)
}
pub fn new_rule(&mut self, width: i32, height: i32, depth: i32) -> NodeRef {
self.alloc(
id::RULE,
0,
NodeData::Rule {
width,
depth,
height,
dir: 0,
index: 0,
left: 0,
right: 0,
},
)
}
pub fn new_disc(&mut self, subtype: u16, penalty: i32) -> NodeRef {
self.alloc(
id::DISC,
subtype,
NodeData::Disc {
pre: NodeRef::NULL,
post: NodeRef::NULL,
replace: NodeRef::NULL,
penalty,
},
)
}
pub fn new_math(&mut self, surround: i32, subtype: u16) -> NodeRef {
self.alloc(
id::MATH,
subtype,
NodeData::Math {
surround,
glue: Glue::ZERO,
},
)
}
#[inline]
pub fn data(&self, p: NodeRef) -> &NodeData {
&self.at(p).data
}
#[inline]
pub fn data_mut(&mut self, p: NodeRef) -> &mut NodeData {
&mut self.at_mut(p).data
}
#[inline]
pub fn subtype(&self, p: NodeRef) -> u16 {
self.at(p).subtype
}
#[inline]
pub fn set_subtype(&mut self, p: NodeRef, s: u16) {
self.at_mut(p).subtype = s;
}
#[inline]
pub fn prev(&self, p: NodeRef) -> NodeRef {
self.at(p).prev
}
#[inline]
pub fn set_next(&mut self, p: NodeRef, q: NodeRef) {
self.at_mut(p).next = q;
}
#[inline]
pub fn set_prev(&mut self, p: NodeRef, q: NodeRef) {
self.at_mut(p).prev = q;
}
#[inline]
pub fn is_glyph(&self, p: NodeRef) -> bool {
self.at(p).id == id::GLYPH
}
#[inline]
pub fn glyph(&self, p: NodeRef) -> Option<&GlyphData> {
match &self.at(p).data {
NodeData::Glyph(g) => Some(g),
_ => None,
}
}
#[inline]
pub fn glyph_mut(&mut self, p: NodeRef) -> Option<&mut GlyphData> {
match &mut self.at_mut(p).data {
NodeData::Glyph(g) => Some(g),
_ => None,
}
}
#[inline]
pub fn glue_spec(&self, p: NodeRef) -> Glue {
match &self.at(p).data {
NodeData::Glue { spec, .. } => *spec,
NodeData::Math { glue, .. } => *glue,
_ => Glue::ZERO,
}
}
pub fn width(&self, p: NodeRef) -> i32 {
match &self.at(p).data {
NodeData::Box(b) => b.width,
NodeData::Unset { b, .. } => b.width,
NodeData::Rule { width, .. } => *width,
NodeData::Glue { spec, .. } => spec.width,
NodeData::Kern {
kern,
expansion_factor,
} => *kern + *expansion_factor,
NodeData::Math { surround, glue } => {
if glue.width != 0 || glue.stretch != 0 || glue.shrink != 0 {
glue.width
} else {
*surround
}
}
NodeData::MarginKern { width, .. } => *width,
_ => 0,
}
}
pub fn penalty(&self, p: NodeRef) -> i32 {
match &self.at(p).data {
NodeData::Penalty { penalty } => *penalty,
NodeData::Disc { penalty, .. } => *penalty,
_ => 0,
}
}
pub fn disc_lists(&self, p: NodeRef) -> (NodeRef, NodeRef, NodeRef) {
match &self.at(p).data {
NodeData::Disc {
pre, post, replace, ..
} => (*pre, *post, *replace),
_ => (NodeRef::NULL, NodeRef::NULL, NodeRef::NULL),
}
}
pub fn set_disc_lists(&mut self, p: NodeRef, l: (NodeRef, NodeRef, NodeRef)) {
if let NodeData::Disc {
pre, post, replace, ..
} = &mut self.at_mut(p).data
{
*pre = l.0;
*post = l.1;
*replace = l.2;
}
for q in [l.0, l.1, l.2] {
if !q.is_null() {
self.at_mut(q).prev = NodeRef::NULL;
}
}
}
pub fn list_ptr(&self, p: NodeRef) -> NodeRef {
match &self.at(p).data {
NodeData::Box(b) => b.head,
NodeData::Unset { b, .. } => b.head,
NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head,
_ => NodeRef::NULL,
}
}
pub fn set_list_ptr(&mut self, p: NodeRef, l: NodeRef) {
match &mut self.at_mut(p).data {
NodeData::Box(b) => b.head = l,
NodeData::Unset { b, .. } => b.head = l,
NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head = l,
_ => {}
}
}
#[inline]
pub fn attr(&self, p: NodeRef) -> Option<&AttrList> {
self.at(p).attr.as_ref()
}
#[inline]
pub fn set_attr(&mut self, p: NodeRef, a: Option<AttrList>) {
self.at_mut(p).attr = a;
}
#[inline]
pub fn noad(&self, p: NodeRef) -> Option<&NoadData> {
match &self.at(p).data {
NodeData::Noad(n) => Some(n),
_ => None,
}
}
#[inline]
pub fn noad_mut(&mut self, p: NodeRef) -> Option<&mut NoadData> {
match &mut self.at_mut(p).data {
NodeData::Noad(n) => Some(n),
_ => None,
}
}
pub fn list_ptr_kernel(&self, p: NodeRef) -> NodeRef {
if p.is_null() {
return NodeRef::NULL;
}
match &self.at(p).data {
NodeData::SubList { head } => *head,
_ => NodeRef::NULL,
}
}
pub fn set_kernel_list(&mut self, p: NodeRef, l: NodeRef) {
if p.is_null() {
return;
}
if let NodeData::SubList { head } = &mut self.at_mut(p).data {
*head = l;
}
}
pub fn scripts_allowed(&self, p: NodeRef) -> bool {
matches!(self.at(p).data, NodeData::Noad(_))
&& matches!(self.id(p), id::NOAD | id::RADICAL | id::ACCENT)
}
pub fn list_len(&self, mut p: NodeRef) -> usize {
let mut n = 0;
while !p.is_null() {
n += 1;
p = self.next(p);
}
n
}
}
pub fn glue_ratio(num: i32, den: i32) -> f64 {
f64::from((f64::from(num) / f64::from(den)) as f32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn glue_ratio_is_single_precision() {
assert_eq!(glue_ratio(1021537, 873808), 1.1690634489059448);
}
#[test]
fn alloc_link_copy_flush() {
let mut n = Nodes::new();
let b = n.new_null_box(true);
let k = n.new_kern(5, kern_sub::USER);
let g = n.new_glue(Glue::fixed(7), glue_sub::USER);
n.link(k, g);
n.box_data_mut(b).unwrap().head = k;
assert_eq!(n.in_use, 3);
let c = n.copy_node(b);
assert_eq!(n.in_use, 6);
let ch = n.box_data(c).unwrap().head;
assert_ne!(ch, k);
assert_eq!(n.get(n.next(ch)).data, n.get(g).data);
n.flush_list(c);
n.flush_list(b);
assert_eq!(n.in_use, 0);
}
#[test]
fn circular_list() {
let mut n = Nodes::new();
let a = n.new_kern(1, kern_sub::USER);
let b = n.new_glue(Glue::fixed(2), glue_sub::USER);
n.link(a, b);
n.link(b, a);
assert_eq!(n.bounded_tail(a), None);
let c = n.copy_list(a);
assert!(!c.is_null());
assert_eq!(n.double_free, None);
n.flush_list(a);
assert_eq!(n.double_free, Some((id::KERN, a.0)));
assert_eq!(type_name(id::KERN), "kern");
}
}