1use std::collections::HashMap;
19use std::fmt::Write as _;
20use std::rc::Rc;
21
22use crate::Engine;
23use crate::eqtb::{CodeTable, Meaning, Page, Val};
24use crate::input::{Level, LineSource};
25use crate::nodes::{NodeData, NodeRef};
26use crate::save::SaveEntry;
27use crate::token::Macro;
28
29pub struct Fp(u64);
31
32impl Default for Fp {
33 fn default() -> Self {
34 Fp(0xcbf2_9ce4_8422_2325)
35 }
36}
37
38impl std::fmt::Write for Fp {
39 fn write_str(&mut self, s: &str) -> std::fmt::Result {
40 self.bytes(s.as_bytes());
41 Ok(())
42 }
43}
44
45impl Fp {
46 pub fn get(&self) -> u64 {
48 self.0
49 }
50
51 pub fn bytes(&mut self, b: &[u8]) {
53 for &x in b {
54 self.0 ^= u64::from(x);
55 self.0 = self.0.wrapping_mul(0x0100_0000_01b3);
56 }
57 }
58
59 pub fn u(&mut self, v: u64) {
61 self.0 ^= v;
62 self.0 = self.0.wrapping_mul(0x0100_0000_01b3).rotate_left(29);
63 }
64
65 pub fn i(&mut self, v: i64) {
67 self.u(v as u64);
68 }
69
70 pub fn debug(&mut self, v: &impl std::fmt::Debug) {
72 let _ = write!(self, "{v:?}");
73 }
74
75 pub fn glue(&mut self, g: &crate::glue::Glue) {
77 self.i(g.width.into());
78 self.i(g.stretch.into());
79 self.i(g.shrink.into());
80 self.u(u64::from(g.stretch_order) << 8 | u64::from(g.shrink_order));
81 }
82
83 pub fn tokens(&mut self, t: &[crate::token::Token]) {
85 self.u(t.len() as u64);
86 for x in t {
87 self.u(x.0.into());
88 }
89 }
90
91 pub fn map<K: std::fmt::Debug, V: std::fmt::Debug>(&mut self, m: impl Iterator<Item = (K, V)>) {
94 let mut v: Vec<String> = m.map(|(k, x)| format!("{k:?}\u{0}{x:?}")).collect();
95 v.sort();
96 self.u(v.len() as u64);
97 for e in v {
98 self.bytes(e.as_bytes());
99 self.u(0);
100 }
101 }
102}
103
104#[derive(Default)]
109pub struct FpCache {
110 pages: HashMap<usize, (Rc<Page>, u64)>,
111 macros: HashMap<usize, (Rc<Macro>, u64)>,
112}
113
114fn page_hash(p: &Page) -> u64 {
115 let mut h = Fp::default();
116 for s in p.iter() {
117 h.i(s.val);
118 h.u(s.level.into());
119 }
120 h.get()
121}
122
123fn macro_hash(m: &Macro) -> u64 {
124 let mut h = Fp::default();
125 h.u(m.protected.into());
126 h.tokens(&m.tokens);
127 h.get()
128}
129
130impl Engine {
131 pub fn prepare_fingerprints(&mut self) {
136 let mut c = FpCache::default();
137 let e = &self.eqtb;
138 let tables = e
139 .catcode_tables
140 .values()
141 .chain([&e.lc, &e.uc, &e.sf, &e.math, &e.del]);
142 for t in tables {
143 for p in t.pages().iter().flatten() {
144 c.pages
145 .entry(Rc::as_ptr(p) as usize)
146 .or_insert_with(|| (p.clone(), page_hash(p)));
147 }
148 }
149 for s in &e.cs {
150 if let Some(m) = &s.val.mac {
151 c.macros
152 .entry(Rc::as_ptr(m) as usize)
153 .or_insert_with(|| (m.clone(), macro_hash(m)));
154 }
155 }
156 self.fp_cache = Some(Box::new(c));
157 }
158
159 fn fp_code_table(&self, h: &mut Fp, t: &CodeTable) {
161 for (k, p) in t.pages().iter().enumerate() {
162 if let Some(p) = p {
163 h.u(k as u64);
164 let cached = self
165 .fp_cache
166 .as_ref()
167 .and_then(|c| c.pages.get(&(Rc::as_ptr(p) as usize)));
168 h.u(cached.map_or_else(|| page_hash(p), |c| c.1));
169 }
170 }
171 h.u(u64::MAX);
172 }
173
174 fn fp_meaning(&self, h: &mut Fp, m: &Meaning) {
176 h.u(m.cmd as u64);
177 h.u(m.chr.into());
178 match &m.mac {
179 Some(mac) => {
180 let cached = self
181 .fp_cache
182 .as_ref()
183 .and_then(|c| c.macros.get(&(Rc::as_ptr(mac) as usize)));
184 h.u(cached.map_or_else(|| macro_hash(mac), |c| c.1));
185 }
186 None => h.u(0),
187 }
188 }
189
190 pub fn fingerprint(&mut self) -> (Vec<(&'static str, u64)>, u64) {
192 let backend = self.backend.as_mut().map_or(0, |b| b.fingerprint());
193 let parts: Vec<(&'static str, u64)> = vec![
194 ("input", self.fp_input()),
195 ("meanings", self.fp_meanings()),
196 ("registers", self.fp_registers()),
197 ("save stack", self.fp_save()),
198 ("nest", self.fp_nest()),
199 ("page builder", self.fp_page()),
200 ("conditionals", self.fp_cond()),
201 ("pdf", self.fp_pdf()),
202 ("backend", backend),
203 ("misc", self.fp_misc()),
204 ];
205 let mut all = Fp::default();
206 for (_, h) in &parts {
207 all.u(*h);
208 }
209 (parts, all.get())
210 }
211
212 pub fn fingerprint_dump(&self) -> String {
216 let mut out = String::new();
217 let e = &self.eqtb;
218 let names = ["int_par", "count", "attr", "dimen_par", "dimen"];
219 for (n, v) in names
220 .iter()
221 .zip([&e.int_par, &e.count, &e.attr, &e.dimen_par, &e.dimen])
222 {
223 for (i, s) in v.iter().enumerate() {
224 let _ = writeln!(out, "{n} {i} {} {}", s.val, s.level);
225 }
226 }
227 for (n, v) in ["glue_par", "skip", "muskip"]
228 .iter()
229 .zip([&e.glue_par, &e.skip, &e.muskip])
230 {
231 for (i, s) in v.iter().enumerate() {
232 let _ = writeln!(out, "{n} {i} {:?} {}", s.val, s.level);
233 }
234 }
235 for (n, v) in ["toks_par", "toks"].iter().zip([&e.toks_par, &e.toks]) {
236 for (i, s) in v.iter().enumerate() {
237 let mut h = Fp::default();
238 h.tokens(&s.val);
239 let _ = writeln!(out, "{n} {i} {:x} {}", h.get(), s.level);
240 }
241 }
242 for (i, s) in e.boxes.iter().enumerate() {
243 let mut h = Fp::default();
244 self.fp_list(&mut h, s.val);
245 let _ = writeln!(out, "box {i} {:x} {}", h.get(), s.level);
246 }
247 let _ = writeln!(
248 out,
249 "misc {:?}",
250 (&e.cur_font, &e.math_font, &e.par_shape, &e.etex_shape)
251 );
252 let _ = writeln!(
253 out,
254 "misc2 {:?}",
255 (&e.dir_par, &e.local_count, e.attrs_in_use)
256 );
257 for ((name, _), slot) in self.hash.entries().zip(&e.cs) {
258 let m = &slot.val;
259 if matches!(
260 m.cmd,
261 crate::cmd::Cmd::AssignInt | crate::cmd::Cmd::AssignDimen
262 ) && m.chr >= crate::scan::REG_OFFSET
263 {
264 let _ = writeln!(
265 out,
266 "name {:?} {} = {}",
267 m.cmd,
268 m.chr - crate::scan::REG_OFFSET,
269 String::from_utf8_lossy(name)
270 );
271 }
272 }
273 let mut tables: Vec<(&u32, &crate::eqtb::CodeTable)> = e.catcode_tables.iter().collect();
274 tables.sort_by_key(|(k, _)| **k);
275 for (k, t) in tables {
276 let mut h = Fp::default();
277 for (k, page) in t.dump_pages() {
278 h.u(k as u64);
279 for v in page {
280 h.i(v);
281 }
282 }
283 let _ = writeln!(out, "catcodes {k} {:x}", h.get());
284 }
285 out
286 }
287
288 fn fp_list(&self, h: &mut Fp, mut p: NodeRef) {
290 while !p.is_null() {
291 self.fp_node(h, p);
292 p = self.nodes.next(p);
293 }
294 h.u(u64::MAX);
295 }
296
297 fn fp_node(&self, h: &mut Fp, p: NodeRef) {
299 let n = self.nodes.get(p);
300 h.u(n.id.into());
301 h.u(n.subtype.into());
302 match &n.attr {
303 Some(a) => h.debug(&&a[..]),
304 None => h.u(0),
305 }
306 match &n.data {
307 NodeData::Box(b) => {
308 h.debug(&(b.width, b.depth, b.height, b.dir, b.shift));
309 h.debug(&(b.glue_order, b.glue_sign, b.glue_set.to_bits()));
310 self.fp_list(h, b.head);
311 }
312 NodeData::Unset {
313 b,
314 span_count,
315 stretch,
316 shrink,
317 stretch_order,
318 shrink_order,
319 } => {
320 h.debug(&(b.width, b.depth, b.height, b.dir, b.shift));
321 h.debug(&(span_count, stretch, shrink, stretch_order, shrink_order));
322 self.fp_list(h, b.head);
323 }
324 NodeData::Ins {
325 cost,
326 depth,
327 height,
328 spec,
329 head,
330 } => {
331 h.debug(&(cost, depth, height, spec));
332 self.fp_list(h, *head);
333 }
334 NodeData::Adjust { head } | NodeData::SubList { head } => self.fp_list(h, *head),
335 NodeData::Disc {
336 pre,
337 post,
338 replace,
339 penalty,
340 } => {
341 h.i((*penalty).into());
342 self.fp_list(h, *pre);
343 self.fp_list(h, *post);
344 self.fp_list(h, *replace);
345 }
346 NodeData::LocalPar {
347 pen_inter,
348 pen_broken,
349 dir,
350 box_left,
351 box_left_width,
352 box_right,
353 box_right_width,
354 } => {
355 h.debug(&(pen_inter, pen_broken, dir, box_left_width, box_right_width));
356 self.fp_list(h, *box_left);
357 self.fp_list(h, *box_right);
358 }
359 NodeData::Glue { spec, leader } => {
360 h.debug(spec);
361 self.fp_list(h, *leader);
362 }
363 NodeData::Glyph(g) => {
364 h.debug(&(g.chr, g.font, g.lang, g.left, g.right, g.uchyph));
365 h.debug(&(g.xoffset, g.yoffset, g.expansion_factor, g.data));
366 self.fp_list(h, g.components);
367 }
368 NodeData::MarginKern { width, glyph } => {
369 h.i((*width).into());
370 if !glyph.is_null() {
371 self.fp_node(h, *glyph);
372 }
373 }
374 NodeData::Noad(d) => {
375 h.debug(&(d.options, d.width, d.fraction));
376 for l in [
377 d.nucleus,
378 d.sub,
379 d.sup,
380 d.left,
381 d.degree,
382 d.accent,
383 d.bot_accent,
384 d.new_hlist,
385 ] {
386 self.fp_list(h, l);
387 }
388 }
389 NodeData::Fraction(d) => {
390 h.debug(&(d.thickness, d.options));
391 for l in [d.num, d.denom, d.left, d.right, d.middle, d.new_hlist] {
392 self.fp_list(h, l);
393 }
394 }
395 NodeData::Fence(d) => {
396 h.debug(&(d.italic, d.height, d.depth, d.options, d.class));
397 self.fp_list(h, d.delim);
398 self.fp_list(h, d.new_hlist);
399 }
400 NodeData::Choice(c) => {
401 for l in c.iter() {
402 self.fp_list(h, *l);
403 }
404 }
405 other => h.debug(other),
407 }
408 }
409
410 fn fp_val(&self, h: &mut Fp, v: &Val) {
412 match v {
413 Val::Meaning(m) => self.fp_meaning(h, m),
414 Val::Node(p) => self.fp_list(h, *p),
415 Val::Toks(t) => h.tokens(t),
416 other => h.debug(other),
417 }
418 }
419
420 fn fp_input(&self) -> u64 {
424 let mut h = Fp::default();
425 let inp = &self.input;
426 h.u(inp.stack.len() as u64);
427 for l in &inp.stack {
428 match l {
429 Level::Text(t) => {
430 h.u(1);
431 h.debug(&(t.state, t.loc, t.is_input_file, t.eof_seen));
432 h.debug(&(t.grp_boundary, t.if_depth, t.catmode));
433 h.debug(&&t.buf[..]);
434 h.bytes(&t.name);
435 match &t.source {
436 LineSource::File { data, pos } => {
437 let rest = data.get(*pos..).unwrap_or_default();
438 h.u(2);
439 h.bytes(rest);
440 }
441 other => h.debug(other),
442 }
443 }
444 Level::Tokens(t) => {
445 h.u(3);
446 h.tokens(&t.list);
447 h.debug(&(t.loc, t.kind, t.param_start, t.name));
448 }
449 }
450 }
451 for p in &inp.param_stack {
452 h.tokens(p);
453 }
454 h.debug(&(inp.align_state, inp.scanner_status, inp.warning_index));
455 h.debug(&(inp.force_eof, inp.in_open));
456 h.get()
457 }
458
459 fn fp_meanings(&self) -> u64 {
461 let mut h = Fp::default();
462 h.u(self.hash.len() as u64);
463 for (name, kind) in self.hash.entries() {
464 h.bytes(name);
465 h.debug(kind);
466 }
467 for s in &self.eqtb.cs {
468 h.u(s.level.into());
469 self.fp_meaning(&mut h, &s.val);
470 }
471 h.get()
472 }
473
474 fn fp_registers(&self) -> u64 {
476 let mut h = Fp::default();
477 let e = &self.eqtb;
478 let ints = |h: &mut Fp, v: &[crate::eqtb::Slot<i32>]| {
479 h.u(v.len() as u64);
480 for s in v {
481 h.i(s.val.into());
482 h.u(s.level.into());
483 }
484 };
485 for v in [&e.int_par, &e.count, &e.attr, &e.dimen_par, &e.dimen] {
486 ints(&mut h, v);
487 }
488 for v in [&e.glue_par, &e.skip, &e.muskip] {
489 h.u(v.len() as u64);
490 for s in v {
491 h.glue(&s.val);
492 h.u(s.level.into());
493 }
494 }
495 for v in [&e.toks_par, &e.toks] {
496 h.u(v.len() as u64);
497 for s in v {
498 h.tokens(&s.val);
499 h.u(s.level.into());
500 }
501 }
502 h.u(e.boxes.len() as u64);
503 for s in &e.boxes {
504 self.fp_list(&mut h, s.val);
505 h.u(s.level.into());
506 }
507 for s in &e.local_box {
508 self.fp_list(&mut h, s.val);
509 h.u(s.level.into());
510 }
511 h.debug(&(&e.cur_font, &e.math_font, &e.par_shape, &e.etex_shape));
512 h.debug(&(&e.dir_par, &e.local_count, e.attrs_in_use));
513 h.map(e.math_param.iter());
514 let mut tables: Vec<(&u32, &crate::eqtb::CodeTable)> = e.catcode_tables.iter().collect();
515 tables.sort_by_key(|(k, _)| **k);
516 let all = tables
517 .iter()
518 .map(|(_, t)| *t)
519 .chain([&e.lc, &e.uc, &e.sf, &e.math, &e.del]);
520 for (k, _) in &tables {
521 h.u((**k).into());
522 }
523 for t in all {
524 self.fp_code_table(&mut h, t);
525 }
526 h.get()
527 }
528
529 fn fp_save(&self) -> u64 {
531 let mut h = Fp::default();
532 let s = &self.save;
533 h.debug(&(s.cur_level, s.cur_group, s.cur_boundary));
534 for e in &s.stack {
535 match e {
536 SaveEntry::Restore { loc, old, level } => {
537 h.u(1);
538 h.debug(&(loc, level));
539 self.fp_val(&mut h, old);
540 }
541 SaveEntry::InsertToken(t) => {
542 h.u(2);
543 h.u(t.0.into());
544 }
545 SaveEntry::Boundary {
546 group, boundary, ..
547 } => {
548 h.u(3);
549 h.debug(&(group, boundary));
550 }
551 SaveEntry::Value(v) => {
552 h.u(4);
553 h.i(*v);
554 }
555 SaveEntry::RestoreCodes { kind, old, upto } => {
556 h.u(5);
557 h.debug(&(kind, upto));
558 self.fp_code_table(&mut h, old);
559 }
560 }
561 }
562 h.get()
563 }
564
565 fn fp_nest(&self) -> u64 {
567 let mut h = Fp::default();
568 for l in &self.nest {
569 h.debug(&(l.mode, l.prev_graf, l.prev_depth, l.space_factor));
570 h.debug(&(l.clang, l.math_style));
571 self.fp_list(&mut h, self.nodes.next(l.head));
572 self.fp_list(&mut h, l.incompleat_noad);
573 self.fp_list(&mut h, l.delim_ptr);
574 }
575 h.debug(&(self.mode, self.local_level));
576 h.get()
577 }
578
579 fn fp_page(&self) -> u64 {
581 let mut h = Fp::default();
582 let p = &self.page;
583 self.fp_list(&mut h, self.nodes.next(p.contrib_head));
584 self.fp_list(&mut h, self.nodes.next(p.page_head));
585 h.debug(&(p.so_far, p.fi_stretch, p.max_depth, p.contents));
586 h.debug(&(p.least_cost, p.best_size, p.best_so_far, p.best_infinite));
587 let mut q = p.page_head;
589 let mut k = 0u64;
590 while !q.is_null() && q != p.best_break {
591 q = self.nodes.next(q);
592 k += 1;
593 }
594 h.u(if q.is_null() { u64::MAX } else { k });
595 h.u(p.ins.len() as u64);
596 h.debug(&(&p.last_glue, p.last_penalty, p.last_kern, p.last_node_type));
597 h.map(self.marks.classes.iter());
598 let ts = &self.ts;
599 h.debug(&(ts.output_active, ts.last_totals, &ts.box_attrs));
600 for l in ts.disc_ptr {
601 self.fp_list(&mut h, l);
602 }
603 for l in [ts.adjust_head, ts.pre_adjust_head] {
604 if !l.is_null() {
605 self.fp_list(&mut h, self.nodes.next(l));
606 }
607 }
608 h.get()
609 }
610
611 fn fp_cond(&self) -> u64 {
613 let mut h = Fp::default();
614 let c = &self.cond;
615 h.u(c.stack.len() as u64);
616 h.debug(&(c.if_limit, c.cur_if, c.is_in_csname));
617 h.get()
618 }
619
620 fn fp_pdf(&self) -> u64 {
622 let mut h = Fp::default();
623 let p = &self.pdf;
624 h.debug(&(&p.page_images, &p.objects, &p.catalog, &p.open_action));
625 h.debug(&(&p.info, &p.names, &p.trailer, &p.outlines));
626 h.debug(&(
627 &p.glyph_to_unicode,
628 &p.colorstacks,
629 p.last_obj,
630 p.last_annot,
631 ));
632 h.debug(&(p.last_link, p.counter.get(), &p.map_entries, &p.lua));
633 h.debug(&(p.retval, p.pos, &p.page_annots, p.output_opened));
634 h.debug(&(&p.dest_order, &p.threads, &p.pending, p.fatal));
635 h.debug(&(&p.active_links, p.stream_written));
636 h.map(p.kinds.iter());
637 h.map(p.page_refs.iter());
638 h.map(p.fonts.iter());
639 h.map(p.font_attrs.iter());
640 h.map(p.dests.iter());
641 h.map(p.struct_dests.iter());
642 h.debug(&(&self.pending_forms, &self.form_objects));
643 h.debug(&(
644 self.images.len(),
645 self.last_image_index,
646 self.last_image_pages,
647 ));
648 h.debug(&(self.box_resources.len(), self.last_box_index));
649 h.map(self.colorstack_values.iter());
650 h.get()
651 }
652
653 fn fp_misc(&self) -> u64 {
655 let mut h = Fp::default();
656 h.u(self.fonts.fonts.len() as u64);
657 for f in &self.fonts.fonts {
658 h.bytes(&f.name);
659 h.i(f.size.into());
660 }
661 h.debug(&(self.dead_cycles, self.insert_penalties, self.last_badness));
662 h.debug(&(
663 self.random_seed,
664 &self.randoms,
665 self.last_x_pos,
666 self.last_y_pos,
667 ));
668 h.debug(&(self.mag_set, self.total_pages, self.err.interaction));
669 h.debug(&(self.err.history, self.long_state, self.pack_dir));
670 h.debug(&(self.cur_cmd, self.cur_chr, self.cur_cs, self.cur_tok));
671 h.debug(&(&self.deferred_writes, &self.text_dirs, &self.math_glues));
672 h.map(self.lua_functions.iter());
673 h.u(self.languages.len() as u64);
674 h.debug(&self.printer.open_write_streams());
675 h.debug(&self.write_paths);
676 for r in &self.files.read_files {
677 h.debug(r);
678 }
679 h.get()
680 }
681}