use std::collections::HashMap;
use crate::types::{CaptureEvent, Pos, RuleId, TreeEvent};
#[derive(Clone, Debug)]
pub enum MemoEntry {
Hit {
end_pos: Pos,
events: Box<[CaptureEvent]>,
tree_events: Box<[TreeEvent]>,
},
Miss { furthest: Pos },
}
pub enum MemoQuery {
Unknown,
Miss { furthest: Pos },
Hit {
end_pos: Pos,
events: Vec<CaptureEvent>,
tree_events: Vec<TreeEvent>,
},
}
pub enum MemoReplay {
Unknown,
Miss { furthest: Pos },
Hit { end_pos: Pos },
}
pub struct MemoTable {
table: HashMap<u64, MemoEntry>,
}
impl MemoTable {
#[must_use]
pub fn new() -> Self {
Self {
table: HashMap::with_capacity(4096),
}
}
#[inline]
pub fn clear(&mut self) {
self.table.clear();
}
#[must_use]
#[inline]
pub fn query(&self, rule: RuleId, pos: Pos) -> MemoQuery {
match self.table.get(&pack(rule, pos)) {
None => MemoQuery::Unknown,
Some(MemoEntry::Miss { furthest }) => MemoQuery::Miss {
furthest: *furthest,
},
Some(MemoEntry::Hit {
end_pos,
events,
tree_events,
}) => MemoQuery::Hit {
end_pos: *end_pos,
events: events.to_vec(),
tree_events: tree_events.to_vec(),
},
}
}
#[must_use]
#[inline]
pub fn query_replay(
&self,
rule: RuleId,
pos: Pos,
events: &mut Vec<CaptureEvent>,
tree_events: &mut Vec<TreeEvent>,
) -> MemoReplay {
match self.table.get(&pack(rule, pos)) {
None => MemoReplay::Unknown,
Some(MemoEntry::Miss { furthest }) => MemoReplay::Miss {
furthest: *furthest,
},
Some(MemoEntry::Hit {
end_pos,
events: cached_events,
tree_events: cached_tree,
}) => {
events.extend_from_slice(cached_events);
tree_events.extend_from_slice(cached_tree);
MemoReplay::Hit { end_pos: *end_pos }
}
}
}
#[inline]
pub fn insert_hit(
&mut self,
rule: RuleId,
pos: Pos,
end_pos: Pos,
events: Box<[CaptureEvent]>,
tree_events: Box<[TreeEvent]>,
) {
self.table.insert(
pack(rule, pos),
MemoEntry::Hit {
end_pos,
events,
tree_events,
},
);
}
#[inline]
pub fn insert_miss(&mut self, rule: RuleId, pos: Pos, furthest: Pos) {
self.table
.insert(pack(rule, pos), MemoEntry::Miss { furthest });
}
#[must_use]
pub fn len(&self) -> usize {
self.table.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.table.is_empty()
}
}
impl Default for MemoTable {
fn default() -> Self {
Self::new()
}
}
#[inline]
fn pack(rule: RuleId, pos: Pos) -> u64 {
(u64::from(rule) << 32) | u64::from(pos)
}