1use core::net::IpAddr;
2
3use crate::{RouteModification, RoutingTable, iptrie, node};
4
5#[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 #[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 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 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 table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Remove);
172 assert_eq!(0, table.size());
173 }
174
175 table.modify(pfx!("1.2.3.4/8"), |_entry| RouteModification::Noop);
177 assert_eq!(0, table.size());
178 }
179}