cidr_map/
map.rs

1// The heart of this CidrMap implementation is derived from rust-bitstring-trees
2// which is Copyright (c) 2017 Stefan Bühler and used here under the terms
3// of its MIT License.
4// The modifications made here are to remove the use of unsafe code and to
5// expose the ability to search, rather than simply iterate, the underlying
6// radix tree.
7use bitstring::BitString;
8pub use cidr::{AnyIpCidr, IpCidr};
9#[cfg(feature = "lua")]
10use config::{any_err, get_or_create_sub_module};
11#[cfg(feature = "lua")]
12use mlua::prelude::LuaUserData;
13#[cfg(feature = "lua")]
14use mlua::{FromLua, Lua, MetaMethod, UserDataMethods};
15#[cfg(feature = "lua")]
16use mod_memoize::CacheValue;
17use serde::de::{MapAccess, Visitor};
18use serde::ser::SerializeMap;
19use serde::{Deserialize, Deserializer, Serialize, Serializer};
20use std::fmt::Debug;
21use std::net::IpAddr;
22use std::str::FromStr;
23use std::sync::Arc;
24
25#[derive(Clone, PartialEq)]
26pub struct CidrMap<V>
27where
28    V: Clone,
29{
30    root: Arc<Option<Node<V>>>,
31}
32
33impl<V> Debug for CidrMap<V>
34where
35    V: Clone + Debug,
36{
37    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
38        use std::fmt::DebugMap;
39        let mut map = fmt.debug_map();
40
41        fn walk<V: Clone + Debug>(node: &Node<V>, map: &mut DebugMap) {
42            match node {
43                Node::InnerNode(inner) => {
44                    walk(&inner.children.left, map);
45                    walk(&inner.children.right, map);
46                }
47                Node::Leaf(leaf) => {
48                    map.key(&leaf.key.to_string());
49                    map.value(&leaf.value);
50                }
51            }
52        }
53
54        if let Some(root) = self.root() {
55            walk(root, &mut map);
56        }
57
58        map.finish()
59    }
60}
61
62struct MapVis<T>
63where
64    T: Clone + PartialEq,
65{
66    map: CidrMap<T>,
67}
68
69impl<'de, T> Visitor<'de> for MapVis<T>
70where
71    T: Clone + PartialEq + Deserialize<'de>,
72{
73    type Value = CidrMap<T>;
74
75    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
76        formatter.write_str("a CidrMap")
77    }
78
79    fn visit_map<M>(mut self, mut access: M) -> Result<Self::Value, M::Error>
80    where
81        M: MapAccess<'de>,
82    {
83        while let Some((key, value)) = access.next_entry()? {
84            self.map.insert(key, value);
85        }
86
87        Ok(self.map)
88    }
89}
90
91impl<'de, V> Deserialize<'de> for CidrMap<V>
92where
93    V: Clone + PartialEq + Deserialize<'de>,
94{
95    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
96    where
97        D: Deserializer<'de>,
98    {
99        deserializer.deserialize_map(MapVis {
100            map: CidrMap::new(),
101        })
102    }
103}
104
105impl<V> Serialize for CidrMap<V>
106where
107    V: Clone + PartialEq + Serialize,
108{
109    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
110    where
111        S: Serializer,
112    {
113        let mut map = serializer.serialize_map(None)?;
114        for (k, v) in self.iter() {
115            map.serialize_entry(k, v)?;
116        }
117        map.end()
118    }
119}
120
121/// Nodes of a CidrMap can be either an InnerNode (with two children)
122/// or a leaf node.
123#[derive(Debug, Clone, PartialEq)]
124pub enum Node<V>
125where
126    V: Clone,
127{
128    /// Inner node
129    InnerNode(InnerNode<V>),
130    /// Leaf node
131    Leaf(Leaf<V>),
132}
133
134/// Leaf nodes represent prefixes part of the set
135#[derive(Clone, Debug, PartialEq)]
136pub struct Leaf<V>
137where
138    V: Clone,
139{
140    pub key: AnyIpCidr,
141    pub value: V,
142}
143
144/// Inner node with two direct children.
145#[derive(Clone, Debug, PartialEq)]
146pub struct InnerNode<V>
147where
148    V: Clone,
149{
150    pub(crate) key: AnyIpCidr,
151    pub(crate) children: Box<Children<V>>,
152}
153
154#[derive(Clone, Debug, PartialEq)]
155pub(crate) struct Children<V>
156where
157    V: Clone,
158{
159    pub(crate) left: Node<V>,
160    pub(crate) right: Node<V>,
161}
162
163impl<V> InnerNode<V>
164where
165    V: Clone,
166{
167    pub fn key(&self) -> &AnyIpCidr {
168        &self.key
169    }
170
171    pub fn pick_side<'a>(&'a self, subkey: &AnyIpCidr) -> &'a Node<V> {
172        if subkey.get(self.key.len()) {
173            &self.children.right
174        } else {
175            &self.children.left
176        }
177    }
178
179    pub fn pick_side_mut<'a>(&'a mut self, subkey: &AnyIpCidr) -> &'a mut Node<V> {
180        if subkey.get(self.key.len()) {
181            &mut self.children.right
182        } else {
183            &mut self.children.left
184        }
185    }
186
187    pub fn left(&self) -> &Node<V> {
188        &self.children.left
189    }
190
191    pub fn right(&self) -> &Node<V> {
192        &self.children.right
193    }
194}
195
196impl<V> Node<V>
197where
198    V: Clone,
199{
200    fn new_leaf(key: AnyIpCidr, value: V) -> Self {
201        Self::Leaf(Leaf { key, value })
202    }
203
204    fn new_children_unknown_order(
205        shared_prefix_len: usize,
206        a: Node<V>,
207        b: Node<V>,
208    ) -> Box<Children<V>> {
209        let a_right = a.key().get(shared_prefix_len);
210        assert_eq!(!a_right, b.key().get(shared_prefix_len));
211        if a_right {
212            Box::new(Children { left: b, right: a })
213        } else {
214            Box::new(Children { left: a, right: b })
215        }
216    }
217
218    fn new_inner_unknown_order(shared_prefix_len: usize, a: Node<V>, b: Node<V>) -> Node<V> {
219        let mut key = *a.key();
220        key.clip(shared_prefix_len);
221        Node::InnerNode(InnerNode {
222            key,
223            children: Self::new_children_unknown_order(shared_prefix_len, a, b),
224        })
225    }
226
227    /// The longest shared prefix of all nodes in this sub tree.
228    pub fn key(&self) -> &AnyIpCidr {
229        match *self {
230            Node::Leaf(ref leaf) => &leaf.key,
231            Node::InnerNode(ref inner) => &inner.key,
232        }
233    }
234
235    fn leaf_ref(&self) -> Option<&Leaf<V>> {
236        match *self {
237            Node::Leaf(ref leaf) => Some(leaf),
238            _ => None,
239        }
240    }
241
242    /// convert self node to leaf with key clipped to key_len and given
243    /// value
244    fn convert_leaf(&mut self, key_len: usize, value: V) {
245        *self = match self {
246            Node::Leaf(leaf) => {
247                let mut leaf = leaf.clone();
248                leaf.key.clip(key_len);
249                leaf.value = value;
250                Node::Leaf(leaf)
251            }
252            Node::InnerNode(inner) => {
253                let mut key = inner.key;
254                key.clip(key_len);
255                Self::new_leaf(key, value)
256            }
257        };
258    }
259
260    fn insert_uncompressed(&mut self, key: AnyIpCidr, value: V)
261    where
262        V: Clone,
263    {
264        let (self_key_len, shared_prefix_len) = {
265            let key_ref = self.key();
266            (key_ref.len(), key_ref.shared_prefix_len(&key))
267        };
268
269        if shared_prefix_len == key.len() {
270            // either key == self.key, or key is a prefix of self.key
271            // => replace subtree
272            self.convert_leaf(shared_prefix_len, value);
273        } else if shared_prefix_len < self_key_len {
274            debug_assert!(shared_prefix_len < key.len());
275            // need to split path to current node; requires new parent
276            *self = Self::new_inner_unknown_order(
277                shared_prefix_len,
278                self.clone(),
279                Self::new_leaf(key, value),
280            );
281        } else {
282            debug_assert!(shared_prefix_len == self_key_len);
283            debug_assert!(shared_prefix_len < key.len());
284            // new key below in tree
285            match *self {
286                Node::Leaf(_) => {
287                    // linear split of path down to leaf
288                    let old_value = self.leaf_ref().unwrap().value.clone();
289                    let mut new_node = Self::new_leaf(key, value);
290                    for l in (shared_prefix_len..key.len()).rev() {
291                        let mut other_key = key;
292                        other_key.clip(l + 1);
293                        other_key.flip(l);
294                        new_node = Self::new_inner_unknown_order(
295                            l,
296                            new_node,
297                            Self::new_leaf(other_key, old_value.clone()),
298                        );
299                    }
300                    *self = new_node;
301                }
302                Node::InnerNode(ref mut inner) => {
303                    inner.pick_side_mut(&key).insert_uncompressed(key, value);
304                }
305            }
306        }
307    }
308
309    fn insert(&mut self, key: AnyIpCidr, value: V)
310    where
311        V: Clone + PartialEq,
312    {
313        let (self_key_len, shared_prefix_len) = {
314            let key_ref = self.key();
315            (key_ref.len(), key_ref.shared_prefix_len(&key))
316        };
317
318        if shared_prefix_len == key.len() {
319            // either key == self.key, or key is a prefix of self.key
320            // => replace subtree
321            self.convert_leaf(shared_prefix_len, value);
322        // no need to compress
323        } else if shared_prefix_len < self_key_len {
324            debug_assert!(shared_prefix_len < key.len());
325            if shared_prefix_len + 1 == self_key_len && shared_prefix_len + 1 == key.len() {
326                if let Node::Leaf(ref mut this) = *self {
327                    if this.value == value {
328                        // we'd split this, and compress it below.
329                        // shortcut the allocations here
330                        this.key.clip(shared_prefix_len);
331                        return; // no need split path
332                    }
333                }
334            }
335
336            // need to split path to current node; requires new parent
337            *self = Self::new_inner_unknown_order(
338                shared_prefix_len,
339                self.clone(),
340                Self::new_leaf(key, value),
341            );
342        // no need to compress - shortcut check above would
343        // have found it
344        } else {
345            debug_assert!(shared_prefix_len == self_key_len);
346            debug_assert!(shared_prefix_len < key.len());
347            // new key below in tree
348            match *self {
349                Node::Leaf(_) => {
350                    // linear split of path down to leaf
351                    let new_node = {
352                        let old_value = &self.leaf_ref().unwrap().value;
353                        if *old_value == value {
354                            // below in tree, but same value - nothing new
355                            return;
356                        }
357                        let mut new_node = Self::new_leaf(key, value);
358                        for l in (shared_prefix_len..key.len()).rev() {
359                            let mut other_key = key;
360                            other_key.clip(l + 1);
361                            other_key.flip(l);
362                            new_node = Self::new_inner_unknown_order(
363                                l,
364                                new_node,
365                                Self::new_leaf(other_key, old_value.clone()),
366                            );
367                        }
368                        new_node
369                    };
370                    *self = new_node;
371                    // we checked value before, nothing to compress
372                    return;
373                }
374                Node::InnerNode(ref mut inner) => {
375                    inner.pick_side_mut(&key).insert(key, value);
376                }
377            }
378            // after recursion check for compression
379            self.compress();
380        }
381    }
382
383    fn compress(&mut self)
384    where
385        V: PartialEq,
386    {
387        let self_key_len = self.key().len();
388
389        // compress: if node has two children, and both sub keys are
390        // exactly one bit longer than the key of the parent node, and
391        // both child nodes are leafs and share the same value, make the
392        // current node a leaf
393        let compress = match *self {
394            Node::InnerNode(ref inner) => {
395                let left_value = match inner.children.left {
396                    Node::Leaf(ref leaf) if leaf.key.len() == self_key_len + 1 => &leaf.value,
397                    _ => return, // not a leaf or more than one bit longer
398                };
399                let right_value = match inner.children.right {
400                    Node::Leaf(ref leaf) if leaf.key.len() == self_key_len + 1 => &leaf.value,
401                    _ => return, // not a leaf or more than one bit longer
402                };
403                left_value == right_value
404            }
405            Node::Leaf(_) => return, // already compressed
406        };
407        if compress {
408            *self = match self {
409                // move value from left
410                Node::InnerNode(inner) => match &inner.children.left {
411                    Node::Leaf(leaf) => Node::Leaf(Leaf {
412                        key: inner.key,
413                        value: leaf.value.clone(),
414                    }),
415                    _ => unreachable!(),
416                },
417                _ => unreachable!(),
418            };
419        }
420    }
421}
422
423impl<V> Default for CidrMap<V>
424where
425    V: Clone,
426{
427    fn default() -> Self {
428        Self::new()
429    }
430}
431
432impl<V> CidrMap<V>
433where
434    V: Clone,
435{
436    pub fn new() -> Self {
437        Self {
438            root: Arc::new(None),
439        }
440    }
441
442    pub fn contains(&self, ip: IpAddr) -> bool {
443        self.get_prefix_match(ip).is_some()
444    }
445
446    pub fn get_prefix_match(&self, ip: IpAddr) -> Option<&V> {
447        let key: AnyIpCidr = IpCidr::new_host(ip).into();
448        self.get_prefix_match_cidr(&key)
449    }
450
451    pub fn get_prefix_match_cidr(&self, key: &AnyIpCidr) -> Option<&V> {
452        let node = self.root()?;
453        Self::find_item(node, key)
454    }
455
456    fn find_item<'a>(node: &'a Node<V>, ip: &AnyIpCidr) -> Option<&'a V> {
457        match node {
458            Node::Leaf(leaf) => {
459                if leaf.key.contains(&ip.first_address().unwrap()) {
460                    Some(&leaf.value)
461                } else {
462                    None
463                }
464            }
465            Node::InnerNode(inner) => Self::find_item(inner.pick_side(ip), ip),
466        }
467    }
468
469    /// Add a new prefix => value mapping.
470    ///
471    /// As values can't be compared for equality it cannot merge
472    /// neighbour prefixes that map to the same value.
473    pub fn insert_uncompressed(&mut self, key: AnyIpCidr, value: V)
474    where
475        V: Clone,
476    {
477        let root = Arc::make_mut(&mut self.root);
478        match root {
479            None => {
480                *root = Some(Node::new_leaf(key, value));
481            }
482            Some(node) => {
483                node.insert_uncompressed(key, value);
484            }
485        }
486    }
487
488    /// Add a new prefix => value mapping.  (Partially) overwrites old
489    /// mappings.
490    pub fn insert(&mut self, key: AnyIpCidr, value: V)
491    where
492        V: Clone + PartialEq,
493    {
494        let root = Arc::make_mut(&mut self.root);
495        match root {
496            None => {
497                *root = Some(Node::new_leaf(key, value));
498            }
499            Some(node) => {
500                node.insert(key, value);
501            }
502        }
503    }
504
505    /// Read-only access to the tree.
506    ///
507    /// An empty map doesn't have any nodes (i.e. `None`).
508    pub fn root(&self) -> Option<&Node<V>> {
509        (*self.root).as_ref()
510    }
511
512    /// Iterate over all values in the map
513    pub fn iter(&'_ self) -> Iter<'_, V> {
514        Iter::new(self)
515    }
516}
517
518#[derive(Clone, Copy, PartialEq, Eq)]
519enum Direction {
520    Left,
521    Right,
522    Up,
523}
524
525/// Iterate over tree
526pub struct Iter<'a, V: 'a>
527where
528    V: Clone,
529{
530    stack: Vec<(Direction, &'a Node<V>)>,
531}
532
533impl<'a, V> Iter<'a, V>
534where
535    V: Clone,
536{
537    /// new iterator
538    pub fn new(tree: &'a CidrMap<V>) -> Self {
539        match tree.root() {
540            None => Iter { stack: Vec::new() },
541            Some(node) => Iter {
542                stack: vec![(Direction::Left, node)],
543            },
544        }
545    }
546}
547
548impl<'a, V> Iterator for Iter<'a, V>
549where
550    V: Clone,
551{
552    type Item = (&'a AnyIpCidr, &'a V);
553
554    fn next(&mut self) -> Option<Self::Item> {
555        if self.stack.is_empty() {
556            return None;
557        }
558
559        // go up in tree from last visited node
560        while Direction::Up == self.stack[self.stack.len() - 1].0 {
561            if 1 == self.stack.len() {
562                self.stack.clear();
563                return None;
564            }
565
566            self.stack.pop();
567            // stack cannot be empty yet!
568            debug_assert!(!self.stack.is_empty());
569        }
570
571        loop {
572            let top = self.stack.len() - 1;
573            let (dir, node) = self.stack[top];
574
575            debug_assert!(!self.stack.is_empty());
576            // go down in tree to next node
577            match dir {
578                Direction::Left => match *node {
579                    Node::InnerNode(ref inner) => {
580                        self.stack[top].0 = Direction::Right;
581                        self.stack.push((Direction::Left, inner.left()));
582                    }
583                    Node::Leaf(ref leaf) => {
584                        self.stack[top].0 = Direction::Up;
585                        return Some((&leaf.key, &leaf.value));
586                    }
587                },
588                Direction::Right => match *node {
589                    Node::InnerNode(ref inner) => {
590                        self.stack[top].0 = Direction::Up;
591                        self.stack.push((Direction::Left, inner.right()));
592                    }
593                    Node::Leaf(_) => unreachable!(),
594                },
595                Direction::Up => unreachable!(),
596            }
597        }
598    }
599}
600
601impl<S, V: Clone + PartialEq> FromIterator<(S, V)> for CidrMap<V>
602where
603    S: Into<AnyIpCidr>,
604{
605    fn from_iter<I: IntoIterator<Item = (S, V)>>(iter: I) -> Self {
606        let mut map = CidrMap::new();
607        for (key, value) in iter {
608            map.insert(key.into(), value);
609        }
610        map
611    }
612}
613
614impl<T: Ord + Into<AnyIpCidr>, const N: usize, V: Clone + Ord> From<[(T, V); N]> for CidrMap<V> {
615    /// Converts a `[(T,V); N]` into a `CidrSet`.
616    fn from(mut arr: [(T, V); N]) -> Self {
617        if N == 0 {
618            return CidrMap::new();
619        }
620
621        // use stable sort to preserve the insertion order.
622        arr.sort();
623        let iter = IntoIterator::into_iter(arr);
624        iter.collect()
625    }
626}
627
628impl<V: Clone> From<CidrMap<V>> for Vec<(AnyIpCidr, V)> {
629    fn from(val: CidrMap<V>) -> Self {
630        let mut result = vec![];
631        for (key, value) in val.iter() {
632            result.push((*key, value.clone()));
633        }
634        result
635    }
636}
637
638#[cfg(feature = "lua")]
639impl LuaUserData for CidrMap<CacheValue> {
640    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
641        mod_memoize::Memoized::impl_memoize(methods);
642        methods.add_meta_method(MetaMethod::Index, |lua, this, key: String| {
643            let key = parse_cidr_from_ip_and_or_port(&key).map_err(any_err)?;
644            if let Some(value) = this.get_prefix_match_cidr(&key) {
645                let value = value.as_lua(lua)?;
646                Ok(Some(value))
647            } else {
648                Ok(None)
649            }
650        });
651        methods.add_meta_method_mut(
652            MetaMethod::NewIndex,
653            |lua, this, (key, value): (String, mlua::Value)| {
654                let key = parse_cidr_from_ip_and_or_port(&key).map_err(any_err)?;
655                let value = CacheValue::from_lua(value, lua)?;
656                this.insert(key, value);
657                Ok(())
658            },
659        );
660    }
661}
662
663#[cfg(feature = "lua")]
664fn parse_cidr_from_ip_and_or_port(s: &str) -> anyhow::Result<AnyIpCidr> {
665    match parse_cidr(s) {
666        Ok(c) => Ok(c),
667        Err(err) => {
668            if s.starts_with('[') {
669                if let Some((ip, _port)) = s[1..].split_once(']') {
670                    return parse_cidr(ip).map_err(|err| {
671                        anyhow::anyhow!(
672                            "failed to parse '{ip}', the \
673                             []-enclosed portion of '{s}', as an IP address: {err:#}"
674                        )
675                    });
676                }
677            }
678            if let Some((ip, _port)) = s.rsplit_once(':') {
679                return parse_cidr(ip).map_err(|err| {
680                    anyhow::anyhow!(
681                        "failed to parse '{ip}', the \
682                         :-delimited portion of '{s}', as an IP address: {err:#}"
683                    )
684                });
685            }
686            Err(err)
687        }
688    }
689}
690
691/// The underlying AnyIpCidr::from_str parser is very strict and its error messages
692/// are a little too terse.
693/// We use this alternative parser to augment the error messages with more context
694/// and suggestions.
695// <https://github.com/stbuehler/rust-cidr/issues/8>
696pub fn parse_cidr(s: &str) -> anyhow::Result<AnyIpCidr> {
697    AnyIpCidr::from_str(s).map_err(|err| {
698        match cidr::parsers::parse_any_cidr_full_ignore_hostbits(
699            s,
700            std::str::FromStr::from_str,
701            std::str::FromStr::from_str,
702        ) {
703            Ok(loose) => {
704                anyhow::anyhow!("{s} is not a valid CIDR: {err:#}. Did you mean {loose}?")
705            }
706            Err(err) => {
707                anyhow::anyhow!("{s} is not a valid CIDR: {err:#}")
708            }
709        }
710    })
711}
712
713#[cfg(feature = "lua")]
714pub fn register(lua: &Lua) -> anyhow::Result<()> {
715    use std::collections::HashMap;
716    let cidr_mod = get_or_create_sub_module(lua, "cidr")?;
717
718    cidr_mod.set(
719        "make_map",
720        lua.create_function(|lua, value: Option<HashMap<String, mlua::Value>>| {
721            let mut cmap: CidrMap<mod_memoize::CacheValue> = CidrMap::new();
722
723            if let Some(value) = value {
724                for (k, v) in value {
725                    let k = parse_cidr_from_ip_and_or_port(&k).map_err(any_err)?;
726                    let v = CacheValue::from_lua(v, lua)?;
727                    cmap.insert(k, v);
728                }
729            }
730
731            Ok(cmap)
732        })?,
733    )?;
734
735    Ok(())
736}
737
738#[cfg(test)]
739mod test {
740    use super::*;
741
742    #[test]
743    fn test_parse_error_message() {
744        assert_eq!(
745            parse_cidr("10.0.0.1/24").unwrap_err().to_string(),
746            "10.0.0.1/24 is not a valid CIDR: host part of address was not zero. Did you mean 10.0.0.0/24?"
747        );
748    }
749
750    #[test]
751    fn cidrmap() {
752        let set: CidrMap<&str> = [
753            (parse_cidr("127.0.0.1").unwrap(), "loopbackv4"),
754            (parse_cidr("::1").unwrap(), "loopbackv6"),
755            (parse_cidr("192.168.1.0/24").unwrap(), ".1"),
756            // This entry is overlapped by the preceding entry
757            (parse_cidr("192.168.1.24").unwrap(), ".1"),
758            (parse_cidr("192.168.3.0/28").unwrap(), ".3"),
759            (parse_cidr("192.168.3.2").unwrap(), ".3.split"),
760            (parse_cidr("10.0.3.0/24").unwrap(), "10.3"),
761            (parse_cidr("10.0.4.0/24").unwrap(), "10.4"),
762            (parse_cidr("10.0.7.0/24").unwrap(), "10.7"),
763        ]
764        .into();
765
766        fn get<'a>(set: &'a CidrMap<&str>, key: &str) -> Option<&'a str> {
767            let key = key.parse().unwrap();
768            set.get_prefix_match(key).copied()
769        }
770
771        assert_eq!(get(&set, "127.0.0.1"), Some("loopbackv4"));
772        assert_eq!(get(&set, "127.0.0.2"), None);
773        assert_eq!(get(&set, "::1"), Some("loopbackv6"));
774
775        assert_eq!(get(&set, "192.168.2.1"), None);
776
777        assert_eq!(get(&set, "192.168.1.0"), Some(".1"));
778        assert_eq!(get(&set, "192.168.1.1"), Some(".1"));
779        assert_eq!(get(&set, "192.168.1.100"), Some(".1"));
780        assert_eq!(get(&set, "192.168.1.24"), Some(".1"));
781
782        assert_eq!(get(&set, "192.168.3.0"), Some(".3"));
783        assert_eq!(get(&set, "192.168.3.16"), None);
784        assert_eq!(get(&set, "192.168.3.2"), Some(".3.split"));
785
786        // Note that the snapshot does not contain 192.168.1.24/32; that
787        // overlaps with the broader 192.168.1.0/24 so is "lost"
788        // when extracting the information from the set.
789        // Furthermore, the .3.split value inserted for .3.2
790        // causes more .3 entries to be generated to accomodate the
791        // split in that subnet.
792        k9::snapshot!(
793            &set,
794            r#"
795{
796    "10.0.3.0/24": "10.3",
797    "10.0.4.0/24": "10.4",
798    "10.0.7.0/24": "10.7",
799    "127.0.0.1": "loopbackv4",
800    "192.168.1.0/24": ".1",
801    "192.168.3.0/31": ".3",
802    "192.168.3.2": ".3.split",
803    "192.168.3.3": ".3",
804    "192.168.3.4/30": ".3",
805    "192.168.3.8/29": ".3",
806    "::1": "loopbackv6",
807}
808"#
809        );
810    }
811
812    #[test]
813    fn clone_is_shared_until_written() {
814        let mut map: CidrMap<&str> = CidrMap::new();
815        map.insert(parse_cidr("10.0.0.0/8").unwrap(), "ten");
816
817        let mut copy = map.clone();
818        assert!(Arc::ptr_eq(&map.root, &copy.root));
819
820        copy.insert(parse_cidr("192.168.0.0/16").unwrap(), "private");
821        assert!(!Arc::ptr_eq(&map.root, &copy.root));
822        assert_eq!(map.get_prefix_match("192.168.1.1".parse().unwrap()), None);
823        assert_eq!(
824            copy.get_prefix_match("192.168.1.1".parse().unwrap()),
825            Some(&"private")
826        );
827        assert_eq!(
828            copy.get_prefix_match("10.1.1.1".parse().unwrap()),
829            Some(&"ten")
830        );
831    }
832}