#![allow(unstable_name_collisions)]
#![allow(dead_code)]
#![allow(unused_unsafe)]
use core::cmp;
use core::mem;
use core::ptr::{self, NonNull};
use std::alloc::{Layout, handle_alloc_error};
use crate::Allocator;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum CollectionAllocErr {
CapacityOverflow,
AllocErr,
}
use self::CollectionAllocErr::*;
#[allow(missing_debug_implementations)]
pub struct RawVec<'a, T> {
ptr: NonNull<T>,
len: u32,
cap: u32,
a: &'a Allocator,
}
impl<'a, T> RawVec<'a, T> {
pub fn new_in(a: &'a Allocator) -> Self {
RawVec {
ptr: NonNull::dangling(),
len: 0,
cap: 0,
a,
}
}
#[inline]
pub fn with_capacity_in(cap: usize, a: &'a Allocator) -> Self {
RawVec::allocate_in(cap, false, a)
}
#[inline]
pub fn with_capacity_zeroed_in(cap: usize, a: &'a Allocator) -> Self {
RawVec::allocate_in(cap, true, a)
}
fn allocate_in(cap: usize, zeroed: bool, a: &'a Allocator) -> Self {
if mem::size_of::<T>() != 0 && cap > u32::MAX as usize {
capacity_overflow();
}
let elem_size = mem::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 = mem::align_of::<T>();
let layout = Layout::from_size_align(alloc_size, align).unwrap();
let ptr = a
.arena
.try_alloc_layout(layout)
.unwrap_or_else(|_| handle_alloc_error(layout));
if zeroed {
unsafe {
ptr::write_bytes(ptr.as_ptr(), 0, alloc_size);
}
}
ptr.cast()
};
let cap = cap as u32;
RawVec {
ptr,
len: 0,
cap,
a,
}
}
}
impl<'a, T> RawVec<'a, T> {
pub unsafe fn from_raw_parts_in(ptr: *mut T, len: usize, cap: usize, a: &'a Allocator) -> Self {
let len = len as u32;
let cap = cap as u32;
RawVec {
ptr: unsafe { NonNull::new_unchecked(ptr) },
len,
cap,
a,
}
}
}
impl<'a, T> RawVec<'a, T> {
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 cap(&self) -> usize {
if mem::size_of::<T>() == 0 {
!0
} else {
self.cap as usize
}
}
#[inline(always)]
pub fn cap_u32(&self) -> u32 {
if mem::size_of::<T>() == 0 {
!0
} else {
self.cap
}
}
pub fn bump(&self) -> &'a Allocator {
self.a
}
fn current_layout(&self) -> Option<Layout> {
if self.cap == 0 {
None
} else {
unsafe {
let align = mem::align_of::<T>();
let size = mem::size_of::<T>() * self.cap as usize;
Some(Layout::from_size_align_unchecked(size, align))
}
}
}
pub fn try_reserve_exact(
&mut self,
used_cap: u32,
needed_extra_cap: usize,
) -> Result<(), CollectionAllocErr> {
self.fallible_reserve_internal(used_cap, needed_extra_cap, Exact)
}
pub fn reserve_exact(&mut self, used_cap: u32, needed_extra_cap: usize) {
self.infallible_reserve_internal(used_cap, needed_extra_cap, Exact)
}
fn amortized_new_size(
&self,
used_cap: u32,
needed_extra_cap: usize,
) -> Result<usize, CollectionAllocErr> {
let required_cap = (used_cap as usize)
.checked_add(needed_extra_cap)
.ok_or(CapacityOverflow)?;
let double_cap = (self.cap as usize).checked_mul(2).ok_or(CapacityOverflow)?;
Ok(cmp::max(double_cap, required_cap))
}
pub fn try_reserve(
&mut self,
used_cap: u32,
needed_extra_cap: usize,
) -> Result<(), CollectionAllocErr> {
self.fallible_reserve_internal(used_cap, needed_extra_cap, Amortized)
}
#[inline(always)]
pub fn reserve(&mut self, used_cap: u32, needed_extra_cap: usize) {
self.infallible_reserve_internal(used_cap, needed_extra_cap, Amortized)
}
pub fn reserve_in_place(&mut self, used_cap: u32, needed_extra_cap: usize) -> bool {
unsafe {
let old_layout = match self.current_layout() {
Some(layout) => layout,
None => return false,
};
if self.cap_u32().wrapping_sub(used_cap) as usize >= needed_extra_cap {
return false;
}
let new_cap = self
.amortized_new_size(used_cap, needed_extra_cap)
.unwrap_or_else(|_| capacity_overflow());
let new_layout = Layout::array::<T>(new_cap).unwrap();
alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow());
match self.try_grow_in_place(old_layout, new_layout.size()) {
Ok(()) => {
if new_cap > u32::MAX as usize {
capacity_overflow();
}
self.cap = new_cap as u32;
true
}
Err(_) => false,
}
}
}
pub fn shrink_to_fit(&mut self, amount: u32) {
let elem_size = mem::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.a;
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 = mem::align_of::<T>();
let old_layout = Layout::from_size_align_unchecked(old_size, align);
match self.realloc_buffer(old_layout, new_size) {
Ok(p) => self.ptr = p.cast(),
Err(_) => {
handle_alloc_error(Layout::from_size_align_unchecked(new_size, align))
}
}
}
self.cap = amount;
}
}
}
enum Fallibility {
Fallible,
Infallible,
}
use self::Fallibility::*;
enum ReserveStrategy {
Exact,
Amortized,
}
use self::ReserveStrategy::*;
impl<'a, T> RawVec<'a, T> {
#[inline(always)]
fn fallible_reserve_internal(
&mut self,
used_cap: u32,
needed_extra_cap: usize,
strategy: ReserveStrategy,
) -> Result<(), CollectionAllocErr> {
if self.cap_u32().wrapping_sub(used_cap) as usize >= needed_extra_cap {
return Ok(());
}
self.reserve_internal_or_error(used_cap, needed_extra_cap, Fallible, strategy)
}
#[inline(always)]
fn infallible_reserve_internal(
&mut self,
used_cap: u32,
needed_extra_cap: usize,
strategy: ReserveStrategy,
) {
if self.cap_u32().wrapping_sub(used_cap) as usize >= needed_extra_cap {
return;
}
self.reserve_internal_or_panic(used_cap, needed_extra_cap, strategy)
}
#[inline(never)]
fn reserve_internal_or_panic(
&mut self,
used_cap: u32,
needed_extra_cap: usize,
strategy: ReserveStrategy,
) {
match self.reserve_internal(used_cap, needed_extra_cap, Infallible, strategy) {
Err(CapacityOverflow) => capacity_overflow(),
Err(AllocErr) => unreachable!(),
Ok(()) => { }
}
}
#[inline(never)]
fn reserve_internal_or_error(
&mut self,
used_cap: u32,
needed_extra_cap: usize,
fallibility: Fallibility,
strategy: ReserveStrategy,
) -> Result<(), CollectionAllocErr> {
self.reserve_internal(used_cap, needed_extra_cap, fallibility, strategy)
}
fn reserve_internal(
&mut self,
used_cap: u32,
needed_extra_cap: usize,
fallibility: Fallibility,
strategy: ReserveStrategy,
) -> Result<(), CollectionAllocErr> {
unsafe {
let new_cap = match strategy {
Exact => (used_cap as usize)
.checked_add(needed_extra_cap)
.ok_or(CapacityOverflow)?,
Amortized => self.amortized_new_size(used_cap, needed_extra_cap)?,
};
if new_cap > u32::MAX as usize {
return Err(CapacityOverflow);
}
let new_layout = Layout::array::<T>(new_cap).map_err(|_| CapacityOverflow)?;
alloc_guard(new_layout.size())?;
let res = match self.current_layout() {
Some(layout) => {
debug_assert!(new_layout.align() == layout.align());
self.realloc_buffer(layout, new_layout.size())
}
None => self
.a
.arena
.try_alloc_layout(new_layout)
.map_err(|_| CollectionAllocErr::AllocErr),
};
if let (Err(AllocErr), Infallible) = (&res, fallibility) {
handle_alloc_error(new_layout);
}
self.ptr = res?.cast();
self.cap = new_cap as u32;
Ok(())
}
}
unsafe fn realloc_buffer(
&self,
old_layout: Layout,
new_size: usize,
) -> Result<NonNull<u8>, CollectionAllocErr> {
let new_layout =
Layout::from_size_align(new_size, old_layout.align()).map_err(|_| CapacityOverflow)?;
let new_ptr = self
.a
.arena
.try_alloc_layout(new_layout)
.map_err(|_| CollectionAllocErr::AllocErr)?;
unsafe {
ptr::copy_nonoverlapping(
self.ptr.cast::<u8>().as_ptr(),
new_ptr.as_ptr(),
cmp::min(old_layout.size(), new_size),
);
}
Ok(new_ptr)
}
fn try_grow_in_place(
&self,
_old_layout: Layout,
_new_size: usize,
) -> Result<(), CollectionAllocErr> {
Err(CollectionAllocErr::AllocErr)
}
}
impl<'a, T> RawVec<'a, T> {
pub unsafe fn dealloc_buffer(&mut self) {
let elem_size = mem::size_of::<T>();
if elem_size != 0
&& let Some(layout) = self.current_layout()
{
let _ = layout;
}
}
}
#[inline]
fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> {
if mem::size_of::<usize>() < 8 {
if alloc_size > isize::MAX as usize {
return Err(CapacityOverflow);
}
} else if alloc_size > u32::MAX as usize {
return Err(CapacityOverflow);
}
Ok(())
}
fn capacity_overflow() -> ! {
panic!("capacity overflow")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reserve_does_not_overallocate() {
let bump = Allocator::new();
{
let mut v: RawVec<u32> = RawVec::new_in(&bump);
v.reserve(0, 9);
assert_eq!(9, v.cap());
}
{
let mut v: RawVec<u32> = RawVec::new_in(&bump);
v.reserve(0, 7);
assert_eq!(7, v.cap());
v.reserve(7, 90);
assert_eq!(97, v.cap());
}
{
let mut v: RawVec<u32> = RawVec::new_in(&bump);
v.reserve(0, 12);
assert_eq!(12, v.cap());
v.reserve(12, 3);
assert!(v.cap() >= 12 + 12 / 2);
}
}
#[test]
fn try_reserve_reports_capacity_overflow_above_u32_max() {
let bump = Allocator::new();
let mut v: RawVec<u8> = RawVec::new_in(&bump);
assert_eq!(
v.try_reserve_exact(u32::MAX, 2),
Err(CollectionAllocErr::CapacityOverflow)
);
assert_eq!(
v.try_reserve(u32::MAX, 2),
Err(CollectionAllocErr::CapacityOverflow)
);
}
#[test]
#[should_panic(expected = "capacity overflow")]
fn with_capacity_panics_above_u32_max() {
let bump = Allocator::new();
let _ = RawVec::<u8>::with_capacity_in(u32::MAX as usize + 1, &bump);
}
}