use std::cmp::min; use std::collections::BTreeMap; use std::ops::{Index, IndexMut}; use crate::graph::{Graph, Node, NodeId}; #[derive(Debug)] pub struct Meta { map: BTreeMap, } #[derive(Debug, Default)] pub struct MetaItem { /// Number of references to this node pub refcount: usize, /// Minimum number of bytes that ought to be read for this /// node to find a match pub min_read: usize, /// Marks whether or not this node leads to a loop entry node. pub is_loop_init: bool, /// Ids of other nodes that point to this node while this /// node is on a stack (creating a loop) pub loop_entry_from: Vec, } impl Index for Meta { type Output = MetaItem; fn index(&self, id: NodeId) -> &MetaItem { &self.map[&id] } } impl IndexMut for Meta { fn index_mut(&mut self, id: NodeId) -> &mut MetaItem { self.map.entry(id).or_default() } } impl MetaItem { fn loop_entry(&mut self, id: NodeId) { if let Err(idx) = self.loop_entry_from.binary_search(&id) { self.loop_entry_from.insert(idx, id); } } } impl Meta { pub fn analyze(root: NodeId, graph: &Graph) -> Self { let mut meta = Meta { map: Default::default(), }; meta.first_pass(root, root, graph, &mut Vec::new()); meta } pub fn first_pass( &mut self, this: NodeId, parent: NodeId, graph: &Graph, stack: &mut Vec, ) -> &MetaItem { let meta = &mut self[this]; let is_done = meta.refcount > 0; meta.refcount += 1; if stack.contains(&this) { meta.loop_entry(parent); self[parent].is_loop_init = true; } if is_done { return &self[this]; } stack.push(this); let mut min_read; match &graph[this] { Node::Fork(fork) => { min_read = usize::MAX; for (_, id) in fork.branches() { let meta = self.first_pass(id, this, graph, stack); if meta.is_loop_init { min_read = 1; } else { min_read = min(min_read, meta.min_read + 1); } } if let Some(id) = fork.miss { let meta = self.first_pass(id, this, graph, stack); if meta.is_loop_init { min_read = 0; } else { min_read = min(min_read, meta.min_read); } } if min_read == usize::MAX { min_read = 0; } } Node::Rope(rope) => { min_read = rope.pattern.len(); let meta = self.first_pass(rope.then, this, graph, stack); if !meta.is_loop_init { min_read += meta.min_read; } if let Some(id) = rope.miss.first() { let meta = self.first_pass(id, this, graph, stack); if meta.is_loop_init { min_read = 0; } else { min_read = min(min_read, meta.min_read); } } } Node::Leaf(_) => min_read = 0, } stack.pop(); let meta = &mut self[this]; meta.min_read = min_read; let second_pass = meta.loop_entry_from.clone(); for id in second_pass { self.meta_second_pass(id, graph); } &self[this] } fn meta_second_pass(&mut self, id: NodeId, graph: &Graph) { let mut min_read; match &graph[id] { Node::Fork(fork) => { min_read = usize::MAX; for (_, id) in fork.branches() { let meta = &self[id]; if meta.is_loop_init { min_read = 1; } else { min_read = min(min_read, meta.min_read + 1); } } if min_read == usize::MAX { min_read = 0; } } Node::Rope(rope) => { min_read = rope.pattern.len(); let meta = &self[rope.then]; if !meta.is_loop_init { min_read += meta.min_read; } } Node::Leaf(_) => unreachable!(), } self[id].min_read = min_read; } }