use crate::graph::{Graph, Disambiguate, NodeId, Range};
#[derive(Clone)]
pub struct Fork {
lut: Box<[Option<NodeId>; 256]>,
pub miss: Option<NodeId>,
}
impl Fork {
pub fn new() -> Self {
Fork {
lut: Box::new([None; 256]),
miss: None,
}
}
pub fn miss<M>(mut self, miss: M) -> Self
where
M: Into<Option<NodeId>>,
{
self.miss = miss.into();
self
}
pub fn add_branch<R, T>(&mut self, range: R, then: NodeId, graph: &mut Graph<T>)
where
R: Into<Range>,
T: Disambiguate,
{
for byte in range.into() {
match &mut self.lut[byte as usize] {
Some(other) if *other != then => {
*other = graph.merge(*other, then);
},
opt => *opt = Some(then),
}
}
}
pub fn merge<T>(&mut self, other: Fork, graph: &mut Graph<T>)
where
T: Disambiguate,
{
self.miss = match (self.miss, other.miss) {
(None, None) => None,
(Some(id), None) | (None, Some(id)) => Some(id),
(Some(a), Some(b)) => Some(graph.merge(a, b)),
};
for (left, right) in self.lut.iter_mut().zip(other.lut.iter()) {
*left = match (*left, *right) {
(None, None) => continue,
(Some(id), None) | (None, Some(id)) => Some(id),
(Some(a), Some(b)) => Some(graph.merge(a, b)),
}
}
}
pub fn branches(&self) -> ForkIter<'_> {
ForkIter {
offset: 0,
lut: &*self.lut,
}
}
pub fn contains<R>(&self, range: R) -> Option<NodeId>
where
R: Into<Range>,
{
let mut range = range.into();
let byte = range.next()?;
let first = self.lut[byte as usize]?;
for byte in range {
if first != self.lut[byte as usize]? {
return None;
}
}
Some(first)
}
pub fn branch<R>(mut self, range: R, then: NodeId) -> Self
where
R: Into<Range>,
{
for byte in range.into() {
match &mut self.lut[byte as usize] {
Some(other) if *other != then => {
panic!("Overlapping branches");
},
opt => *opt = Some(then),
}
}
self
}
pub fn shake<T>(&self, graph: &Graph<T>, filter: &mut [bool]) {
if let Some(id) = self.miss {
if !filter[id.get()] {
filter[id.get()] = true;
graph[id].shake(graph, filter);
}
}
for (_, id) in self.branches() {
if !filter[id.get()] {
filter[id.get()] = true;
graph[id].shake(graph, filter);
}
}
}
}
pub struct ForkIter<'a> {
offset: usize,
lut: &'a [Option<NodeId>; 256],
}
impl<'a> Iterator for ForkIter<'a> {
type Item = (Range, NodeId);
fn next(&mut self) -> Option<Self::Item> {
self.offset += self.lut[self.offset..]
.iter()
.take_while(|next| next.is_none())
.count();
let then = self.lut.get(self.offset).copied().flatten()?;
let start = self.offset;
self.offset += self.lut[self.offset..]
.iter()
.take_while(|next| **next == Some(then))
.count();
Some((
Range {
start: start as u8,
end: (self.offset - 1) as u8,
},
then,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::Node;
use pretty_assertions::assert_eq;
#[test]
fn fork_iter() {
let mut buf = [None; 256];
for byte in b'4'..=b'7' {
buf[byte as usize] = Some(NodeId::new(1));
}
for byte in b'a'..=b'd' {
buf[byte as usize] = Some(NodeId::new(2));
}
let iter = ForkIter {
offset: 0,
lut: &buf,
};
assert_eq!(
&[
(Range { start: b'4', end: b'7' }, NodeId::new(1)),
(Range { start: b'a', end: b'd' }, NodeId::new(2)),
],
&*iter.collect::<Vec<_>>(),
);
}
#[test]
fn merge_no_conflict() {
let mut graph = Graph::new();
let leaf1 = graph.push(Node::Leaf("FOO"));
let leaf2 = graph.push(Node::Leaf("BAR"));
let mut fork = Fork::new().branch(b'1', leaf1);
fork.merge(
Fork::new().branch(b'2', leaf2),
&mut graph,
);
assert_eq!(
fork,
Fork::new()
.branch(b'1', leaf1)
.branch(b'2', leaf2)
);
}
#[test]
fn merge_miss_right() {
let mut graph = Graph::new();
let leaf1 = graph.push(Node::Leaf("FOO"));
let leaf2 = graph.push(Node::Leaf("BAR"));
let mut fork = Fork::new().branch(b'1', leaf1);
fork.merge(
Fork::new().miss(leaf2),
&mut graph,
);
assert_eq!(
fork,
Fork::new()
.branch(b'1', leaf1)
.miss(leaf2)
);
}
#[test]
fn merge_miss_left() {
let mut graph = Graph::new();
let leaf1 = graph.push(Node::Leaf("FOO"));
let leaf2 = graph.push(Node::Leaf("BAR"));
let mut fork = Fork::new().miss(leaf1);
fork.merge(
Fork::new().branch(b'2', leaf2),
&mut graph,
);
assert_eq!(
fork,
Fork::new()
.branch(b'2', leaf2)
.miss(leaf1)
);
}
#[test]
fn contains_byte() {
let fork = Fork::new().branch('a'..='z', NodeId::new(42));
assert_eq!(fork.contains(b't'), Some(NodeId::new(42)));
}
#[test]
fn contains_range() {
let fork = Fork::new()
.branch('a'..='m', NodeId::new(42))
.branch('n'..='z', NodeId::new(42));
assert_eq!(fork.contains('i'..='r'), Some(NodeId::new(42)));
assert_eq!(fork.contains('a'..='z'), Some(NodeId::new(42)));
}
#[test]
fn contains_different_ranges() {
let fork = Fork::new()
.branch('a'..='m', NodeId::new(42))
.branch('n'..='z', NodeId::new(47));
assert_eq!(fork.contains('i'..='r'), None);
assert_eq!(fork.contains('a'..='z'), None);
assert_eq!(fork.contains('d'..='f'), Some(NodeId::new(42)));
assert_eq!(fork.contains('n'..='p'), Some(NodeId::new(47)));
}
}