use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop, MaybeUninit};
use crate::allocator::alloc::Layout;
use crate::utils::max;
const TAG_BITS: u32 = 2;
pub(super) const TAG_INLINE: usize = 0b00 << (usize::BITS - TAG_BITS);
pub(super) const TAG_REFERENCED: usize = 0b01 << (usize::BITS - TAG_BITS);
pub(super) const TAG_HEAP: usize = 0b10 << (usize::BITS - TAG_BITS);
pub(super) const TAG_MASK: usize = 0b11 << (usize::BITS - TAG_BITS);
pub(super) const LEN_MASK: usize = !TAG_MASK;
#[repr(C)]
pub(super) struct HeapHeader {
pub(super) capacity: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Variant {
Inline,
Referenced,
Heap,
}
#[repr(C)]
pub(super) union SmallVecData<T, const N: usize> {
pub(super) inline: ManuallyDrop<[MaybeUninit<T>; N]>,
pub(super) referenced: *const T,
pub(super) heap: *mut T,
}
#[repr(C)]
pub struct SmallVec<'a, T, const N: usize = 8> {
pub(super) tagged_len: usize,
pub(super) data: SmallVecData<T, N>,
pub(super) _marker: PhantomData<&'a ()>,
}
impl<'a, T, const N: usize> SmallVec<'a, T, N> {
#[inline(always)]
pub(super) fn tag(&self) -> usize {
self.tagged_len & TAG_MASK
}
#[inline(always)]
pub(super) fn raw_len(&self) -> usize {
self.tagged_len & LEN_MASK
}
#[inline(always)]
pub(super) fn variant(&self) -> Variant {
match self.tag() {
TAG_INLINE => Variant::Inline,
TAG_REFERENCED => Variant::Referenced,
TAG_HEAP => Variant::Heap,
_ => {
debug_assert!(false, "invalid SmallVec tag bits");
unsafe { core::hint::unreachable_unchecked() }
}
}
}
#[inline(always)]
pub(super) fn encode(tag: usize, len: usize) -> usize {
debug_assert!(len & TAG_MASK == 0, "length overflows into tag bits");
tag | len
}
#[inline]
pub(super) const fn heap_align() -> usize {
max(mem::align_of::<HeapHeader>(), mem::align_of::<T>())
}
#[inline]
pub(super) const fn heap_offset() -> usize {
max(mem::size_of::<HeapHeader>(), Self::heap_align())
}
#[inline]
pub(super) fn heap_size(capacity: usize) -> usize {
mem::size_of::<T>()
.checked_mul(capacity)
.and_then(|size| Self::heap_offset().checked_add(size))
.filter(|&size| size < isize::MAX as usize - Self::heap_align())
.unwrap_or_else(|| capacity_overflow())
}
#[inline]
pub(super) fn heap_layout(capacity: usize) -> Layout {
unsafe {
Layout::from_size_align_unchecked(
Self::heap_size(capacity),
Self::heap_align(),
)
}
}
#[inline]
pub(super) unsafe fn header_from_ptr(data_ptr: *mut T) -> &'a HeapHeader {
unsafe { &*(data_ptr.cast::<u8>().sub(Self::heap_offset()) as *const HeapHeader) }
}
#[inline]
pub(super) unsafe fn alloc_ptr_from_data(data_ptr: *mut T) -> *mut u8 {
unsafe { data_ptr.cast::<u8>().sub(Self::heap_offset()) }
}
}
#[cold]
#[track_caller]
pub(super) fn capacity_overflow() -> ! {
panic!("capacity overflow");
}
unsafe impl<T: Send + Sync, const N: usize> Send for SmallVec<'_, T, N> {}
unsafe impl<T: Send + Sync, const N: usize> Sync for SmallVec<'_, T, N> {}
const _: () = assert!(
(0b11usize << (usize::BITS - 2)) & !0b11usize.wrapping_shl(usize::BITS - 2) == 0,
"tag constants are consistent"
);
const _: () = assert!(TAG_INLINE == 0, "Inline tag must be zero for cheap default");
const _: () = assert!(
mem::size_of::<HeapHeader>() == mem::size_of::<usize>(),
"HeapHeader must be one word"
);