use super::{AllocErr, LayoutErr, RawVecErr, UninitAlloc};
use std::{
alloc::{alloc, dealloc, handle_alloc_error, realloc, Layout},
fmt,
marker::PhantomData,
mem,
ptr::NonNull,
slice,
};
pub struct RawVec<T> {
nnptr: NonNull<T>,
cap: usize,
_marker: PhantomData<T>,
}
impl<T> RawVec<T> {
pub fn new() -> Self {
Self { nnptr: NonNull::dangling(), cap: 0, _marker: PhantomData }
}
pub fn with_capacity(cap: usize) -> Self {
match Self::try_with_capacity(cap) {
Ok(this) => this,
Err(RawVecErr::Alloc(err)) => handle_alloc_error(err.layout),
Err(RawVecErr::Layout(err)) => {
panic!("Capacity overflows memory size: {}", err)
},
}
}
pub fn try_with_capacity(cap: usize) -> Result<Self, RawVecErr> {
let layout = Self::make_layout(cap)?;
let res = if layout.size() == 0 {
Ok(NonNull::dangling())
} else {
NonNull::new(unsafe { alloc(layout) })
.map(NonNull::cast::<T>)
.ok_or(AllocErr { layout }.into())
};
res.map(|nnptr| Self { nnptr, cap, _marker: PhantomData })
}
pub unsafe fn from_vec(mut vec: Vec<T>) -> Self {
let this = Self {
nnptr: NonNull::new_unchecked(vec.as_mut_ptr()),
cap: vec.capacity(),
_marker: PhantomData,
};
mem::forget(vec);
this
}
pub unsafe fn from_raw_parts(nnptr: NonNull<T>, cap: usize) -> Self {
Self { nnptr, cap, _marker: PhantomData }
}
pub unsafe fn from_raw_slice(mut raw: NonNull<[T]>) -> Self {
Self {
nnptr: NonNull::new_unchecked(raw.as_mut().as_mut_ptr()),
cap: raw.as_ref().len(),
_marker: PhantomData,
}
}
pub fn cap(&self) -> usize {
self.cap
}
pub fn raw(&self) -> NonNull<T> {
self.nnptr
}
pub fn raw_slice(&self) -> NonNull<[T]> {
unsafe { NonNull::from(self.as_slice()) }
}
pub fn into_raw_slice(self) -> NonNull<[T]> {
let ptr = self.raw_slice();
mem::forget(self);
ptr
}
pub unsafe fn as_slice(&self) -> &[T] {
slice::from_raw_parts(self.nnptr.as_ptr(), self.cap())
}
pub unsafe fn as_mut_slice(&mut self) -> &mut [T] {
slice::from_raw_parts_mut(self.nnptr.as_ptr(), self.cap())
}
pub unsafe fn into_vec(self, len: usize) -> Vec<T> {
let vec = Vec::from_raw_parts(self.nnptr.as_ptr(), len, self.cap);
mem::forget(self);
vec
}
pub fn resize(&mut self, new_cap: usize) {
match self.try_resize(new_cap) {
Err(RawVecErr::Alloc(err)) => handle_alloc_error(err.layout),
Err(RawVecErr::Layout(err)) => {
panic!("Capacity overflows memory size: {}", err)
},
Ok(_) => (),
}
}
pub fn try_resize(&mut self, new_cap: usize) -> Result<(), RawVecErr> {
let layout = Self::make_layout(new_cap)?;
let res = if layout.size() == 0 {
self.free();
Ok(NonNull::dangling())
} else {
let old = Self::make_layout(self.cap).unwrap();
NonNull::new(unsafe {
realloc(self.nnptr.cast().as_ptr(), old, layout.size())
})
.map(NonNull::cast::<T>)
.ok_or(AllocErr { layout }.into())
};
res.map(|nnptr| {
self.nnptr = nnptr;
self.cap = new_cap;
})
}
fn free(&self) {
if self.cap != 0 && mem::size_of::<T>() != 0 {
let layout = Self::make_layout(self.cap).unwrap();
unsafe {
dealloc(self.nnptr.cast().as_ptr(), layout);
}
}
}
fn make_layout(cap: usize) -> Result<Layout, LayoutErr> {
let total_size =
mem::size_of::<T>().checked_mul(cap).ok_or(LayoutErr)?;
Layout::from_size_align(total_size, mem::align_of::<T>())
.map_err(Into::into)
}
}
impl<T> fmt::Debug for RawVec<T> {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
write!(
fmtr,
"RawVec {} pointer {:?}, cap: {} {}",
'{', self.nnptr, self.cap, '}'
)
}
}
impl<T> Drop for RawVec<T> {
fn drop(&mut self) {
self.free();
}
}
impl<T> From<UninitAlloc<T>> for RawVec<T> {
fn from(alloc: UninitAlloc<T>) -> Self {
Self { nnptr: alloc.into_raw(), cap: 1, _marker: PhantomData }
}
}
unsafe impl<T> Send for RawVec<T> where T: Send {}
unsafe impl<T> Sync for RawVec<T> where T: Sync {}
#[cfg(test)]
mod test {
use super::RawVec;
#[test]
fn cap_is_the_one_passed() {
let mut alloc = RawVec::<usize>::with_capacity(20);
assert_eq!(alloc.cap(), 20);
alloc.resize(50);
assert_eq!(alloc.cap(), 50);
alloc.resize(5);
assert_eq!(alloc.cap(), 5);
}
#[test]
fn from_into_std_vec() {
let vec = unsafe { RawVec::<u128>::with_capacity(465).into_vec(0) };
assert_eq!(vec.capacity(), 465);
let raw = unsafe { RawVec::from_vec(vec) };
assert_eq!(raw.cap(), 465);
}
}