1use crate::{
2 BaseIndex,
3 iptrie::{insert::insert_inner, remove, remove::RemovalInfo, util},
4 node::{Child, StrideOps},
5};
6
7#[derive(Debug, Default)]
9pub enum RouteModification<T> {
10 Insert(T),
12
13 Remove,
15
16 #[default]
19 Noop,
20}
21
22pub fn modify<N>(
40 node: &mut N,
41 prefix: ipnet::IpNet,
42 f: impl FnOnce(Option<&mut N::T>) -> RouteModification<N::T>,
43) -> Option<N::T>
44where
45 N: StrideOps,
46{
47 let prefix = prefix.trunc();
48 let addr = prefix.addr();
49
50 let octets = util::ip_octets(&addr);
51 let (ret, removal) = modify_inner(node, octets, &prefix, f);
52
53 if let Some(removal) = removal {
54 remove::compress_removal_path(node, removal);
55 }
56
57 ret
58}
59
60pub fn modify_inner<N>(
61 mut node: &mut N,
62 octets: &[u8],
63 prefix: &ipnet::IpNet,
64 f: impl FnOnce(Option<&mut N::T>) -> RouteModification<N::T>,
65) -> (Option<N::T>, Option<RemovalInfo<N::T>>)
66where
67 N: StrideOps,
68{
69 let mut compression_root_depth: usize = 0;
70 let (stride_count, overflow_bits) = util::stride_count_and_overflow(prefix);
71
72 for (depth, octet) in octets.iter().copied().enumerate() {
73 if depth == stride_count {
76 let idx = BaseIndex::from_prefix(octet, overflow_bits);
77
78 let val = node.get_prefix_exact_mut(idx);
79 return match f(val) {
80 RouteModification::Remove => {
82 let ret = node.remove_prefix(idx);
83 let removal_info = remove::try_remove_last_child(
84 node,
85 octets,
86 depth,
87 prefix.addr().is_ipv4(),
88 compression_root_depth,
89 );
90
91 (ret, removal_info)
92 }
93 RouteModification::Insert(t) => {
95 let ret = node.insert_prefix(idx, t);
96 (ret, None)
97 }
98 RouteModification::Noop => (None, None),
100 };
101 }
102
103 let Some(child) = node.get_child_mut(octet) else {
104 return match f(None) {
106 RouteModification::Remove | RouteModification::Noop => (None, None),
107 RouteModification::Insert(t) => {
108 if util::is_fringe(depth, prefix) {
109 node.insert_child(octet, Child::Fringe(t));
110 } else {
111 node.insert_child(
112 octet,
113 Child::Leaf {
114 value: t,
115 prefix: *prefix,
116 },
117 );
118 };
119
120 (None, None)
121 }
122 };
123 };
124
125 match child {
126 Child::Leaf {
129 prefix: leaf_prefix,
130 ..
131 } if leaf_prefix != *prefix => {
132 return match f(None) {
133 RouteModification::Noop | RouteModification::Remove => (None, None),
134 RouteModification::Insert(t) => (insert_inner(node, *prefix, t, depth), None),
135 };
136 }
137 Child::Fringe(..) if !util::is_fringe(depth, prefix) => {
141 return match f(None) {
142 RouteModification::Noop | RouteModification::Remove => (None, None),
143 RouteModification::Insert(t) => (insert_inner(node, *prefix, t, depth), None),
144 };
145 }
146
147 Child::Fringe(value) | Child::Leaf { value, .. } => {
150 return match f(Some(value)) {
151 RouteModification::Noop => (None, None),
152 RouteModification::Insert(t) => {
153 let insert_val = if util::is_fringe(depth, prefix) {
154 node.insert_child(octet, Child::Fringe(t))
155 } else {
156 node.insert_child(
157 octet,
158 Child::Leaf {
159 value: t,
160 prefix: *prefix,
161 },
162 )
163 };
164
165 (insert_val.unwrap().into_value(), None)
166 }
167 RouteModification::Remove => {
168 let ret = node.remove_child(octet).unwrap().into_value();
169 let removal_info = remove::try_remove_last_child(
170 node,
171 octets,
172 depth,
173 prefix.addr().is_ipv4(),
174 compression_root_depth,
175 );
176
177 (ret, removal_info)
178 }
179 };
180 }
181 Child::Path(..) => {}
182 }
183
184 let is_deletable = node.child_count() == 1 && node.prefix_count() == 0;
187 if !is_deletable {
188 compression_root_depth = depth;
189 }
190
191 let root = node.get_child_mut(octet).unwrap();
193 node = root.into_node().unwrap();
194 }
195
196 (None, None)
197}
198
199#[cfg(test)]
200mod test {
201 use super::*;
202 use crate::DefaultNode;
203
204 fn test_insert<T>(
205 node: &mut DefaultNode<T>,
206 val: T,
207 pfx: &str,
208 want_pfxs: usize,
209 want_leaves: usize,
210 want_fringes: usize,
211 ) where
212 T: Copy + PartialEq + core::fmt::Debug + 'static,
213 {
214 assert_eq!(
215 None,
216 modify(node, crate::pfx!(pfx), |entry| {
217 assert_eq!(None, entry);
218 RouteModification::Insert(val)
219 })
220 );
221 assert_eq!(
222 Some(val),
223 modify(node, crate::pfx!(pfx), |entry| {
224 assert_eq!(Some(val), entry.copied());
225 RouteModification::Insert(val)
226 })
227 );
228
229 let stats = node.stats();
230 assert_eq!(
231 (want_pfxs, want_leaves, want_fringes),
232 (stats.prefix_count, stats.leaf_count, stats.fringe_count),
233 "{node:#?}",
234 );
235 }
236
237 fn test_remove<T>(
238 node: &mut DefaultNode<T>,
239 expect: T,
240 pfx: &str,
241 want_pfxs: usize,
242 want_leaves: usize,
243 want_fringes: usize,
244 ) where
245 T: Copy + PartialEq + core::fmt::Debug + 'static,
246 {
247 assert_eq!(
248 Some(expect),
249 modify(node, crate::pfx!(pfx), |entry| {
250 assert_eq!(Some(expect), entry.copied());
251 RouteModification::Remove
252 })
253 );
254 assert_eq!(
255 None,
256 modify(node, crate::pfx!(pfx), |entry| {
257 assert_eq!(None, entry.copied());
258 RouteModification::Remove
259 })
260 );
261
262 let stats = node.stats();
263 assert_eq!(
264 (want_pfxs, want_leaves, want_fringes),
265 (stats.prefix_count, stats.leaf_count, stats.fringe_count),
266 "{node:#?}",
267 );
268 }
269
270 #[test]
271 fn basic() {
272 let node = &mut DefaultNode::EMPTY.clone();
273 assert!(node.is_empty());
274
275 test_insert(node, 3, "0.0.0.0/0", 1, 0, 0);
276 test_insert(node, 4, "0.0.0.0/1", 2, 0, 0);
277 test_insert(node, 5, "0.0.0.0/8", 2, 0, 1);
278 test_insert(node, 6, "1.0.32.0/23", 2, 1, 1);
279 test_insert(node, 7, "2.0.0.0/8", 2, 1, 2);
280
281 modify(node, crate::pfx!("2.0.0.0/8"), |val| {
282 assert_eq!(Some(&mut 7), val);
283 *val.unwrap() = 9;
284 RouteModification::Noop
285 });
286
287 test_remove(node, 9, "2.0.0.0/8", 2, 1, 1);
288 test_remove(node, 3, "0.0.0.0/0", 1, 1, 1);
289 test_remove(node, 5, "0.0.0.0/8", 1, 1, 0);
290 test_remove(node, 6, "1.0.32.0/23", 1, 0, 0);
291 test_remove(node, 4, "0.0.0.0/1", 0, 0, 0);
292
293 assert!(node.is_empty());
294 }
295
296 #[test]
297 fn nonmatching_fringe_exists() {
298 let node = &mut DefaultNode::EMPTY.clone();
299
300 test_insert(node, 3, "0.0.0.0/8", 0, 0, 1);
301 test_insert(node, 4, "0.1.2.0/22", 1, 1, 0);
302 }
303
304 #[test]
305 fn nonmatching_leaf_exists() {
306 let node = &mut DefaultNode::EMPTY.clone();
307
308 test_insert(node, 3, "0.2.0.0/15", 0, 1, 0);
309 test_insert(node, 4, "0.1.2.0/22", 1, 1, 0);
310 }
311}