Skip to main content

relibc/
iter.rs

1//! Utilities to help use Rust iterators on C strings.
2
3use core::{
4    iter::{FusedIterator, Iterator},
5    marker::PhantomData,
6    mem::MaybeUninit,
7    ptr::NonNull,
8};
9
10use crate::platform::types::*;
11
12/// A minimal alternative to the `Zero` trait from num-traits, for use in
13/// `NulTerminated`.
14///
15/// May be replaced with the one from num-traits at a later time if so
16/// desired.
17pub unsafe trait Zero {
18    fn is_zero(&self) -> bool;
19}
20
21unsafe impl Zero for c_char {
22    fn is_zero(&self) -> bool {
23        self == &0
24    }
25}
26
27unsafe impl Zero for wchar_t {
28    fn is_zero(&self) -> bool {
29        self == &0
30    }
31}
32
33unsafe impl Zero for *mut c_char {
34    fn is_zero(&self) -> bool {
35        self.is_null()
36    }
37}
38
39/// An iterator over a nul-terminated buffer.
40///
41/// This is intended to allow safe, ergonomic iteration over C-style byte and
42/// wide strings without first having to read through the string and construct
43/// a slice. Assuming the safety requirements are upheld when constructing the
44/// iterator, it allows for string iteration in safe Rust.
45pub struct NulTerminated<'a, T: Zero> {
46    ptr: NonNull<T>,
47    phantom: PhantomData<&'a T>,
48}
49
50impl<'a, T: Zero> Iterator for NulTerminated<'a, T> {
51    type Item = &'a T;
52
53    fn next(&mut self) -> Option<Self::Item> {
54        // SAFETY: the caller is required to ensure a valid pointer to a
55        // 0-terminated buffer is provided, and the zero-check below ensures
56        // that iteration and pointer increments will stop in time.
57        let val_ref = unsafe { self.ptr.as_ref() };
58        if val_ref.is_zero() {
59            None
60        } else {
61            // SAFETY: the caller is required to provide a 0-terminated
62            // buffer, and this point will only be reached if the next element
63            // is at most the terminating 0.
64            self.ptr = unsafe { self.ptr.add(1) };
65            Some(val_ref)
66        }
67    }
68}
69
70impl<'a, T: Zero> NulTerminated<'a, T> {
71    /// Constructs a new iterator, starting at `ptr`, yielding elements of
72    /// type `&T` up to (but not including) the terminating nul.
73    ///
74    /// The iterator returns `None` after the terminating nul has been
75    /// encountered.
76    ///
77    /// # Safety
78    /// The provided pointer must be a valid pointer to a buffer of contiguous
79    /// elements of type `T`, and the value 0 must be present within the
80    /// buffer at or after `ptr` (not necessarily at the end). The buffer must
81    /// not be written to for the lifetime of the iterator.
82    pub unsafe fn new(ptr: *const T) -> Option<Self> {
83        Some(NulTerminated {
84            // NonNull can only wrap only *mut pointers...
85            ptr: NonNull::new(ptr.cast_mut())?,
86            phantom: PhantomData,
87        })
88    }
89}
90
91// Once the terminating nul has been encountered, the pointer will not advance
92// further and the iterator will thus keep returning None.
93impl<'a, T: Zero> FusedIterator for NulTerminated<'a, T> {}
94
95/// An iterator over a nul-terminated buffer, including the terminating nul.
96///
97/// Similar to [`NulTerminated`], but includes the terminating nul.
98pub struct NulTerminatedInclusive<'a, T: Zero> {
99    ptr_opt: Option<NonNull<T>>,
100    phantom: PhantomData<&'a T>,
101}
102
103impl<'a, T: Zero> Iterator for NulTerminatedInclusive<'a, T> {
104    type Item = &'a T;
105
106    fn next(&mut self) -> Option<Self::Item> {
107        if let Some(old_ptr) = self.ptr_opt {
108            // SAFETY: the caller is required to ensure a valid pointer to a
109            // 0-terminated buffer is provided, and the zero-check below
110            // ensures that iteration and pointer increments will stop in
111            // time.
112            let val_ref = unsafe { old_ptr.as_ref() };
113            self.ptr_opt = if val_ref.is_zero() {
114                None
115            } else {
116                // SAFETY: if a terminating nul value has been encountered,
117                // this will not be called
118                Some(unsafe { old_ptr.add(1) })
119            };
120            Some(val_ref)
121        } else {
122            None
123        }
124    }
125}
126
127impl<'a, T: Zero> NulTerminatedInclusive<'a, T> {
128    /// Constructs a new iterator, starting at `ptr`, yielding elements of
129    /// type `&T` up to and including the terminating nul.
130    ///
131    /// The iterator returns `None` after the terminating nul has been
132    /// encountered.
133    ///
134    /// # Safety
135    /// The provided pointer must be a valid pointer to a buffer of contiguous
136    /// elements of type `T`, and the value 0 must be present within the
137    /// buffer at or after `ptr` (not necessarily at the end). The buffer must
138    /// not be written to for the lifetime of the iterator.
139    pub unsafe fn new(ptr: *const T) -> Self {
140        NulTerminatedInclusive {
141            // NonNull can only wrap only *mut pointers...
142            ptr_opt: NonNull::new(ptr.cast_mut()),
143            phantom: PhantomData,
144        }
145    }
146}
147
148// Once the terminating nul has been encountered, the internal Option will be
149// set to None, ensuring that we will keep returning None.
150impl<'a, T: Zero> FusedIterator for NulTerminatedInclusive<'a, T> {}
151
152/// A zipped iterator mapping an input iterator to an "out" pointer.
153///
154/// This is intended to allow safe, iterative writing to an "out pointer".
155/// Special care needs to be taken to avoid creating references past the end
156/// of the output buffer, thus the output is zipped with an "input" iterator
157/// to ensure up-front control of the range of memory on which we create
158/// references.
159pub struct SrcDstPtrIter<'a, I: Iterator, U: Copy> {
160    src_iter: I,
161    dst_ptr: *mut U,
162    phantom: PhantomData<&'a mut U>,
163}
164
165impl<'a, I: Iterator, U: Copy> Iterator for SrcDstPtrIter<'a, I, U> {
166    type Item = (I::Item, &'a mut MaybeUninit<U>);
167
168    fn next(&mut self) -> Option<Self::Item> {
169        if let Some(src_item) = self.src_iter.next() {
170            let old_dst_ptr = self.dst_ptr;
171
172            // SAFETY: due to the caller requirements on `I` upon
173            // construction, the new pointer here may be either valid to turn
174            // into a reference or "one past the end". The latter is okay as
175            // long as it is only represented as a raw pointer.
176            self.dst_ptr = unsafe { self.dst_ptr.add(1) };
177
178            // SAFETY: self.dst_ptr may point "one past the end", but the
179            // caller is required upon construction to ensure that `I` does
180            // not over-iterate, and thus old_dst_ptr is always okay to
181            // dereference.
182            let out_mut_ref = unsafe { old_dst_ptr.as_uninit_mut() }.unwrap();
183
184            Some((src_item, out_mut_ref))
185        } else {
186            None
187        }
188    }
189}
190
191impl<'a, I: Iterator, U: Copy> SrcDstPtrIter<'a, I, U> {
192    /// Constructs a new iterator of "zipped" input and output.
193    ///
194    /// The caller must provide an "input" iterator `I` and an "out pointer"
195    /// `ptr`. Assuming `I` has item type `T`, the new iterator will have
196    /// `type Item = (T, &mut MaybeUninit<U>)`.
197    ///
198    /// # Safety
199    /// `ptr` must be a valid pointer to a writable buffer of contiguous (but
200    /// possibly uninitialized) elements of type `U`. The caller must ensure
201    /// that `I` does not return `Some` any more times than there are elements
202    /// in the output buffer. The caller must ensure that the iterator has
203    /// exclusive access to that buffer for the entire lifetime of the
204    /// iterator.
205    pub unsafe fn new(iter: I, ptr: *mut U) -> Self {
206        SrcDstPtrIter {
207            src_iter: iter,
208            dst_ptr: ptr,
209            phantom: PhantomData,
210        }
211    }
212}