1use std::cmp::Ordering;
2use std::collections::{BTreeMap, BTreeSet, HashMap};
3use std::hash::{BuildHasherDefault, Hash, Hasher};
4use std::rc::Rc;
5
6pub const EMPTY_RETURN_STATE: usize = usize::MAX;
7
8#[derive(Debug, Default)]
14pub struct PredictionFxHasher {
15 hash: u64,
16}
17
18const FX_ROT: u32 = 5;
19const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
20
21impl Hasher for PredictionFxHasher {
22 #[inline]
23 fn write(&mut self, bytes: &[u8]) {
24 let mut bytes = bytes;
25 while bytes.len() >= 8 {
26 let (head, rest) = bytes.split_at(8);
27 let word = u64::from_le_bytes(head.try_into().expect("8-byte chunk"));
28 self.hash = (self.hash.rotate_left(FX_ROT) ^ word).wrapping_mul(FX_SEED);
29 bytes = rest;
30 }
31 for &b in bytes {
32 self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(b)).wrapping_mul(FX_SEED);
33 }
34 }
35
36 #[inline]
37 fn write_u8(&mut self, value: u8) {
38 self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(value)).wrapping_mul(FX_SEED);
39 }
40
41 #[inline]
42 fn write_u32(&mut self, value: u32) {
43 self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(value)).wrapping_mul(FX_SEED);
44 }
45
46 #[inline]
47 fn write_u64(&mut self, value: u64) {
48 self.hash = (self.hash.rotate_left(FX_ROT) ^ value).wrapping_mul(FX_SEED);
49 }
50
51 #[inline]
52 fn write_usize(&mut self, value: usize) {
53 self.hash = (self.hash.rotate_left(FX_ROT) ^ value as u64).wrapping_mul(FX_SEED);
54 }
55
56 #[inline]
57 fn write_i32(&mut self, value: i32) {
58 self.write_u32(i32::cast_unsigned(value));
59 }
60
61 #[inline]
62 fn finish(&self) -> u64 {
63 self.hash
64 }
65}
66
67type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PredictionFxHasher>>;
68
69#[derive(Clone, Debug)]
70pub enum PredictionContext {
71 Empty {
72 cached_hash: u64,
73 },
74 Singleton {
75 parent: Rc<Self>,
76 return_state: usize,
77 cached_hash: u64,
78 },
79 Array {
80 parents: Vec<Rc<Self>>,
81 return_states: Vec<usize>,
82 cached_hash: u64,
83 },
84}
85
86impl PredictionContext {
87 pub fn empty() -> Rc<Self> {
88 EMPTY_PREDICTION_CONTEXT.with(Rc::clone)
89 }
90
91 pub fn singleton(parent: Rc<Self>, return_state: usize) -> Rc<Self> {
92 if return_state == EMPTY_RETURN_STATE {
93 Self::empty()
94 } else {
95 Rc::new(Self::Singleton {
96 cached_hash: prediction_context_singleton_hash(&parent, return_state),
97 parent,
98 return_state,
99 })
100 }
101 }
102
103 fn array(parents: Vec<Rc<Self>>, return_states: Vec<usize>) -> Rc<Self> {
104 Rc::new(Self::Array {
105 cached_hash: prediction_context_array_hash(&parents, &return_states),
106 parents,
107 return_states,
108 })
109 }
110
111 pub const fn cached_hash(&self) -> u64 {
112 match self {
113 Self::Empty { cached_hash }
114 | Self::Singleton { cached_hash, .. }
115 | Self::Array { cached_hash, .. } => *cached_hash,
116 }
117 }
118
119 pub const fn len(&self) -> usize {
120 match self {
121 Self::Empty { .. } => 1,
122 Self::Singleton { .. } => 1,
123 Self::Array { return_states, .. } => return_states.len(),
124 }
125 }
126
127 pub const fn is_empty(&self) -> bool {
128 matches!(self, Self::Empty { .. })
129 }
130
131 pub fn return_state(&self, index: usize) -> Option<usize> {
132 match self {
133 Self::Empty { .. } if index == 0 => Some(EMPTY_RETURN_STATE),
134 Self::Singleton { return_state, .. } if index == 0 => Some(*return_state),
135 Self::Array { return_states, .. } => return_states.get(index).copied(),
136 Self::Empty { .. } => None,
137 Self::Singleton { .. } => None,
138 }
139 }
140
141 pub fn parent(&self, index: usize) -> Option<Rc<Self>> {
142 match self {
143 Self::Empty { .. } => None,
144 Self::Singleton { parent, .. } if index == 0 => Some(Rc::clone(parent)),
145 Self::Array { parents, .. } => parents.get(index).cloned(),
146 Self::Singleton { .. } => None,
147 }
148 }
149
150 pub fn has_empty_path(&self) -> bool {
151 match self {
152 Self::Empty { .. } => true,
153 Self::Singleton { return_state, .. } => *return_state == EMPTY_RETURN_STATE,
154 Self::Array { return_states, .. } => return_states.last() == Some(&EMPTY_RETURN_STATE),
158 }
159 }
160
161 pub fn merge(left: Rc<Self>, right: Rc<Self>) -> Rc<Self> {
162 Self::merge_with_options(left, right, false, None)
163 }
164
165 #[allow(clippy::needless_pass_by_value)]
172 pub fn merge_with_options(
173 left: Rc<Self>,
174 right: Rc<Self>,
175 root_is_wildcard: bool,
176 mut cache: Option<&mut PredictionContextMergeCache>,
177 ) -> Rc<Self> {
178 #[cfg(feature = "perf-counters")]
179 crate::perf::record_context_merge_call();
180 if left == right {
181 #[cfg(feature = "perf-counters")]
182 crate::perf::record_context_merge_identical();
183 return left;
184 }
185 if let Some(cache) = cache.as_deref_mut() {
186 if let Some(merged) = cache.get(&left, &right) {
187 #[cfg(feature = "perf-counters")]
188 crate::perf::record_context_merge_cache_hit();
189 return merged;
190 }
191 #[cfg(feature = "perf-counters")]
192 crate::perf::record_context_merge_cache_miss();
193 }
194 let merged = if root_is_wildcard && (left.is_empty() || right.is_empty()) {
195 Self::empty()
196 } else {
197 #[cfg(feature = "perf-counters")]
198 crate::perf::record_context_merge_uncached();
199 merge_contexts_uncached(&left, &right)
200 };
201 if let Some(cache) = cache {
202 cache.insert(&left, &right, &merged);
203 }
204 merged
205 }
206}
207
208fn merge_contexts_uncached(
209 left: &Rc<PredictionContext>,
210 right: &Rc<PredictionContext>,
211) -> Rc<PredictionContext> {
212 if left == right {
213 return Rc::clone(left);
214 }
215 match (left.as_ref(), right.as_ref()) {
216 (PredictionContext::Empty { .. }, PredictionContext::Empty { .. }) => {
217 PredictionContext::empty()
218 }
219 (
220 PredictionContext::Singleton {
221 parent: left_parent,
222 return_state: left_return_state,
223 ..
224 },
225 PredictionContext::Singleton {
226 parent: right_parent,
227 return_state: right_return_state,
228 ..
229 },
230 ) => merge_two_context_entries(
231 Rc::clone(left_parent),
232 *left_return_state,
233 Rc::clone(right_parent),
234 *right_return_state,
235 ),
236 (PredictionContext::Empty { .. }, PredictionContext::Singleton { .. })
237 | (PredictionContext::Singleton { .. }, PredictionContext::Empty { .. }) => {
238 let (left_parent, left_return_state) = first_context_entry(left);
239 let (right_parent, right_return_state) = first_context_entry(right);
240 merge_two_context_entries(
241 left_parent,
242 left_return_state,
243 right_parent,
244 right_return_state,
245 )
246 }
247 (
248 PredictionContext::Array {
249 parents,
250 return_states,
251 ..
252 },
253 PredictionContext::Singleton { .. } | PredictionContext::Empty { .. },
254 ) => {
255 let (parent, return_state) = first_context_entry(right);
256 merge_array_with_entry(
257 Rc::clone(left),
258 parents,
259 return_states,
260 parent,
261 return_state,
262 false,
263 )
264 }
265 (
266 PredictionContext::Singleton { .. } | PredictionContext::Empty { .. },
267 PredictionContext::Array {
268 parents,
269 return_states,
270 ..
271 },
272 ) => {
273 let (parent, return_state) = first_context_entry(left);
274 merge_array_with_entry(
275 Rc::clone(right),
276 parents,
277 return_states,
278 parent,
279 return_state,
280 true,
281 )
282 }
283 (
284 PredictionContext::Array {
285 parents: left_parents,
286 return_states: left_return_states,
287 ..
288 },
289 PredictionContext::Array {
290 parents: right_parents,
291 return_states: right_return_states,
292 ..
293 },
294 ) => merge_arrays(
295 left_parents,
296 left_return_states,
297 right_parents,
298 right_return_states,
299 ),
300 }
301}
302
303fn first_context_entry(context: &Rc<PredictionContext>) -> (Rc<PredictionContext>, usize) {
304 match context.as_ref() {
305 PredictionContext::Empty { .. } => (Rc::clone(context), EMPTY_RETURN_STATE),
306 PredictionContext::Singleton {
307 parent,
308 return_state,
309 ..
310 } => (Rc::clone(parent), *return_state),
311 PredictionContext::Array { .. } => unreachable!("array contexts have multiple entries"),
312 }
313}
314
315fn merge_two_context_entries(
316 left_parent: Rc<PredictionContext>,
317 left_return_state: usize,
318 right_parent: Rc<PredictionContext>,
319 right_return_state: usize,
320) -> Rc<PredictionContext> {
321 if left_return_state == right_return_state && left_parent == right_parent {
322 return PredictionContext::singleton(left_parent, left_return_state);
323 }
324 let (first_parent, first_return_state, second_parent, second_return_state) =
325 if compare_entries(&right_parent, right_return_state, &left_parent, left_return_state)
326 == Ordering::Less
327 {
328 (
329 right_parent,
330 right_return_state,
331 left_parent,
332 left_return_state,
333 )
334 } else {
335 (
336 left_parent,
337 left_return_state,
338 right_parent,
339 right_return_state,
340 )
341 };
342 PredictionContext::array(
343 vec![first_parent, second_parent],
344 vec![first_return_state, second_return_state],
345 )
346}
347
348fn merge_array_with_entry(
349 array_context: Rc<PredictionContext>,
350 array_parents: &[Rc<PredictionContext>],
351 array_return_states: &[usize],
352 entry_parent: Rc<PredictionContext>,
353 entry_return_state: usize,
354 entry_on_left: bool,
355) -> Rc<PredictionContext> {
356 let mut insert_index = array_parents.len();
357 for (index, (parent, return_state)) in array_parents
358 .iter()
359 .zip(array_return_states)
360 .enumerate()
361 {
362 let ordering = compare_entries(&entry_parent, entry_return_state, parent, *return_state);
363 if ordering == Ordering::Equal && parent == &entry_parent {
364 return array_context;
365 }
366 let should_insert = if entry_on_left {
367 ordering != Ordering::Greater
368 } else {
369 ordering == Ordering::Less
370 };
371 if should_insert {
372 insert_index = index;
373 break;
374 }
375 }
376
377 let mut parents = Vec::with_capacity(array_parents.len() + 1);
378 let mut return_states = Vec::with_capacity(array_return_states.len() + 1);
379 parents.extend(array_parents[..insert_index].iter().cloned());
380 return_states.extend_from_slice(&array_return_states[..insert_index]);
381 parents.push(entry_parent);
382 return_states.push(entry_return_state);
383 parents.extend(array_parents[insert_index..].iter().cloned());
384 return_states.extend_from_slice(&array_return_states[insert_index..]);
385 PredictionContext::array(parents, return_states)
386}
387
388fn merge_arrays(
389 left_parents: &[Rc<PredictionContext>],
390 left_return_states: &[usize],
391 right_parents: &[Rc<PredictionContext>],
392 right_return_states: &[usize],
393) -> Rc<PredictionContext> {
394 let mut parents = Vec::with_capacity(left_parents.len() + right_parents.len());
395 let mut return_states = Vec::with_capacity(left_return_states.len() + right_return_states.len());
396 let mut left_index = 0;
397 let mut right_index = 0;
398
399 while left_index < left_parents.len() && right_index < right_parents.len() {
400 match compare_entries(
401 &left_parents[left_index],
402 left_return_states[left_index],
403 &right_parents[right_index],
404 right_return_states[right_index],
405 ) {
406 Ordering::Less => {
407 parents.push(Rc::clone(&left_parents[left_index]));
408 return_states.push(left_return_states[left_index]);
409 left_index += 1;
410 }
411 Ordering::Greater => {
412 parents.push(Rc::clone(&right_parents[right_index]));
413 return_states.push(right_return_states[right_index]);
414 right_index += 1;
415 }
416 Ordering::Equal => {
420 parents.push(Rc::clone(&left_parents[left_index]));
421 return_states.push(left_return_states[left_index]);
422 left_index += 1;
423 right_index += 1;
424 }
425 }
426 }
427
428 for index in left_index..left_parents.len() {
429 parents.push(Rc::clone(&left_parents[index]));
430 return_states.push(left_return_states[index]);
431 }
432 for index in right_index..right_parents.len() {
433 parents.push(Rc::clone(&right_parents[index]));
434 return_states.push(right_return_states[index]);
435 }
436
437 if parents.len() == 1 {
438 return PredictionContext::singleton(
439 parents.pop().expect("single merged parent"),
440 return_states.pop().expect("single merged return state"),
441 );
442 }
443 PredictionContext::array(parents, return_states)
444}
445
446fn compare_entries(
466 left_parent: &Rc<PredictionContext>,
467 left_return_state: usize,
468 right_parent: &Rc<PredictionContext>,
469 right_return_state: usize,
470) -> Ordering {
471 left_return_state
472 .cmp(&right_return_state)
473 .then_with(|| left_parent.cached_hash().cmp(&right_parent.cached_hash()))
474 .then_with(|| left_parent.cmp(right_parent))
475}
476
477impl PartialEq for PredictionContext {
478 fn eq(&self, other: &Self) -> bool {
479 if std::ptr::eq(self, other) {
480 return true;
481 }
482 if self.cached_hash() != other.cached_hash() {
483 return false;
484 }
485 match (self, other) {
486 (Self::Empty { .. }, Self::Empty { .. }) => true,
487 (
488 Self::Singleton {
489 parent,
490 return_state,
491 ..
492 },
493 Self::Singleton {
494 parent: other_parent,
495 return_state: other_return_state,
496 ..
497 },
498 ) => return_state == other_return_state && parent == other_parent,
499 (
500 Self::Array {
501 parents,
502 return_states,
503 ..
504 },
505 Self::Array {
506 parents: other_parents,
507 return_states: other_return_states,
508 ..
509 },
510 ) => return_states == other_return_states && parents == other_parents,
511 _ => false,
512 }
513 }
514}
515
516impl Eq for PredictionContext {}
517
518thread_local! {
519 static EMPTY_PREDICTION_CONTEXT: Rc<PredictionContext> = Rc::new(PredictionContext::Empty {
520 cached_hash: prediction_context_empty_hash(),
521 });
522}
523
524impl Hash for PredictionContext {
525 fn hash<H: Hasher>(&self, state: &mut H) {
526 state.write_u64(self.cached_hash());
527 }
528}
529
530impl Ord for PredictionContext {
531 fn cmp(&self, other: &Self) -> Ordering {
532 if std::ptr::eq(self, other) {
533 return Ordering::Equal;
534 }
535 self.cached_hash()
536 .cmp(&other.cached_hash())
537 .then_with(|| prediction_context_variant(self).cmp(&prediction_context_variant(other)))
538 .then_with(|| match (self, other) {
539 (Self::Empty { .. }, Self::Empty { .. }) => Ordering::Equal,
540 (
541 Self::Singleton {
542 parent,
543 return_state,
544 ..
545 },
546 Self::Singleton {
547 parent: other_parent,
548 return_state: other_return_state,
549 ..
550 },
551 ) => return_state
552 .cmp(other_return_state)
553 .then_with(|| parent.cmp(other_parent)),
554 (
555 Self::Array {
556 parents,
557 return_states,
558 ..
559 },
560 Self::Array {
561 parents: other_parents,
562 return_states: other_return_states,
563 ..
564 },
565 ) => return_states
566 .cmp(other_return_states)
567 .then_with(|| parents.cmp(other_parents)),
568 _ => Ordering::Equal,
569 })
570 }
571}
572
573impl PartialOrd for PredictionContext {
574 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
575 Some(self.cmp(other))
576 }
577}
578
579const fn prediction_context_variant(context: &PredictionContext) -> u8 {
580 match context {
581 PredictionContext::Empty { .. } => 0,
582 PredictionContext::Singleton { .. } => 1,
583 PredictionContext::Array { .. } => 2,
584 }
585}
586
587fn prediction_context_empty_hash() -> u64 {
588 let mut hasher = PredictionFxHasher::default();
589 hasher.write_u8(0);
590 hasher.finish()
591}
592
593fn prediction_context_singleton_hash(parent: &Rc<PredictionContext>, return_state: usize) -> u64 {
594 let mut hasher = PredictionFxHasher::default();
595 hasher.write_u8(1);
596 hasher.write_u64(parent.cached_hash());
597 hasher.write_usize(return_state);
598 hasher.finish()
599}
600
601fn prediction_context_array_hash(
602 parents: &[Rc<PredictionContext>],
603 return_states: &[usize],
604) -> u64 {
605 let mut hasher = PredictionFxHasher::default();
606 hasher.write_u8(2);
607 hasher.write_usize(parents.len());
608 for parent in parents {
609 hasher.write_u64(parent.cached_hash());
610 }
611 hasher.write_usize(return_states.len());
612 for return_state in return_states {
613 hasher.write_usize(*return_state);
614 }
615 hasher.finish()
616}
617
618#[derive(Debug, Default)]
620pub struct PredictionContextMergeCache {
621 entries: FxHashMap<PredictionContextMergeKey, Rc<PredictionContext>>,
622}
623
624impl PredictionContextMergeCache {
625 pub fn new() -> Self {
626 Self::default()
627 }
628
629 fn get(
630 &self,
631 left: &Rc<PredictionContext>,
632 right: &Rc<PredictionContext>,
633 ) -> Option<Rc<PredictionContext>> {
634 let key = PredictionContextMergeKey::new(left, right);
635 self.entries.get(&key).cloned()
636 }
637
638 fn insert(
639 &mut self,
640 left: &Rc<PredictionContext>,
641 right: &Rc<PredictionContext>,
642 merged: &Rc<PredictionContext>,
643 ) {
644 self.entries.insert(
645 PredictionContextMergeKey::new(left, right),
646 Rc::clone(merged),
647 );
648 }
649}
650
651#[derive(Debug)]
653pub(crate) struct PredictionContextCache {
654 empty: Rc<PredictionContext>,
655 entries: FxHashMap<Rc<PredictionContext>, Rc<PredictionContext>>,
656}
657
658impl PredictionContextCache {
659 pub(crate) fn new() -> Self {
660 Self {
661 empty: PredictionContext::empty(),
662 entries: FxHashMap::default(),
663 }
664 }
665
666 pub(crate) fn get_cached_context(
667 &mut self,
668 context: &Rc<PredictionContext>,
669 ) -> Rc<PredictionContext> {
670 #[cfg(feature = "perf-counters")]
671 crate::perf::record_context_cache_call();
672 if context.is_empty() {
673 #[cfg(feature = "perf-counters")]
674 crate::perf::record_context_cache_empty();
675 return Rc::clone(&self.empty);
676 }
677 if let Some(existing) = self.entries.get(context) {
678 #[cfg(feature = "perf-counters")]
679 crate::perf::record_context_cache_hit();
680 return Rc::clone(existing);
681 }
682 #[cfg(feature = "perf-counters")]
683 crate::perf::record_context_cache_miss();
684 let mut visited = FxHashMap::default();
685 let cached = self.get_cached_context_inner(context, &mut visited);
686 #[cfg(feature = "perf-counters")]
687 crate::perf::record_context_cache_visited(visited.len());
688 cached
689 }
690
691 fn get_cached_context_inner(
692 &mut self,
693 context: &Rc<PredictionContext>,
694 visited: &mut FxHashMap<usize, Rc<PredictionContext>>,
695 ) -> Rc<PredictionContext> {
696 if context.is_empty() {
697 return Rc::clone(&self.empty);
698 }
699 let context_ptr = Rc::as_ptr(context) as usize;
703 if let Some(existing) = visited.get(&context_ptr) {
704 return Rc::clone(existing);
705 }
706 if let Some(existing) = self.entries.get(context) {
707 #[cfg(feature = "perf-counters")]
708 crate::perf::record_context_cache_hit();
709 let existing = Rc::clone(existing);
710 visited.insert(context_ptr, Rc::clone(&existing));
711 return existing;
712 }
713 let cached = match context.as_ref() {
714 PredictionContext::Empty { .. } => Rc::clone(&self.empty),
715 PredictionContext::Singleton {
716 parent,
717 return_state,
718 ..
719 } => {
720 let cached_parent = self.get_cached_context_inner(parent, visited);
721 if Rc::ptr_eq(parent, &cached_parent) {
722 self.add(Rc::clone(context))
723 } else {
724 self.add(PredictionContext::singleton(cached_parent, *return_state))
725 }
726 }
727 PredictionContext::Array {
728 parents,
729 return_states,
730 ..
731 } => {
732 let mut changed = false;
733 let mut cached_parents = Vec::with_capacity(parents.len());
734 for parent in parents {
735 let cached_parent = self.get_cached_context_inner(parent, visited);
736 changed |= !Rc::ptr_eq(parent, &cached_parent);
737 cached_parents.push(cached_parent);
738 }
739 if changed {
740 self.add(PredictionContext::array(
741 cached_parents,
742 return_states.clone(),
743 ))
744 } else {
745 self.add(Rc::clone(context))
746 }
747 }
748 };
749 visited.insert(context_ptr, Rc::clone(&cached));
750 cached
751 }
752
753 fn add(&mut self, context: Rc<PredictionContext>) -> Rc<PredictionContext> {
754 if context.is_empty() {
755 return Rc::clone(&self.empty);
756 }
757 if let Some(existing) = self.entries.get(&context) {
758 #[cfg(feature = "perf-counters")]
759 crate::perf::record_context_cache_hit();
760 return Rc::clone(existing);
761 }
762 #[cfg(feature = "perf-counters")]
763 crate::perf::record_context_cache_insert();
764 self.entries
765 .insert(Rc::clone(&context), Rc::clone(&context));
766 context
767 }
768}
769
770impl Default for PredictionContextCache {
771 fn default() -> Self {
772 Self::new()
773 }
774}
775
776#[derive(Clone, Debug)]
777struct PredictionContextMergeKey {
778 left: Rc<PredictionContext>,
779 right: Rc<PredictionContext>,
780 left_hash: u64,
781 right_hash: u64,
782}
783
784impl PredictionContextMergeKey {
785 fn new(left: &Rc<PredictionContext>, right: &Rc<PredictionContext>) -> Self {
786 let left_hash = prediction_context_hash(left);
787 let right_hash = prediction_context_hash(right);
788 if should_swap_merge_key(left, left_hash, right, right_hash) {
789 return Self {
790 left: Rc::clone(right),
791 right: Rc::clone(left),
792 left_hash: right_hash,
793 right_hash: left_hash,
794 };
795 }
796 Self {
797 left: Rc::clone(left),
798 right: Rc::clone(right),
799 left_hash,
800 right_hash,
801 }
802 }
803}
804
805fn should_swap_merge_key(
806 left: &Rc<PredictionContext>,
807 left_hash: u64,
808 right: &Rc<PredictionContext>,
809 right_hash: u64,
810) -> bool {
811 (right_hash, Rc::as_ptr(right) as usize) < (left_hash, Rc::as_ptr(left) as usize)
812}
813
814impl PartialEq for PredictionContextMergeKey {
815 fn eq(&self, other: &Self) -> bool {
816 self.left_hash == other.left_hash
817 && self.right_hash == other.right_hash
818 && self.left == other.left
819 && self.right == other.right
820 }
821}
822
823impl Eq for PredictionContextMergeKey {}
824
825impl Hash for PredictionContextMergeKey {
826 fn hash<H: Hasher>(&self, state: &mut H) {
827 state.write_u64(self.left_hash);
828 state.write_u64(self.right_hash);
829 }
830}
831
832fn prediction_context_hash(context: &Rc<PredictionContext>) -> u64 {
833 context.cached_hash()
834}
835
836#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
837pub enum SemanticContext {
838 None,
839 Predicate {
840 rule_index: usize,
841 pred_index: usize,
842 context_dependent: bool,
843 },
844 Precedence {
845 precedence: i32,
846 },
847 And(Vec<Self>),
848 Or(Vec<Self>),
849}
850
851impl SemanticContext {
852 pub const fn none() -> Self {
853 Self::None
854 }
855
856 pub fn and(left: Self, right: Self) -> Self {
857 combine_semantic_context(left, right, true)
858 }
859
860 pub fn or(left: Self, right: Self) -> Self {
861 combine_semantic_context(left, right, false)
862 }
863
864 pub const fn is_none(&self) -> bool {
865 matches!(self, Self::None)
866 }
867}
868
869fn combine_semantic_context(
870 left: SemanticContext,
871 right: SemanticContext,
872 and: bool,
873) -> SemanticContext {
874 if left == right {
875 return left;
876 }
877 if left.is_none() {
878 return right;
879 }
880 if right.is_none() {
881 return left;
882 }
883 let mut entries = Vec::new();
884 for context in [left, right] {
885 match (and, context) {
886 (true, SemanticContext::And(children)) | (false, SemanticContext::Or(children)) => {
887 entries.extend(children);
888 }
889 (_, other) => entries.push(other),
890 }
891 }
892 entries.sort();
893 entries.dedup();
894 if and {
895 SemanticContext::And(entries)
896 } else {
897 SemanticContext::Or(entries)
898 }
899}
900
901#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
902pub struct AtnConfig {
903 pub state: usize,
904 pub alt: usize,
905 pub context: Rc<PredictionContext>,
906 pub semantic_context: SemanticContext,
907 pub reaches_into_outer_context: usize,
908 pub precedence_filter_suppressed: bool,
909}
910
911impl AtnConfig {
912 pub const fn new(state: usize, alt: usize, context: Rc<PredictionContext>) -> Self {
913 Self {
914 state,
915 alt,
916 context,
917 semantic_context: SemanticContext::None,
918 reaches_into_outer_context: 0,
919 precedence_filter_suppressed: false,
920 }
921 }
922
923 #[must_use]
924 pub fn with_semantic_context(mut self, semantic_context: SemanticContext) -> Self {
925 self.semantic_context = semantic_context;
926 self
927 }
928
929 #[must_use]
930 pub const fn with_reaches_into_outer_context(mut self, reaches: usize) -> Self {
931 self.reaches_into_outer_context = reaches;
932 self
933 }
934}
935
936#[derive(Clone, Debug, Default)]
937pub struct AtnConfigSet {
938 configs: Vec<AtnConfig>,
939 config_index: FxHashMap<AtnConfigKey, usize>,
940 full_context: bool,
941 unique_alt: Option<usize>,
942 conflicting_alts: BTreeSet<usize>,
943 has_semantic_context: bool,
944 dips_into_outer_context: bool,
945 readonly: bool,
946}
947
948impl AtnConfigSet {
949 pub fn new() -> Self {
950 Self::default()
951 }
952
953 pub fn new_full_context(full_context: bool) -> Self {
954 Self {
955 configs: Vec::new(),
956 config_index: FxHashMap::default(),
957 full_context,
958 unique_alt: None,
959 conflicting_alts: BTreeSet::new(),
960 has_semantic_context: false,
961 dips_into_outer_context: false,
962 readonly: false,
963 }
964 }
965
966 pub fn add(&mut self, config: AtnConfig) -> bool {
967 self.add_with_merge_cache(config, None)
968 }
969
970 pub fn add_with_merge_cache(
973 &mut self,
974 config: AtnConfig,
975 cache: Option<&mut PredictionContextMergeCache>,
976 ) -> bool {
977 assert!(!self.readonly, "cannot mutate readonly ATN config set");
978 #[cfg(feature = "perf-counters")]
979 crate::perf::record_config_add_call();
980 if !config.semantic_context.is_none() {
981 self.has_semantic_context = true;
982 }
983 if config.reaches_into_outer_context > 0 {
984 self.dips_into_outer_context = true;
985 }
986 let key = AtnConfigKey::from(&config);
987 if let Some(existing_index) = self.config_index.get(&key).copied() {
988 #[cfg(feature = "perf-counters")]
989 crate::perf::record_config_merge();
990 let root_is_wildcard = !self.full_context;
991 let existing = &mut self.configs[existing_index];
992 existing.context = PredictionContext::merge_with_options(
993 Rc::clone(&existing.context),
994 config.context,
995 root_is_wildcard,
996 cache,
997 );
998 existing.reaches_into_outer_context = existing
999 .reaches_into_outer_context
1000 .max(config.reaches_into_outer_context);
1001 existing.precedence_filter_suppressed |= config.precedence_filter_suppressed;
1002 self.conflicting_alts.clear();
1010 false
1011 } else {
1012 let index = self.configs.len();
1013 self.config_index.insert(key, index);
1014 self.configs.push(config);
1015 #[cfg(feature = "perf-counters")]
1016 crate::perf::record_config_insert(self.configs.len());
1017 self.unique_alt = None;
1018 self.conflicting_alts.clear();
1019 true
1020 }
1021 }
1022
1023 pub fn configs(&self) -> &[AtnConfig] {
1024 &self.configs
1025 }
1026
1027 pub(crate) fn into_configs(self) -> Vec<AtnConfig> {
1031 self.configs
1032 }
1033
1034 pub const fn is_empty(&self) -> bool {
1035 self.configs.is_empty()
1036 }
1037
1038 pub const fn len(&self) -> usize {
1039 self.configs.len()
1040 }
1041
1042 pub fn set_readonly(&mut self, readonly: bool) {
1043 self.readonly = readonly;
1044 if readonly {
1045 self.config_index.clear();
1046 }
1047 }
1048
1049 pub(crate) fn optimize_contexts(&mut self, cache: &mut PredictionContextCache) {
1050 assert!(!self.readonly, "cannot mutate readonly ATN config set");
1051 for config in &mut self.configs {
1052 config.context = cache.get_cached_context(&config.context);
1053 }
1054 }
1055
1056 pub const fn is_readonly(&self) -> bool {
1057 self.readonly
1058 }
1059
1060 pub const fn full_context(&self) -> bool {
1061 self.full_context
1062 }
1063
1064 pub const fn has_semantic_context(&self) -> bool {
1065 self.has_semantic_context
1066 }
1067
1068 pub const fn set_has_semantic_context(&mut self, value: bool) {
1069 self.has_semantic_context = value;
1070 }
1071
1072 pub const fn dips_into_outer_context(&self) -> bool {
1073 self.dips_into_outer_context
1074 }
1075
1076 pub fn unique_alt(&mut self) -> Option<usize> {
1077 if self.unique_alt.is_none() {
1078 self.unique_alt = unique_alt(self.configs());
1079 }
1080 self.unique_alt
1081 }
1082
1083 pub fn alts(&self) -> BTreeSet<usize> {
1084 self.configs.iter().map(|config| config.alt).collect()
1085 }
1086
1087 pub fn conflicting_alt_subsets(&self) -> Vec<BTreeSet<usize>> {
1088 conflicting_alt_subsets(self.configs())
1089 }
1090
1091 pub fn conflicting_alts(&mut self) -> BTreeSet<usize> {
1092 if self.conflicting_alts.is_empty() {
1093 self.conflicting_alts = self
1094 .conflicting_alt_subsets()
1095 .into_iter()
1096 .filter(|alts| alts.len() > 1)
1097 .flatten()
1098 .collect();
1099 }
1100 self.conflicting_alts.clone()
1101 }
1102}
1103
1104impl PartialEq for AtnConfigSet {
1105 fn eq(&self, other: &Self) -> bool {
1106 self.configs == other.configs
1107 && self.full_context == other.full_context
1108 && self.has_semantic_context == other.has_semantic_context
1109 && self.dips_into_outer_context == other.dips_into_outer_context
1110 }
1111}
1112
1113impl Eq for AtnConfigSet {}
1114
1115impl Ord for AtnConfigSet {
1116 fn cmp(&self, other: &Self) -> Ordering {
1117 self.configs
1118 .cmp(&other.configs)
1119 .then_with(|| self.full_context.cmp(&other.full_context))
1120 .then_with(|| self.has_semantic_context.cmp(&other.has_semantic_context))
1121 .then_with(|| {
1122 self.dips_into_outer_context
1123 .cmp(&other.dips_into_outer_context)
1124 })
1125 }
1126}
1127
1128impl PartialOrd for AtnConfigSet {
1129 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1130 Some(self.cmp(other))
1131 }
1132}
1133
1134#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1135struct AtnConfigKey {
1136 state: usize,
1137 alt: usize,
1138 semantic_context: SemanticContext,
1139}
1140
1141impl From<&AtnConfig> for AtnConfigKey {
1142 fn from(config: &AtnConfig) -> Self {
1143 Self {
1144 state: config.state,
1145 alt: config.alt,
1146 semantic_context: config.semantic_context.clone(),
1147 }
1148 }
1149}
1150
1151pub fn unique_alt(configs: &[AtnConfig]) -> Option<usize> {
1152 let mut alt = None;
1153 for config in configs {
1154 match alt {
1155 None => alt = Some(config.alt),
1156 Some(existing) if existing == config.alt => {}
1157 Some(_) => return None,
1158 }
1159 }
1160 alt
1161}
1162
1163pub fn conflicting_alt_subsets(configs: &[AtnConfig]) -> Vec<BTreeSet<usize>> {
1164 let mut by_state_context =
1168 FxHashMap::<(usize, Rc<PredictionContext>), BTreeSet<usize>>::default();
1169 for config in configs {
1170 by_state_context
1171 .entry((config.state, Rc::clone(&config.context)))
1172 .or_default()
1173 .insert(config.alt);
1174 }
1175 by_state_context.into_values().collect()
1176}
1177
1178pub fn resolves_to_just_one_viable_alt(configs: &[AtnConfig]) -> Option<usize> {
1179 single_viable_alt(&conflicting_alt_subsets(configs))
1180}
1181
1182fn single_viable_alt(alt_subsets: &[BTreeSet<usize>]) -> Option<usize> {
1183 let mut result = None;
1184 for alts in alt_subsets {
1185 let min_alt = alts.iter().next().copied()?;
1186 match result {
1187 None => result = Some(min_alt),
1188 Some(existing) if existing == min_alt => {}
1189 Some(_) => return None,
1190 }
1191 }
1192 result
1193}
1194
1195pub fn has_sll_conflict_terminating_prediction(
1196 configs: &AtnConfigSet,
1197 is_rule_stop_state: impl Fn(usize) -> bool,
1198) -> bool {
1199 if configs
1200 .configs()
1201 .iter()
1202 .all(|config| is_rule_stop_state(config.state))
1203 {
1204 return true;
1205 }
1206 let alt_subsets = configs.conflicting_alt_subsets();
1207 alt_subsets.iter().any(|alts| alts.len() > 1)
1208 && !has_state_associated_with_one_alt(configs.configs())
1209}
1210
1211fn has_state_associated_with_one_alt(configs: &[AtnConfig]) -> bool {
1212 let mut by_state = BTreeMap::<usize, BTreeSet<usize>>::new();
1213 for config in configs {
1214 by_state.entry(config.state).or_default().insert(config.alt);
1215 }
1216 by_state.values().any(|alts| alts.len() == 1)
1217}
1218
1219#[cfg(test)]
1220mod tests {
1221 use super::*;
1222
1223 #[test]
1224 fn config_set_deduplicates_configs() {
1225 let empty = PredictionContext::empty();
1226 let mut set = AtnConfigSet::new();
1227 assert!(set.add(AtnConfig::new(1, 1, Rc::clone(&empty))));
1228 assert!(!set.add(AtnConfig::new(1, 1, Rc::clone(&empty))));
1229 assert_eq!(set.len(), 1);
1230 }
1231
1232 #[test]
1233 fn sll_conflict_does_not_stop_for_empty_contexts_alone() {
1234 let empty = PredictionContext::empty();
1235 let mut set = AtnConfigSet::new();
1236 set.add(AtnConfig::new(1, 1, Rc::clone(&empty)));
1237 set.add(AtnConfig::new(2, 2, empty));
1238
1239 assert!(!has_sll_conflict_terminating_prediction(&set, |_| false));
1240 }
1241
1242 #[test]
1243 fn sll_conflict_stops_when_all_configs_reached_rule_stop() {
1244 let empty = PredictionContext::empty();
1245 let mut set = AtnConfigSet::new();
1246 set.add(AtnConfig::new(10, 1, Rc::clone(&empty)));
1247 set.add(AtnConfig::new(11, 2, empty));
1248
1249 assert!(has_sll_conflict_terminating_prediction(&set, |state| {
1250 matches!(state, 10 | 11)
1251 }));
1252 }
1253
1254 #[test]
1255 fn viable_alt_resolves_to_shared_conflict_minimum() {
1256 let empty = PredictionContext::empty();
1257 let mut set = AtnConfigSet::new_full_context(true);
1258 set.add(AtnConfig::new(10, 1, Rc::clone(&empty)));
1259 set.add(AtnConfig::new(10, 2, Rc::clone(&empty)));
1260 set.add(AtnConfig::new(11, 1, empty));
1261
1262 assert_eq!(resolves_to_just_one_viable_alt(set.configs()), Some(1));
1263 }
1264
1265 #[test]
1266 fn viable_alt_keeps_looking_for_different_conflict_minimums() {
1267 let empty = PredictionContext::empty();
1268 let mut set = AtnConfigSet::new_full_context(true);
1269 set.add(AtnConfig::new(10, 1, Rc::clone(&empty)));
1270 set.add(AtnConfig::new(10, 2, Rc::clone(&empty)));
1271 set.add(AtnConfig::new(11, 2, Rc::clone(&empty)));
1272 set.add(AtnConfig::new(11, 3, empty));
1273
1274 assert_eq!(resolves_to_just_one_viable_alt(set.configs()), None);
1275 }
1276
1277 #[test]
1278 fn singleton_context_reports_parent_and_return_state() {
1279 let empty = PredictionContext::empty();
1280 let context = PredictionContext::singleton(Rc::clone(&empty), 42);
1281 assert_eq!(context.return_state(0), Some(42));
1282 assert_eq!(context.parent(0), Some(empty));
1283 }
1284
1285 #[test]
1286 fn merge_with_empty_preserves_non_empty_return_state() {
1287 let empty = PredictionContext::empty();
1288 let singleton = PredictionContext::singleton(Rc::clone(&empty), 42);
1289
1290 let merged = PredictionContext::merge(Rc::clone(&singleton), Rc::clone(&empty));
1291
1292 assert_eq!(merged.len(), 2);
1293 assert_eq!(merged.return_state(0), Some(42));
1294 assert_eq!(merged.parent(0), Some(empty.clone()));
1295 assert_eq!(merged.return_state(1), Some(EMPTY_RETURN_STATE));
1296 assert_eq!(merged.parent(1), Some(empty));
1297 }
1298
1299 #[test]
1300 fn merge_deduplicates_entries_with_same_parent_and_return_state() {
1301 let empty = PredictionContext::empty();
1302 let parent_one = PredictionContext::singleton(Rc::clone(&empty), 1);
1303 let parent_two = PredictionContext::singleton(Rc::clone(&empty), 2);
1304 let left = PredictionContext::array(vec![Rc::clone(&parent_one), parent_two], vec![42, 42]);
1305 let right = PredictionContext::singleton(Rc::clone(&parent_one), 42);
1306
1307 let merged = PredictionContext::merge(left, right);
1308
1309 assert_eq!(merged.len(), 2);
1310 }
1311
1312 #[test]
1313 fn merge_arrays_linearly_preserves_order_and_deduplicates_entries() {
1314 let empty = PredictionContext::empty();
1315 let parent_one = PredictionContext::singleton(Rc::clone(&empty), 1);
1316 let parent_two = PredictionContext::singleton(Rc::clone(&empty), 2);
1317 let parent_three = PredictionContext::singleton(Rc::clone(&empty), 3);
1318 let left = PredictionContext::array(
1319 vec![Rc::clone(&parent_one), Rc::clone(&parent_three)],
1320 vec![10, 30],
1321 );
1322 let right =
1323 PredictionContext::array(vec![parent_two, Rc::clone(&parent_three)], vec![20, 30]);
1324
1325 let merged = PredictionContext::merge(left, right);
1326
1327 assert_eq!(merged.len(), 3);
1328 assert_eq!(merged.return_state(0), Some(10));
1329 assert_eq!(merged.parent(0), Some(parent_one));
1330 assert_eq!(merged.return_state(1), Some(20));
1331 assert_eq!(merged.return_state(2), Some(30));
1332 assert_eq!(merged.parent(2), Some(parent_three));
1333 }
1334
1335 fn singleton_with_forced_hash(
1340 parent: Rc<PredictionContext>,
1341 return_state: usize,
1342 cached_hash: u64,
1343 ) -> Rc<PredictionContext> {
1344 Rc::new(PredictionContext::Singleton {
1345 parent,
1346 return_state,
1347 cached_hash,
1348 })
1349 }
1350
1351 #[test]
1352 fn merge_is_order_independent_under_hash_collision() {
1353 let empty = PredictionContext::empty();
1356 let parent_a = singleton_with_forced_hash(Rc::clone(&empty), 100, 0xDEAD_BEEF);
1357 let parent_b = singleton_with_forced_hash(Rc::clone(&empty), 200, 0xDEAD_BEEF);
1358 assert_ne!(parent_a, parent_b, "parents must be structurally distinct");
1359 assert_eq!(
1360 parent_a.cached_hash(),
1361 parent_b.cached_hash(),
1362 "test must force the hash collision"
1363 );
1364
1365 let left = PredictionContext::singleton(Rc::clone(&parent_a), 7);
1368 let right = PredictionContext::singleton(Rc::clone(&parent_b), 7);
1369 let merged_lr = PredictionContext::merge(Rc::clone(&left), Rc::clone(&right));
1370 let merged_rl = PredictionContext::merge(right, left);
1371 assert_eq!(
1372 merged_lr, merged_rl,
1373 "merge_two_context_entries must canonicalize regardless of operand order"
1374 );
1375
1376 let shared = PredictionContext::singleton(Rc::clone(&empty), 30);
1382 let left_array = PredictionContext::array(
1383 vec![Rc::clone(&parent_a), Rc::clone(&shared)],
1384 vec![7, 30],
1385 );
1386 let right_array =
1387 PredictionContext::array(vec![Rc::clone(&parent_b), shared], vec![7, 30]);
1388 let merged_arrays_lr =
1389 PredictionContext::merge(Rc::clone(&left_array), Rc::clone(&right_array));
1390 let merged_arrays_rl = PredictionContext::merge(right_array, left_array);
1391 assert_eq!(
1392 merged_arrays_lr, merged_arrays_rl,
1393 "merge_arrays must canonicalize colliding-hash entries to one order"
1394 );
1395 assert_eq!(merged_arrays_lr.len(), 3, "two rs=7 entries + one rs=30");
1396 }
1397
1398 #[test]
1399 fn prediction_context_cache_reuses_equal_context_graphs() {
1400 let mut cache = PredictionContextCache::new();
1401 let left_parent = PredictionContext::singleton(PredictionContext::empty(), 1);
1402 let right_parent = PredictionContext::singleton(PredictionContext::empty(), 1);
1403 let left = PredictionContext::singleton(left_parent, 42);
1404 let right = PredictionContext::singleton(right_parent, 42);
1405
1406 let cached_left = cache.get_cached_context(&left);
1407 let cached_right = cache.get_cached_context(&right);
1408 let cached_left_parent = cached_left.parent(0).expect("singleton parent");
1409 let cached_right_parent = cached_right.parent(0).expect("singleton parent");
1410
1411 assert!(Rc::ptr_eq(&cached_left, &cached_right));
1412 assert!(Rc::ptr_eq(&cached_left_parent, &cached_right_parent));
1413 }
1414
1415 #[test]
1416 fn config_set_optimize_contexts_canonicalizes_contexts() {
1417 let mut cache = PredictionContextCache::new();
1418 let first = PredictionContext::singleton(PredictionContext::empty(), 7);
1419 let second = PredictionContext::singleton(PredictionContext::empty(), 7);
1420 let mut set = AtnConfigSet::new();
1421 set.add(AtnConfig::new(1, 1, first));
1422 set.add(AtnConfig::new(2, 2, second));
1423
1424 set.optimize_contexts(&mut cache);
1425
1426 assert!(Rc::ptr_eq(&set.configs[0].context, &set.configs[1].context));
1427 }
1428}