1use core::net::IpAddr;
10
11use crate::{
12 base_index::BaseIndex,
13 node::{Child, PrefixOpsExt, StrideOps},
14};
15
16mod insert;
17mod lookup;
18mod modify;
19mod remove;
20pub mod util;
21
22pub use insert::insert;
23pub use lookup::*;
24pub use modify::{RouteModification, modify};
25pub use remove::remove;
26
27pub const MAX_DEPTH: usize = 16;
29
30pub type StridePath = [u8; MAX_DEPTH];
32
33pub fn contains<N>(mut node: &N, ip: IpAddr) -> bool
46where
47 N: StrideOps,
48{
49 for &octet in util::ip_octets(&ip) {
50 let index = BaseIndex::from_pfx_7(octet);
51
52 if node.prefix_count() != 0 && node.supersets_prefix(index) {
53 return true;
54 }
55
56 let Some(child) = node.get_child(octet) else {
57 return false;
58 };
59
60 match child {
61 Child::Path(n) => node = n,
62 Child::Fringe(_) => return true,
63 Child::Leaf { prefix, .. } => return prefix.contains(&ip),
64 }
65 }
66
67 false
68}
69
70#[cfg(test)]
71mod test {
72 use core::str::FromStr;
73
74 use super::*;
75 use crate::{Node, test_util::unique_prefixes};
76
77 #[test]
78 fn bart_examples_insert_get_delete() {
79 let mut node = Node::<_>::EMPTY;
80
81 let p1 = ipnet::IpNet::from_str("10.0.0.0/8").unwrap();
82 let p2 = ipnet::IpNet::from_str("10.1.0.0/16").unwrap();
83 let p3 = ipnet::IpNet::from_str("2001:db8::/32").unwrap();
84
85 assert_eq!(None, insert::insert(&mut node, p1, 100usize));
86 assert_eq!(None, insert::insert(&mut node, p2, 200usize));
87 assert_eq!(None, insert::insert(&mut node, p3, 300usize));
88
89 std::println!("post-insert: {node:#?}");
90
91 assert_eq!(Some(200), lookup_prefix_exact(&node, p2).copied());
92 assert_eq!(Some(100), remove::remove(&mut node, p1));
93 assert_eq!(None, lookup_prefix_exact(&node, p1));
94
95 std::println!("post-remove: {node:#?}");
96 }
97
98 fn test_insert_delete(
99 pfxs: &[&str],
100 want_pfxs: usize,
101 want_leaves: usize,
102 want_fringes: usize,
103 ) {
104 let mut node = Node::<_>::EMPTY;
105
106 for &pfx in pfxs {
107 insert::insert(&mut node, crate::pfx!(pfx), ());
108 insert::insert(&mut node, crate::pfx!(pfx), ()); }
110
111 let stats = node.stats();
112
113 assert_eq!(want_pfxs, stats.prefix_count);
114 assert_eq!(want_leaves, stats.leaf_count);
115 assert_eq!(want_fringes, stats.fringe_count);
116
117 for &pfx in pfxs {
118 remove::remove(&mut node, crate::pfx!(pfx));
119 remove::remove(&mut node, crate::pfx!(pfx)); }
121
122 let stats = node.stats();
123
124 assert_eq!(0, stats.prefix_count);
125 assert_eq!(0, stats.leaf_count);
126 assert_eq!(0, stats.fringe_count);
127 }
128
129 #[test]
130 fn bart_insert_delete_examples() {
131 test_insert_delete(&[], 0, 0, 0);
133
134 test_insert_delete(&["0.0.0.0/0"], 1, 0, 0);
136 test_insert_delete(&["::/0"], 1, 0, 0);
137
138 test_insert_delete(&["0.0.0.0/32"], 0, 1, 0);
140 test_insert_delete(&["::/32"], 0, 1, 0);
141
142 test_insert_delete(&["0.0.0.0/8"], 0, 0, 1);
144 test_insert_delete(&["::/8"], 0, 0, 1);
145
146 test_insert_delete(
148 &["0.0.0.0/0", "0.0.0.0/1", "0.0.0.0/2", "0.0.0.0/3"],
149 4,
150 0,
151 0,
152 );
153 test_insert_delete(&["::/0", "::/1", "::/2", "::/3"], 4, 0, 0);
154
155 test_insert_delete(
157 &[
158 "0.0.0.0/0",
160 "0.0.0.0/1",
161 "0.0.0.0/2",
162 "0.0.0.0/3",
163 "0.0.0.0/9",
165 "1.0.0.0/9",
166 "2.0.0.0/9",
167 "3.0.0.0/9",
168 ],
169 4,
170 4,
171 0,
172 );
173 test_insert_delete(
174 &[
175 "::/0", "::/1", "::/2", "::/3", "::/9", "0100::/9", "0200::/9", "0300::/9", ],
178 4,
179 4,
180 0,
181 );
182
183 test_insert_delete(
185 &[
186 "0.0.0.0/0",
188 "0.0.0.0/1",
189 "0.0.0.0/2",
190 "0.0.0.0/3",
191 "0.0.0.0/9",
193 "1.0.0.0/9",
194 "2.0.0.0/9",
195 "3.0.0.0/9",
196 "5.0.0.0/8",
198 "6.0.0.0/8",
199 "7.0.0.0/8",
200 "8.0.0.0/8",
201 ],
202 4,
203 4,
204 4,
205 );
206 test_insert_delete(
207 &[
208 "::/0", "::/1", "::/2", "::/3", "::/9", "0100::/9", "0200::/9", "0300::/9", "0400::/8", "0500::/8", "0600::/8", "0700::/8", ],
212 4,
213 4,
214 4,
215 );
216
217 test_insert_delete(
219 &[
220 "0.0.0.0/9",
222 "0.0.0.0/10",
223 "0.1.0.0/19",
225 "0.2.0.0/16",
227 ],
228 2,
229 1,
230 1,
231 );
232 test_insert_delete(&["::/9", "::/10", "0010::/19", "0020::/16"], 2, 1, 1);
233
234 test_insert_delete(
236 &[
237 "0.0.0.0/12", "0.0.0.0/16", "0.0.0.0/24", ],
241 2,
242 0,
243 1,
244 );
245 test_insert_delete(
246 &[
247 "::/12", "::/16", "::/24", ],
251 2,
252 0,
253 1,
254 );
255 }
256
257 proptest::proptest! {
258 #[test]
259 fn insert_remove(pfxs in unique_prefixes()) {
260 const PRINT: bool = false;
261 let mut node = Node::<()>::EMPTY;
262
263 for pfx in &pfxs {
264 if PRINT {
265 std::println!("insert {pfx}");
266 }
267
268 proptest::prop_assert!(insert(&mut node, *pfx, ()).is_none());
269 }
270
271 if PRINT {
272 for (path, desc) in node.descendants(false) {
273 std::println!("{path:?}: {desc:#?}");
274 }
275 }
276
277 let stats = node.stats();
278 proptest::prop_assert_eq!(pfxs.len(), stats.leaf_count + stats.prefix_count + stats.fringe_count);
279
280 for pfx in &pfxs {
281 if PRINT {
282 std::println!("remove {pfx}");
283 }
284
285 proptest::prop_assert!(remove(&mut node, *pfx).is_some());
286 }
287
288 proptest::prop_assert!(node.is_empty());
289 }
290 }
291}