1use std::{collections::HashMap, fmt::Debug, iter, str};
2
3use regex::bytes::Regex;
4
5pub type Key = Vec<u8>;
6pub type KeyValue<K, V> = (K, V);
7
8#[derive(Debug, PartialEq, Eq)]
9pub enum InsertResult {
10 Ok,
11 Existing,
12 Failed,
13}
14
15#[derive(Debug, PartialEq, Eq)]
16pub enum RemoveResult {
17 Ok,
18 NotFound,
19}
20
21fn find_last_dot(input: &[u8]) -> Option<usize> {
22 (0..input.len()).rev().find(|&i| input[i] == b'.')
24}
25
26fn find_last_slash(input: &[u8]) -> Option<usize> {
27 (0..input.len()).rev().find(|&i| input[i] == b'/')
29}
30
31#[derive(Debug, Default)]
38pub struct TrieNode<V> {
39 key_value: Option<KeyValue<Key, V>>,
40 wildcard: Option<KeyValue<Key, V>>,
41 children: HashMap<Key, TrieNode<V>>,
42 regexps: Vec<(Regex, TrieNode<V>)>,
43}
44
45#[derive(Debug)]
52pub enum TrieSubMatch<'a, 'b> {
53 Wildcard(&'a [u8]),
54 Regexp(&'a [u8], &'b Regex),
55}
56
57pub type TrieMatches<'a, 'b> = Vec<TrieSubMatch<'a, 'b>>;
62
63impl<V: PartialEq> std::cmp::PartialEq for TrieNode<V> {
64 fn eq(&self, other: &Self) -> bool {
65 self.key_value == other.key_value
66 && self.wildcard == other.wildcard
67 && self.children == other.children
68 && self.regexps.len() == other.regexps.len()
69 && self
70 .regexps
71 .iter()
72 .zip(other.regexps.iter())
73 .fold(true, |b, (left, right)| {
74 b && left.0.as_str() == right.0.as_str() && left.1 == right.1
75 })
76 }
77}
78
79impl<V: Debug + Clone> TrieNode<V> {
80 pub fn new(key: Key, value: V) -> TrieNode<V> {
81 TrieNode {
82 key_value: Some((key, value)),
83 wildcard: None,
84 children: HashMap::new(),
85 regexps: Vec::new(),
86 }
87 }
88
89 pub fn wildcard(key: Key, value: V) -> TrieNode<V> {
90 TrieNode {
91 key_value: None,
92 wildcard: Some((key, value)),
93 children: HashMap::new(),
94 regexps: Vec::new(),
95 }
96 }
97
98 pub fn root() -> TrieNode<V> {
99 TrieNode {
100 key_value: None,
101 wildcard: None,
102 children: HashMap::new(),
103 regexps: Vec::new(),
104 }
105 }
106
107 pub fn is_empty(&self) -> bool {
108 self.key_value.is_none()
109 && self.wildcard.is_none()
110 && self.regexps.is_empty()
111 && self.children.is_empty()
112 }
113
114 pub fn insert(&mut self, key: Key, value: V) -> InsertResult {
115 if key.is_empty() {
117 return InsertResult::Failed;
118 }
119 if key[..] == b"."[..] {
120 return InsertResult::Failed;
121 }
122
123 #[cfg(debug_assertions)]
124 let before = self.count_values();
125
126 let insert_result = self.insert_recursive(&key, &key, value);
127
128 #[cfg(debug_assertions)]
147 {
148 let after = self.count_values();
149 match insert_result {
150 InsertResult::Ok => debug_assert_eq!(
151 after,
152 before + 1,
153 "a fresh insert must add exactly one value to the trie",
154 ),
155 InsertResult::Existing => debug_assert_eq!(
156 after, before,
157 "an Existing insert must not change the trie value count",
158 ),
159 InsertResult::Failed => debug_assert_eq!(
160 after, before,
161 "a failed insert must leave the trie value count untouched",
162 ),
163 }
164 self.check_invariants();
165 }
166
167 insert_result
168 }
169
170 pub fn insert_recursive(&mut self, partial_key: &[u8], key: &Key, value: V) -> InsertResult {
171 if partial_key.is_empty() {
178 return InsertResult::Failed;
179 }
180 debug_assert!(
184 partial_key.len() <= key.len(),
185 "insert recursion must consume the key, never grow past it",
186 );
187
188 if partial_key[partial_key.len() - 1] == b'/' {
189 let pos = find_last_slash(&partial_key[..partial_key.len() - 1]);
190
191 if let Some(pos) = pos {
192 if pos > 0 && partial_key[pos - 1] != b'.' {
193 return InsertResult::Failed;
194 }
195
196 if let Ok(s) = str::from_utf8(&partial_key[pos + 1..partial_key.len() - 1]) {
197 let anchored_s = format!("\\A{s}\\z");
198 debug_assert!(
199 anchored_s.starts_with("\\A") && anchored_s.ends_with("\\z"),
200 "segment regex must be fully anchored so it matches the whole segment only",
201 );
202 for t in self.regexps.iter_mut() {
203 if t.0.as_str() == anchored_s {
204 if pos > 0 {
216 return t.1.insert_recursive(&partial_key[..pos - 1], key, value);
217 } else {
218 return InsertResult::Existing;
219 }
220 }
221 }
222
223 let anchored = format!("\\A{s}\\z");
228 if let Ok(r) = Regex::new(&anchored) {
229 if pos > 0 {
230 let mut node = TrieNode::root();
231 let pos = pos - 1;
232
233 let res = node.insert_recursive(&partial_key[..pos], key, value);
234
235 if res == InsertResult::Ok {
236 self.regexps.push((r, node));
237 }
238
239 return res;
240 } else {
241 let node = TrieNode::new(key.to_vec(), value);
242 self.regexps.push((r, node));
243 return InsertResult::Ok;
244 }
245 }
246 }
247 }
248
249 return InsertResult::Failed;
250 }
251
252 let pos = find_last_dot(partial_key);
253 match pos {
254 None => {
255 if self.children.contains_key(partial_key) {
256 InsertResult::Existing
257 } else if partial_key == &b"*"[..] {
258 if self.wildcard.is_some() {
259 InsertResult::Existing
260 } else {
261 self.wildcard = Some((key.to_vec(), value));
262 InsertResult::Ok
263 }
264 } else {
265 let node = TrieNode::new(key.to_vec(), value);
266 self.children.insert(partial_key.to_vec(), node);
267 InsertResult::Ok
268 }
269 }
270 Some(pos) => {
271 debug_assert_eq!(
275 partial_key[..pos].len() + partial_key[pos..].len(),
276 partial_key.len(),
277 "dot-split must partition partial_key without losing bytes",
278 );
279 debug_assert_eq!(
280 partial_key[pos], b'.',
281 "find_last_dot must point at a '.' byte",
282 );
283 if let Some(child) = self.children.get_mut(&partial_key[pos..]) {
284 return child.insert_recursive(&partial_key[..pos], key, value);
285 }
286
287 let mut node = TrieNode::root();
288 let res = node.insert_recursive(&partial_key[..pos], key, value);
289
290 if res == InsertResult::Ok {
291 self.children.insert(partial_key[pos..].to_vec(), node);
292 }
293
294 res
295 }
296 }
297 }
298
299 pub fn remove(&mut self, key: &Key) -> RemoveResult {
300 #[cfg(debug_assertions)]
301 let before = self.count_values();
302
303 let remove_result = self.remove_recursive(key);
304
305 #[cfg(debug_assertions)]
309 {
310 let after = self.count_values();
311 match remove_result {
312 RemoveResult::Ok => debug_assert_eq!(
313 after + 1,
314 before,
315 "a successful remove must drop exactly one value from the trie",
316 ),
317 RemoveResult::NotFound => debug_assert_eq!(
318 after, before,
319 "a NotFound remove must not change the trie value count",
320 ),
321 }
322 self.check_invariants();
323 }
324
325 remove_result
326 }
327
328 pub fn remove_recursive(&mut self, partial_key: &[u8]) -> RemoveResult {
329 if partial_key.is_empty() {
332 if self.key_value.is_some() {
333 self.key_value = None;
334 return RemoveResult::Ok;
335 } else {
336 return RemoveResult::NotFound;
337 }
338 }
339
340 if partial_key == &b"*"[..] {
341 if self.wildcard.is_some() {
342 self.wildcard = None;
343 return RemoveResult::Ok;
344 } else {
345 return RemoveResult::NotFound;
346 }
347 }
348
349 if partial_key[partial_key.len() - 1] == b'/' {
350 let pos = find_last_slash(&partial_key[..partial_key.len() - 1]);
351
352 if let Some(pos) = pos {
353 if pos > 0 && partial_key[pos - 1] != b'.' {
354 return RemoveResult::NotFound;
355 }
356
357 if let Ok(s) = str::from_utf8(&partial_key[pos + 1..partial_key.len() - 1]) {
358 let anchored_s = format!("\\A{s}\\z");
359 if pos > 0 {
360 let mut remove_result = RemoveResult::NotFound;
361 for t in self.regexps.iter_mut() {
362 if t.0.as_str() == anchored_s
363 && t.1.remove_recursive(&partial_key[..pos - 1]) == RemoveResult::Ok
364 {
365 remove_result = RemoveResult::Ok;
366 }
367 }
368 return remove_result;
369 } else {
370 let len = self.regexps.len();
371 self.regexps.retain(|(r, _)| r.as_str() != anchored_s);
372 if len > self.regexps.len() {
373 return RemoveResult::Ok;
374 }
375 }
376 }
377 }
378
379 return RemoveResult::NotFound;
380 }
381
382 let pos = find_last_dot(partial_key);
383 let (prefix, suffix) = match pos {
384 None => (&b""[..], partial_key),
385 Some(pos) => (&partial_key[..pos], &partial_key[pos..]),
386 };
387 debug_assert_eq!(
389 prefix.len() + suffix.len(),
390 partial_key.len(),
391 "dot-split must partition the key without losing or duplicating bytes",
392 );
393
394 match self.children.get_mut(suffix) {
395 Some(child) => match child.remove_recursive(prefix) {
396 RemoveResult::NotFound => RemoveResult::NotFound,
397 RemoveResult::Ok => {
398 if child.is_empty() {
402 self.children.remove(suffix);
403 debug_assert!(
404 !self.children.contains_key(suffix),
405 "an emptied child subtree must be removed from the parent",
406 );
407 } else {
408 #[cfg(debug_assertions)]
412 debug_assert!(
413 child.count_values() > 0,
414 "a retained child subtree must still hold at least one value",
415 );
416 }
417 RemoveResult::Ok
418 }
419 },
420 None => RemoveResult::NotFound,
421 }
422 }
423
424 pub fn lookup_with_path<'a, 'b>(
433 &'b self,
434 partial_key: &'a [u8],
435 accept_wildcard: bool,
436 mut trace: TrieMatches<'a, 'b>,
437 ) -> Option<(&'b KeyValue<Key, V>, TrieMatches<'a, 'b>)> {
438 if partial_key.is_empty() {
439 return self.key_value.as_ref().map(|kv| (kv, trace));
440 }
441
442 let pos = find_last_dot(partial_key);
443 let (prefix, suffix) = match pos {
444 None => (&b""[..], partial_key),
445 Some(pos) => (&partial_key[..pos], &partial_key[pos..]),
446 };
447 debug_assert_eq!(
451 prefix.len() + suffix.len(),
452 partial_key.len(),
453 "dot-split must partition the key without losing or duplicating bytes",
454 );
455 debug_assert!(
456 pos.is_none() || suffix.first() == Some(&b'.'),
457 "a dotted split must place the separator at the head of the suffix",
458 );
459
460 match self.children.get(suffix) {
461 Some(child) => child.lookup_with_path(prefix, accept_wildcard, trace),
462 None => {
463 if prefix.is_empty() && self.wildcard.is_some() && accept_wildcard {
464 let segment = if !suffix.is_empty() && suffix[0] == b'.' {
465 &suffix[1..]
466 } else {
467 suffix
468 };
469 trace.push(TrieSubMatch::Wildcard(segment));
470 self.wildcard.as_ref().map(|kv| (kv, trace))
471 } else {
472 for (regexp, child) in self.regexps.iter() {
473 let segment = if !suffix.is_empty() && suffix[0] == b'.' {
474 &suffix[1..]
475 } else {
476 suffix
477 };
478 if regexp.is_match(segment) {
479 let mut next = trace;
480 next.push(TrieSubMatch::Regexp(segment, regexp));
481 return child.lookup_with_path(prefix, accept_wildcard, next);
482 }
483 }
484 None
485 }
486 }
487 }
488 }
489
490 pub fn lookup(&self, partial_key: &[u8], accept_wildcard: bool) -> Option<&KeyValue<Key, V>> {
491 if partial_key.is_empty() {
494 return self.key_value.as_ref();
495 }
496
497 let pos = find_last_dot(partial_key);
498 let (prefix, suffix) = match pos {
499 None => (&b""[..], partial_key),
500 Some(pos) => (&partial_key[..pos], &partial_key[pos..]),
501 };
502 debug_assert_eq!(
504 prefix.len() + suffix.len(),
505 partial_key.len(),
506 "dot-split must partition the key without losing or duplicating bytes",
507 );
508 debug_assert!(
509 !suffix.is_empty(),
510 "the suffix the trie matches children against must be non-empty",
511 );
512
513 match self.children.get(suffix) {
514 Some(child) => child.lookup(prefix, accept_wildcard),
515 None => {
516 if prefix.is_empty() && self.wildcard.is_some() && accept_wildcard {
519 self.wildcard.as_ref()
521 } else {
522 for (regexp, child) in self.regexps.iter() {
525 let suffix = if suffix[0] == b'.' {
526 &suffix[1..]
527 } else {
528 suffix
529 };
530 if regexp.is_match(suffix) {
533 return child.lookup(prefix, accept_wildcard);
535 }
536 }
537
538 None
539 }
540 }
541 }
542 }
543
544 pub fn lookup_mut(
545 &mut self,
546 partial_key: &[u8],
547 accept_wildcard: bool,
548 ) -> Option<&mut KeyValue<Key, V>> {
549 if partial_key.is_empty() {
552 return self.key_value.as_mut();
553 }
554
555 if partial_key == &b"*"[..] {
556 return self.wildcard.as_mut();
557 }
558
559 if partial_key[partial_key.len() - 1] == b'/' {
560 let pos = find_last_slash(&partial_key[..partial_key.len() - 1]);
561
562 if let Some(pos) = pos {
563 if pos > 0 && partial_key[pos - 1] != b'.' {
564 return None;
565 }
566
567 if let Ok(s) = str::from_utf8(&partial_key[pos + 1..partial_key.len() - 1]) {
568 let anchored_s = format!("\\A{s}\\z");
569 for t in self.regexps.iter_mut() {
570 if t.0.as_str() == anchored_s {
571 let rest = if pos > 0 {
580 &partial_key[..pos - 1]
581 } else {
582 &partial_key[..0]
583 };
584 return t.1.lookup_mut(rest, accept_wildcard);
585 }
586 }
587 }
588 }
589
590 return None;
591 }
592
593 let pos = find_last_dot(partial_key);
594 let (prefix, suffix) = match pos {
595 None => (&b""[..], partial_key),
596 Some(pos) => (&partial_key[..pos], &partial_key[pos..]),
597 };
598 debug_assert_eq!(
600 prefix.len() + suffix.len(),
601 partial_key.len(),
602 "dot-split must partition the key without losing or duplicating bytes",
603 );
604 debug_assert!(
605 !suffix.is_empty(),
606 "the suffix the trie matches children against must be non-empty",
607 );
608
609 match self.children.get_mut(suffix) {
610 Some(child) => child.lookup_mut(prefix, accept_wildcard),
611 None => {
612 if prefix.is_empty() && self.wildcard.is_some() && accept_wildcard {
615 self.wildcard.as_mut()
617 } else {
618 for &mut (ref regexp, ref mut child) in self.regexps.iter_mut() {
621 let suffix = if suffix[0] == b'.' {
622 &suffix[1..]
623 } else {
624 suffix
625 };
626 if regexp.is_match(suffix) {
629 return child.lookup_mut(prefix, accept_wildcard);
631 }
632 }
633
634 None
635 }
636 }
637 }
638 }
639
640 pub fn print(&self) {
641 self.print_recursive(b"", 0)
642 }
643
644 pub fn print_recursive(&self, partial_key: &[u8], indent: u8) {
645 let raw_prefix: Vec<u8> = iter::repeat_n(b' ', 2 * indent as usize).collect();
646 let prefix = str::from_utf8(&raw_prefix).unwrap();
647
648 print!("{}{}: ", prefix, str::from_utf8(partial_key).unwrap());
649 if let Some((ref key, ref value)) = self.key_value {
650 print!("({}, {:?}) | ", str::from_utf8(key).unwrap(), value);
651 } else {
652 print!("None | ");
653 }
654
655 if let Some((key, value)) = &self.wildcard {
656 println!("({}, {:?})", str::from_utf8(key).unwrap(), value);
657 } else {
658 println!("None");
659 }
660
661 for (child_key, child) in self.children.iter() {
662 child.print_recursive(child_key, indent + 1);
663 }
664
665 for (regexp, child) in self.regexps.iter() {
666 child.print_recursive(regexp.as_str().as_bytes(), indent + 1);
668 }
669 }
670
671 pub fn for_each_value_mut<F: FnMut(&mut V)>(&mut self, f: &mut F) {
677 if let Some((_, ref mut value)) = self.key_value {
678 f(value);
679 }
680 if let Some((_, ref mut value)) = self.wildcard {
681 f(value);
682 }
683 for child in self.children.values_mut() {
684 child.for_each_value_mut(f);
685 }
686 for (_, child) in self.regexps.iter_mut() {
687 child.for_each_value_mut(f);
688 }
689 }
690
691 #[cfg(debug_assertions)]
697 fn count_values(&self) -> usize {
698 let local = self.key_value.is_some() as usize + self.wildcard.is_some() as usize;
699 let in_children: usize = self.children.values().map(TrieNode::count_values).sum();
700 let in_regexps: usize = self.regexps.iter().map(|(_, c)| c.count_values()).sum();
701 local + in_children + in_regexps
702 }
703
704 #[cfg(debug_assertions)]
721 fn check_invariants(&self) {
722 for i in 0..self.regexps.len() {
724 for j in (i + 1)..self.regexps.len() {
725 debug_assert_ne!(
726 self.regexps[i].0.as_str(),
727 self.regexps[j].0.as_str(),
728 "trie node must not hold two subtrees for the same regex segment",
729 );
730 }
731 }
732
733 for (child_key, child) in self.children.iter() {
734 debug_assert!(
735 !child_key.is_empty(),
736 "trie child must not be keyed by the empty segment",
737 );
738 debug_assert!(
742 !child.is_empty(),
743 "trie must not strand an empty child subtree (remove must prune)",
744 );
745 debug_assert!(
746 child.count_values() > 0,
747 "trie child subtree must lead to at least one value",
748 );
749 child.check_invariants();
750 }
751
752 for (_, child) in self.regexps.iter() {
753 debug_assert!(
754 !child.is_empty(),
755 "trie must not strand an empty regex subtree (remove must prune)",
756 );
757 debug_assert!(
758 child.count_values() > 0,
759 "trie regex subtree must lead to at least one value",
760 );
761 child.check_invariants();
762 }
763 }
764
765 pub fn domain_insert(&mut self, key: Key, value: V) -> InsertResult {
766 self.insert(key, value)
767 }
768
769 pub fn domain_remove(&mut self, key: &Key) -> RemoveResult {
770 self.remove(key)
771 }
772
773 pub fn domain_lookup(&self, key: &[u8], accept_wildcard: bool) -> Option<&KeyValue<Key, V>> {
774 self.lookup(key, accept_wildcard)
775 }
776
777 pub fn domain_lookup_mut(
778 &mut self,
779 key: &[u8],
780 accept_wildcard: bool,
781 ) -> Option<&mut KeyValue<Key, V>> {
782 self.lookup_mut(key, accept_wildcard)
783 }
784
785 pub fn size(&self) -> usize {
786 ::std::mem::size_of::<TrieNode<V>>()
787 + ::std::mem::size_of::<Option<KeyValue<Key, V>>>() * 2
788 + self
789 .children
790 .iter()
791 .fold(0, |acc, c| acc + c.0.len() + c.1.size())
792 }
793
794 pub fn to_hashmap(&self) -> HashMap<Key, V> {
795 let mut h = HashMap::new();
796
797 self.to_hashmap_recursive(&mut h);
798
799 h
800 }
801
802 pub fn to_hashmap_recursive(&self, h: &mut HashMap<Key, V>) {
803 if let Some((key, value)) = &self.key_value {
804 h.insert(key.clone(), value.clone());
805 }
806
807 if let Some((key, value)) = &self.wildcard {
808 h.insert(key.clone(), value.clone());
809 }
810
811 for child in self.children.values() {
812 child.to_hashmap_recursive(h);
813 }
814 }
815}
816
817#[cfg(test)]
818mod tests {
819 use super::*;
820
821 #[test]
822 fn insert() {
823 let mut root: TrieNode<u8> = TrieNode::root();
824 root.print();
825
826 assert_eq!(
827 root.domain_insert(Vec::from(&b"abcd"[..]), 1),
828 InsertResult::Ok
829 );
830 root.print();
831 assert_eq!(
832 root.domain_insert(Vec::from(&b"abce"[..]), 2),
833 InsertResult::Ok
834 );
835 root.print();
836 assert_eq!(
837 root.domain_insert(Vec::from(&b"abgh"[..]), 3),
838 InsertResult::Ok
839 );
840 root.print();
841
842 assert_eq!(
843 root.domain_lookup(&b"abce"[..], true),
844 Some(&(b"abce"[..].to_vec(), 2))
845 );
846 }
848
849 #[test]
850 fn remove() {
851 let mut root: TrieNode<u8> = TrieNode::root();
852 println!("creating root:");
853 root.print();
854
855 println!("adding (abcd, 1)");
856 assert_eq!(root.insert(Vec::from(&b"abcd"[..]), 1), InsertResult::Ok);
857 root.print();
858 println!("adding (abce, 2)");
859 assert_eq!(root.insert(Vec::from(&b"abce"[..]), 2), InsertResult::Ok);
860 root.print();
861 println!("adding (abgh, 3)");
862 assert_eq!(root.insert(Vec::from(&b"abgh"[..]), 3), InsertResult::Ok);
863 root.print();
864
865 let mut root2: TrieNode<u8> = TrieNode::root();
866
867 assert_eq!(root2.insert(Vec::from(&b"abcd"[..]), 1), InsertResult::Ok);
868 assert_eq!(root2.insert(Vec::from(&b"abgh"[..]), 3), InsertResult::Ok);
869
870 println!("before remove");
871 root.print();
872 assert_eq!(root.remove(&Vec::from(&b"abce"[..])), RemoveResult::Ok);
873 println!("after remove");
874 root.print();
875
876 println!("expected");
877 root2.print();
878 assert_eq!(root, root2);
879
880 assert_eq!(root.remove(&Vec::from(&b"abgh"[..])), RemoveResult::Ok);
881 println!("after remove");
882 root.print();
883 println!("expected");
884 let mut root3: TrieNode<u8> = TrieNode::root();
885 assert_eq!(root3.insert(Vec::from(&b"abcd"[..]), 1), InsertResult::Ok);
886 root3.print();
887 assert_eq!(root, root3);
888 }
889
890 #[test]
891 fn insert_remove_through_regex() {
892 let mut root: TrieNode<u8> = TrieNode::root();
893 println!("creating root:");
894 root.print();
895
896 println!("adding (www./.*/.com, 1)");
897 assert_eq!(
898 root.insert(Vec::from(&b"www./.*/.com"[..]), 1),
899 InsertResult::Ok
900 );
901 root.print();
902 println!("adding (www.doc./.*/.com, 2)");
903 assert_eq!(
904 root.insert(Vec::from(&b"www.doc./.*/.com"[..]), 2),
905 InsertResult::Ok
906 );
907 root.print();
908 assert_eq!(
909 root.domain_lookup(b"www.sozu.com".as_ref(), false),
910 Some(&(b"www./.*/.com".to_vec(), 1))
911 );
912 assert_eq!(
913 root.domain_lookup(b"www.doc.sozu.com".as_ref(), false),
914 Some(&(b"www.doc./.*/.com".to_vec(), 2))
915 );
916
917 assert_eq!(
918 root.domain_remove(&b"www./.*/.com".to_vec()),
919 RemoveResult::Ok
920 );
921 root.print();
922 assert_eq!(root.domain_lookup(b"www.sozu.com".as_ref(), false), None);
923 assert_eq!(
924 root.domain_lookup(b"www.doc.sozu.com".as_ref(), false),
925 Some(&(b"www.doc./.*/.com".to_vec(), 2))
926 );
927 }
928
929 #[test]
941 fn insert_rejects_malformed_keys_without_panicking() {
942 for key in [
943 &b""[..],
944 b".",
945 b"..",
946 b"...",
947 b"/",
948 b"///",
949 b"a/",
950 b".a",
951 b".a.b",
952 b"example.com/",
953 b"www.example.com/",
954 b"abc/[0-9]+/.example.com",
956 b"/[/.example.com",
958 b"a/*/",
959 ] {
960 let mut root: TrieNode<u8> = TrieNode::root();
961 assert_eq!(
962 root.insert(key.to_vec(), 1),
963 InsertResult::Failed,
964 "{:?} must be rejected",
965 String::from_utf8_lossy(key),
966 );
967 assert!(
968 root.is_empty(),
969 "{:?} was rejected but still mutated the trie",
970 String::from_utf8_lossy(key),
971 );
972 assert_eq!(root.domain_lookup(key, false), None);
973 assert_eq!(root.domain_lookup(key, true), None);
974 }
975 }
976
977 #[test]
980 fn a_rejected_insert_leaves_existing_entries_intact() {
981 let mut root: TrieNode<u8> = TrieNode::root();
982 assert_eq!(
983 root.insert(b"www.example.com".to_vec(), 1),
984 InsertResult::Ok
985 );
986 assert_eq!(root.insert(b"*.wild.com".to_vec(), 2), InsertResult::Ok);
987 assert_eq!(
988 root.insert(b"www.example.com/".to_vec(), 3),
989 InsertResult::Failed
990 );
991 assert_eq!(
992 root.domain_lookup(b"www.example.com", false),
993 Some(&(b"www.example.com".to_vec(), 1))
994 );
995 assert_eq!(
996 root.domain_lookup(b"any.wild.com", true),
997 Some(&(b"*.wild.com".to_vec(), 2))
998 );
999 }
1000
1001 #[test]
1002 fn segment_regex_rejects_partial_matches() {
1003 let mut root: TrieNode<u8> = TrieNode::root();
1004 assert_eq!(
1007 root.insert(Vec::from(&b"/cdn[0-9]+/.example.com"[..]), 7),
1008 InsertResult::Ok
1009 );
1010
1011 assert_eq!(
1013 root.domain_lookup(b"cdn1.example.com".as_ref(), false),
1014 Some(&(b"/cdn[0-9]+/.example.com".to_vec(), 7))
1015 );
1016 assert_eq!(
1017 root.domain_lookup(b"cdn123.example.com".as_ref(), false),
1018 Some(&(b"/cdn[0-9]+/.example.com".to_vec(), 7))
1019 );
1020
1021 assert_eq!(
1025 root.domain_lookup(b"cdn1xxx.example.com".as_ref(), false),
1026 None
1027 );
1028 assert_eq!(
1030 root.domain_lookup(b"xxxcdn1.example.com".as_ref(), false),
1031 None
1032 );
1033 assert_eq!(
1035 root.domain_lookup(b"cdnabc.example.com".as_ref(), false),
1036 None
1037 );
1038 }
1039
1040 #[test]
1041 fn add_child_to_leaf() {
1042 let mut root1: TrieNode<u8> = TrieNode::root();
1043
1044 println!("creating root1:");
1045 root1.print();
1046 println!("adding (abcd, 1)");
1047 assert_eq!(root1.insert(Vec::from(&b"abcd"[..]), 1), InsertResult::Ok);
1048 root1.print();
1049 println!("adding (abce, 2)");
1050 assert_eq!(root1.insert(Vec::from(&b"abce"[..]), 2), InsertResult::Ok);
1051 root1.print();
1052 println!("adding (abc, 3)");
1053 assert_eq!(root1.insert(Vec::from(&b"abc"[..]), 3), InsertResult::Ok);
1054
1055 println!("root1:");
1056 root1.print();
1057
1058 let mut root2: TrieNode<u8> = TrieNode::root();
1059
1060 assert_eq!(root2.insert(Vec::from(&b"abc"[..]), 3), InsertResult::Ok);
1061 assert_eq!(root2.insert(Vec::from(&b"abcd"[..]), 1), InsertResult::Ok);
1062 assert_eq!(root2.insert(Vec::from(&b"abce"[..]), 2), InsertResult::Ok);
1063
1064 println!("root2:");
1065 root2.print();
1066 assert_eq!(root2.remove(&Vec::from(&b"abc"[..])), RemoveResult::Ok);
1067
1068 println!("root2 after,remove:");
1069 root2.print();
1070 let mut expected: TrieNode<u8> = TrieNode::root();
1071
1072 assert_eq!(
1073 expected.insert(Vec::from(&b"abcd"[..]), 1),
1074 InsertResult::Ok
1075 );
1076 assert_eq!(
1077 expected.insert(Vec::from(&b"abce"[..]), 2),
1078 InsertResult::Ok
1079 );
1080
1081 println!("root2 after insert");
1082 root2.print();
1083 println!("expected");
1084 expected.print();
1085 assert_eq!(root2, expected);
1086 }
1087
1088 #[test]
1089 fn domains() {
1090 let mut root: TrieNode<u8> = TrieNode::root();
1091 root.print();
1092
1093 assert_eq!(
1094 root.domain_insert(Vec::from(&b"www.example.com"[..]), 1),
1095 InsertResult::Ok
1096 );
1097 root.print();
1098 assert_eq!(
1099 root.domain_insert(Vec::from(&b"test.example.com"[..]), 2),
1100 InsertResult::Ok
1101 );
1102 root.print();
1103 assert_eq!(
1104 root.domain_insert(Vec::from(&b"*.alldomains.org"[..]), 3),
1105 InsertResult::Ok
1106 );
1107 root.print();
1108 assert_eq!(
1109 root.domain_insert(Vec::from(&b"alldomains.org"[..]), 4),
1110 InsertResult::Ok
1111 );
1112 assert_eq!(
1113 root.domain_insert(Vec::from(&b"pouet.alldomains.org"[..]), 5),
1114 InsertResult::Ok
1115 );
1116 root.print();
1117 assert_eq!(
1118 root.domain_insert(Vec::from(&b"hello.com"[..]), 6),
1119 InsertResult::Ok
1120 );
1121 assert_eq!(
1122 root.domain_insert(Vec::from(&b"*.hello.com"[..]), 7),
1123 InsertResult::Ok
1124 );
1125 assert_eq!(
1126 root.domain_insert(Vec::from(&b"images./cdn[0-9]+/.hello.com"[..]), 8),
1127 InsertResult::Ok
1128 );
1129 root.print();
1130 assert_eq!(
1131 root.domain_insert(Vec::from(&b"/test[0-9]+/.www.hello.com"[..]), 9),
1132 InsertResult::Ok
1133 );
1134 root.print();
1135
1136 assert_eq!(root.domain_lookup(&b"example.com"[..], true), None);
1137 assert_eq!(
1138 root.domain_lookup(&b"blah.test.example.com"[..], true),
1139 None
1140 );
1141 assert_eq!(
1142 root.domain_lookup(&b"www.example.com"[..], true),
1143 Some(&(b"www.example.com"[..].to_vec(), 1))
1144 );
1145 assert_eq!(
1146 root.domain_lookup(&b"alldomains.org"[..], true),
1147 Some(&(b"alldomains.org"[..].to_vec(), 4))
1148 );
1149 assert_eq!(
1150 root.domain_lookup(&b"test.hello.com"[..], true),
1151 Some(&(b"*.hello.com"[..].to_vec(), 7))
1152 );
1153 assert_eq!(
1154 root.domain_lookup(&b"images.cdn10.hello.com"[..], true),
1155 Some(&(b"images./cdn[0-9]+/.hello.com"[..].to_vec(), 8))
1156 );
1157 assert_eq!(
1158 root.domain_lookup(&b"test42.www.hello.com"[..], true),
1159 Some(&(b"/test[0-9]+/.www.hello.com"[..].to_vec(), 9))
1160 );
1161 assert_eq!(
1162 root.domain_lookup(&b"test.alldomains.org"[..], true),
1163 Some(&(b"*.alldomains.org"[..].to_vec(), 3))
1164 );
1165 assert_eq!(
1166 root.domain_lookup(&b"hello.alldomains.org"[..], true),
1167 Some(&(b"*.alldomains.org"[..].to_vec(), 3))
1168 );
1169 assert_eq!(
1170 root.domain_lookup(&b"pouet.alldomains.org"[..], true),
1171 Some(&(b"pouet.alldomains.org"[..].to_vec(), 5))
1172 );
1173 assert_eq!(
1174 root.domain_lookup(&b"blah.test.alldomains.org"[..], true),
1175 None
1176 );
1177
1178 assert_eq!(
1179 root.domain_remove(&Vec::from(&b"alldomains.org"[..])),
1180 RemoveResult::Ok
1181 );
1182 println!("after remove");
1183 root.print();
1184 assert_eq!(root.domain_lookup(&b"alldomains.org"[..], true), None);
1185 assert_eq!(
1186 root.domain_lookup(&b"test.alldomains.org"[..], true),
1187 Some(&(b"*.alldomains.org"[..].to_vec(), 3))
1188 );
1189 assert_eq!(
1190 root.domain_lookup(&b"hello.alldomains.org"[..], true),
1191 Some(&(b"*.alldomains.org"[..].to_vec(), 3))
1192 );
1193 assert_eq!(
1194 root.domain_lookup(&b"pouet.alldomains.org"[..], true),
1195 Some(&(b"pouet.alldomains.org"[..].to_vec(), 5))
1196 );
1197 assert_eq!(
1198 root.domain_lookup(&b"test.hello.com"[..], true),
1199 Some(&(b"*.hello.com"[..].to_vec(), 7))
1200 );
1201 assert_eq!(
1202 root.domain_lookup(&b"blah.test.alldomains.org"[..], true),
1203 None
1204 );
1205 }
1206
1207 #[test]
1208 fn wildcard() {
1209 let mut root: TrieNode<u8> = TrieNode::root();
1210 root.print();
1211 root.domain_insert("*.clever-cloud.com".as_bytes().to_vec(), 2u8);
1212 root.domain_insert("services.clever-cloud.com".as_bytes().to_vec(), 0u8);
1213 root.domain_insert("*.services.clever-cloud.com".as_bytes().to_vec(), 1u8);
1214
1215 let res = root.domain_lookup(b"test.services.clever-cloud.com", true);
1216 println!("query result: {res:?}");
1217
1218 assert_eq!(
1219 root.domain_lookup(b"pgstudio.services.clever-cloud.com", true),
1220 Some(&("*.services.clever-cloud.com".as_bytes().to_vec(), 1u8))
1221 );
1222 }
1223
1224 fn hm_insert(h: std::collections::HashMap<String, u32>) -> bool {
1225 let mut root: TrieNode<u32> = TrieNode::root();
1226
1227 for (k, v) in h.iter() {
1228 if k.is_empty() {
1229 continue;
1230 }
1231
1232 if k.as_bytes()[0] == b'.' {
1233 continue;
1234 }
1235
1236 if k.contains('/') {
1237 continue;
1238 }
1239
1240 if k == "*" {
1241 continue;
1242 }
1243
1244 assert_eq!(
1247 root.insert(Vec::from(k.as_bytes()), *v),
1248 InsertResult::Ok,
1249 "could not insert ({k}, {v})"
1250 );
1251 }
1253
1254 for (k, v) in h.iter() {
1256 if k.is_empty() {
1257 continue;
1258 }
1259
1260 if k.as_bytes()[0] == b'.' {
1261 continue;
1262 }
1263
1264 if k.contains('/') {
1265 continue;
1266 }
1267
1268 if k == "*" {
1269 continue;
1270 }
1271
1272 match root.lookup(k.as_bytes(), false) {
1274 None => {
1275 println!("did not find key '{k}'");
1276 return false;
1277 }
1278 Some(&(ref k1, v1)) => {
1279 if k.as_bytes() != &k1[..] || *v != v1 {
1280 println!(
1281 "request ({}, {}), got ({}, {})",
1282 k,
1283 v,
1284 str::from_utf8(&k1[..]).unwrap(),
1285 v1
1286 );
1287 return false;
1288 }
1289 }
1290 }
1291 }
1292
1293 true
1294 }
1295
1296 #[test]
1305 fn insert_disappearing_tree() {
1306 let h: std::collections::HashMap<String, u32> = [
1307 (String::from("\n\u{3}"), 0),
1308 (String::from("\n\u{0}"), 1),
1309 (String::from("\n"), 2),
1310 ]
1311 .iter()
1312 .cloned()
1313 .collect();
1314 assert!(hm_insert(h));
1315 }
1316
1317 #[test]
1318 fn size() {
1319 assert_size!(TrieNode<u32>, 136);
1320 }
1321
1322 #[test]
1329 fn leftmost_regex_segment_reinsert_and_lookup_mut_do_not_panic() {
1330 let mut root: TrieNode<u8> = TrieNode::root();
1331
1332 assert_eq!(
1333 root.insert(Vec::from(&b"/test[0-9]/.example.com"[..]), 7),
1334 InsertResult::Ok
1335 );
1336 assert_eq!(
1339 root.insert(Vec::from(&b"/test[0-9]/.example.com"[..]), 8),
1340 InsertResult::Existing
1341 );
1342
1343 let resolved = root.domain_lookup_mut(b"test4.example.com", false);
1348 assert_eq!(
1349 resolved.map(|(_, v)| *v),
1350 Some(7),
1351 "leftmost-regex host must resolve via lookup_mut without panicking",
1352 );
1353
1354 assert_eq!(
1356 root.domain_lookup(b"test4.example.com", false),
1357 Some(&(b"/test[0-9]/.example.com"[..].to_vec(), 7))
1358 );
1359
1360 assert_eq!(
1362 root.domain_remove(&Vec::from(&b"/test[0-9]/.example.com"[..])),
1363 RemoveResult::Ok
1364 );
1365 assert_eq!(root.domain_lookup(b"test4.example.com", false), None);
1366 }
1367}