use std::alloc::{self, handle_alloc_error, Layout, LayoutError};
use std::boxed::Box;
use std::cmp;
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop, MaybeUninit};
use std::ptr::NonNull;
use std::slice;
pub(crate) enum TryReserveError {
CapacityOverflow,
AllocError(Layout),
}
#[allow(missing_debug_implementations)]
pub(crate) struct RawVec<T> {
ptr: NonNull<T>,
cap: usize,
_marker: PhantomData<T>,
}
impl<T> RawVec<T> {
pub(crate) const MIN_NON_ZERO_CAP: usize = if mem::size_of::<T>() == 1 {
8
} else if mem::size_of::<T>() <= 1024 {
4
} else {
1
};
#[must_use]
pub const fn new() -> Self {
Self { ptr: NonNull::dangling(), cap: 0, _marker: PhantomData }
}
#[must_use]
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self::allocate(capacity)
}
#[allow(unused)]
pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit<T>]> {
debug_assert!(
len <= self.capacity(),
"`len` must be smaller than or equal to `self.capacity()`"
);
let me = ManuallyDrop::new(self);
unsafe {
let slice = slice::from_raw_parts_mut(me.ptr() as *mut MaybeUninit<T>, len);
Box::from_raw(slice)
}
}
fn allocate(capacity: usize) -> Self {
if mem::size_of::<T>() == 0 || capacity == 0 {
Self::new()
} else {
let layout = match Layout::array::<T>(capacity) {
Ok(layout) => layout,
Err(_) => capacity_overflow(),
};
match alloc_guard(layout.size()) {
Ok(_) => {}
Err(_) => capacity_overflow(),
}
let ptr = unsafe { alloc::alloc(layout) };
let ptr = match NonNull::new(ptr as *mut T) {
Some(p) => p,
None => alloc::handle_alloc_error(layout),
};
Self {
ptr,
cap: capacity,
_marker: PhantomData,
}
}
}
#[inline]
pub unsafe fn from_raw_parts(ptr: *mut T, capacity: usize) -> Self {
Self { ptr: unsafe { NonNull::new_unchecked(ptr) }, cap: capacity, _marker: PhantomData }
}
#[inline]
pub fn ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
#[inline(always)]
pub fn capacity(&self) -> usize {
if mem::size_of::<T>() == 0 { usize::MAX } else { self.cap }
}
fn current_memory(&self) -> Option<(NonNull<u8>, Layout)> {
if mem::size_of::<T>() == 0 || self.cap == 0 {
None
} else {
unsafe {
let layout = Layout::array::<T>(self.cap).unwrap_unchecked();
Some((self.ptr.cast(), layout))
}
}
}
#[inline]
pub fn reserve(&mut self, len: usize, additional: usize) {
#[cold]
fn do_reserve_and_handle<T>(
slf: &mut RawVec<T>,
len: usize,
additional: usize,
) {
handle_reserve(slf.grow_amortized(len, additional));
}
if self.needs_to_grow(len, additional) {
do_reserve_and_handle(self, len, additional);
}
}
#[inline(never)]
pub fn reserve_for_push(&mut self, len: usize) {
handle_reserve(self.grow_amortized(len, 1));
}
#[allow(unused)]
pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> {
if self.needs_to_grow(len, additional) {
self.grow_amortized(len, additional)
} else {
Ok(())
}
}
pub fn reserve_exact(&mut self, len: usize, additional: usize) {
handle_reserve(self.try_reserve_exact(len, additional));
}
pub fn try_reserve_exact(
&mut self,
len: usize,
additional: usize,
) -> Result<(), TryReserveError> {
if self.needs_to_grow(len, additional) { self.grow_exact(len, additional) } else { Ok(()) }
}
pub fn shrink_to_fit(&mut self, cap: usize) {
handle_reserve(self.shrink(cap));
}
fn needs_to_grow(&self, len: usize, additional: usize) -> bool {
additional > self.capacity().wrapping_sub(len)
}
fn set_ptr_and_cap(&mut self, ptr: NonNull<u8>, cap: usize) {
self.ptr = unsafe { NonNull::new_unchecked(ptr.cast().as_ptr()) };
self.cap = cap;
}
fn grow_amortized(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> {
debug_assert!(additional > 0);
if mem::size_of::<T>() == 0 {
return Err(TryReserveError::CapacityOverflow);
}
let required_cap = len.checked_add(additional).ok_or(TryReserveError::CapacityOverflow)?;
let cap = cmp::max(self.cap * 2, required_cap);
let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
let new_layout = Layout::array::<T>(cap);
let ptr = finish_grow(new_layout, self.current_memory())?;
self.set_ptr_and_cap(ptr, cap);
Ok(())
}
fn grow_exact(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> {
if mem::size_of::<T>() == 0 {
return Err(TryReserveError::CapacityOverflow);
}
let cap = len.checked_add(additional).ok_or(TryReserveError::CapacityOverflow)?;
let new_layout = Layout::array::<T>(cap);
let ptr = finish_grow(new_layout, self.current_memory())?;
self.set_ptr_and_cap(ptr, cap);
Ok(())
}
fn shrink(&mut self, cap: usize) -> Result<(), TryReserveError> {
assert!(cap <= self.capacity(), "Tried to shrink to a larger capacity");
let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) };
let ptr = unsafe {
let new_layout = Layout::array::<T>(cap).unwrap_unchecked();
let ptr = alloc::realloc(ptr.as_ptr(), layout, new_layout.size());
NonNull::new(ptr).unwrap()
};
self.set_ptr_and_cap(ptr, cap);
Ok(())
}
}
#[inline(never)]
fn finish_grow(
new_layout: Result<Layout, LayoutError>,
current_memory: Option<(NonNull<u8>, Layout)>,
) -> Result<NonNull<u8>, TryReserveError> {
let new_layout = new_layout.map_err(|_| TryReserveError::CapacityOverflow)?;
alloc_guard(new_layout.size())?;
let ptr = if let Some((ptr, old_layout)) = current_memory {
debug_assert_eq!(old_layout.align(), new_layout.align());
unsafe {
alloc::realloc(ptr.as_ptr(), old_layout, new_layout.size())
}
} else {
unsafe { alloc::alloc(new_layout) }
};
NonNull::new(ptr).ok_or(TryReserveError::AllocError(new_layout))
}
impl<T> Drop for RawVec<T> {
fn drop(&mut self) {
if let Some((ptr, layout)) = self.current_memory() {
unsafe { alloc::dealloc(ptr.as_ptr(), layout) }
}
}
}
#[inline]
fn handle_reserve(result: Result<(), TryReserveError>) {
match result {
Err(TryReserveError::CapacityOverflow) => capacity_overflow(),
Err(TryReserveError::AllocError(layout)) => handle_alloc_error(layout),
Ok(()) => { }
}
}
#[inline]
fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> {
if usize::BITS < 64 && alloc_size > isize::MAX as usize {
Err(TryReserveError::CapacityOverflow)
} else {
Ok(())
}
}
fn capacity_overflow() -> ! {
panic!("capacity overflow");
}