use std::alloc;
use std::ptr::NonNull;
mod fixed;
mod growable;
pub use fixed::CompactBytesSlice;
pub use growable::CompactBytes;
const INLINE_MASK: u8 = 0b1000_0000;
#[repr(C, align(8))]
#[derive(Copy, Clone)]
struct InlineBytes<const CAPACITY: usize> {
buffer: [u8; CAPACITY],
data: u8,
}
type InlineBytes23 = InlineBytes<23>;
static_assertions::assert_eq_size!(InlineBytes23, Vec<u8>);
type InlineBytes15 = InlineBytes<15>;
static_assertions::assert_eq_size!(InlineBytes15, Box<[u8]>);
impl<const CAPACITY: usize> InlineBytes<CAPACITY> {
const CAPACITY: usize = CAPACITY;
#[inline]
pub unsafe fn new(slice: &[u8]) -> Self {
debug_assert!(slice.len() <= CAPACITY);
let len = slice.len();
let mut buffer = [0u8; CAPACITY];
unsafe {
buffer
.as_mut_ptr()
.copy_from_nonoverlapping(slice.as_ptr(), len)
};
let data = INLINE_MASK | (len as u8);
InlineBytes { buffer, data }
}
#[inline]
pub const fn empty() -> Self {
let buffer = [0u8; CAPACITY];
#[allow(clippy::identity_op)]
let data = INLINE_MASK | 0;
InlineBytes { buffer, data }
}
pub fn len(&self) -> usize {
(self.data & !INLINE_MASK) as usize
}
unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(new_len <= CAPACITY);
self.data = INLINE_MASK | (new_len as u8);
}
}
#[repr(C)]
struct HeapBytesGrowable {
ptr: NonNull<u8>,
len: usize,
cap: usize,
}
static_assertions::assert_eq_size!(HeapBytesGrowable, Vec<u8>);
impl HeapBytesGrowable {
pub const MIN_ALLOCATION_SIZE: usize = std::mem::size_of::<usize>() * 2;
pub const MAX_ALLOCATION_SIZE: usize = usize::MAX >> 1;
#[inline]
pub fn new(slice: &[u8]) -> Self {
let len = slice.len();
let cap = len.max(Self::MIN_ALLOCATION_SIZE);
debug_assert!(cap <= Self::MAX_ALLOCATION_SIZE, "too large of allocation");
let ptr = heap::alloc_ptr(cap);
unsafe { ptr.as_ptr().copy_from_nonoverlapping(slice.as_ptr(), len) };
HeapBytesGrowable { ptr, len, cap }
}
pub fn with_capacity(capacity: usize) -> Self {
assert!(
capacity <= Self::MAX_ALLOCATION_SIZE,
"too large of allocation"
);
let len = 0;
let cap = capacity.max(Self::MIN_ALLOCATION_SIZE);
let ptr = heap::alloc_ptr(cap);
HeapBytesGrowable { ptr, len, cap }
}
pub fn with_additional(slice: &[u8], additional: usize) -> Self {
let new_capacity = Self::amortized_growth(slice.len(), additional);
let mut row = Self::with_capacity(new_capacity);
debug_assert!(row.cap > slice.len());
unsafe {
std::ptr::copy_nonoverlapping(slice.as_ptr(), row.ptr.as_ptr(), slice.len());
};
row.len = slice.len();
row
}
pub unsafe fn set_len(&mut self, len: usize) {
self.len = len;
}
pub fn realloc(&mut self, new_capacity: usize) -> Result<usize, ()> {
if new_capacity < self.len {
return Err(());
}
if new_capacity == 0 {
return Err(());
}
let new_capacity = new_capacity.max(Self::MIN_ALLOCATION_SIZE);
if new_capacity == self.cap {
return Ok(new_capacity);
}
let cur_layout = heap::layout(self.cap);
let new_layout = heap::layout(new_capacity);
let new_size = new_layout.size();
if new_size < new_capacity {
return Err(());
}
let raw_ptr = unsafe { alloc::realloc(self.ptr.as_ptr(), cur_layout, new_size) };
let ptr = NonNull::new(raw_ptr).ok_or(())?;
self.ptr = ptr;
self.cap = new_capacity;
Ok(new_capacity)
}
#[inline]
fn dealloc(&mut self) {
heap::dealloc_ptr(self.ptr, self.cap);
}
#[inline(always)]
pub fn amortized_growth(cur_len: usize, additional: usize) -> usize {
let required = cur_len.saturating_add(additional);
let amortized = cur_len.saturating_mul(3) / 2;
amortized.max(required)
}
}
impl Drop for HeapBytesGrowable {
fn drop(&mut self) {
self.dealloc()
}
}
#[repr(C)]
struct HeapBytesFixed {
ptr: NonNull<u8>,
len: usize,
}
static_assertions::assert_eq_size!(HeapBytesFixed, Box<[u8]>);
impl HeapBytesFixed {
pub const MAX_ALLOCATION_SIZE: usize = usize::MAX >> 1;
#[inline]
pub fn new(slice: &[u8]) -> Self {
let len = slice.len();
debug_assert!(len <= Self::MAX_ALLOCATION_SIZE, "too large of allocation");
let ptr = heap::alloc_ptr(len);
unsafe { ptr.as_ptr().copy_from_nonoverlapping(slice.as_ptr(), len) };
HeapBytesFixed { ptr, len }
}
#[inline]
fn dealloc(&mut self) {
heap::dealloc_ptr(self.ptr, self.len);
}
}
mod heap {
use std::alloc;
use std::ptr::NonNull;
#[inline]
pub(crate) fn alloc_ptr(capacity: usize) -> NonNull<u8> {
let layout = layout(capacity);
debug_assert!(layout.size() > 0);
let ptr = unsafe { alloc::alloc(layout) };
NonNull::new(ptr).expect("failed to allocate HeapRow")
}
#[inline]
pub(crate) fn dealloc_ptr(ptr: NonNull<u8>, capacity: usize) {
let layout = layout(capacity);
unsafe { alloc::dealloc(ptr.as_ptr(), layout) };
}
#[inline(always)]
pub(crate) fn layout(capacity: usize) -> alloc::Layout {
debug_assert!(capacity > 0, "tried to allocate a HeapRow with 0 capacity");
alloc::Layout::array::<u8>(capacity).expect("valid capacity")
}
}