dia_ip_range/
ip_v4_component_range.rs

1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4Dia-IP-Range
5
6Copyright (C) 2021-2024  Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2021-2024".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27//! # IPv4 component range
28
29use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
30
31mod impls;
32
33const ONE: u8 = 1;
34
35/// # IPv4 component range
36///
37/// A range can be respresented in string, following these rules:
38///
39/// - A single `u8` value can be used. It will be parsed as a one-value range.
40/// - Start and end are placed inside one of `[]`, `[)`..., separated by a comma.
41/// - `[` and `]` are inclusive.
42/// - `(` and `)` are exclusive.
43/// - White spaces can be included. They will be ignored by parser.
44/// - Length of the string must be equal to or smaller than 16 bytes. It's for protection against flood attack.
45///
46/// This struct can be used to make [`IPv4Range`][struct:IPv4Range].
47///
48/// ## Examples
49///
50/// ```
51/// use {
52///     core::str::FromStr,
53///     dia_ip_range::IPv4ComponentRange,
54/// };
55///
56/// assert_eq!(
57///     IPv4ComponentRange::from_str("[2,100)")?,
58///     IPv4ComponentRange::from(2..100),
59/// );
60/// assert_eq!(
61///     IPv4ComponentRange::from_str("(9,99]")?,
62///     IPv4ComponentRange::from(10..100),
63/// );
64///
65/// # dia_ip_range::Result::Ok(())
66/// ```
67///
68/// [struct:IPv4Range]: struct.IPv4Range.html
69#[derive(Debug, Eq, PartialEq, Hash, Clone)]
70pub struct IPv4ComponentRange {
71    start: Option<u8>,
72    end: Option<u8>,
73}
74
75impl From<u8> for IPv4ComponentRange {
76
77    fn from(n: u8) -> Self {
78        Self {
79            start: Some(n),
80            end: Some(n),
81        }
82    }
83
84}
85
86impl From<Range<u8>> for IPv4ComponentRange {
87
88    fn from(range: Range<u8>) -> Self {
89        Self {
90            start: Some(range.start),
91            end: range.end.checked_sub(ONE),
92        }
93    }
94
95}
96
97impl From<RangeFrom<u8>> for IPv4ComponentRange {
98
99    fn from(range: RangeFrom<u8>) -> Self {
100        Self {
101            start: Some(range.start),
102            end: Some(u8::max_value()),
103        }
104    }
105
106}
107
108impl From<RangeFull> for IPv4ComponentRange {
109
110    fn from(_: RangeFull) -> Self {
111        Self {
112            start: Some(u8::min_value()),
113            end: Some(u8::max_value()),
114        }
115    }
116
117}
118
119impl From<RangeInclusive<u8>> for IPv4ComponentRange {
120
121    fn from(range: RangeInclusive<u8>) -> Self {
122        Self {
123            start: Some(*range.start()),
124            end: Some(*range.end()),
125        }
126    }
127
128}
129
130impl From<RangeTo<u8>> for IPv4ComponentRange {
131
132    fn from(range: RangeTo<u8>) -> Self {
133        Self {
134            start: Some(u8::min_value()),
135            end: range.end.checked_sub(ONE),
136        }
137    }
138
139}
140
141impl From<RangeToInclusive<u8>> for IPv4ComponentRange {
142
143    fn from(range: RangeToInclusive<u8>) -> Self {
144        Self {
145            start: Some(u8::min_value()),
146            end: Some(range.end),
147        }
148    }
149
150}
151
152impl Iterator for IPv4ComponentRange {
153
154    type Item = u8;
155
156    fn next(&mut self) -> Option<Self::Item> {
157        match (self.start, self.end) {
158            (Some(start), Some(end)) if start <= end => {
159                self.start = start.checked_add(ONE);
160                Some(start)
161            },
162            _ => None,
163        }
164    }
165
166    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
167        while n > 0 {
168            match (self.start, self.end) {
169                (Some(start), Some(end)) => match end.checked_sub(start).map(|skip| usize::from(skip).min(n)) {
170                    Some(skip) if skip > 0 => {
171                        self.start = Some(start + skip as u8);
172                        n -= skip;
173                    },
174                    _ => self.start = None,
175                },
176                _ => return None,
177            };
178        }
179
180        self.next()
181    }
182
183    // See ExactSizeIterator implementation for details.
184    #[cfg(not(target_pointer_width = "8"))]
185    fn size_hint(&self) -> (usize, Option<usize>) {
186        match (self.start, self.end) {
187            (Some(start), Some(end)) if end >= start => {
188                let result = usize::from(end - start);
189                match result.checked_add(ONE.into()) {
190                    None => (result, None),
191                    Some(result) => (result, Some(result)),
192                }
193            },
194            _ => (0, Some(0)),
195        }
196    }
197
198}
199
200// Size of `0..=255` is `256`. `usize` on machines with 8-bit pointer width cannot hold such value. Provided function ExactSizeIterator::len()
201// will panic in such case.
202#[cfg(not(target_pointer_width = "8"))]
203impl ExactSizeIterator for IPv4ComponentRange {}
204
205#[test]
206fn test_iterator_nth() -> crate::Result<()> {
207    use alloc::vec::Vec;
208
209    for (range, size_hint, values) in [
210        (0..5, (5, Some(5)), (0_usize..=9).collect::<Vec<_>>()), (0..255, (255, Some(255)), (0..512).collect()),
211    ] {
212        for n in values {
213            let mut ipv4_component_range = IPv4ComponentRange::from(range.clone());
214
215            assert_eq!(ipv4_component_range.size_hint(), size_hint);
216            assert_eq!(size_hint.0, ipv4_component_range.clone().count());
217
218            if n < range.end.into() {
219                assert_eq!(n, ipv4_component_range.nth(n).unwrap().into());
220            } else {
221                assert!(ipv4_component_range.nth(n).is_none());
222                assert!(ipv4_component_range.next().is_none());
223            }
224        }
225    }
226    for (range, size_hint, values) in [
227        (0..=5, (6, Some(6)), (0_usize..=9).collect::<Vec<_>>()), (0..=255, (256, Some(256)), (0..512).collect()),
228    ] {
229        for n in values {
230            let mut ipv4_component_range = IPv4ComponentRange::from(range.clone());
231
232            assert_eq!(ipv4_component_range.size_hint(), size_hint);
233            assert_eq!(size_hint.0, ipv4_component_range.clone().count());
234
235            if n <= (*range.end()).into() {
236                assert_eq!(n, ipv4_component_range.nth(n).unwrap().into());
237            } else {
238                assert!(ipv4_component_range.nth(n).is_none());
239                assert!(ipv4_component_range.next().is_none());
240            }
241        }
242    }
243
244    Ok(())
245}
246
247#[test]
248fn test_iterator() -> crate::Result<()> {
249    use core::str::FromStr;
250
251    for range in ["[1,0]", "(0,0)", "(0,1)"] {
252        assert_eq!(IPv4ComponentRange::from_str(range)?.count(), 0);
253    }
254    for (range, expected) in [("0", 0), ("1", 1), ("[0,0]", 0), ("(0,1]", 1), ("(1,3)", 2)] {
255        let mut range = IPv4ComponentRange::from_str(range)?;
256        assert_eq!(range.size_hint(), (1, Some(1)));
257        assert_eq!(range.nth(0), Some(expected));
258        assert_eq!(range.next(), None);
259    }
260    for (range, expected) in [
261        ("(0,2)", None), ("(0,3)", Some(2)), ("(1,4)", Some(3)),
262        ("[0,2]", Some(1)), ("[0,3]", Some(1)), ("[1,4]", Some(2)),
263        ("[0,2)", Some(1)), ("[0,1)", None),
264        ("(1,3]", Some(3)), ("(1,2]", None),
265    ] {
266        let mut range = IPv4ComponentRange::from_str(range)?;
267        assert_eq!(range.nth(1), expected);
268    }
269
270    Ok(())
271}