Implement iterators on pointers
This commit is contained in:
committed by
Jeremy Soller
parent
7e38bb1476
commit
0a23acd401
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
+140
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user