iptrie/
set.rs

1//! Generic prefix trie set structures
2use crate::prefix::*;
3use crate::trie::lctrie::LevelCompressedTrie;
4use crate::trie::patricia::RadixTrie;
5use std::num::NonZeroUsize;
6
7#[cfg(feature = "graphviz")]
8use crate::graphviz::DotWriter;
9use crate::trie::common::Leaf;
10#[cfg(feature = "graphviz")]
11use std::fmt::Display;
12
13/// A set of Ip prefixes based on a radix binary trie
14#[derive(Clone)]
15pub struct RTrieSet<P: IpPrefix>(pub(crate) RadixTrie<P, ()>);
16
17/// A set of Ip prefixes based on a level-compressed trie
18pub struct LCTrieSet<P: IpPrefix>(pub(crate) LevelCompressedTrie<P, ()>);
19
20impl<P: IpRootPrefix> RTrieSet<P> {
21    /// Creates a new set which contains the root prefix.
22    #[inline]
23    pub fn new() -> Self {
24        Self::with_capacity(1000)
25    }
26
27    /// Creates a new set with an initial capacity.
28    ///
29    /// The returned set already contains the root prefix.
30    #[inline]
31    pub fn with_capacity(capacity: usize) -> Self {
32        Self(RadixTrie::new((), capacity))
33    }
34}
35
36impl<P: IpPrefix> RTrieSet<P> {
37    /// Returns the size of the set.
38    ///
39    /// Notice that it never equals zero since the top prefix is
40    /// always present in the set.
41    #[inline]
42    #[allow(clippy::len_without_is_empty)]
43    pub fn len(&self) -> NonZeroUsize {
44        self.0.len()
45    }
46
47    /// Compress this Patricia trie in a LC-Trie.
48    ///
49    /// For lookup algorithms, a Patricia trie performs unit bit checking and LC-Trie
50    /// performs multi bits checking. So the last one is more performant but it
51    /// cannot be modified (no insertion or removal operations are provided).
52    #[inline]
53    pub fn compress(self) -> LCTrieSet<P> {
54        LCTrieSet(LevelCompressedTrie::new(self.0))
55    }
56
57    #[inline]
58    pub fn shrink_to_fit(&mut self) {
59        self.0.shrink_to_fit()
60    }
61
62    /// Inserts a new element in the set.
63    ///
64    /// If the specified element already exists in the set, `false` is returned.
65    ///
66    /// # Example
67    /// ```
68    /// #  use iptrie::*;
69    /// use std::net::Ipv4Addr;
70    ///
71    /// let addr = Ipv4Addr::new(1,1,1,1);
72    /// let mut trie = Ipv4RTrieSet::new();
73    ///
74    /// let ip20 = Ipv4Prefix::new(addr, 20).unwrap();
75    /// let ip22 = Ipv4Prefix::new(addr, 22).unwrap();
76    ///
77    /// assert_eq!( trie.insert(ip20), true);
78    /// assert_eq!( trie.insert(ip22), true);
79    /// assert_eq!( trie.insert(ip20), false);
80    /// ```
81    #[inline]
82    pub fn insert(&mut self, k: P) -> bool {
83        self.0.insert(k, ()).is_none()
84    }
85
86    /// Checks if an element is present (exact match).
87    ///
88    /// # Example
89    /// ```
90    /// #  use iptrie::*;
91    /// use std::net::Ipv4Addr;
92    ///
93    /// let addr = Ipv4Addr::new(1,1,1,1);
94    ///
95    /// let ip20 = Ipv4Prefix::new(addr, 20).unwrap();
96    /// let ip22 = Ipv4Prefix::new(addr, 22).unwrap();
97    /// let ip24 = Ipv4Prefix::new(addr, 24).unwrap();
98    ///
99    /// let trie = Ipv4RTrieSet::from_iter([ip20,ip24]);
100    ///
101    /// assert_eq!( trie.contains(&ip20), true);
102    /// assert_eq!( trie.contains(&ip22), false);
103    /// assert_eq!( trie.contains(&ip24), true);
104    /// ```
105    #[inline]
106    pub fn contains<Q>(&self, k: &Q) -> bool
107    where
108        Q: IpPrefix<Addr = P::Addr>,
109        P: IpPrefixCovering<Q>,
110    {
111        self.0.get(k).is_some()
112    }
113
114    /// Removes a previously inserted prefix (exact match).
115    ///
116    /// Returns `false` is the element was not present in the set
117    /// and `true` if the removal is effective.
118    ///
119    /// # Example
120    /// ```
121    /// #  use iptrie::*;
122    /// use std::net::Ipv4Addr;
123    ///
124    /// let addr = Ipv4Addr::new(1,1,1,1);
125    ///
126    /// let ip20 = Ipv4Prefix::new(addr, 20).unwrap();
127    /// let ip22 = Ipv4Prefix::new(addr, 22).unwrap();
128    ///
129    /// let mut trie = Ipv4RTrieSet::from_iter([ip20,ip22]);
130    ///
131    /// assert_eq!( trie.contains(&ip20), true);
132    /// assert_eq!(trie.remove(&ip20), true);
133    /// assert_eq!(trie.remove(&ip20), false);
134    ///
135    /// assert_eq!( trie.contains(&ip22), true);
136    /// assert_eq!( trie.contains(&ip20), false);
137    /// ```
138    #[inline]
139    pub fn remove<Q>(&mut self, k: &Q) -> bool
140    where
141        Q: IpPrefix<Addr = P::Addr>,
142        P: IpPrefixCovering<Q>,
143    {
144        self.0.remove(k).is_some()
145    }
146
147    /// Replace an existing prefix.
148    ///
149    /// Adds a prefix to the set, replacing the existing one, if any (exact match performed).
150    /// Returns the replaced value.
151    ///
152    /// # Example
153    /// ```
154    /// # use iptrie::*;
155    /// # use iptrie::set::*;
156    /// use std::net::Ipv4Addr;
157    /// use ipnet::Ipv4Net;
158    /// let mut trie = RTrieSet::new();
159    ///
160    /// let addr1 = Ipv4Addr::new(1,1,1,1);
161    /// let addr2 = Ipv4Addr::new(1,1,1,2);
162    /// let ip20 = Ipv4Net::new(addr1, 20).unwrap();
163    /// let ip20b = Ipv4Net::new(addr2, 20).unwrap();
164    ///
165    /// trie.insert(ip20);
166    /// assert_eq!(trie.get(&ip20).unwrap().to_string(), "1.1.1.1/20".to_string());
167    ///
168    /// assert_eq!(trie.insert(ip20b), false);
169    /// assert_eq!(trie.get(&ip20).unwrap().to_string(), "1.1.1.1/20".to_string());
170    ///
171    /// assert_eq!(trie.replace(ip20b), Some(ip20));
172    /// assert_eq!(trie.get(&ip20).unwrap().to_string(), "1.1.1.2/20".to_string());
173    /// ```
174    #[inline]
175    pub fn replace(&mut self, k: P) -> Option<P> {
176        self.0.replace(k, ()).map(|l| *l.prefix())
177    }
178
179    /// Gets the value associated with an exact match of the key.
180    ///
181    /// To access to the longest prefix match, use [`Self::lookup`].
182    ///
183    /// # Example
184    /// ```
185    /// # use iptrie::*;
186    /// # use iptrie::set::*;
187    /// use std::net::Ipv4Addr;
188    /// use ipnet::Ipv4Net;
189    /// let mut trie = RTrieSet::new();
190    ///
191    /// let addr1 = Ipv4Addr::new(1,1,1,1);
192    /// let addr2 = Ipv4Addr::new(1,1,1,2);
193    /// let ip20 = Ipv4Net::new(addr1, 20).unwrap();
194    /// let ip20b = Ipv4Net::new(addr2, 20).unwrap();
195    ///
196    /// trie.insert(ip20);
197    /// assert_eq!(trie.get(&ip20).unwrap().to_string(), "1.1.1.1/20".to_string());
198    ///
199    /// trie.insert(ip20b);
200    /// assert_eq!(trie.get(&ip20).unwrap().to_string(), "1.1.1.1/20".to_string());
201    /// ```
202    #[inline]
203    pub fn get<Q>(&self, k: &Q) -> Option<&P>
204    where
205        Q: IpPrefix<Addr = P::Addr>,
206        P: IpPrefixCovering<Q>,
207    {
208        self.0.get(k).map(|(k, _)| k)
209    }
210
211    /// Gets the longest prefix which matches the given key.
212    ///
213    /// As the top prefix always matches, it never fails.
214    ///
215    /// To access to the exact prefix match, use [`Self::get`].
216    ///
217    /// # Example
218    /// ```
219    /// # use iptrie::*;
220    /// use std::net::Ipv4Addr;
221    /// let mut trie = Ipv4RTrieSet::new();
222    ///
223    /// let addr = Ipv4Addr::new(1,1,1,1);
224    /// let ip20 = Ipv4Prefix::new(addr, 20).unwrap();
225    /// let ip22 = Ipv4Prefix::new(addr, 22).unwrap();
226    /// let ip24 = Ipv4Prefix::new(addr, 24).unwrap();
227    ///
228    /// trie.insert(ip20);
229    /// trie.insert(ip24);
230    ///
231    /// assert_eq!( trie.lookup(&ip20), &ip20);
232    /// assert_eq!( trie.lookup(&ip22), &ip20);
233    /// assert_eq!( trie.lookup(&ip24), &ip24);
234    ///
235    /// assert_eq!( trie.lookup(&addr), &ip24);
236    /// ```
237    #[inline]
238    pub fn lookup<Q>(&self, k: &Q) -> &P
239    where
240        Q: IpPrefix<Addr = P::Addr>,
241        P: IpPrefixCovering<Q>,
242    {
243        self.0.lookup(k).0
244    }
245
246    /// Iterates over all the prefixes of this set.
247    #[inline]
248    pub fn iter(&self) -> impl Iterator<Item = &P> + '_ {
249        self.0.leaves.0.iter().map(Leaf::prefix)
250    }
251
252    #[inline]
253    pub fn info(&self) {
254        self.0.info()
255    }
256}
257
258impl<P: IpRootPrefix> Default for RTrieSet<P> {
259    #[inline]
260    fn default() -> Self {
261        Self::new()
262    }
263}
264
265impl<P: IpPrefix> Extend<P> for RTrieSet<P> {
266    fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
267        iter.into_iter().for_each(|item| {
268            self.insert(item);
269        })
270    }
271}
272
273impl<P: IpRootPrefix> FromIterator<P> for RTrieSet<P> {
274    fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
275        let mut trieset = Self::default();
276        trieset.extend(iter);
277        trieset
278    }
279}
280
281impl<P: IpPrefix> LCTrieSet<P> {
282    /// Returns the size of the set.
283    ///
284    /// Notice that it never equals zero since the top prefix is
285    /// always present in the set.
286    #[inline]
287    #[allow(clippy::len_without_is_empty)]
288    pub fn len(&self) -> NonZeroUsize {
289        self.0.len()
290    }
291
292    #[inline]
293    pub fn info(&self) {
294        self.0.info()
295    }
296
297    /// Checks if an element is present (exact match).
298    ///
299    /// # Example
300    /// ```
301    /// #  use iptrie::*;
302    /// use std::net::Ipv4Addr;
303    ///
304    /// let addr = Ipv4Addr::new(1,1,1,1);
305    ///
306    /// let ip20 = Ipv4Prefix::new(addr, 20).unwrap();
307    /// let ip22 = Ipv4Prefix::new(addr, 22).unwrap();
308    /// let ip24 = Ipv4Prefix::new(addr, 24).unwrap();
309    ///
310    /// let trie = Ipv4RTrieSet::from_iter([ip20,ip24]);
311    /// let lctrie = trie.compress();
312    ///
313    /// assert_eq!( lctrie.contains(&ip20), true);
314    /// assert_eq!( lctrie.contains(&ip22), false);
315    /// assert_eq!( lctrie.contains(&ip24), true);
316    /// ```
317    #[inline]
318    pub fn contains<Q>(&self, k: &Q) -> bool
319    where
320        Q: IpPrefix<Addr = P::Addr>,
321        P: IpPrefixCovering<Q>,
322    {
323        self.0.get(k).is_some()
324    }
325
326    /// Gets the value associated with an exact match of the key.
327    ///
328    /// To access to the longest prefix match, use [`Self::lookup`].
329    ///
330    /// # Example
331    /// ```
332    /// # use iptrie::*;
333    /// # use iptrie::set::*;
334    /// use std::net::Ipv4Addr;
335    /// let mut trie = RTrieSet::new();
336    ///
337    /// let addr = Ipv4Addr::new(1,1,1,1);
338    /// let ip20 = Ipv4Prefix::new(addr, 20).unwrap();
339    /// let ip22 = Ipv4Prefix::new(addr, 22).unwrap();
340    /// let ip24 = Ipv4Prefix::new(addr, 24).unwrap();
341    ///
342    /// trie.insert(ip24);
343    /// trie.insert(ip20);
344    ///
345    /// let lctrie = trie.compress();
346    ///
347    /// assert!( lctrie.get(&ip24).is_some());
348    /// assert!( lctrie.get(&ip22).is_none());
349    /// assert!( lctrie.get(&ip20).is_some());
350    /// ```
351    #[inline]
352    pub fn get<Q>(&self, k: &Q) -> Option<&P>
353    where
354        Q: IpPrefix<Addr = P::Addr>,
355        P: IpPrefixCovering<Q>,
356    {
357        self.0.get(k).map(|(k, _)| k)
358    }
359
360    /// Gets the longest prefix which matches the given key.
361    ///
362    /// As the top prefix always matches, it never fails.
363    ///
364    /// To access to the exact prefix match, use [`Self::get`].
365    ///
366    /// # Example
367    /// ```
368    /// # use iptrie::*;
369    /// use std::net::Ipv4Addr;
370    /// let mut trie = Ipv4RTrieSet::new();
371    ///
372    /// let addr = Ipv4Addr::new(1,1,1,1);
373    /// let ip20 = Ipv4Prefix::new(addr, 20).unwrap();
374    /// let ip22 = Ipv4Prefix::new(addr, 22).unwrap();
375    /// let ip24 = Ipv4Prefix::new(addr, 24).unwrap();
376    ///
377    /// trie.insert(ip20);
378    /// trie.insert(ip24);
379    ///
380    /// let lctrie = trie.compress();
381    ///
382    /// assert_eq!( lctrie.lookup(&ip20), &ip20);
383    /// assert_eq!( lctrie.lookup(&ip22), &ip20);
384    /// assert_eq!( lctrie.lookup(&ip24), &ip24);
385    ///
386    /// assert_eq!( lctrie.lookup(&addr), &ip24);
387    /// ```
388    #[inline]
389    pub fn lookup<Q>(&self, k: &Q) -> &P
390    where
391        Q: IpPrefix<Addr = P::Addr>,
392        P: IpPrefixCovering<Q>,
393    {
394        self.0.lookup(k).0
395    }
396
397    /// Iterates over all the prefixes of this set.
398    #[inline]
399    pub fn iter(&self) -> impl Iterator<Item = &P> + '_ {
400        self.0.leaves.0.iter().map(Leaf::prefix)
401    }
402}
403
404impl<P: IpRootPrefix> FromIterator<P> for LCTrieSet<P> {
405    fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
406        RTrieSet::from_iter(iter).compress()
407    }
408}
409
410#[cfg(feature = "graphviz")]
411impl<P: IpPrefix + Display> DotWriter for RTrieSet<P> {
412    fn write_dot(&self, dot: &mut dyn std::io::Write) -> std::io::Result<()> {
413        self.0.write_dot(dot)
414    }
415}
416
417#[cfg(feature = "graphviz")]
418impl<P: IpPrefix + Display> DotWriter for LCTrieSet<P> {
419    fn write_dot(&self, dot: &mut dyn std::io::Write) -> std::io::Result<()> {
420        self.0.write_dot(dot)
421    }
422}