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