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