prefix_trie/
lib.rs

1//! This crate provides a simple prefix tree for IP prefixes. Any lookup performs longest-prefix
2//! match. This crate supports both IPv4 and IPv6 (from either [ipnet](https://docs.rs/ipnet/2.10.0)
3//! or [ipnetwork](https://crates.io/crates/ipnetwork) or [cidr](https://crates.io/crates/cidr)).
4//! It also  supports any tuple `(R, u8)`, where `R` is any unsigned primitive integer (`u8`, `u16`,
5//! `u32`, `u64`, `u128`, or `usize`).
6//!
7//! This crate also provides a [`joint::JointPrefixMap`] and [`joint::JointPrefixSet`] that contains
8//! two tables, one for IPv4 and one for IPv6.
9//!
10//! # Comparison with related projects
11//!
12//! [`ip_network_table-deps-treebitmap`](https://crates.io/crates/ip_network_table-deps-treebitmap)
13//! provides an IP lookup table, similar to [`PrefixMap`].
14//!
15//! The following compares the two approaches in the case of *dense* or *sparse* maps. Each test
16//! case performs 100'000 modifications or lookups. However, the dense cases randomly picks any IPv4
17//! address, while the sparse case only pick 20 different IPv4 addresses. See `benches/benchmark.rs`
18//! for more details.
19//!
20//! | Operation       | Mode   | `PrefixMap` | `treebitmap` | factor |
21//! |-----------------|--------|-------------|--------------|--------|
22//! | Insert & Remove | dense  | **31.78ms** | 47.52ms      | ~1.5x  |
23//! | Lookup          | dense  | 32.36ms     | **8.409ms**  | ~0.25x |
24//! | Insert & Remove | sparse | **6.645ms** | 7.329ms      | ~1.1x  |
25//! | Lookup          | sparse | **8.394ms** | 12.30ms      | ~1.5x  |
26//!
27//!
28//! In addition, `prefix-trie` includes a [`PrefixSet`] analogous to `std::collections::HashSet`,
29//! including union, intersection and difference operations that are implemented as simultaneous
30//! tree traversals. Further, `prefix-trie` has an interface similar to `std::collections`, and
31//! offers a general longest-prefix match that is not limited to individual addresses. Finally,
32//! `prefix-trie` allows you to (mutably) borrow a sub-trie using views.
33//!
34//! # Description of the Tree
35//!
36//! The tree is structured as follows: Each node consists of a prefix, a container for a potential
37//! value (`Option`), and two optional children. Adding a new child, or traversing into the tree is
38//! done as follows: we look at the most significant bit that is **not** part of the prefix
39//! itself. If it is not set, then we take the left branch, and otherwise, we take the right one.
40//!
41//! # Traversals
42//!
43//! Any iteration over all elements in the tree is implemented as a graph traversal that will yield
44//! elements in lexicographic order.
45//!
46//! The library offers set operations of different maps or sets. We implement a union, intersection,
47//! difference, and covering_difference. These iterators are implemented using simultaneous tree
48//! traversals. They will yield elements in lexicographic order. Whenever appropriate, the yielded
49//! items will also include the longest prefix match.
50//!
51//! # [`TrieView`] and [`TrieViewMut`]
52//!
53//! You can create a view of a (sub)-trie. Such a view has an any node as its root. Any operations
54//! on that view will only traverse that node and all its children. You can iterate over all
55//! children, search in that sub-trie, and perform set operations (union, intersection, difference,
56//! or the covering difference) on them.
57//!
58//! A view can point to one of three possible nodes:
59//! - A node in the tree that is actually present in the map,
60//! - A branching node that does not exist in the map, but is needed for the tree structure (or that
61//!   was deleted using the function `remove_keep_tree`)
62//! - A virtual node that does not exist as a node in the tree. This is only the case if you call
63//!   [`TrieView::find`] or [`AsView::view_at`] with a node that is not present in the tree, but
64//!   that contains elements present in the tree. Virtual nodes are treated as if they are actually
65//!   present in the tree as branching nodes.
66//!
67//! # Operations on the tree
68//!
69//! There are several operations one can do on the tree. Regular inserts are handled using the
70//! `Entry` structure. An `Entry` is a pointer to a location in the tree to either insert a value or
71//! modify an existing one. Removals however are different.
72//!
73//! The following are the computational complexities of the functions, where `n` is the number of
74//! elements in the tree.
75//!
76//! | Operation                                  | Complexity |
77//! |--------------------------------------------|------------|
78//! | `entry`, `insert`                          | `O(log n)` |
79//! | `remove`, `remove_keep_tree`               | `O(log n)` |
80//! | `remove_children` (calling `drop` on `T`)  | `O(n)`     |
81//! | `get`, `get_lpm`, `get_mut`                | `O(log n)` |
82//! | `retain`                                   | `O(n)`     |
83//! | `clear` (calling `drop` on `T`)            | `O(n)`     |
84//! | Operations on [`map::Entry`]               | `O(1)`     |
85//! | `len` and `is_empty`                       | `O(1)`     |
86//! | `union`, `intersection`, `difference`, ... | `O(n)`     |
87//!
88//! There are three kinds of removals you! can do:
89//!
90//! - [`PrefixMap::remove`] will remove an entry from the tree and modify the tree structure as if
91//!   the value was never inserted before. [`PrefixMap::remove`] will always exactly revert the
92//!   operation of [`PrefixMap::insert`]. When only calling this function to remove elements, you
93//!   are guaranteed that the tree structure is indistinguishable to a different tree where you
94//!   only inserted elements.
95//! - [`PrefixMap::remove_children`] will remove all entries that are contained within the given
96//!   prefix. This operation will search for the node with the shortest prefix length that is
97//!   contained within the given prefix and remove it, including all of its children.
98//! - [`PrefixMap::remove_keep_tree`] will not change anything in the tree structure. It will only
99//!   remove a value from a node. As soon as you call `remove_keep_tree` once on a tree structure,
100//!   the tree will no longer be optimal.
101//!
102//! # TODO
103//!
104//! Migrate to a TreeBitMap, described by
105//! [W. Eatherton, Z. Dittia, G. Varghes](https://doi.org/10.1145/997150.997160).
106
107#![allow(clippy::collapsible_else_if)]
108#![deny(missing_docs)]
109
110mod fmt;
111#[cfg(test)]
112mod fuzzing;
113pub(crate) mod inner;
114mod prefix;
115#[cfg(feature = "serde")]
116mod serde;
117#[cfg(feature = "ipnet")]
118#[cfg(test)]
119mod test;
120
121pub mod joint;
122pub mod map;
123pub mod set;
124pub mod trieview;
125
126pub use map::PrefixMap;
127pub use prefix::Prefix;
128pub use set::PrefixSet;
129pub use trieview::{AsView, AsViewMut, TrieView, TrieViewMut};
130
131#[inline(always)]
132pub(crate) fn to_right<P: Prefix>(branch_p: &P, child_p: &P) -> bool {
133    child_p.is_bit_set(branch_p.prefix_len())
134}