#![allow(
unused_mut,
unused_unsafe,
clippy::allow_attributes,
clippy::uninlined_format_args,
clippy::enum_glob_use,
clippy::equatable_if_let,
clippy::needless_pass_by_value,
clippy::inline_always
)]
#![allow(unstable_name_collisions)]
#![allow(dead_code)]
use std::{
alloc::Layout,
cmp,
ptr::{self, NonNull},
};
use crate::alloc::Alloc;
pub enum AllocError {
AllocErr,
CapacityOverflow,
}
#[allow(missing_debug_implementations)]
#[repr(C)]
pub struct RawVec<'a, T, A: Alloc> {
ptr: NonNull<T>,
len: u32,
cap: u32,
alloc: &'a A,
}
impl<'a, T, A: Alloc> RawVec<'a, T, A> {
#[inline(always)]
pub fn new_in(alloc: &'a A) -> Self {
RawVec { ptr: NonNull::dangling(), alloc, cap: 0, len: 0 }
}
#[inline]
pub fn with_capacity_in(cap: usize, alloc: &'a A) -> Self {
unsafe {
let elem_size = size_of::<T>();
let alloc_size = cap.checked_mul(elem_size).unwrap_or_else(|| capacity_overflow());
alloc_guard(alloc_size).unwrap_or_else(|_| capacity_overflow());
let ptr = if alloc_size == 0 {
NonNull::<T>::dangling()
} else {
let align = align_of::<T>();
let layout = Layout::from_size_align(alloc_size, align).unwrap();
alloc.alloc(layout).cast::<T>()
};
#[expect(clippy::cast_possible_truncation)]
let cap = cap as u32;
RawVec { ptr, alloc, cap, len: 0 }
}
}
#[inline(always)]
pub unsafe fn from_raw_parts_in(ptr: *mut T, len: usize, cap: usize, alloc: &'a A) -> Self {
let ptr = unsafe { NonNull::new_unchecked(ptr) };
#[expect(clippy::cast_possible_truncation)]
let len = len as u32;
#[expect(clippy::cast_possible_truncation)]
let cap = cap as u32;
RawVec { ptr, len, cap, alloc }
}
#[inline(always)]
pub fn ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
#[inline(always)]
pub fn len_u32(&self) -> u32 {
self.len
}
#[inline(always)]
pub fn len_usize(&self) -> usize {
self.len as usize
}
#[inline(always)]
pub fn set_len(&mut self, new_len: u32) {
self.len = new_len;
}
#[inline(always)]
pub fn increase_len(&mut self, increment: u32) {
self.len += increment;
}
#[inline(always)]
pub fn decrease_len(&mut self, decrement: u32) {
self.len -= decrement;
}
#[inline(always)]
pub fn capacity_u32(&self) -> u32 {
if size_of::<T>() == 0 { !0 } else { self.cap }
}
#[inline(always)]
pub fn capacity_usize(&self) -> usize {
if size_of::<T>() == 0 { !0 } else { self.cap as usize }
}
#[inline(always)]
pub unsafe fn bump(&self) -> &'a A {
self.alloc
}
#[inline]
fn current_layout(&self) -> Option<Layout> {
if self.cap == 0 {
None
} else {
unsafe {
let align = align_of::<T>();
let size = size_of::<T>() * self.cap as usize;
Some(Layout::from_size_align_unchecked(size, align))
}
}
}
pub fn try_reserve_exact(&mut self, len: u32, additional: usize) -> Result<(), AllocError> {
if self.needs_to_grow(len, additional) {
self.grow_exact(len, additional)?
}
Ok(())
}
pub fn reserve_exact(&mut self, len: u32, additional: usize) {
if let Err(err) = self.try_reserve_exact(len, additional) {
handle_error(err)
}
}
pub fn try_reserve(&mut self, len: u32, additional: usize) -> Result<(), AllocError> {
if self.needs_to_grow(len, additional) {
self.grow_amortized(len, additional)?;
}
Ok(())
}
#[inline]
pub fn reserve(&mut self, len: u32, additional: usize) {
#[cold]
fn do_reserve_and_handle<T, A: Alloc>(slf: &mut RawVec<T, A>, len: u32, additional: usize) {
if let Err(err) = slf.grow_amortized(len, additional) {
handle_error(err);
}
}
if self.needs_to_grow(len, additional) {
do_reserve_and_handle(self, len, additional);
}
}
#[inline]
pub fn grow_one(&mut self) {
if let Err(err) = self.grow_amortized(self.cap, 1) {
handle_error(err);
}
}
pub fn shrink_to_fit(&mut self, amount: u32) {
let elem_size = size_of::<T>();
if elem_size == 0 {
self.cap = amount;
return;
}
assert!(self.cap >= amount, "Tried to shrink to a larger capacity");
if amount == 0 {
unsafe {
let a = self.alloc;
self.dealloc_buffer();
ptr::write(self, RawVec::new_in(a));
}
} else if self.cap != amount {
unsafe {
let old_size = elem_size * self.cap as usize;
let new_size = elem_size * amount as usize;
let align = align_of::<T>();
let old_layout = Layout::from_size_align_unchecked(old_size, align);
let new_layout = Layout::from_size_align_unchecked(new_size, align);
self.ptr =
self.alloc.shrink(self.ptr.cast::<u8>(), old_layout, new_layout).cast::<T>();
}
self.cap = amount;
}
}
}
impl<T, A: Alloc> RawVec<'_, T, A> {
#[inline]
fn needs_to_grow(&self, len: u32, additional: usize) -> bool {
additional > self.capacity_u32().wrapping_sub(len) as usize
}
fn grow_exact(&mut self, len: u32, additional: usize) -> Result<(), AllocError> {
unsafe {
let new_cap =
(len as usize).checked_add(additional).ok_or(AllocError::CapacityOverflow)?;
let new_layout =
Layout::array::<T>(new_cap).map_err(|_| AllocError::CapacityOverflow)?;
self.ptr = self.finish_grow(new_layout)?.cast();
#[expect(clippy::cast_possible_truncation)]
let new_cap = new_cap as u32;
self.cap = new_cap;
Ok(())
}
}
fn grow_amortized(&mut self, len: u32, additional: usize) -> Result<(), AllocError> {
unsafe {
let required_cap =
(len as usize).checked_add(additional).ok_or(AllocError::CapacityOverflow)?;
let cap = cmp::max((self.capacity_u32() as usize) * 2, required_cap);
let new_layout = Layout::array::<T>(cap).map_err(|_| AllocError::CapacityOverflow)?;
self.ptr = self.finish_grow(new_layout)?.cast();
#[expect(clippy::cast_possible_truncation)]
let cap = cap as u32;
self.cap = cap;
Ok(())
}
}
#[inline]
fn finish_grow(&self, new_layout: Layout) -> Result<NonNull<u8>, AllocError> {
alloc_guard(new_layout.size())?;
let new_ptr = match self.current_layout() {
Some(layout) => unsafe {
#[cold]
#[inline(never)]
unsafe fn grow<A: Alloc>(
alloc: &A,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> NonNull<u8> {
alloc.grow(ptr, old_layout, new_layout)
}
debug_assert!(new_layout.align() == layout.align());
grow(self.alloc, self.ptr.cast(), layout, new_layout)
},
None => self.alloc.alloc(new_layout),
};
Ok(new_ptr)
}
}
impl<T, A: Alloc> RawVec<'_, T, A> {
pub unsafe fn dealloc_buffer(&mut self) {
let elem_size = size_of::<T>();
if elem_size != 0
&& let Some(layout) = self.current_layout()
{
self.alloc.dealloc(self.ptr.cast(), layout);
}
}
}
#[inline]
fn alloc_guard(alloc_size: usize) -> Result<(), AllocError> {
if size_of::<usize>() < 8 {
if alloc_size > isize::MAX as usize {
return Err(AllocError::CapacityOverflow);
}
} else if alloc_size > u32::MAX as usize {
return Err(AllocError::CapacityOverflow);
}
Ok(())
}
#[cold]
#[inline(never)]
fn capacity_overflow() -> ! {
panic!("capacity overflow")
}
#[inline(never)]
#[cold]
fn handle_error(error: AllocError) -> ! {
match error {
AllocError::CapacityOverflow => capacity_overflow(),
AllocError::AllocErr => panic!("encountered allocation error"),
}
}
#[cfg(test)]
mod tests {
use crate::bump::Bump;
use super::*;
#[test]
fn reserve_does_not_overallocate() {
let arena = Bump::new();
{
let mut v: RawVec<u32, _> = RawVec::new_in(&arena);
v.reserve(0, 9);
assert_eq!(9, v.capacity_u32());
}
{
let mut v: RawVec<u32, _> = RawVec::new_in(&arena);
v.reserve(0, 7);
assert_eq!(7, v.capacity_u32());
v.reserve(7, 90);
assert_eq!(97, v.capacity_u32());
}
{
let mut v: RawVec<u32, _> = RawVec::new_in(&arena);
v.reserve(0, 12);
assert_eq!(12, v.capacity_u32());
v.reserve(12, 3);
assert!(v.capacity_u32() >= 12 + 12 / 2);
}
}
}