metrale_grammar/fsm/builder/
trie.rs1use ahash::AHashSet;
7use std::collections::BTreeSet;
8
9use crate::fsm::edge::{FsmEdge, cmp_edge_range, edge_type};
10use crate::fsm::fsm::{Fsm, NO_NEXT_STATE};
11use crate::fsm::with_start_end::FsmWithStartEnd;
12
13pub struct TrieBuildResult {
15 pub fsm: FsmWithStartEnd,
17 pub end_states: Vec<i32>,
19}
20
21pub fn build_trie(
30 patterns: &[&[u8]],
31 excluded_patterns: &[&[u8]],
32 allow_overlap: bool,
33 add_back_edges: bool,
34) -> Option<TrieBuildResult> {
35 let mut fsm = Fsm::with_states(1);
36 let start = 0usize;
37 let mut ends: AHashSet<i32> = AHashSet::new();
38 let mut end_states: Vec<i32> = Vec::new();
39
40 for pattern in patterns {
41 if !allow_overlap && pattern.is_empty() {
42 return None;
43 }
44 let mut current = 0i32;
45 for &ch in *pattern {
46 let ch16 = ch as i16;
47 let mut next = fsm.next_state(current as usize, ch16 as i32, edge_type::CHAR_RANGE);
48 if next == NO_NEXT_STATE {
49 next = fsm.add_state() as i32;
50 fsm.add_edge(current as usize, next as usize, ch16, ch16);
51 }
52 current = next;
53 if !allow_overlap && ends.contains(¤t) {
54 return None;
55 }
56 }
57 if !allow_overlap && !fsm.edges(current as usize).is_empty() {
58 return None;
59 }
60 ends.insert(current);
61 end_states.push(current);
62 }
63
64 let mut dead_states: AHashSet<i32> = AHashSet::new();
65
66 if add_back_edges {
67 for excluded in excluded_patterns {
68 if !allow_overlap && excluded.is_empty() {
69 return None;
70 }
71 let mut current = 0i32;
72 for &ch in *excluded {
73 let ch16 = ch as i16;
74 let mut next = fsm.next_state(current as usize, ch16 as i32, edge_type::CHAR_RANGE);
75 if next == NO_NEXT_STATE {
76 next = fsm.add_state() as i32;
77 fsm.add_edge(current as usize, next as usize, ch16, ch16);
78 }
79 current = next;
80 if !allow_overlap && ends.contains(¤t) {
81 return None;
82 }
83 }
84 if !allow_overlap && !fsm.edges(current as usize).is_empty() {
85 return None;
86 }
87 ends.insert(current);
88 dead_states.insert(current);
89 }
90
91 add_back_edges_to_fsm(&mut fsm, start, &ends);
92
93 if !dead_states.is_empty() {
95 for state in 0..fsm.num_states() {
96 let kept: Vec<FsmEdge> = fsm
97 .edges(state)
98 .iter()
99 .copied()
100 .filter(|e| !dead_states.contains(&e.target))
101 .collect();
102 *fsm.edges_mut(state) = kept;
103 }
104 }
105 }
106
107 let mut is_end_state = vec![false; fsm.num_states()];
108 for &end in &ends {
109 is_end_state[end as usize] = true;
110 }
111 Some(TrieBuildResult {
112 fsm: FsmWithStartEnd::new(fsm, start, is_end_state, false),
113 end_states,
114 })
115}
116
117fn add_back_edges_to_fsm(fsm: &mut Fsm, start: usize, ends: &AHashSet<i32>) {
119 for i in 0..fsm.num_states() {
120 if i == start || ends.contains(&(i as i32)) {
121 continue;
122 }
123 let mut edge_set: BTreeSet<OrdRange> = fsm.edges(i).iter().map(|e| OrdRange(*e)).collect();
124
125 for root_edge in fsm.edges(start) {
127 edge_set.insert(OrdRange(*root_edge));
128 }
129 fill_range_gaps(&mut edge_set, start);
131
132 let new_edges: Vec<FsmEdge> = edge_set.iter().map(|o| o.0).collect();
133 *fsm.edges_mut(i) = new_edges;
134 }
135
136 let mut start_set: BTreeSet<OrdRange> = fsm.edges(start).iter().map(|e| OrdRange(*e)).collect();
138 fill_range_gaps(&mut start_set, start);
139 *fsm.edges_mut(start) = start_set.iter().map(|o| o.0).collect();
140}
141
142fn fill_range_gaps(edge_set: &mut BTreeSet<OrdRange>, start: usize) {
145 edge_set.insert(OrdRange(FsmEdge::new(-1, -1, 0)));
147 edge_set.insert(OrdRange(FsmEdge::new(256, 256, 0)));
148
149 let ordered: Vec<FsmEdge> = edge_set.iter().map(|o| o.0).collect();
150 let mut to_insert: Vec<FsmEdge> = Vec::new();
151 for w in ordered.windows(2) {
152 let prev = w[0];
153 let cur = w[1];
154 if prev.max + 1 != cur.min {
155 to_insert.push(FsmEdge::new(prev.max + 1, cur.min - 1, start as i32));
156 }
157 }
158 for e in to_insert {
159 edge_set.insert(OrdRange(e));
160 }
161 edge_set.remove(&OrdRange(FsmEdge::new(-1, -1, 0)));
162 edge_set.remove(&OrdRange(FsmEdge::new(256, 256, 0)));
163}
164
165#[derive(Clone, Copy)]
169struct OrdRange(FsmEdge);
170impl PartialEq for OrdRange {
171 fn eq(&self, other: &Self) -> bool {
172 cmp_edge_range(&self.0, &other.0) == std::cmp::Ordering::Equal
173 }
174}
175impl Eq for OrdRange {}
176impl PartialOrd for OrdRange {
177 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
178 Some(self.cmp(other))
179 }
180}
181impl Ord for OrdRange {
182 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
183 cmp_edge_range(&self.0, &other.0)
184 }
185}
186
187#[cfg(test)]
188#[path = "trie_tests.rs"]
189mod tests;