Skip to content

Commit 6f506ca

Browse files
committed
expose some arithmetic methods on Inet types
1 parent b7d60d9 commit 6f506ca

File tree

5 files changed

+485
-112
lines changed

5 files changed

+485
-112
lines changed

src/inet/combined.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,61 @@ impl IpInet {
8383
}
8484
}
8585

86+
/// decrements host part (without changing the network part);
87+
/// returns true on wrap around
88+
pub fn decrement(&mut self) -> bool {
89+
match self {
90+
Self::V4(mut c) => c.decrement(),
91+
Self::V6(mut c) => c.decrement(),
92+
}
93+
}
94+
95+
/// Returns previous address in network or `None` if it was the first address in the network
96+
pub const fn previous(self) -> Option<Self> {
97+
match self {
98+
Self::V4(c) => match c.previous() {
99+
Some(c) => Some(Self::V4(c)),
100+
None => None,
101+
},
102+
Self::V6(c) => match c.previous() {
103+
Some(c) => Some(Self::V6(c)),
104+
None => None,
105+
},
106+
}
107+
}
108+
109+
/// Find the nth host after the current one in the current network
110+
///
111+
/// Returned boolean indicates whether an overflow occured.
112+
pub const fn overflowing_add(self, step: u128) -> (Self, bool) {
113+
match self {
114+
Self::V4(c) => {
115+
let (c, overflow) = c.overflowing_add(step);
116+
(Self::V4(c), overflow)
117+
},
118+
Self::V6(c) => {
119+
let (c, overflow) = c.overflowing_add(step);
120+
(Self::V6(c), overflow)
121+
},
122+
}
123+
}
124+
125+
/// Find the nth host before the current one in the current network
126+
///
127+
/// Returned boolean indicates whether an overflow occured.
128+
pub const fn overflowing_sub(self, step: u128) -> (Self, bool) {
129+
match self {
130+
Self::V4(c) => {
131+
let (c, overflow) = c.overflowing_sub(step);
132+
(Self::V4(c), overflow)
133+
},
134+
Self::V6(c) => {
135+
let (c, overflow) = c.overflowing_sub(step);
136+
(Self::V6(c), overflow)
137+
},
138+
}
139+
}
140+
86141
/// network (i.e. drops the host information)
87142
pub const fn network(&self) -> IpCidr {
88143
match self {
@@ -203,6 +258,22 @@ impl Inet for IpInet {
203258
self.next()
204259
}
205260

261+
fn decrement(&mut self) -> bool {
262+
self.decrement()
263+
}
264+
265+
fn previous(self) -> Option<Self> {
266+
self.previous()
267+
}
268+
269+
fn overflowing_add(self, step: u128) -> (Self, bool) {
270+
self.overflowing_add(step)
271+
}
272+
273+
fn overflowing_sub(self, step: u128) -> (Self, bool) {
274+
self.overflowing_sub(step)
275+
}
276+
206277
fn network(&self) -> IpCidr {
207278
self.network()
208279
}
@@ -276,3 +347,23 @@ impl From<Ipv6Inet> for IpInet {
276347
Self::V6(c)
277348
}
278349
}
350+
351+
impl core::ops::Add<u128> for IpInet {
352+
type Output = IpInet;
353+
354+
fn add(self, step: u128) -> Self::Output {
355+
let (result, overflow) = self.overflowing_add(step);
356+
debug_assert!(!overflow, "{} + {} overflow", self, step);
357+
result
358+
}
359+
}
360+
361+
impl core::ops::Sub<u128> for IpInet {
362+
type Output = IpInet;
363+
364+
fn sub(self, step: u128) -> Self::Output {
365+
let (result, overflow) = self.overflowing_sub(step);
366+
debug_assert!(!overflow, "{} - {} overflow", self, step);
367+
result
368+
}
369+
}

src/inet/direct.rs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,69 @@ macro_rules! impl_inet_for {
126126
}
127127
}
128128

129+
/// decrements host part (without changing the network part);
130+
/// returns true on wrap around
131+
pub fn decrement(&mut self) -> bool {
132+
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_prev(
133+
self.address,
134+
self.network_length,
135+
);
136+
self.address = address;
137+
overflow
138+
}
139+
140+
/// Returns previous address in network or `None` if it was the first address in the network
141+
pub const fn previous(self) -> Option<Self> {
142+
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_prev(
143+
self.address,
144+
self.network_length,
145+
);
146+
if overflow {
147+
None
148+
} else {
149+
Some(Self {
150+
address,
151+
network_length: self.network_length,
152+
})
153+
}
154+
}
155+
156+
/// Find the nth host after the current one in the current network
157+
///
158+
/// Returned boolean indicates whether an overflow occured.
159+
pub const fn overflowing_add(self, step: u128) -> (Self, bool) {
160+
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_inc(
161+
self.address,
162+
self.network_length,
163+
step,
164+
);
165+
(
166+
Self {
167+
address,
168+
network_length: self.network_length,
169+
},
170+
overflow,
171+
)
172+
}
173+
174+
/// Find the nth host before the current one in the current network
175+
///
176+
/// Returned boolean indicates whether an overflow occured.
177+
pub const fn overflowing_sub(self, step: u128) -> (Self, bool) {
178+
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_dec(
179+
self.address,
180+
self.network_length,
181+
step,
182+
);
183+
(
184+
Self {
185+
address,
186+
network_length: self.network_length,
187+
},
188+
overflow,
189+
)
190+
}
191+
129192
/// network (i.e. drops the host information)
130193
pub const fn network(&self) -> $cidr {
131194
$cidr {
@@ -226,6 +289,22 @@ macro_rules! impl_inet_for {
226289
self.next()
227290
}
228291

292+
fn decrement(&mut self) -> bool {
293+
self.decrement()
294+
}
295+
296+
fn previous(self) -> Option<Self> {
297+
self.previous()
298+
}
299+
300+
fn overflowing_add(self, step: u128) -> (Self, bool) {
301+
self.overflowing_add(step)
302+
}
303+
304+
fn overflowing_sub(self, step: u128) -> (Self, bool) {
305+
self.overflowing_sub(step)
306+
}
307+
229308
fn network(&self) -> $cidr {
230309
self.network()
231310
}
@@ -295,8 +374,86 @@ macro_rules! impl_inet_for {
295374
inet_from_str(s)
296375
}
297376
}
377+
378+
impl core::ops::Add<u128> for $n {
379+
type Output = $n;
380+
381+
fn add(self, step: u128) -> Self::Output {
382+
let (result, overflow) = self.overflowing_add(step);
383+
debug_assert!(!overflow, "{} + {} overflow", self, step);
384+
result
385+
}
386+
}
387+
388+
impl core::ops::Sub<u128> for $n {
389+
type Output = $n;
390+
391+
fn sub(self, step: u128) -> Self::Output {
392+
let (result, overflow) = self.overflowing_sub(step);
393+
debug_assert!(!overflow, "{} - {} overflow", self, step);
394+
result
395+
}
396+
}
298397
};
299398
}
300399

301400
impl_inet_for! {Ipv4Inet : cidr Ipv4Cidr : addr Ipv4Addr : pair Ipv4InetPair : family Family::Ipv4}
302401
impl_inet_for! {Ipv6Inet : cidr Ipv6Cidr : addr Ipv6Addr : pair Ipv6InetPair : family Family::Ipv6}
402+
403+
impl Ipv4Inet {
404+
/// Find the nth host after the current one in the current network (32-bit IPv4 variant)
405+
///
406+
/// Returned boolean indicates whether an overflow occured.
407+
pub const fn overflowing_add_u32(self, step: u32) -> (Self, bool) {
408+
let (address, overflow) = <Ipv4Addr as PrivUnspecAddress>::_Tools::_overflowing_inc_u32(
409+
self.address,
410+
self.network_length,
411+
step,
412+
);
413+
(
414+
Self {
415+
address,
416+
network_length: self.network_length,
417+
},
418+
overflow,
419+
)
420+
}
421+
422+
/// Find the nth host before the current one in the current network (32-bit IPv4 variant)
423+
///
424+
/// Returned boolean indicates whether an overflow occured.
425+
pub const fn overflowing_sub_u32(self, step: u32) -> (Self, bool) {
426+
let (address, overflow) = <Ipv4Addr as PrivUnspecAddress>::_Tools::_overflowing_dec_u32(
427+
self.address,
428+
self.network_length,
429+
step,
430+
);
431+
(
432+
Self {
433+
address,
434+
network_length: self.network_length,
435+
},
436+
overflow,
437+
)
438+
}
439+
}
440+
441+
impl core::ops::Add<u32> for Ipv4Inet {
442+
type Output = Ipv4Inet;
443+
444+
fn add(self, step: u32) -> Self::Output {
445+
let (result, overflow) = self.overflowing_add_u32(step);
446+
debug_assert!(!overflow, "{} + {} overflow", self, step);
447+
result
448+
}
449+
}
450+
451+
impl core::ops::Sub<u32> for Ipv4Inet {
452+
type Output = Ipv4Inet;
453+
454+
fn sub(self, step: u32) -> Self::Output {
455+
let (result, overflow) = self.overflowing_sub_u32(step);
456+
debug_assert!(!overflow, "{} - {} overflow", self, step);
457+
result
458+
}
459+
}

src/inet/tests.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
use core::cmp::Ordering;
1+
use core::{
2+
cmp::Ordering,
3+
str::FromStr,
4+
};
25
use std::net::{
36
IpAddr,
47
Ipv4Addr,
58
Ipv6Addr,
69
};
710

811
use crate::{
12+
errors::NetworkParseError,
13+
Inet,
914
IpInet,
1015
Ipv4Inet,
1116
Ipv6Inet,
@@ -963,3 +968,67 @@ fn order_v6() {
963968
fn order() {
964969
test_order(Ordering::Less, "192.0.2.0/24", "2001:DB8:1234:5678::/64");
965970
}
971+
972+
fn test_nth<I>(a: &str, b: &str, step: u128, overflow: bool)
973+
where
974+
I: Inet
975+
+ FromStr<Err = NetworkParseError>
976+
+ core::ops::Add<u128, Output = I>
977+
+ core::ops::Sub<u128, Output = I>,
978+
{
979+
let a = a.parse::<I>().unwrap();
980+
let b = b.parse::<I>().unwrap();
981+
982+
assert_eq!(a.overflowing_add(step), (b, overflow));
983+
assert_eq!(b.overflowing_sub(step), (a, overflow));
984+
985+
if !overflow {
986+
// overflow would trigger debug asserts here
987+
assert_eq!(a + step, b);
988+
assert_eq!(b - step, a);
989+
}
990+
}
991+
992+
#[test]
993+
fn test_nth_v4() {
994+
test_nth::<Ipv4Inet>("255.255.255.255/0", "0.0.0.0/0", 1, true);
995+
test_nth::<IpInet>("255.255.255.255/0", "0.0.0.0/0", 1, true);
996+
test_nth::<Ipv4Inet>("255.255.255.255/0", "127.0.0.1/0", 0x7f000002, true);
997+
test_nth::<IpInet>("255.255.255.255/0", "127.0.0.1/0", 0x7f000002, true);
998+
999+
test_nth::<Ipv4Inet>("192.0.2.23/24", "192.0.2.42/24", 19, false);
1000+
test_nth::<IpInet>("192.0.2.23/24", "192.0.2.42/24", 19, false);
1001+
test_nth::<Ipv4Inet>("192.0.2.42/24", "192.0.2.23/24", 237, true);
1002+
test_nth::<IpInet>("192.0.2.42/24", "192.0.2.23/24", 237, true);
1003+
}
1004+
1005+
#[test]
1006+
fn test_nth_v6() {
1007+
test_nth::<Ipv6Inet>("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", "::/0", 1, true);
1008+
test_nth::<IpInet>("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", "::/0", 1, true);
1009+
1010+
test_nth::<Ipv6Inet>(
1011+
"2001:DB8:1234:5678::23/64",
1012+
"2001:DB8:1234:5678::42/64",
1013+
0x1f,
1014+
false,
1015+
);
1016+
test_nth::<IpInet>(
1017+
"2001:DB8:1234:5678::23/64",
1018+
"2001:DB8:1234:5678::42/64",
1019+
0x1f,
1020+
false,
1021+
);
1022+
test_nth::<Ipv6Inet>(
1023+
"2001:DB8:1234:5678::42/64",
1024+
"2001:DB8:1234:5678::23/64",
1025+
(1 << 64) - 0x1f,
1026+
true,
1027+
);
1028+
test_nth::<IpInet>(
1029+
"2001:DB8:1234:5678::42/64",
1030+
"2001:DB8:1234:5678::23/64",
1031+
(1 << 64) - 0x1f,
1032+
true,
1033+
);
1034+
}

0 commit comments

Comments
 (0)