Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl Ipv4Network {

/// Returns an iterator over `Ipv4Network`. Each call to `next` will return the next
/// `Ipv4Addr` in the given network. `None` will be returned when there are no more
/// addresses.
/// addresses. Use `rev()` to iterate in descending order.
pub fn iter(self) -> Ipv4NetworkIterator {
let mask = ipv4_mask_bits(self.prefix);
let start = self.addr.to_bits() & mask;
Expand Down Expand Up @@ -373,6 +373,19 @@ impl Iterator for Ipv4NetworkIterator {
}
}

impl DoubleEndedIterator for Ipv4NetworkIterator {
fn next_back(&mut self) -> Option<Ipv4Addr> {
let next = self.next?;
let end = self.end;
if next == end {
self.next = None;
} else {
self.end -= 1;
}
Some(end.into())
}
}

impl IntoIterator for &'_ Ipv4Network {
type IntoIter = Ipv4NetworkIterator;
type Item = Ipv4Addr;
Expand Down
15 changes: 14 additions & 1 deletion src/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl Ipv6Network {

/// Returns an iterator over `Ipv6Network`. Each call to `next` will return the next
/// `Ipv6Addr` in the given network. `None` will be returned when there are no more
/// addresses.
/// addresses. Use `rev()` to iterate in descending order.
///
/// # Warning
///
Expand Down Expand Up @@ -373,6 +373,19 @@ impl Iterator for Ipv6NetworkIterator {
}
}

impl DoubleEndedIterator for Ipv6NetworkIterator {
fn next_back(&mut self) -> Option<Ipv6Addr> {
let next = self.next?;
let end = self.end;
if next == end {
self.next = None;
} else {
self.end -= 1;
}
Some(end.into())
}
}

impl IntoIterator for &'_ Ipv6Network {
type IntoIter = Ipv6NetworkIterator;
type Item = Ipv6Addr;
Expand Down
18 changes: 18 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,15 @@ impl IpNetwork {
/// Returns an iterator over the addresses contained in the network.
///
/// This lists all the addresses in the network range, in ascending order.
/// Use `rev()` to iterate in descending order:
///
/// ```
/// use ipnetwork::IpNetwork;
/// use std::net::IpAddr;
///
/// let net: IpNetwork = "192.0.2.0/30".parse().unwrap();
/// assert_eq!(net.iter().rev().next(), Some("192.0.2.3".parse::<IpAddr>().unwrap()));
/// ```
pub fn iter(&self) -> IpNetworkIterator {
let inner = match self {
IpNetwork::V4(ip) => IpNetworkIteratorInner::V4(ip.iter()),
Expand Down Expand Up @@ -394,6 +403,15 @@ impl Iterator for IpNetworkIterator {
}
}

impl DoubleEndedIterator for IpNetworkIterator {
fn next_back(&mut self) -> Option<IpAddr> {
match &mut self.inner {
IpNetworkIteratorInner::V4(iter) => iter.next_back().map(IpAddr::V4),
IpNetworkIteratorInner::V6(iter) => iter.next_back().map(IpAddr::V6),
}
}
}

impl IntoIterator for &'_ IpNetwork {
type IntoIter = IpNetworkIterator;
type Item = IpAddr;
Expand Down
87 changes: 87 additions & 0 deletions tests/reverse_iterators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::collections::VecDeque;
use std::fmt::Debug;
use std::net::{Ipv4Addr, Ipv6Addr};

use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};

fn check_both_ends<I>(iter: I)
where
I: DoubleEndedIterator + Clone,
I::Item: Eq + Debug,
{
let expected: Vec<_> = iter.clone().collect();
assert_eq!(
iter.clone().rev().collect::<Vec<_>>(),
expected.into_iter().rev().collect::<Vec<_>>()
);

// Exercise every front/back consumption pattern for this four-address network.
for pattern in 0..16 {
let mut remaining: VecDeque<_> = iter.clone().collect();
let mut actual = iter.clone();
for bit in 0..4 {
assert_eq!(actual.size_hint(), (remaining.len(), Some(remaining.len())));
let (got, want) = if pattern & (1 << bit) == 0 {
(actual.next(), remaining.pop_front())
} else {
(actual.next_back(), remaining.pop_back())
};
assert_eq!(got, want);
}
assert_eq!(actual.next(), None);
assert_eq!(actual.next_back(), None);
assert_eq!(actual.next(), None);
}
}

#[test]
fn reverse_and_interleave_ipv4() {
let net: Ipv4Network = "192.0.2.1/30".parse().unwrap();
check_both_ends(net.iter());
}

#[test]
fn reverse_and_interleave_ipv6() {
let net: Ipv6Network = "2001:db8::1/126".parse().unwrap();
check_both_ends(net.iter());
}

#[test]
fn reverse_and_interleave_ip_network() {
for cidr in ["192.0.2.1/30", "2001:db8::1/126"] {
let net: IpNetwork = cidr.parse().unwrap();
check_both_ends(net.iter());
}
}

#[test]
fn singleton_networks_do_not_underflow_or_repeat() {
for addr in [Ipv4Addr::UNSPECIFIED, Ipv4Addr::BROADCAST] {
let mut iter = Ipv4Network::from(addr).iter();
assert_eq!(iter.next_back(), Some(addr));
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next(), None);
}
for addr in [Ipv6Addr::UNSPECIFIED, Ipv6Addr::from(u128::MAX)] {
let mut iter = Ipv6Network::from(addr).iter();
assert_eq!(iter.next_back(), Some(addr));
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next(), None);
}
}

#[test]
fn reverse_from_highest_address() {
let net: Ipv4Network = "255.255.255.255/31".parse().unwrap();
let mut iter = net.iter();
assert_eq!(iter.next_back(), Some(Ipv4Addr::BROADCAST));
assert_eq!(iter.next(), Some(Ipv4Addr::from(u32::MAX - 1)));
assert_eq!(iter.next_back(), None);

let net: Ipv6Network = "::/0".parse().unwrap();
let mut iter = net.iter();
assert_eq!(iter.next_back(), Some(Ipv6Addr::from(u128::MAX)));
assert_eq!(iter.next(), Some(Ipv6Addr::UNSPECIFIED));
assert_eq!(iter.next_back(), Some(Ipv6Addr::from(u128::MAX - 1)));
assert_eq!(iter.next(), Some(Ipv6Addr::LOCALHOST));
}
Loading