Skip to content

Commit 88150bd

Browse files
committed
Make ptr range iterable
1 parent 6414e0b commit 88150bd

File tree

3 files changed

+441
-0
lines changed

3 files changed

+441
-0
lines changed

library/core/src/iter/range.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod ptr;
2+
13
use crate::char;
24
use crate::convert::TryFrom;
35
use crate::mem;

library/core/src/iter/range/ptr.rs

+274
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
use crate::cmp::Ordering;
2+
use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
3+
use crate::mem;
4+
use crate::ops::Range;
5+
6+
macro_rules! impl_iterator_for_ptr_range {
7+
($mutability:ident /* const or mut */) => {
8+
/// Iteration of a pointer range, as is common in code that interfaces
9+
/// with C++ iterators.
10+
///
11+
/// # Safety
12+
///
13+
/// Traversing a pointer range is always safe, but **using the resulting
14+
/// pointers** is not!
15+
///
16+
/// The pointers between the start and end of a range "remember" the
17+
/// [allocated object] that they refer into. Pointers resulting from
18+
/// pointer arithmetic must not be used to read or write to any other
19+
/// allocated object.
20+
///
21+
/// As a consequence, pointers from a range traversal are only
22+
/// dereferenceable if start and end of the original range both point
23+
/// into the same allocated object. Dereferencing a pointer obtained via
24+
/// iteration when this is not the case is Undefined Behavior.
25+
///
26+
/// [allocated object]: crate::ptr#allocated-object
27+
///
28+
/// # Example
29+
///
30+
#[doc = example!($mutability)]
31+
#[stable(feature = "iterate_ptr_range", since = "1.58.0")]
32+
impl<T> Iterator for Range<*$mutability T> {
33+
type Item = *$mutability T;
34+
35+
fn next(&mut self) -> Option<Self::Item> {
36+
if self.is_empty() {
37+
None
38+
} else {
39+
let curr = self.start;
40+
let next = curr.wrapping_add(1);
41+
self.start = if (curr..self.end).contains(&next) {
42+
next
43+
} else {
44+
// Saturate to self.end if the wrapping_add wrapped or
45+
// landed beyond end.
46+
self.end
47+
};
48+
Some(curr)
49+
}
50+
}
51+
52+
fn size_hint(&self) -> (usize, Option<usize>) {
53+
if self.is_empty() {
54+
(0, Some(0))
55+
} else if mem::size_of::<T>() == 0 {
56+
// T is zero sized so there are infinity of them in the
57+
// nonempty range.
58+
(usize::MAX, None)
59+
} else {
60+
// In between self.start and self.end there are some number
61+
// of whole elements of type T, followed by possibly a
62+
// remainder element if T's size doesn't evenly divide the
63+
// byte distance between the endpoints. The remainder
64+
// element still counts as being part of this range, since
65+
// the pointer to it does lie between self.start and
66+
// self.end.
67+
let byte_offset = self.end as usize - self.start as usize;
68+
let number_of_whole_t = byte_offset / mem::size_of::<T>();
69+
let remainder_bytes = byte_offset % mem::size_of::<T>();
70+
let maybe_remainder_t = (remainder_bytes > 0) as usize;
71+
let hint = number_of_whole_t + maybe_remainder_t;
72+
(hint, Some(hint))
73+
}
74+
}
75+
76+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
77+
let _ = self.advance_by(n);
78+
self.next()
79+
}
80+
81+
fn last(mut self) -> Option<Self::Item> {
82+
self.next_back()
83+
}
84+
85+
fn min(mut self) -> Option<Self::Item> {
86+
self.next()
87+
}
88+
89+
fn max(mut self) -> Option<Self::Item> {
90+
self.next_back()
91+
}
92+
93+
fn is_sorted(self) -> bool {
94+
true
95+
}
96+
97+
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
98+
match self.size_hint().1 {
99+
None => {
100+
// T is zero sized. Advancing does nothing.
101+
Ok(())
102+
}
103+
Some(len) => match n.cmp(&len) {
104+
Ordering::Less => {
105+
// Advance past n number of whole elements.
106+
self.start = self.start.wrapping_add(n);
107+
Ok(())
108+
}
109+
Ordering::Equal => {
110+
// Advance past every single element in the
111+
// iterator, including perhaps the remainder
112+
// element, leaving an empty iterator.
113+
self.start = self.end;
114+
Ok(())
115+
}
116+
Ordering::Greater => {
117+
// Advance too far.
118+
self.start = self.end;
119+
Err(len)
120+
}
121+
}
122+
}
123+
}
124+
125+
#[doc(hidden)]
126+
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
127+
self.start.wrapping_add(idx)
128+
}
129+
}
130+
131+
#[stable(feature = "iterate_ptr_range", since = "1.58.0")]
132+
impl<T> DoubleEndedIterator for Range<*$mutability T> {
133+
fn next_back(&mut self) -> Option<Self::Item> {
134+
match self.size_hint().1 {
135+
None => {
136+
// T is zero sized so the iterator never progresses past
137+
// start, even if going backwards.
138+
Some(self.start)
139+
}
140+
Some(0) => {
141+
None
142+
}
143+
Some(len) => {
144+
self.end = self.start.wrapping_add(len - 1);
145+
Some(self.end)
146+
}
147+
}
148+
}
149+
150+
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
151+
match self.size_hint().1 {
152+
None => {
153+
// T is zero sized.
154+
Some(self.start)
155+
}
156+
Some(len) => {
157+
if n < len {
158+
self.end = self.start.wrapping_add(len - n - 1);
159+
Some(self.end)
160+
} else {
161+
self.end = self.start;
162+
None
163+
}
164+
}
165+
}
166+
}
167+
168+
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
169+
match self.size_hint().1 {
170+
None => {
171+
// T is zero sized. Advancing does nothing.
172+
Ok(())
173+
}
174+
Some(len) => match n.cmp(&len) {
175+
Ordering::Less => {
176+
// Advance leaving `len - n` elements in the
177+
// iterator. Careful to preserve the remainder
178+
// element if told to advance by 0.
179+
if n > 0 {
180+
self.end = self.start.wrapping_add(len - n);
181+
}
182+
Ok(())
183+
}
184+
Ordering::Equal => {
185+
// Advance past every single element in the
186+
// iterator, leaving an empty iterator.
187+
self.end = self.start;
188+
Ok(())
189+
}
190+
Ordering::Greater => {
191+
// Advance too far.
192+
self.end = self.start;
193+
Err(len)
194+
}
195+
}
196+
}
197+
}
198+
}
199+
200+
#[stable(feature = "iterate_ptr_range", since = "1.58.0")]
201+
impl<T> FusedIterator for Range<*$mutability T> {}
202+
203+
#[unstable(feature = "trusted_len", issue = "37572")]
204+
unsafe impl<T> TrustedLen for Range<*$mutability T> {}
205+
206+
#[doc(hidden)]
207+
#[unstable(feature = "trusted_random_access", issue = "none")]
208+
unsafe impl<T> TrustedRandomAccess for Range<*$mutability T> {}
209+
210+
#[doc(hidden)]
211+
#[unstable(feature = "trusted_random_access", issue = "none")]
212+
unsafe impl<T> TrustedRandomAccessNoCoerce for Range<*$mutability T> {
213+
const MAY_HAVE_SIDE_EFFECT: bool = false;
214+
}
215+
};
216+
}
217+
218+
macro_rules! example {
219+
(const) => {
220+
doc_comment_to_literal! {
221+
/// ```
222+
/// // Designed to be called from C++ or C.
223+
/// #[no_mangle]
224+
/// unsafe extern "C" fn demo(start: *const u16, end: *const u16) {
225+
/// for ptr in start..end {
226+
/// println!("{}", *ptr);
227+
/// }
228+
/// }
229+
///
230+
/// fn main() {
231+
/// let slice = &[1u16, 2, 3];
232+
/// let range = slice.as_ptr_range();
233+
/// unsafe { demo(range.start, range.end); }
234+
/// }
235+
/// ```
236+
}
237+
};
238+
239+
(mut) => {
240+
doc_comment_to_literal! {
241+
/// ```
242+
/// #![feature(vec_spare_capacity)]
243+
///
244+
/// use core::ptr;
245+
///
246+
/// // Designed to be called from C++ or C.
247+
/// #[no_mangle]
248+
/// unsafe extern "C" fn demo(start: *mut u16, end: *mut u16) {
249+
/// for (i, ptr) in (start..end).enumerate() {
250+
/// ptr::write(ptr, i as u16);
251+
/// }
252+
/// }
253+
///
254+
/// fn main() {
255+
/// let mut vec: Vec<u16> = Vec::with_capacity(100);
256+
/// let range = vec.spare_capacity_mut().as_mut_ptr_range();
257+
/// unsafe {
258+
/// demo(range.start.cast::<u16>(), range.end.cast::<u16>());
259+
/// vec.set_len(100);
260+
/// }
261+
/// }
262+
/// ```
263+
}
264+
};
265+
}
266+
267+
macro_rules! doc_comment_to_literal {
268+
($(#[doc = $example:literal])*) => {
269+
concat!($($example, '\n'),*)
270+
};
271+
}
272+
273+
impl_iterator_for_ptr_range!(const);
274+
impl_iterator_for_ptr_range!(mut);

0 commit comments

Comments
 (0)