diff --git a/src/header/string/mod.rs b/src/header/string/mod.rs index 285c19cfc59948e2788fd4db1bbebdc9edd9e814..0d16d6d2d127f6e4c501ec8d516ba0823572b8b0 100644 --- a/src/header/string/mod.rs +++ b/src/header/string/mod.rs @@ -1,11 +1,12 @@ //! string implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/string.h.html -use core::{mem, ptr, slice, usize}; +use core::{iter::once, mem, ptr, slice, usize}; use cbitset::BitSet256; use crate::{ header::{errno::*, signal}, + iter::{NulTerminated, SrcDstPtrIter}, platform::{self, types::*}, }; @@ -154,17 +155,10 @@ pub unsafe extern "C" fn strcoll(s1: *const c_char, s2: *const c_char) -> c_int #[no_mangle] pub unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char { - let mut i = 0; - - loop { - let byte = *src.offset(i); - *dst.offset(i) = byte; - - if byte == 0 { - break; - } - - i += 1; + let src_iter = unsafe { NulTerminated::new(src) }; + let src_dest_iter = unsafe { SrcDstPtrIter::new(src_iter.chain(once(&0)), dst) }; + for (src_item, dst_item) in src_dest_iter { + dst_item.write(*src_item); } dst @@ -262,19 +256,12 @@ pub unsafe extern "C" fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: siz #[no_mangle] pub unsafe extern "C" fn strlen(s: *const c_char) -> size_t { - strnlen(s, usize::MAX) + unsafe { NulTerminated::new(s) }.count() } #[no_mangle] pub unsafe extern "C" fn strnlen(s: *const c_char, size: size_t) -> size_t { - let mut i = 0; - while i < size { - if *s.add(i) == 0 { - break; - } - i += 1; - } - i as size_t + unsafe { NulTerminated::new(s) }.take(size).count() } #[no_mangle] diff --git a/src/header/wchar/mod.rs b/src/header/wchar/mod.rs index 0dea96faa84a4c4f4ec9afacfb8f542885514658..67e268e09f5bf5ac32aed7fdd1cba258fedbe51c 100644 --- a/src/header/wchar/mod.rs +++ b/src/header/wchar/mod.rs @@ -12,6 +12,7 @@ use crate::{ time::*, wctype::*, }, + iter::NulTerminated, platform::{self, types::*, ERRNO}, }; @@ -497,13 +498,7 @@ pub extern "C" fn wcsftime( #[no_mangle] pub unsafe extern "C" fn wcslen(ws: *const wchar_t) -> size_t { - let mut i = 0; - loop { - if *ws.add(i) == 0 { - return i; - } - i += 1; - } + unsafe { NulTerminated::new(ws) }.count() } #[no_mangle] diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000000000000000000000000000000000000..5b9f7cb1c90361b09e59ed0913fef84b044e080b --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,140 @@ +//! Utilities to help use Rust iterators on C strings. + +use core::{iter::Iterator, marker::PhantomData, mem::MaybeUninit, ptr::NonNull}; + +use crate::platform::types::*; + +/// A minimal alternative to the `Zero` trait from num-traits, for use in +/// `NulTerminated`. +/// +/// May be replaced with the one from num-traits at a later time if so +/// desired. +pub unsafe trait Zero { + fn is_zero(&self) -> bool; +} + +unsafe impl Zero for c_char { + fn is_zero(&self) -> bool { + self == &0 + } +} + +unsafe impl Zero for wchar_t { + fn is_zero(&self) -> bool { + self == &0 + } +} + +/// An iterator over a nul-terminated buffer. +/// +/// This is intended to allow safe, ergonomic iteration over C-style byte and +/// wide strings without first having to read through the string and construct +/// a slice. Assuming the safety requirements are upheld when constructing the +/// iterator, it allows for string iteration in safe Rust. +pub struct NulTerminated<'a, T: Zero> { + ptr: NonNull<T>, + phantom: PhantomData<&'a T>, +} + +impl<'a, T: Zero> Iterator for NulTerminated<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<Self::Item> { + // SAFETY: the caller is required to ensure a valid pointer to a + // 0-terminated buffer is provided, and the zero-check below ensures + // that iteration and pointer increments will stop in time. + let val_ref = unsafe { self.ptr.as_ref() }; + if val_ref.is_zero() { + None + } else { + // SAFETY: the caller is required to provide a 0-terminated + // buffer, and this point will only be reached if the next element + // is at most the terminating 0. + self.ptr = unsafe { self.ptr.add(1) }; + Some(val_ref) + } + } +} + +impl<'a, T: Zero> NulTerminated<'a, T> { + /// Constructs a new iterator, starting at `ptr`, yielding elements of + /// type `&T` up to (but not including) the terminating nul. + /// + /// The iterator returns `None` after the terminating nul has been + /// encountered. + /// + /// # Safety + /// The provided pointer must be a valid pointer to a buffer of contiguous + /// elements of type `T`, and the value 0 must be present within the + /// buffer at or after `ptr` (not necessarily at the end). The buffer must + /// not be written to for the lifetime of the iterator. + pub unsafe fn new(ptr: *const T) -> Self { + NulTerminated { + // NonNull can only wrap only *mut pointers... + ptr: NonNull::new(ptr.cast_mut()).unwrap(), + phantom: PhantomData, + } + } +} + +/// A zipped iterator mapping an input iterator to an "out" pointer. +/// +/// This is intended to allow safe, iterative writing to an "out pointer". +/// Special care needs to be taken to avoid creating references past the end +/// of the output buffer, thus the output is zipped with an "input" iterator +/// to ensure up-front control of the range of memory on which we create +/// references. +pub struct SrcDstPtrIter<'a, I: Iterator, U: Copy> { + src_iter: I, + dst_ptr: *mut U, + phantom: PhantomData<&'a mut U>, +} + +impl<'a, I: Iterator, U: Copy> Iterator for SrcDstPtrIter<'a, I, U> { + type Item = (I::Item, &'a mut MaybeUninit<U>); + + fn next(&mut self) -> Option<Self::Item> { + if let Some(src_item) = self.src_iter.next() { + let old_dst_ptr = self.dst_ptr; + + // SAFETY: due to the caller requirements on `I` upon + // construction, the new pointer here may be either valid to turn + // into a reference or "one past the end". The latter is okay as + // long as it is only represented as a raw pointer. + self.dst_ptr = unsafe { self.dst_ptr.add(1) }; + + // SAFETY: self.dst_ptr may point "one past the end", but the + // caller is required upon construction to ensure that `I` does + // not over-iterate, and thus old_dst_ptr is always okay to + // dereference. + let out_mut_ref = unsafe { old_dst_ptr.as_uninit_mut() }.unwrap(); + + Some((src_item, out_mut_ref)) + } else { + None + } + } +} + +impl<'a, I: Iterator, U: Copy> SrcDstPtrIter<'a, I, U> { + /// Constructs a new iterator of "zipped" input and output. + /// + /// The caller must provide an "input" iterator `I` and an "out pointer" + /// `ptr`. Assuming `I` has item type `T`, the new iterator will have + /// `type Item = (T, &mut MaybeUninit<U>)`. + /// + /// # Safety + /// `ptr` must be a valid pointer to a writable buffer of contiguous (but + /// possibly uninitialized) elements of type `U`. The caller must ensure + /// that `I` does not return `Some` any more times than there are elements + /// in the output buffer. The caller must ensure that the iterator has + /// exclusive access to that buffer for the entire lifetime of the + /// iterator. + pub unsafe fn new(iter: I, ptr: *mut U) -> Self { + SrcDstPtrIter { + src_iter: iter, + dst_ptr: ptr, + phantom: PhantomData, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 2104fbc851a5c1c0b8081957ee990afbb502475c..8ab4e35145c794f252916c2f2c768e82034d4dd5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ #![feature(lang_items)] #![feature(let_chains)] #![feature(linkage)] +#![feature(ptr_as_uninit)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] #![feature(sync_unsafe_cell)] @@ -52,6 +53,7 @@ pub mod error; pub mod fs; pub mod header; pub mod io; +pub mod iter; pub mod ld_so; pub mod platform; pub mod pthread;