Skip to main content

ts_bart/table/
simple.rs

1use core::net::IpAddr;
2
3use crate::{RouteModification, RoutingTable, iptrie, node};
4
5/// Simple routing table that doesn't segregate IPv4 and IPv6
6/// routes.
7///
8/// The prefix `8.0.0.0/8` is treated equivalently to `0800::/8` by this table.
9/// You most likely want [`SplitStackTable`][super::SplitStackTable] if you're
10/// operating in a dual-IP-stack environment.
11#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
12pub struct SimpleTable<Node> {
13    root: Node,
14    size: usize,
15}
16
17impl<Node> SimpleTable<Node> {
18    /// Get a handle to the root node.
19    #[inline]
20    pub const fn root(&self) -> &Node {
21        &self.root
22    }
23}
24
25impl<T, C> SimpleTable<crate::Node<T, C>>
26where
27    C: ?Sized + crate::Storage,
28{
29    /// The empty table.
30    pub const EMPTY: Self = SimpleTable {
31        root: crate::Node::EMPTY,
32        size: 0,
33    };
34}
35
36impl<Node> RoutingTable for SimpleTable<Node>
37where
38    Node: node::StrideOps,
39{
40    type Value = Node::T;
41
42    #[inline]
43    fn contains(&self, ip: IpAddr) -> bool {
44        iptrie::contains(&self.root, ip)
45    }
46
47    #[inline]
48    fn insert(&mut self, prefix: ipnet::IpNet, val: Node::T) -> Option<Node::T> {
49        let ret = iptrie::insert(&mut self.root, prefix.trunc(), val);
50
51        if ret.is_none() {
52            self.size += 1;
53        }
54
55        ret
56    }
57
58    #[inline]
59    fn remove(&mut self, prefix: ipnet::IpNet) -> Option<Node::T> {
60        iptrie::remove(&mut self.root, prefix).inspect(|_| {
61            self.size -= 1;
62        })
63    }
64
65    #[inline]
66    fn modify_impl(
67        &mut self,
68        prefix: ipnet::IpNet,
69        modify: &mut dyn FnMut(Option<&mut Node::T>) -> RouteModification<Node::T>,
70    ) -> Option<Node::T> {
71        enum Op {
72            Noop,
73            Insert,
74            Remove,
75        }
76
77        let mut op = Op::Noop;
78
79        let ret = iptrie::modify(&mut self.root, prefix, |val| {
80            let ret = modify(val);
81            op = match &ret {
82                RouteModification::Noop => Op::Noop,
83                RouteModification::Remove => Op::Remove,
84                RouteModification::Insert(..) => Op::Insert,
85            };
86
87            ret
88        });
89
90        match (&ret, op) {
91            (None, Op::Insert) => {
92                self.size += 1;
93            }
94            (Some(..), Op::Remove) => {
95                self.size -= 1;
96            }
97            _ => {}
98        }
99
100        ret
101    }
102
103    #[inline]
104    fn clear(&mut self) {
105        self.root = Node::default();
106        self.size = 0;
107    }
108
109    #[inline]
110    fn lookup(&self, ip: IpAddr) -> Option<&Node::T> {
111        iptrie::lookup_address(&self.root, ip)
112    }
113
114    fn lookup_all(&self, ip: IpAddr) -> iptrie::LookupIter<'_, Self::Value> {
115        iptrie::lookup_address_all(&self.root, ip)
116    }
117
118    #[inline]
119    fn lookup_prefix_exact(&self, prefix: ipnet::IpNet) -> Option<&Node::T> {
120        iptrie::lookup_prefix_exact(&self.root, prefix)
121    }
122
123    #[inline]
124    fn lookup_prefix(&self, prefix: ipnet::IpNet) -> Option<&Node::T> {
125        iptrie::lookup_prefix_lpm(&self.root, prefix).map(|(_, t)| t)
126    }
127
128    #[inline]
129    fn lookup_prefix_lpm(&self, prefix: ipnet::IpNet) -> Option<(ipnet::IpNet, &Node::T)> {
130        iptrie::lookup_prefix_lpm(&self.root, prefix)
131    }
132
133    #[inline]
134    fn size(&self) -> usize {
135        self.size
136    }
137}
138
139#[cfg(test)]
140mod test {
141    use super::*;
142    use crate::{RoutingTableExt, pfx};
143
144    #[test]
145    fn size_tracking() {
146        let mut table = crate::SimpleTable::EMPTY;
147
148        table.insert(pfx!("1.2.3.4/8"), 32);
149        assert_eq!(1, table.size());
150
151        table.remove(pfx!("1.2.3.4/8"));
152        assert_eq!(0, table.size());
153
154        {
155            table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Insert(33));
156            assert_eq!(1, table.size());
157
158            // Idempotent
159            table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Insert(33));
160            assert_eq!(1, table.size());
161        }
162
163        table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Noop);
164        assert_eq!(1, table.size());
165
166        {
167            table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Remove);
168            assert_eq!(0, table.size());
169
170            // Idempotent (no panic)
171            table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Remove);
172            assert_eq!(0, table.size());
173        }
174
175        // Noop on empty table is fine
176        table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Noop);
177        assert_eq!(0, table.size());
178    }
179}