at master 7.5 kB view raw
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}