hyperbuild/pattern.rs
1// Can't use pub const fn constructor due to Copy trait, so allow directly creating struct publicly for now.
2pub struct TrieNode<V: 'static + Copy> {
3 // Using a children array of size 256 would probably be fastest, but waste too much memory and cause slow compiles
4 // and large binaries. Instead, we only store the children between the first and last defined (see `gen/trie.ts`).
5 // When getting a child, use `index - offset`.
6 pub offset: usize,
7 pub value: Option<V>,
8 pub children: &'static [Option<&'static TrieNode<V>>],
9}
10
11pub enum TrieNodeMatch<V: 'static + Copy> {
12 Found { len: usize, value: V },
13 NotFound { reached: usize },
14}
15
16impl<V: 'static + Copy> TrieNode<V> {
17 // Find the node that matches the shortest prefix of {@param text} that:
18 // - has a value (except the start node if it has a value);
19 // - fails to match any further characters (the node itself matches); or,
20 // - the entire text (essentially same as previous point).
21 //
22 // For example, given a trie with only two paths "&" and "&":
23 // - "&" will return node `p`.
24 // - "&ere" will return node `p`.
25 // - "&" will return node `p`.
26 // - "&ere" will return node `p`.
27 // - "&am" will return node `m`.
28 // - Further matching "p;" will return node `p`.
29 // - Further matching "xyz" will return node `m` (itself).
30 // - "&amx" will return node `m`.
31 // - "&ax" will return node `a`.
32 // - "+ax" will return itself.
33 // - "" will return the itself.
34 #[inline(always)]
35 pub fn shortest_matching_prefix(&self, text: &[u8], from: usize) -> (&TrieNode<V>, usize) {
36 let mut node: &TrieNode<V> = self;
37 let mut pos = from;
38 while let Some(&c) = text.get(pos) {
39 match node.children.get((c as usize).wrapping_sub(node.offset)) {
40 Some(Some(child)) => node = child,
41 None | Some(None) => break,
42 };
43 pos += 1;
44 if node.value.is_some() {
45 break;
46 };
47 };
48 (node, pos)
49 }
50
51 #[inline(always)]
52 pub fn longest_matching_prefix(&self, text: &[u8]) -> TrieNodeMatch<V> {
53 let mut node: &TrieNode<V> = self;
54 let mut value: Option<TrieNodeMatch<V>> = None;
55 let mut pos = 0;
56 while let Some(&c) = text.get(pos) {
57 match node.children.get((c as usize).wrapping_sub(node.offset)) {
58 Some(Some(child)) => node = child,
59 None | Some(None) => break,
60 };
61 pos += 1;
62 match node.value {
63 Some(v) => value = Some(TrieNodeMatch::Found { len: pos, value: v }),
64 None => {}
65 };
66 };
67 value.unwrap_or(TrieNodeMatch::NotFound { reached: pos })
68 }
69}