Skip to content

Commit 7be4829

Browse files
authored
Add a RangeInclusive library with non-mutating iterators (theseus-os#977)
* The `core::ops::RangeInclusive` type has many problems: weird mutable iterator behavior, non-Clone/Copy, unexpected extra memory usage beyond the two bounds, etc. This crate implements a version of `RangeInclusive` that aims to address these problems with an API that offers a separate iterator type rather than implementing `Iterator` directly on the range. * This will be used as the inner implementation for both `PageRange` and `FrameRange` in an upcoming PR.
1 parent 75f7dda commit 7be4829

File tree

4 files changed

+260
-0
lines changed

4 files changed

+260
-0
lines changed

libs/range_inclusive/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Generated by Cargo
2+
/target/

libs/range_inclusive/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
authors = ["Ramla Ijaz <[email protected]>"]
3+
name = "range_inclusive"
4+
description = "An implementation of RangeInclusive with an iterator that doesn't mutate the range."
5+
version = "0.1.0"

libs/range_inclusive/src/lib.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//! A `RangeInclusive` implementation that offers a separate iterator type.
2+
//!
3+
//! This `RangeInclusive` does not directly implement the `Iterator` trait,
4+
//! allowing one to iterate over a copy of the range without mutating the original range.
5+
//! This is accomplished by requiring that the generic type `Idx` implements `Clone`;
6+
//! in the future we can remove this trait bound by using references, though most users
7+
//! of a range type typically do so with numeric types that are cheap to copy/clone.
8+
//!
9+
//! All behavior except iteration matches that of `std::ops::RangeInclusive`.
10+
//! Due to the iterator behavior, there is no need to waste space tracking
11+
//! whether the range has been `exhausted`, meaning that this `RangeInclusive`
12+
//! is exactly the size of its two bounds.
13+
14+
#![no_std]
15+
#![feature(step_trait)]
16+
17+
#[cfg(test)]
18+
mod test;
19+
20+
use core::iter::Step;
21+
use core::ops::{RangeBounds, Bound, Bound::Included};
22+
23+
/// A range bounded inclusively below and above (`start..=end`).
24+
///
25+
/// The `RangeInclusive` `start..=end` contains all values with `x >= start`
26+
/// and `x <= end`. It is empty unless `start <= end`.
27+
///
28+
/// See the crate-level docs for more information.
29+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
30+
pub struct RangeInclusive<Idx: Clone + PartialOrd> {
31+
pub(crate) start: Idx,
32+
pub(crate) end: Idx
33+
}
34+
35+
impl<Idx: Clone + PartialOrd> RangeInclusive<Idx> {
36+
/// Creates a new inclusive range.
37+
#[inline]
38+
pub const fn new(start: Idx, end: Idx) -> Self {
39+
Self{ start, end }
40+
}
41+
42+
/// Returns the lower bound of the range (inclusive).
43+
#[inline]
44+
pub const fn start(&self) -> &Idx {
45+
&self.start
46+
}
47+
48+
/// Returns the upper bound of the range (inclusive).
49+
#[inline]
50+
pub const fn end(&self) -> &Idx {
51+
&self.end
52+
}
53+
54+
/// Destructures the `RangeInclusive` into (lower bound, upper (inclusive) bound).
55+
#[inline]
56+
pub fn into_inner(self) -> (Idx, Idx) {
57+
(self.start, self.end)
58+
}
59+
60+
/// Returns `true` if the range contains no items.
61+
pub fn is_empty(&self) -> bool {
62+
!(self.start <= self.end)
63+
}
64+
65+
/// Returns an iterator with the same `start` and `end` values as the range.
66+
pub fn iter(&self) -> RangeInclusiveIterator<Idx> {
67+
RangeInclusiveIterator { current: self.start.clone(), end: self.end.clone() }
68+
}
69+
70+
/// Returns `true` if `item` is contained in the range.
71+
pub fn contains<U>(&self, item: &U) -> bool
72+
where
73+
Idx: PartialOrd<U>,
74+
U: ?Sized + PartialOrd<Idx>,
75+
{
76+
<Self as RangeBounds<Idx>>::contains(self, item)
77+
}
78+
}
79+
80+
impl<T: Clone + PartialOrd> RangeBounds<T> for RangeInclusive<T> {
81+
fn start_bound(&self) -> Bound<&T> {
82+
Included(&self.start)
83+
}
84+
fn end_bound(&self) -> Bound<&T> {
85+
Included(&self.end)
86+
}
87+
}
88+
89+
impl<'a, Idx: Clone + PartialOrd + Step> IntoIterator for &'a RangeInclusive<Idx> {
90+
type Item = Idx;
91+
type IntoIter = RangeInclusiveIterator<Idx>;
92+
93+
fn into_iter(self) -> RangeInclusiveIterator<Idx> {
94+
self.iter()
95+
}
96+
}
97+
98+
/// An iterator for the `RangeInclusive` type.
99+
///
100+
/// By creating a separate iterator, the original range is not mutated.
101+
pub struct RangeInclusiveIterator<Idx> {
102+
/// The current value of the iterator which is returned by `next()`.
103+
current: Idx,
104+
/// The end value of the iterator.
105+
end: Idx
106+
}
107+
108+
impl<A: Step> Iterator for RangeInclusiveIterator<A> {
109+
type Item = A;
110+
111+
fn next(&mut self) -> Option<Self::Item> {
112+
if self.current > self.end {
113+
None
114+
} else {
115+
let n = Step::forward_checked(self.current.clone(), 1).expect("`Step` invariants not upheld");
116+
Some(core::mem::replace(&mut self.current, n))
117+
}
118+
}
119+
}
120+
121+
impl<A: Step> ExactSizeIterator for RangeInclusiveIterator<A> {
122+
fn len(&self) -> usize {
123+
Step::steps_between(&self.current, &self.end).map(|x| x+1).unwrap_or(0)
124+
}
125+
}
126+
127+
impl<A: Step> DoubleEndedIterator for RangeInclusiveIterator<A> {
128+
fn next_back(&mut self) -> Option<Self::Item> {
129+
if self.current > self.end {
130+
None
131+
} else {
132+
let n = Step::backward_checked(self.end.clone(), 1).expect("`Step` invariants not upheld");
133+
Some(core::mem::replace(&mut self.end, n))
134+
}
135+
}
136+
}

libs/range_inclusive/src/test.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
extern crate std;
2+
use self::std::vec::Vec;
3+
4+
use crate::*;
5+
6+
#[test]
7+
fn greater_end() {
8+
let range = RangeInclusive::new(0 , 1);
9+
assert!(!range.is_empty());
10+
11+
let mut range_elements = Vec::new();
12+
for r in range.iter() {
13+
range_elements.push(r);
14+
}
15+
assert_eq!(range_elements[0], range.start);
16+
assert_eq!(range_elements[range_elements.len() - 1], range.end);
17+
assert_eq!(range_elements.len(), range.end - range.start + 1);
18+
19+
20+
let range = RangeInclusive::new(10 , 17);
21+
assert!(!range.is_empty());
22+
23+
let mut range_elements = Vec::new();
24+
for r in range.iter() {
25+
range_elements.push(r);
26+
}
27+
assert_eq!(range_elements[0], range.start);
28+
assert_eq!(range_elements[range_elements.len() - 1], range.end);
29+
assert_eq!(range_elements.len(), range.end - range.start + 1);
30+
31+
32+
let range = RangeInclusive::new(597 , 982);
33+
assert!(!range.is_empty());
34+
35+
let mut range_elements = Vec::new();
36+
for r in range.iter() {
37+
range_elements.push(r);
38+
}
39+
assert_eq!(range_elements[0], range.start);
40+
assert_eq!(range_elements[range_elements.len() - 1], range.end);
41+
assert_eq!(range_elements.len(), range.end - range.start + 1);
42+
}
43+
44+
#[test]
45+
fn equal_start_end() {
46+
let range = RangeInclusive::new(0 , 0);
47+
assert!(!range.is_empty());
48+
49+
let mut range_elements = Vec::new();
50+
for r in range.iter() {
51+
range_elements.push(r);
52+
}
53+
assert_eq!(range_elements[0], range.start);
54+
assert_eq!(range_elements[range_elements.len() - 1], range.end);
55+
assert_eq!(range_elements.len(), range.end - range.start + 1);
56+
57+
let range = RangeInclusive::new(597 , 597);
58+
assert!(!range.is_empty());
59+
60+
let mut range_elements = Vec::new();
61+
for r in range.iter() {
62+
range_elements.push(r);
63+
}
64+
assert_eq!(range_elements[0], range.start);
65+
assert_eq!(range_elements[range_elements.len() - 1], range.end);
66+
assert_eq!(range_elements.len(), range.end - range.start + 1);
67+
}
68+
69+
#[test]
70+
fn greater_start() {
71+
let range = RangeInclusive::new(782 , 597);
72+
assert!(range.is_empty());
73+
let mut range_elements = Vec::new();
74+
for r in range.iter() {
75+
range_elements.push(r);
76+
}
77+
assert!(range_elements.is_empty());
78+
79+
let range = RangeInclusive::new(1 , 0);
80+
assert!(range.is_empty());
81+
let mut range_elements = Vec::new();
82+
for r in range.iter() {
83+
range_elements.push(r);
84+
}
85+
assert!(range_elements.is_empty());
86+
}
87+
88+
#[test]
89+
fn other_iterators() {
90+
let range = RangeInclusive::new(1 , 6);
91+
let mut iter = range.iter();
92+
assert_eq!(iter.len(), 6);
93+
assert_eq!(Some(1), iter.next());
94+
95+
assert_eq!(iter.len(), 5);
96+
assert_eq!(Some(6), iter.next_back());
97+
98+
assert_eq!(iter.len(), 4);
99+
assert_eq!(Some(5), iter.next_back());
100+
101+
assert_eq!(iter.len(), 3);
102+
assert_eq!(Some(2), iter.next());
103+
104+
assert_eq!(iter.len(), 2);
105+
assert_eq!(Some(3), iter.next());
106+
107+
assert_eq!(iter.len(), 1);
108+
assert_eq!(Some(4), iter.next());
109+
110+
assert_eq!(iter.len(), 0);
111+
assert_eq!(None, iter.next());
112+
113+
assert_eq!(iter.len(), 0);
114+
assert_eq!(None, iter.next_back());
115+
116+
assert_eq!(iter.len(), 0);
117+
}

0 commit comments

Comments
 (0)