Implement CVec, an abstraction to return vectors from Rust
This commit is contained in:
+175
@@ -0,0 +1,175 @@
|
||||
use core::iter::IntoIterator;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::{self, NonNull};
|
||||
use core::{cmp, mem, slice};
|
||||
use crate::platform::types::*;
|
||||
use crate::platform;
|
||||
|
||||
/// Error that occurs when an allocation fails
|
||||
#[derive(Debug, Default, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct AllocError;
|
||||
|
||||
/// A normal vector allocated in Rust needs to be dropped from Rust
|
||||
/// too, in order to avoid UB. This CVec is an abstraction that works
|
||||
/// using only C allocations functions and can therefore be dropped
|
||||
/// from C. Just like the Rust Vec, this does bounds checks to assure
|
||||
/// you never reach isize::MAX. Unless you need to drop something from
|
||||
/// C, prefer Rust's builtin Vec.
|
||||
pub struct CVec<T> {
|
||||
ptr: NonNull<T>,
|
||||
len: usize,
|
||||
cap: usize
|
||||
}
|
||||
impl<T> CVec<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ptr: NonNull::dangling(),
|
||||
len: 0,
|
||||
cap: 0
|
||||
}
|
||||
}
|
||||
fn check_bounds(i: usize) -> Result<usize, AllocError> {
|
||||
if i > core::isize::MAX as usize {
|
||||
Err(AllocError)
|
||||
} else {
|
||||
Ok(i)
|
||||
}
|
||||
}
|
||||
fn check_mul(x: usize, y: usize) -> Result<usize, AllocError> {
|
||||
x.checked_mul(y).ok_or(AllocError)
|
||||
.and_then(Self::check_bounds)
|
||||
}
|
||||
pub fn with_capacity(cap: usize) -> Result<Self, AllocError> {
|
||||
if cap == 0 {
|
||||
return Ok(Self::new());
|
||||
}
|
||||
let size = Self::check_mul(cap, mem::size_of::<T>())?;
|
||||
let ptr = NonNull::new(unsafe { platform::alloc(size) as *mut T }).ok_or(AllocError)?;
|
||||
Ok(Self {
|
||||
ptr,
|
||||
len: 0,
|
||||
cap
|
||||
})
|
||||
}
|
||||
unsafe fn resize(&mut self, cap: usize) -> Result<(), AllocError> {
|
||||
let size = Self::check_mul(cap, mem::size_of::<T>())?;
|
||||
let ptr = NonNull::new(platform::realloc(self.ptr.as_ptr() as *mut c_void, size) as *mut T).ok_or(AllocError)?;
|
||||
self.ptr = ptr;
|
||||
self.cap = cap;
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn drop_range(&mut self, start: usize, end: usize) {
|
||||
let mut start = self.ptr.as_ptr().add(start);
|
||||
let end = self.ptr.as_ptr().add(end);
|
||||
while start < end {
|
||||
ptr::drop_in_place(start);
|
||||
start = start.add(1);
|
||||
}
|
||||
}
|
||||
pub fn reserve(&mut self, required: usize) -> Result<(), AllocError> {
|
||||
let reserved_len = self.len.checked_add(required)
|
||||
.ok_or(AllocError)
|
||||
.and_then(Self::check_bounds)?;
|
||||
let new_cap = cmp::min(reserved_len.next_power_of_two(), core::isize::MAX as usize);
|
||||
if new_cap > self.cap {
|
||||
unsafe {
|
||||
self.resize(new_cap)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn push(&mut self, elem: T) -> Result<(), AllocError> {
|
||||
unsafe {
|
||||
self.reserve(1)?;
|
||||
ptr::write(self.ptr.as_ptr().add(self.len), elem);
|
||||
}
|
||||
self.len += 1; // no need to bounds check, as new len <= cap
|
||||
Ok(())
|
||||
}
|
||||
pub fn extend_from_slice(&mut self, elems: &[T]) -> Result<(), AllocError>
|
||||
where T: Copy
|
||||
{
|
||||
unsafe {
|
||||
self.reserve(elems.len())?;
|
||||
ptr::copy_nonoverlapping(elems.as_ptr(), self.ptr.as_ptr().add(self.len), elems.len());
|
||||
}
|
||||
self.len += elems.len(); // no need to bounds check, as new len <= cap
|
||||
Ok(())
|
||||
}
|
||||
pub fn append(&mut self, other: &mut Self) -> Result<(), AllocError> {
|
||||
unsafe {
|
||||
self.reserve(other.len())?;
|
||||
ptr::copy_nonoverlapping(other.as_ptr(), self.ptr.as_ptr().add(self.len), other.len());
|
||||
}
|
||||
self.len += other.len(); // no need to bounds check, as new len <= cap
|
||||
Ok(())
|
||||
}
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
if len < self.len {
|
||||
unsafe {
|
||||
self.drop_range(len, self.len);
|
||||
}
|
||||
self.len = len;
|
||||
}
|
||||
}
|
||||
pub fn shrink_to_fit(&mut self) -> Result<(), AllocError> {
|
||||
if self.len < self.cap {
|
||||
unsafe {
|
||||
self.resize(self.len)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.cap
|
||||
}
|
||||
pub fn as_ptr(&self) -> *const T {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
pub fn as_mut_ptr(&mut self) -> *mut T {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
/// Leaks the inner data. This is safe to drop from C!
|
||||
pub fn leak(mut self) -> *mut T {
|
||||
let ptr = self.as_mut_ptr();
|
||||
mem::forget(self);
|
||||
ptr
|
||||
}
|
||||
}
|
||||
impl<T> Deref for CVec<T> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self.ptr.as_ptr(), self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> DerefMut for CVec<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> Drop for CVec<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.drop_range(0, self.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, T> IntoIterator for &'a CVec<T> {
|
||||
type Item = <&'a [T] as IntoIterator>::Item;
|
||||
type IntoIter = <&'a [T] as IntoIterator>::IntoIter;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
<&[T]>::into_iter(&*self)
|
||||
}
|
||||
}
|
||||
impl<'a, T> IntoIterator for &'a mut CVec<T> {
|
||||
type Item = <&'a mut [T] as IntoIterator>::Item;
|
||||
type IntoIter = <&'a mut [T] as IntoIterator>::IntoIter;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
<&mut [T]>::into_iter(&mut *self)
|
||||
}
|
||||
}
|
||||
+26
-22
@@ -4,12 +4,13 @@ use alloc::boxed::Box;
|
||||
use core::{mem, ptr};
|
||||
|
||||
use c_str::CStr;
|
||||
use c_vec::CVec;
|
||||
use fs::File;
|
||||
use header::{errno, fcntl, stdlib, string};
|
||||
use io::{Seek, SeekFrom};
|
||||
use platform;
|
||||
use platform::types::*;
|
||||
use platform::{Pal, Sys};
|
||||
use platform;
|
||||
|
||||
const DIR_BUF_SIZE: usize = mem::size_of::<dirent>() * 3;
|
||||
|
||||
@@ -134,15 +135,16 @@ pub unsafe extern "C" fn scandir(
|
||||
return -1;
|
||||
}
|
||||
|
||||
let old_errno = platform::errno;
|
||||
let mut vec = match CVec::with_capacity(4) {
|
||||
Ok(vec) => vec,
|
||||
Err(err) => return -1
|
||||
};
|
||||
|
||||
let mut len: isize = 0;
|
||||
let mut cap: isize = 4;
|
||||
*namelist = platform::alloc(cap as usize * mem::size_of::<*mut dirent>()) as *mut *mut dirent;
|
||||
let old_errno = platform::errno;
|
||||
platform::errno = 0;
|
||||
|
||||
loop {
|
||||
platform::errno = 0;
|
||||
let entry = readdir(dir);
|
||||
let entry: *mut dirent = readdir(dir);
|
||||
if entry.is_null() {
|
||||
break;
|
||||
}
|
||||
@@ -153,30 +155,31 @@ pub unsafe extern "C" fn scandir(
|
||||
}
|
||||
}
|
||||
|
||||
if len >= cap {
|
||||
cap *= 2;
|
||||
*namelist = platform::realloc(
|
||||
*namelist as *mut c_void,
|
||||
cap as usize * mem::size_of::<*mut dirent>(),
|
||||
) as *mut *mut dirent;
|
||||
}
|
||||
|
||||
let copy = platform::alloc(mem::size_of::<dirent>()) as *mut dirent;
|
||||
*copy = (*entry).clone();
|
||||
*(*namelist).offset(len) = copy;
|
||||
len += 1;
|
||||
if copy.is_null() {
|
||||
break;
|
||||
}
|
||||
ptr::write(copy, (*entry).clone());
|
||||
if let Err(_) = vec.push(copy) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
let len = vec.len();
|
||||
if let Err(_) = vec.shrink_to_fit() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if platform::errno != 0 {
|
||||
while len > 0 {
|
||||
len -= 1;
|
||||
platform::free(*(*namelist).offset(len) as *mut c_void);
|
||||
for ptr in &mut vec {
|
||||
platform::free(*ptr as *mut c_void);
|
||||
}
|
||||
platform::free(*namelist as *mut c_void);
|
||||
-1
|
||||
} else {
|
||||
*namelist = vec.leak();
|
||||
|
||||
platform::errno = old_errno;
|
||||
stdlib::qsort(
|
||||
*namelist as *mut c_void,
|
||||
@@ -184,6 +187,7 @@ pub unsafe extern "C" fn scandir(
|
||||
mem::size_of::<*mut dirent>(),
|
||||
mem::transmute(compare),
|
||||
);
|
||||
|
||||
len as c_int
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,8 +404,7 @@ unsafe fn inner_scanf<R: Read>(
|
||||
let mut ptr: Option<*mut c_char> = if ignore { None } else { Some(ap.arg()) };
|
||||
|
||||
// While we haven't used up all the width, and it matches
|
||||
while width.map(|w| w > 0).unwrap_or(true) && !invert == matches.contains(&byte)
|
||||
{
|
||||
while width.map(|w| w > 0).unwrap_or(true) && !invert == matches.contains(&byte) {
|
||||
if let Some(ref mut ptr) = ptr {
|
||||
**ptr = byte as c_char;
|
||||
*ptr = ptr.offset(1);
|
||||
|
||||
@@ -51,6 +51,7 @@ extern crate spin;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod c_str;
|
||||
pub mod c_vec;
|
||||
pub mod cxa;
|
||||
pub mod db;
|
||||
pub mod fs;
|
||||
|
||||
@@ -54,10 +54,28 @@ int main(void) {
|
||||
char hostbuf[100];
|
||||
char pathbuf[100];
|
||||
|
||||
// don't push NUL, make sure scanf does that
|
||||
memset(protobuf, 97, 16);
|
||||
memset(slashbuf, 97, 4);
|
||||
memset(hostbuf, 97, 100);
|
||||
memset(pathbuf, 97, 100);
|
||||
|
||||
int ret = sscanf(
|
||||
"https://redox-os.org", "%15[^\n/:]:%3[/]%[^\n/?#]%[^\n]",
|
||||
&protobuf, &slashbuf, &hostbuf, &pathbuf
|
||||
);
|
||||
if (ret < 4) {
|
||||
*pathbuf = 0;
|
||||
}
|
||||
if (ret < 3) {
|
||||
*hostbuf = 0;
|
||||
}
|
||||
if (ret < 2) {
|
||||
*slashbuf = 0;
|
||||
}
|
||||
if (ret < 1) {
|
||||
*protobuf = 0;
|
||||
}
|
||||
|
||||
printf("%d \"%s\" \"%s\" \"%s\" \"%s\"\n", ret, &protobuf, &slashbuf, &hostbuf, &pathbuf);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user