ip_cidr/
base.rs

1//! Base module
2
3use core::fmt;
4
5///Network address trait
6pub trait NetworkAddress: Clone + Copy + fmt::Debug + fmt::Display + PartialEq + Eq + PartialOrd + Ord {
7    ///Max possible length of the address in bits
8    const BITS_LEN: u8;
9}
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
12///CIDR representation of IPv4 network
13pub struct Cidr<A> {
14    prefix: u8,
15    addr: A,
16}
17
18impl<A: NetworkAddress> Cidr<A> {
19    #[inline]
20    ///Constructs new CIDR verifying that `prefix` fits provided `addrs`
21    ///
22    ///Returns `None` if `prefix` is greater than address length
23    pub const fn new(addr: A, prefix: u8) -> Option<Self> {
24        if prefix > A::BITS_LEN {
25            None
26        } else {
27            Some(Self {
28                addr,
29                prefix,
30            })
31        }
32    }
33
34    #[inline]
35    ///Constructs new CIDR with single `addr`
36    pub const fn new_single(addr: A) -> Self {
37        Self {
38            addr,
39            prefix: A::BITS_LEN
40        }
41    }
42
43    #[inline(always)]
44    ///Returns address
45    pub const fn addr(&self) -> A {
46        self.addr
47    }
48
49    #[inline(always)]
50    ///Returns prefix
51    pub const fn prefix(&self) -> u8 {
52        self.prefix
53    }
54}
55
56impl<A: NetworkAddress> fmt::Display for Cidr<A> {
57    #[inline(always)]
58    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
59        let Self { addr, prefix } = self;
60        fmt.write_fmt(format_args!("{addr}/{prefix}"))
61    }
62}
63
64macro_rules! impl_base_methods {
65    ($typ:ty where REPR=$repr:ident) => {
66        #[inline]
67        ///Computes network mask for provided `prefix`, assuming `prefix` is valid prefix
68        pub const fn mask(prefix: u8) -> $typ {
69            match prefix {
70                0 => <$typ>::UNSPECIFIED,
71                prefix => {
72                    let mask = $repr::MAX << (BITS_LEN.saturating_sub(prefix));
73                    <$typ>::from_bits(mask)
74                }
75            }
76        }
77
78        #[inline]
79        ///Computes network address from provided `addr` and `prefix`, which is lowest possible address within CIDR block
80        pub const fn network_addr(addr: $typ, prefix: u8) -> $typ {
81            let mask = mask(prefix).to_bits();
82            let addr = addr.to_bits() & mask;
83            <$typ>::from_bits(addr)
84        }
85
86        #[inline]
87        ///Computes network address from provided `addr` and `prefix`, which is highest possible address within CIDR block
88        pub const fn broadcast_addr(addr: $typ, prefix: u8) -> $typ {
89            let mask = mask(prefix).to_bits();
90            let broadcast = addr.to_bits() | !mask;
91            <$typ>::from_bits(broadcast)
92        }
93
94        #[inline]
95        ///Returns number of possible addresses
96        pub const fn size(prefix: u8) -> $repr {
97            match prefix {
98                0 => $repr::MAX,
99                prefix => 1 << (BITS_LEN.saturating_sub(prefix))
100            }
101        }
102
103        impl $crate::base::Cidr<$typ> {
104            #[inline(always)]
105            ///Computes network address from provided `addr` and `prefix`, which is lowest possible address within CIDR block
106            pub const fn network_addr(&self) -> $typ {
107                network_addr(self.addr(), self.prefix())
108            }
109
110            #[inline(always)]
111            ///Computes network address from provided `addr` and `prefix`, which is highest possible address within CIDR block
112            pub const fn broadcast_addr(&self) -> $typ {
113                broadcast_addr(self.addr(), self.prefix())
114            }
115
116            #[inline(always)]
117            ///Checks if a given `addr` is contained within `self`
118            pub const fn contains(&self, addr: $typ) -> bool {
119                (addr.to_bits() & mask(self.prefix()).to_bits()) == self.network_addr().to_bits()
120            }
121
122            #[inline(always)]
123            ///Returns number of possible addresses
124            pub const fn size(&self) -> $repr {
125                size(self.prefix())
126            }
127
128            #[inline(always)]
129            ///Attempts to fetch address by `idx` within the block `self`
130            pub const fn get(&self, idx: $repr) -> Option<$typ> {
131                if idx >= self.size() {
132                    return None;
133                }
134
135                Some(self.get_unchecked(idx))
136            }
137
138            #[inline]
139            ///Returns address corresponding `idx` without checking size according to the prefix
140            ///
141            ///This is safe in a sense as it is only wrapping math operation, but it should be only used
142            ///when you know need to iterate over possible addresses by pre-computing size
143            pub const fn get_unchecked(&self, idx: $repr) -> $typ {
144                let net = self.network_addr().to_bits();
145                <$typ>::from_bits(net.wrapping_add(idx))
146            }
147        }
148    }
149}
150
151pub(super) use impl_base_methods;