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) = if compare_entries(
325 &right_parent,
326 right_return_state,
327 &left_parent,
328 left_return_state,
329 )
330 == Ordering::Less
331 {
332 (
333 right_parent,
334 right_return_state,
335 left_parent,
336 left_return_state,
337 )
338 } else {
339 (
340 left_parent,
341 left_return_state,
342 right_parent,
343 right_return_state,
344 )
345 };
346 PredictionContext::array(
347 vec![first_parent, second_parent],
348 vec![first_return_state, second_return_state],
349 )
350}
351
352fn merge_array_with_entry(
353 array_context: Rc<PredictionContext>,
354 array_parents: &[Rc<PredictionContext>],
355 array_return_states: &[usize],
356 entry_parent: Rc<PredictionContext>,
357 entry_return_state: usize,
358 entry_on_left: bool,
359) -> Rc<PredictionContext> {
360 let mut insert_index = array_parents.len();
361 for (index, (parent, return_state)) in array_parents.iter().zip(array_return_states).enumerate()
362 {
363 let ordering = compare_entries(&entry_parent, entry_return_state, parent, *return_state);
364 if ordering == Ordering::Equal && parent == &entry_parent {
365 return array_context;
366 }
367 let should_insert = if entry_on_left {
368 ordering != Ordering::Greater
369 } else {
370 ordering == Ordering::Less
371 };
372 if should_insert {
373 insert_index = index;
374 break;
375 }
376 }
377
378 let mut parents = Vec::with_capacity(array_parents.len() + 1);
379 let mut return_states = Vec::with_capacity(array_return_states.len() + 1);
380 parents.extend(array_parents[..insert_index].iter().cloned());
381 return_states.extend_from_slice(&array_return_states[..insert_index]);
382 parents.push(entry_parent);
383 return_states.push(entry_return_state);
384 parents.extend(array_parents[insert_index..].iter().cloned());
385 return_states.extend_from_slice(&array_return_states[insert_index..]);
386 PredictionContext::array(parents, return_states)
387}
388
389fn merge_arrays(
390 left_parents: &[Rc<PredictionContext>],
391 left_return_states: &[usize],
392 right_parents: &[Rc<PredictionContext>],
393 right_return_states: &[usize],
394) -> Rc<PredictionContext> {
395 let mut parents = Vec::with_capacity(left_parents.len() + right_parents.len());
396 let mut return_states =
397 Vec::with_capacity(left_return_states.len() + right_return_states.len());
398 let mut left_index = 0;
399 let mut right_index = 0;
400
401 while left_index < left_parents.len() && right_index < right_parents.len() {
402 match compare_entries(
403 &left_parents[left_index],
404 left_return_states[left_index],
405 &right_parents[right_index],
406 right_return_states[right_index],
407 ) {
408 Ordering::Less => {
409 parents.push(Rc::clone(&left_parents[left_index]));
410 return_states.push(left_return_states[left_index]);
411 left_index += 1;
412 }
413 Ordering::Greater => {
414 parents.push(Rc::clone(&right_parents[right_index]));
415 return_states.push(right_return_states[right_index]);
416 right_index += 1;
417 }
418 Ordering::Equal => {
422 parents.push(Rc::clone(&left_parents[left_index]));
423 return_states.push(left_return_states[left_index]);
424 left_index += 1;
425 right_index += 1;
426 }
427 }
428 }
429
430 for index in left_index..left_parents.len() {
431 parents.push(Rc::clone(&left_parents[index]));
432 return_states.push(left_return_states[index]);
433 }
434 for index in right_index..right_parents.len() {
435 parents.push(Rc::clone(&right_parents[index]));
436 return_states.push(right_return_states[index]);
437 }
438
439 if parents.len() == 1 {
440 return PredictionContext::singleton(
441 parents.pop().expect("single merged parent"),
442 return_states.pop().expect("single merged return state"),
443 );
444 }
445 PredictionContext::array(parents, return_states)
446}
447
448fn compare_entries(
468 left_parent: &Rc<PredictionContext>,
469 left_return_state: usize,
470 right_parent: &Rc<PredictionContext>,
471 right_return_state: usize,
472) -> Ordering {
473 left_return_state
474 .cmp(&right_return_state)
475 .then_with(|| left_parent.cached_hash().cmp(&right_parent.cached_hash()))
476 .then_with(|| left_parent.cmp(right_parent))
477}
478
479impl PartialEq for PredictionContext {
480 fn eq(&self, other: &Self) -> bool {
481 if std::ptr::eq(self, other) {
482 return true;
483 }
484 if self.cached_hash() != other.cached_hash() {
485 return false;
486 }
487 match (self, other) {
488 (Self::Empty { .. }, Self::Empty { .. }) => true,
489 (
490 Self::Singleton {
491 parent,
492 return_state,
493 ..
494 },
495 Self::Singleton {
496 parent: other_parent,
497 return_state: other_return_state,
498 ..
499 },
500 ) => return_state == other_return_state && parent == other_parent,
501 (
502 Self::Array {
503 parents,
504 return_states,
505 ..
506 },
507 Self::Array {
508 parents: other_parents,
509 return_states: other_return_states,
510 ..
511 },
512 ) => return_states == other_return_states && parents == other_parents,
513 _ => false,
514 }
515 }
516}
517
518impl Eq for PredictionContext {}
519
520thread_local! {
521 static EMPTY_PREDICTION_CONTEXT: Rc<PredictionContext> = Rc::new(PredictionContext::Empty {
522 cached_hash: prediction_context_empty_hash(),
523 });
524}
525
526impl Hash for PredictionContext {
527 fn hash<H: Hasher>(&self, state: &mut H) {
528 state.write_u64(self.cached_hash());
529 }
530}
531
532impl Ord for PredictionContext {
533 fn cmp(&self, other: &Self) -> Ordering {
534 if std::ptr::eq(self, other) {
535 return Ordering::Equal;
536 }
537 self.cached_hash()
538 .cmp(&other.cached_hash())
539 .then_with(|| prediction_context_variant(self).cmp(&prediction_context_variant(other)))
540 .then_with(|| match (self, other) {
541 (Self::Empty { .. }, Self::Empty { .. }) => Ordering::Equal,
542 (
543 Self::Singleton {
544 parent,
545 return_state,
546 ..
547 },
548 Self::Singleton {
549 parent: other_parent,
550 return_state: other_return_state,
551 ..
552 },
553 ) => return_state
554 .cmp(other_return_state)
555 .then_with(|| parent.cmp(other_parent)),
556 (
557 Self::Array {
558 parents,
559 return_states,
560 ..
561 },
562 Self::Array {
563 parents: other_parents,
564 return_states: other_return_states,
565 ..
566 },
567 ) => return_states
568 .cmp(other_return_states)
569 .then_with(|| parents.cmp(other_parents)),
570 _ => Ordering::Equal,
571 })
572 }
573}
574
575impl PartialOrd for PredictionContext {
576 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
577 Some(self.cmp(other))
578 }
579}
580
581const fn prediction_context_variant(context: &PredictionContext) -> u8 {
582 match context {
583 PredictionContext::Empty { .. } => 0,
584 PredictionContext::Singleton { .. } => 1,
585 PredictionContext::Array { .. } => 2,
586 }
587}
588
589fn prediction_context_empty_hash() -> u64 {
590 let mut hasher = PredictionFxHasher::default();
591 hasher.write_u8(0);
592 hasher.finish()
593}
594
595fn prediction_context_singleton_hash(parent: &Rc<PredictionContext>, return_state: usize) -> u64 {
596 let mut hasher = PredictionFxHasher::default();
597 hasher.write_u8(1);
598 hasher.write_u64(parent.cached_hash());
599 hasher.write_usize(return_state);
600 hasher.finish()
601}
602
603fn prediction_context_array_hash(
604 parents: &[Rc<PredictionContext>],
605 return_states: &[usize],
606) -> u64 {
607 let mut hasher = PredictionFxHasher::default();
608 hasher.write_u8(2);
609 hasher.write_usize(parents.len());
610 for parent in parents {
611 hasher.write_u64(parent.cached_hash());
612 }
613 hasher.write_usize(return_states.len());
614 for return_state in return_states {
615 hasher.write_usize(*return_state);
616 }
617 hasher.finish()
618}
619
620#[derive(Debug, Default)]
622pub struct PredictionContextMergeCache {
623 entries: FxHashMap<PredictionContextMergeKey, Rc<PredictionContext>>,
624}
625
626impl PredictionContextMergeCache {
627 pub fn new() -> Self {
628 Self::default()
629 }
630
631 fn get(
632 &self,
633 left: &Rc<PredictionContext>,
634 right: &Rc<PredictionContext>,
635 ) -> Option<Rc<PredictionContext>> {
636 let key = PredictionContextMergeKey::new(left, right);
637 self.entries.get(&key).cloned()
638 }
639
640 fn insert(
641 &mut self,
642 left: &Rc<PredictionContext>,
643 right: &Rc<PredictionContext>,
644 merged: &Rc<PredictionContext>,
645 ) {
646 self.entries.insert(
647 PredictionContextMergeKey::new(left, right),
648 Rc::clone(merged),
649 );
650 }
651}
652
653#[derive(Debug)]
655pub(crate) struct PredictionContextCache {
656 empty: Rc<PredictionContext>,
657 entries: FxHashMap<Rc<PredictionContext>, Rc<PredictionContext>>,
658}
659
660impl PredictionContextCache {
661 pub(crate) fn new() -> Self {
662 Self {
663 empty: PredictionContext::empty(),
664 entries: FxHashMap::default(),
665 }
666 }
667
668 pub(crate) fn get_cached_context(
669 &mut self,
670 context: &Rc<PredictionContext>,
671 ) -> Rc<PredictionContext> {
672 #[cfg(feature = "perf-counters")]
673 crate::perf::record_context_cache_call();
674 if context.is_empty() {
675 #[cfg(feature = "perf-counters")]
676 crate::perf::record_context_cache_empty();
677 return Rc::clone(&self.empty);
678 }
679 if let Some(existing) = self.entries.get(context) {
680 #[cfg(feature = "perf-counters")]
681 crate::perf::record_context_cache_hit();
682 return Rc::clone(existing);
683 }
684 #[cfg(feature = "perf-counters")]
685 crate::perf::record_context_cache_miss();
686 let mut visited = FxHashMap::default();
687 let cached = self.get_cached_context_inner(context, &mut visited);
688 #[cfg(feature = "perf-counters")]
689 crate::perf::record_context_cache_visited(visited.len());
690 cached
691 }
692
693 fn get_cached_context_inner(
694 &mut self,
695 context: &Rc<PredictionContext>,
696 visited: &mut FxHashMap<usize, Rc<PredictionContext>>,
697 ) -> Rc<PredictionContext> {
698 if context.is_empty() {
699 return Rc::clone(&self.empty);
700 }
701 let context_ptr = Rc::as_ptr(context) as usize;
705 if let Some(existing) = visited.get(&context_ptr) {
706 return Rc::clone(existing);
707 }
708 if let Some(existing) = self.entries.get(context) {
709 #[cfg(feature = "perf-counters")]
710 crate::perf::record_context_cache_hit();
711 let existing = Rc::clone(existing);
712 visited.insert(context_ptr, Rc::clone(&existing));
713 return existing;
714 }
715 let cached = match context.as_ref() {
716 PredictionContext::Empty { .. } => Rc::clone(&self.empty),
717 PredictionContext::Singleton {
718 parent,
719 return_state,
720 ..
721 } => {
722 let cached_parent = self.get_cached_context_inner(parent, visited);
723 if Rc::ptr_eq(parent, &cached_parent) {
724 self.add(Rc::clone(context))
725 } else {
726 self.add(PredictionContext::singleton(cached_parent, *return_state))
727 }
728 }
729 PredictionContext::Array {
730 parents,
731 return_states,
732 ..
733 } => {
734 let mut changed = false;
735 let mut cached_parents = Vec::with_capacity(parents.len());
736 for parent in parents {
737 let cached_parent = self.get_cached_context_inner(parent, visited);
738 changed |= !Rc::ptr_eq(parent, &cached_parent);
739 cached_parents.push(cached_parent);
740 }
741 if changed {
742 self.add(PredictionContext::array(
743 cached_parents,
744 return_states.clone(),
745 ))
746 } else {
747 self.add(Rc::clone(context))
748 }
749 }
750 };
751 visited.insert(context_ptr, Rc::clone(&cached));
752 cached
753 }
754
755 fn add(&mut self, context: Rc<PredictionContext>) -> Rc<PredictionContext> {
756 if context.is_empty() {
757 return Rc::clone(&self.empty);
758 }
759 if let Some(existing) = self.entries.get(&context) {
760 #[cfg(feature = "perf-counters")]
761 crate::perf::record_context_cache_hit();
762 return Rc::clone(existing);
763 }
764 #[cfg(feature = "perf-counters")]
765 crate::perf::record_context_cache_insert();
766 self.entries
767 .insert(Rc::clone(&context), Rc::clone(&context));
768 context
769 }
770}
771
772impl Default for PredictionContextCache {
773 fn default() -> Self {
774 Self::new()
775 }
776}
777
778#[derive(Clone, Debug)]
779struct PredictionContextMergeKey {
780 left: Rc<PredictionContext>,
781 right: Rc<PredictionContext>,
782 left_hash: u64,
783 right_hash: u64,
784}
785
786impl PredictionContextMergeKey {
787 fn new(left: &Rc<PredictionContext>, right: &Rc<PredictionContext>) -> Self {
788 let left_hash = prediction_context_hash(left);
789 let right_hash = prediction_context_hash(right);
790 if should_swap_merge_key(left, left_hash, right, right_hash) {
791 return Self {
792 left: Rc::clone(right),
793 right: Rc::clone(left),
794 left_hash: right_hash,
795 right_hash: left_hash,
796 };
797 }
798 Self {
799 left: Rc::clone(left),
800 right: Rc::clone(right),
801 left_hash,
802 right_hash,
803 }
804 }
805}
806
807fn should_swap_merge_key(
808 left: &Rc<PredictionContext>,
809 left_hash: u64,
810 right: &Rc<PredictionContext>,
811 right_hash: u64,
812) -> bool {
813 (right_hash, Rc::as_ptr(right) as usize) < (left_hash, Rc::as_ptr(left) as usize)
814}
815
816impl PartialEq for PredictionContextMergeKey {
817 fn eq(&self, other: &Self) -> bool {
818 self.left_hash == other.left_hash
819 && self.right_hash == other.right_hash
820 && self.left == other.left
821 && self.right == other.right
822 }
823}
824
825impl Eq for PredictionContextMergeKey {}
826
827impl Hash for PredictionContextMergeKey {
828 fn hash<H: Hasher>(&self, state: &mut H) {
829 state.write_u64(self.left_hash);
830 state.write_u64(self.right_hash);
831 }
832}
833
834fn prediction_context_hash(context: &Rc<PredictionContext>) -> u64 {
835 context.cached_hash()
836}
837
838#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
839pub enum SemanticContext {
840 None,
841 Predicate {
842 rule_index: usize,
843 pred_index: usize,
844 context_dependent: bool,
845 },
846 Precedence {
847 precedence: i32,
848 },
849 And(Vec<Self>),
850 Or(Vec<Self>),
851}
852
853impl SemanticContext {
854 pub const fn none() -> Self {
855 Self::None
856 }
857
858 pub fn and(left: Self, right: Self) -> Self {
859 combine_semantic_context(left, right, true)
860 }
861
862 pub fn or(left: Self, right: Self) -> Self {
863 combine_semantic_context(left, right, false)
864 }
865
866 pub const fn is_none(&self) -> bool {
867 matches!(self, Self::None)
868 }
869}
870
871fn combine_semantic_context(
872 left: SemanticContext,
873 right: SemanticContext,
874 and: bool,
875) -> SemanticContext {
876 if left == right {
877 return left;
878 }
879 if left.is_none() {
880 return right;
881 }
882 if right.is_none() {
883 return left;
884 }
885 let mut entries = Vec::new();
886 for context in [left, right] {
887 match (and, context) {
888 (true, SemanticContext::And(children)) | (false, SemanticContext::Or(children)) => {
889 entries.extend(children);
890 }
891 (_, other) => entries.push(other),
892 }
893 }
894 entries.sort();
895 entries.dedup();
896 if and {
897 SemanticContext::And(entries)
898 } else {
899 SemanticContext::Or(entries)
900 }
901}
902
903#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
904pub struct AtnConfig {
905 pub state: usize,
906 pub alt: usize,
907 pub context: Rc<PredictionContext>,
908 pub semantic_context: SemanticContext,
909 pub reaches_into_outer_context: usize,
910 pub precedence_filter_suppressed: bool,
911}
912
913impl AtnConfig {
914 pub const fn new(state: usize, alt: usize, context: Rc<PredictionContext>) -> Self {
915 Self {
916 state,
917 alt,
918 context,
919 semantic_context: SemanticContext::None,
920 reaches_into_outer_context: 0,
921 precedence_filter_suppressed: false,
922 }
923 }
924
925 #[must_use]
926 pub fn with_semantic_context(mut self, semantic_context: SemanticContext) -> Self {
927 self.semantic_context = semantic_context;
928 self
929 }
930
931 #[must_use]
932 pub const fn with_reaches_into_outer_context(mut self, reaches: usize) -> Self {
933 self.reaches_into_outer_context = reaches;
934 self
935 }
936}
937
938#[derive(Clone, Debug, Default)]
939pub struct AtnConfigSet {
940 configs: Vec<AtnConfig>,
941 config_index: FxHashMap<AtnConfigKey, usize>,
942 full_context: bool,
943 unique_alt: Option<usize>,
944 conflicting_alts: BTreeSet<usize>,
945 has_semantic_context: bool,
946 dips_into_outer_context: bool,
947 readonly: bool,
948}
949
950impl AtnConfigSet {
951 pub fn new() -> Self {
952 Self::default()
953 }
954
955 pub fn new_full_context(full_context: bool) -> Self {
956 Self {
957 configs: Vec::new(),
958 config_index: FxHashMap::default(),
959 full_context,
960 unique_alt: None,
961 conflicting_alts: BTreeSet::new(),
962 has_semantic_context: false,
963 dips_into_outer_context: false,
964 readonly: false,
965 }
966 }
967
968 pub fn add(&mut self, config: AtnConfig) -> bool {
969 self.add_with_merge_cache(config, None)
970 }
971
972 pub fn add_with_merge_cache(
975 &mut self,
976 config: AtnConfig,
977 cache: Option<&mut PredictionContextMergeCache>,
978 ) -> bool {
979 assert!(!self.readonly, "cannot mutate readonly ATN config set");
980 #[cfg(feature = "perf-counters")]
981 crate::perf::record_config_add_call();
982 if !config.semantic_context.is_none() {
983 self.has_semantic_context = true;
984 }
985 if config.reaches_into_outer_context > 0 {
986 self.dips_into_outer_context = true;
987 }
988 let key = AtnConfigKey::from(&config);
989 if let Some(existing_index) = self.config_index.get(&key).copied() {
990 #[cfg(feature = "perf-counters")]
991 crate::perf::record_config_merge();
992 let root_is_wildcard = !self.full_context;
993 let existing = &mut self.configs[existing_index];
994 existing.context = PredictionContext::merge_with_options(
995 Rc::clone(&existing.context),
996 config.context,
997 root_is_wildcard,
998 cache,
999 );
1000 existing.reaches_into_outer_context = existing
1001 .reaches_into_outer_context
1002 .max(config.reaches_into_outer_context);
1003 existing.precedence_filter_suppressed |= config.precedence_filter_suppressed;
1004 self.conflicting_alts.clear();
1012 false
1013 } else {
1014 let index = self.configs.len();
1015 self.config_index.insert(key, index);
1016 self.configs.push(config);
1017 #[cfg(feature = "perf-counters")]
1018 crate::perf::record_config_insert(self.configs.len());
1019 self.unique_alt = None;
1020 self.conflicting_alts.clear();
1021 true
1022 }
1023 }
1024
1025 pub fn configs(&self) -> &[AtnConfig] {
1026 &self.configs
1027 }
1028
1029 pub(crate) fn into_configs(self) -> Vec<AtnConfig> {
1033 self.configs
1034 }
1035
1036 pub const fn is_empty(&self) -> bool {
1037 self.configs.is_empty()
1038 }
1039
1040 pub const fn len(&self) -> usize {
1041 self.configs.len()
1042 }
1043
1044 pub fn set_readonly(&mut self, readonly: bool) {
1045 self.readonly = readonly;
1046 if readonly {
1047 self.config_index.clear();
1048 }
1049 }
1050
1051 pub(crate) fn optimize_contexts(&mut self, cache: &mut PredictionContextCache) {
1052 assert!(!self.readonly, "cannot mutate readonly ATN config set");
1053 for config in &mut self.configs {
1054 config.context = cache.get_cached_context(&config.context);
1055 }
1056 }
1057
1058 pub const fn is_readonly(&self) -> bool {
1059 self.readonly
1060 }
1061
1062 pub const fn full_context(&self) -> bool {
1063 self.full_context
1064 }
1065
1066 pub const fn has_semantic_context(&self) -> bool {
1067 self.has_semantic_context
1068 }
1069
1070 pub const fn set_has_semantic_context(&mut self, value: bool) {
1071 self.has_semantic_context = value;
1072 }
1073
1074 pub const fn dips_into_outer_context(&self) -> bool {
1075 self.dips_into_outer_context
1076 }
1077
1078 pub fn unique_alt(&mut self) -> Option<usize> {
1079 if self.unique_alt.is_none() {
1080 self.unique_alt = unique_alt(self.configs());
1081 }
1082 self.unique_alt
1083 }
1084
1085 pub fn alts(&self) -> BTreeSet<usize> {
1086 self.configs.iter().map(|config| config.alt).collect()
1087 }
1088
1089 pub fn conflicting_alt_subsets(&self) -> Vec<BTreeSet<usize>> {
1090 conflicting_alt_subsets(self.configs())
1091 }
1092
1093 pub fn conflicting_alts(&mut self) -> BTreeSet<usize> {
1094 if self.conflicting_alts.is_empty() {
1095 self.conflicting_alts = self
1096 .conflicting_alt_subsets()
1097 .into_iter()
1098 .filter(|alts| alts.len() > 1)
1099 .flatten()
1100 .collect();
1101 }
1102 self.conflicting_alts.clone()
1103 }
1104}
1105
1106impl PartialEq for AtnConfigSet {
1107 fn eq(&self, other: &Self) -> bool {
1108 self.configs == other.configs
1109 && self.full_context == other.full_context
1110 && self.has_semantic_context == other.has_semantic_context
1111 && self.dips_into_outer_context == other.dips_into_outer_context
1112 }
1113}
1114
1115impl Eq for AtnConfigSet {}
1116
1117impl Ord for AtnConfigSet {
1118 fn cmp(&self, other: &Self) -> Ordering {
1119 self.configs
1120 .cmp(&other.configs)
1121 .then_with(|| self.full_context.cmp(&other.full_context))
1122 .then_with(|| self.has_semantic_context.cmp(&other.has_semantic_context))
1123 .then_with(|| {
1124 self.dips_into_outer_context
1125 .cmp(&other.dips_into_outer_context)
1126 })
1127 }
1128}
1129
1130impl PartialOrd for AtnConfigSet {
1131 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1132 Some(self.cmp(other))
1133 }
1134}
1135
1136#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1137struct AtnConfigKey {
1138 state: usize,
1139 alt: usize,
1140 semantic_context: SemanticContext,
1141}
1142
1143impl From<&AtnConfig> for AtnConfigKey {
1144 fn from(config: &AtnConfig) -> Self {
1145 Self {
1146 state: config.state,
1147 alt: config.alt,
1148 semantic_context: config.semantic_context.clone(),
1149 }
1150 }
1151}
1152
1153pub fn unique_alt(configs: &[AtnConfig]) -> Option<usize> {
1154 let mut alt = None;
1155 for config in configs {
1156 match alt {
1157 None => alt = Some(config.alt),
1158 Some(existing) if existing == config.alt => {}
1159 Some(_) => return None,
1160 }
1161 }
1162 alt
1163}
1164
1165pub fn conflicting_alt_subsets(configs: &[AtnConfig]) -> Vec<BTreeSet<usize>> {
1166 let mut by_state_context =
1170 FxHashMap::<(usize, Rc<PredictionContext>), BTreeSet<usize>>::default();
1171 for config in configs {
1172 by_state_context
1173 .entry((config.state, Rc::clone(&config.context)))
1174 .or_default()
1175 .insert(config.alt);
1176 }
1177 by_state_context.into_values().collect()
1178}
1179
1180pub fn resolves_to_just_one_viable_alt(configs: &[AtnConfig]) -> Option<usize> {
1181 single_viable_alt(&conflicting_alt_subsets(configs))
1182}
1183
1184fn single_viable_alt(alt_subsets: &[BTreeSet<usize>]) -> Option<usize> {
1185 let mut result = None;
1186 for alts in alt_subsets {
1187 let min_alt = alts.iter().next().copied()?;
1188 match result {
1189 None => result = Some(min_alt),
1190 Some(existing) if existing == min_alt => {}
1191 Some(_) => return None,
1192 }
1193 }
1194 result
1195}
1196
1197pub fn has_sll_conflict_terminating_prediction(
1198 configs: &AtnConfigSet,
1199 is_rule_stop_state: impl Fn(usize) -> bool,
1200) -> bool {
1201 if configs
1202 .configs()
1203 .iter()
1204 .all(|config| is_rule_stop_state(config.state))
1205 {
1206 return true;
1207 }
1208 let alt_subsets = configs.conflicting_alt_subsets();
1209 alt_subsets.iter().any(|alts| alts.len() > 1)
1210 && !has_state_associated_with_one_alt(configs.configs())
1211}
1212
1213fn has_state_associated_with_one_alt(configs: &[AtnConfig]) -> bool {
1214 let mut by_state = BTreeMap::<usize, BTreeSet<usize>>::new();
1215 for config in configs {
1216 by_state.entry(config.state).or_default().insert(config.alt);
1217 }
1218 by_state.values().any(|alts| alts.len() == 1)
1219}
1220
1221#[cfg(test)]
1222mod tests {
1223 use super::*;
1224
1225 #[test]
1226 fn config_set_deduplicates_configs() {
1227 let empty = PredictionContext::empty();
1228 let mut set = AtnConfigSet::new();
1229 assert!(set.add(AtnConfig::new(1, 1, Rc::clone(&empty))));
1230 assert!(!set.add(AtnConfig::new(1, 1, Rc::clone(&empty))));
1231 assert_eq!(set.len(), 1);
1232 }
1233
1234 #[test]
1235 fn sll_conflict_does_not_stop_for_empty_contexts_alone() {
1236 let empty = PredictionContext::empty();
1237 let mut set = AtnConfigSet::new();
1238 set.add(AtnConfig::new(1, 1, Rc::clone(&empty)));
1239 set.add(AtnConfig::new(2, 2, empty));
1240
1241 assert!(!has_sll_conflict_terminating_prediction(&set, |_| false));
1242 }
1243
1244 #[test]
1245 fn sll_conflict_stops_when_all_configs_reached_rule_stop() {
1246 let empty = PredictionContext::empty();
1247 let mut set = AtnConfigSet::new();
1248 set.add(AtnConfig::new(10, 1, Rc::clone(&empty)));
1249 set.add(AtnConfig::new(11, 2, empty));
1250
1251 assert!(has_sll_conflict_terminating_prediction(&set, |state| {
1252 matches!(state, 10 | 11)
1253 }));
1254 }
1255
1256 #[test]
1257 fn viable_alt_resolves_to_shared_conflict_minimum() {
1258 let empty = PredictionContext::empty();
1259 let mut set = AtnConfigSet::new_full_context(true);
1260 set.add(AtnConfig::new(10, 1, Rc::clone(&empty)));
1261 set.add(AtnConfig::new(10, 2, Rc::clone(&empty)));
1262 set.add(AtnConfig::new(11, 1, empty));
1263
1264 assert_eq!(resolves_to_just_one_viable_alt(set.configs()), Some(1));
1265 }
1266
1267 #[test]
1268 fn viable_alt_keeps_looking_for_different_conflict_minimums() {
1269 let empty = PredictionContext::empty();
1270 let mut set = AtnConfigSet::new_full_context(true);
1271 set.add(AtnConfig::new(10, 1, Rc::clone(&empty)));
1272 set.add(AtnConfig::new(10, 2, Rc::clone(&empty)));
1273 set.add(AtnConfig::new(11, 2, Rc::clone(&empty)));
1274 set.add(AtnConfig::new(11, 3, empty));
1275
1276 assert_eq!(resolves_to_just_one_viable_alt(set.configs()), None);
1277 }
1278
1279 #[test]
1280 fn singleton_context_reports_parent_and_return_state() {
1281 let empty = PredictionContext::empty();
1282 let context = PredictionContext::singleton(Rc::clone(&empty), 42);
1283 assert_eq!(context.return_state(0), Some(42));
1284 assert_eq!(context.parent(0), Some(empty));
1285 }
1286
1287 #[test]
1288 fn merge_with_empty_preserves_non_empty_return_state() {
1289 let empty = PredictionContext::empty();
1290 let singleton = PredictionContext::singleton(Rc::clone(&empty), 42);
1291
1292 let merged = PredictionContext::merge(Rc::clone(&singleton), Rc::clone(&empty));
1293
1294 assert_eq!(merged.len(), 2);
1295 assert_eq!(merged.return_state(0), Some(42));
1296 assert_eq!(merged.parent(0), Some(empty.clone()));
1297 assert_eq!(merged.return_state(1), Some(EMPTY_RETURN_STATE));
1298 assert_eq!(merged.parent(1), Some(empty));
1299 }
1300
1301 #[test]
1302 fn merge_deduplicates_entries_with_same_parent_and_return_state() {
1303 let empty = PredictionContext::empty();
1304 let parent_one = PredictionContext::singleton(Rc::clone(&empty), 1);
1305 let parent_two = PredictionContext::singleton(Rc::clone(&empty), 2);
1306 let left = PredictionContext::array(vec![Rc::clone(&parent_one), parent_two], vec![42, 42]);
1307 let right = PredictionContext::singleton(Rc::clone(&parent_one), 42);
1308
1309 let merged = PredictionContext::merge(left, right);
1310
1311 assert_eq!(merged.len(), 2);
1312 }
1313
1314 #[test]
1315 fn merge_arrays_linearly_preserves_order_and_deduplicates_entries() {
1316 let empty = PredictionContext::empty();
1317 let parent_one = PredictionContext::singleton(Rc::clone(&empty), 1);
1318 let parent_two = PredictionContext::singleton(Rc::clone(&empty), 2);
1319 let parent_three = PredictionContext::singleton(Rc::clone(&empty), 3);
1320 let left = PredictionContext::array(
1321 vec![Rc::clone(&parent_one), Rc::clone(&parent_three)],
1322 vec![10, 30],
1323 );
1324 let right =
1325 PredictionContext::array(vec![parent_two, Rc::clone(&parent_three)], vec![20, 30]);
1326
1327 let merged = PredictionContext::merge(left, right);
1328
1329 assert_eq!(merged.len(), 3);
1330 assert_eq!(merged.return_state(0), Some(10));
1331 assert_eq!(merged.parent(0), Some(parent_one));
1332 assert_eq!(merged.return_state(1), Some(20));
1333 assert_eq!(merged.return_state(2), Some(30));
1334 assert_eq!(merged.parent(2), Some(parent_three));
1335 }
1336
1337 fn singleton_with_forced_hash(
1342 parent: Rc<PredictionContext>,
1343 return_state: usize,
1344 cached_hash: u64,
1345 ) -> Rc<PredictionContext> {
1346 Rc::new(PredictionContext::Singleton {
1347 parent,
1348 return_state,
1349 cached_hash,
1350 })
1351 }
1352
1353 #[test]
1354 fn merge_is_order_independent_under_hash_collision() {
1355 let empty = PredictionContext::empty();
1358 let parent_a = singleton_with_forced_hash(Rc::clone(&empty), 100, 0xDEAD_BEEF);
1359 let parent_b = singleton_with_forced_hash(Rc::clone(&empty), 200, 0xDEAD_BEEF);
1360 assert_ne!(parent_a, parent_b, "parents must be structurally distinct");
1361 assert_eq!(
1362 parent_a.cached_hash(),
1363 parent_b.cached_hash(),
1364 "test must force the hash collision"
1365 );
1366
1367 let left = PredictionContext::singleton(Rc::clone(&parent_a), 7);
1370 let right = PredictionContext::singleton(Rc::clone(&parent_b), 7);
1371 let merged_lr = PredictionContext::merge(Rc::clone(&left), Rc::clone(&right));
1372 let merged_rl = PredictionContext::merge(right, left);
1373 assert_eq!(
1374 merged_lr, merged_rl,
1375 "merge_two_context_entries must canonicalize regardless of operand order"
1376 );
1377
1378 let shared = PredictionContext::singleton(Rc::clone(&empty), 30);
1384 let left_array =
1385 PredictionContext::array(vec![Rc::clone(&parent_a), Rc::clone(&shared)], vec![7, 30]);
1386 let right_array = PredictionContext::array(vec![Rc::clone(&parent_b), shared], vec![7, 30]);
1387 let merged_arrays_lr =
1388 PredictionContext::merge(Rc::clone(&left_array), Rc::clone(&right_array));
1389 let merged_arrays_rl = PredictionContext::merge(right_array, left_array);
1390 assert_eq!(
1391 merged_arrays_lr, merged_arrays_rl,
1392 "merge_arrays must canonicalize colliding-hash entries to one order"
1393 );
1394 assert_eq!(merged_arrays_lr.len(), 3, "two rs=7 entries + one rs=30");
1395 }
1396
1397 #[test]
1398 fn prediction_context_cache_reuses_equal_context_graphs() {
1399 let mut cache = PredictionContextCache::new();
1400 let left_parent = PredictionContext::singleton(PredictionContext::empty(), 1);
1401 let right_parent = PredictionContext::singleton(PredictionContext::empty(), 1);
1402 let left = PredictionContext::singleton(left_parent, 42);
1403 let right = PredictionContext::singleton(right_parent, 42);
1404
1405 let cached_left = cache.get_cached_context(&left);
1406 let cached_right = cache.get_cached_context(&right);
1407 let cached_left_parent = cached_left.parent(0).expect("singleton parent");
1408 let cached_right_parent = cached_right.parent(0).expect("singleton parent");
1409
1410 assert!(Rc::ptr_eq(&cached_left, &cached_right));
1411 assert!(Rc::ptr_eq(&cached_left_parent, &cached_right_parent));
1412 }
1413
1414 #[test]
1415 fn config_set_optimize_contexts_canonicalizes_contexts() {
1416 let mut cache = PredictionContextCache::new();
1417 let first = PredictionContext::singleton(PredictionContext::empty(), 7);
1418 let second = PredictionContext::singleton(PredictionContext::empty(), 7);
1419 let mut set = AtnConfigSet::new();
1420 set.add(AtnConfig::new(1, 1, first));
1421 set.add(AtnConfig::new(2, 2, second));
1422
1423 set.optimize_contexts(&mut cache);
1424
1425 assert!(Rc::ptr_eq(&set.configs[0].context, &set.configs[1].context));
1426 }
1427}