1use core::{
7 fmt::{Debug, Display, Formatter},
8 num::NonZeroU8,
9};
10
11#[repr(transparent)]
19#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct BaseIndex(pub NonZeroU8);
21
22impl Debug for BaseIndex {
23 #[inline]
24 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
25 let (pfx, bits) = self.prefix();
26 write!(f, "BaseIndex({} => {pfx}/{bits})", self.0.get())
27 }
28}
29
30impl Display for BaseIndex {
31 #[inline]
32 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
33 Display::fmt(&self.0.get(), f)
34 }
35}
36
37impl BaseIndex {
38 #[inline]
44 pub const fn new(val: u8) -> Self {
45 Self::try_new(val).expect("index value was zero")
46 }
47
48 #[inline]
50 pub const fn try_new(val: u8) -> Option<Self> {
51 let Some(idx) = NonZeroU8::new(val) else {
52 return None;
53 };
54
55 Some(Self(idx))
56 }
57
58 #[inline]
60 pub const fn get(&self) -> u8 {
61 self.0.get()
62 }
63
64 #[inline]
71 pub const fn from_prefix(octet: u8, prefix_len: u8) -> Self {
72 assert!(
73 prefix_len <= 7,
74 "BaseIndex prefix length must be between 0 and 7"
75 );
76 BaseIndex::new(octet.unbounded_shr(8 - prefix_len as u32) + (1 << prefix_len))
77 }
78
79 #[inline]
83 pub const fn from_pfx_7(octet: u8) -> Self {
84 let ret = Self::new(0x80 + (octet.unbounded_shr(1)));
85 debug_assert!(ret.prefix().1 == 7);
86 ret
87 }
88
89 pub const fn prefix(self) -> (u8, u8) {
92 let pfx_len = self.len() - 1;
93
94 let shift_bits = 8 - pfx_len;
95 let mask = 0xffu8.unbounded_shr(shift_bits as _);
96
97 let octet = (self.get() & mask).unbounded_shl(shift_bits as _);
98
99 (octet, pfx_len)
100 }
101
102 #[inline]
105 pub const fn prefix_bits(&self, depth: usize) -> u8 {
106 let pfx_len_in_stride = self.len() - 1;
107 let base_bits = depth * 8;
108
109 base_bits as u8 + pfx_len_in_stride
110 }
111
112 #[inline]
118 pub const fn range(&self) -> core::ops::RangeInclusive<u8> {
119 let (first, pfx_len) = self.prefix();
120 let last = first | !net_mask(pfx_len);
121
122 first..=last
123 }
124
125 #[inline]
128 #[allow(clippy::len_without_is_empty)]
129 pub const fn len(&self) -> u8 {
130 (u8::BITS - self.get().leading_zeros()) as _
131 }
132
133 #[inline]
135 pub fn cmp_rank(&self, other: &Self) -> core::cmp::Ordering {
136 let (a_octet, a_bits) = self.prefix();
137 let (b_octet, b_bits) = other.prefix();
138
139 a_octet.cmp(&b_octet).then(a_bits.cmp(&b_bits))
140 }
141
142 #[inline]
144 pub const fn fmt_prefix(&self) -> impl Debug + use<> {
145 struct PrefixFormatter(u8, u8);
146 impl Debug for PrefixFormatter {
147 #[inline]
148 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
149 write!(f, "{}/{}", self.0, self.1)
150 }
151 }
152
153 let (prefix, bits) = self.prefix();
154 PrefixFormatter(prefix, bits)
155 }
156
157 #[inline]
167 pub const fn parent(&self) -> Option<BaseIndex> {
168 let val = self.0.get();
169 if val == 1 {
170 return None;
171 }
172
173 Some(BaseIndex::new(val / 2))
174 }
175
176 #[inline]
189 pub const fn children(&self) -> Option<(BaseIndex, BaseIndex)> {
190 let val = self.0.get();
191 if val >= 128 {
192 return None;
193 }
194
195 Some((BaseIndex::new(val * 2), BaseIndex::new(val * 2 + 1)))
196 }
197}
198
199impl From<BaseIndex> for u8 {
200 #[inline]
201 fn from(value: BaseIndex) -> Self {
202 value.get()
203 }
204}
205
206impl From<BaseIndex> for NonZeroU8 {
207 #[inline]
208 fn from(value: BaseIndex) -> Self {
209 value.0
210 }
211}
212
213impl From<NonZeroU8> for BaseIndex {
214 #[inline]
215 fn from(value: NonZeroU8) -> Self {
216 Self(value)
217 }
218}
219
220impl TryFrom<u8> for BaseIndex {
221 type Error = ();
222
223 #[inline]
224 fn try_from(value: u8) -> Result<Self, Self::Error> {
225 Self::try_new(value).ok_or(())
226 }
227}
228
229#[inline]
231pub const fn net_mask(bits: u8) -> u8 {
232 assert!(bits <= 8);
233
234 0xffu8.unbounded_shl((8 - bits) as _)
235}
236
237#[cfg(test)]
239mod test {
240 use super::*;
241
242 #[test]
243 fn test_octet_to_idx() {
244 assert_eq!(128, BaseIndex::from_pfx_7(0).get());
245 assert_eq!(255, BaseIndex::from_pfx_7(255).get());
246 assert_eq!(192, BaseIndex::from_pfx_7(128).get());
247 }
248
249 #[test]
250 fn test_pfx_bits() {
251 assert_eq!(0, BaseIndex::new(1).prefix_bits(0));
252 assert_eq!(4, BaseIndex::new(19).prefix_bits(0));
253 assert_eq!(124, BaseIndex::new(19).prefix_bits(15));
254 }
255
256 #[test]
257 fn test_pfx_to_idx() {
258 assert_eq!(1, BaseIndex::from_prefix(0, 0).get());
259 assert_eq!(2, BaseIndex::from_prefix(0, 1).get());
260 assert_eq!(3, BaseIndex::from_prefix(128, 1).get());
261 assert_eq!(21, BaseIndex::from_prefix(80, 4).get());
262 assert_eq!(255, BaseIndex::from_prefix(255, 7).get());
263 }
264
265 #[test]
266 fn test_idx_to_pfx() {
267 assert_eq!((0, 0), BaseIndex::new(1).prefix());
268 assert_eq!((224, 3), BaseIndex::new(15).prefix());
269 assert_eq!((254, 7), BaseIndex::new(255).prefix());
270
271 assert_eq!((0, 1), BaseIndex::new(2).prefix());
273 assert_eq!((128, 1), BaseIndex::new(3).prefix());
274
275 assert_eq!((0, 2), BaseIndex::new(4).prefix());
276 assert_eq!((64, 2), BaseIndex::new(5).prefix());
277 assert_eq!((128, 2), BaseIndex::new(6).prefix());
278 assert_eq!((192, 2), BaseIndex::new(7).prefix());
279
280 assert_eq!((224, 3), BaseIndex::new(15).prefix());
281 assert_eq!((240, 4), BaseIndex::new(31).prefix());
282 assert_eq!((248, 5), BaseIndex::new(63).prefix());
283 assert_eq!((252, 6), BaseIndex::new(127).prefix());
284 assert_eq!((254, 7), BaseIndex::new(255).prefix());
285 }
286
287 #[test]
288 fn test_idx_to_range() {
289 assert_eq!(0..=255, BaseIndex::new(1).range());
290 assert_eq!(0..=127, BaseIndex::new(2).range());
291 assert_eq!(128..=255, BaseIndex::new(3).range());
292 assert_eq!(0..=63, BaseIndex::new(4).range());
293 assert_eq!(0..=31, BaseIndex::new(8).range());
294 assert_eq!(160..=191, BaseIndex::new(13).range(),);
295 assert_eq!(68..=71, BaseIndex::new(81).range());
296 assert_eq!(252..=253, BaseIndex::new(254).range());
297 assert_eq!(254..=255, BaseIndex::new(255).range());
298 }
299
300 #[test]
301 fn test_net_mask() {
302 assert_eq!(0, net_mask(0));
303 assert_eq!(0x80, net_mask(1));
304 assert_eq!(0xc0, net_mask(2));
305 assert_eq!(0xe0, net_mask(3));
306 assert_eq!(0xf0, net_mask(4));
307 assert_eq!(0xf8, net_mask(5));
308 assert_eq!(0xfc, net_mask(6));
309 assert_eq!(0xfe, net_mask(7));
310 assert_eq!(0xff, net_mask(8));
311 }
312
313 #[test]
314 #[should_panic]
315 fn test_net_mask_panics() {
316 net_mask(9);
317 }
318
319 #[test]
320 fn roundtrip_pfx_7() {
321 let idx = BaseIndex::from_pfx_7(131);
322 std::println!("{idx:?}");
323
324 let (_pfx, bits) = idx.prefix();
326 assert_eq!(bits, 7);
327 }
328}