use std::{
fmt::Debug,
hash::{Hash, Hasher},
marker::PhantomData,
ptr::NonNull,
};
use deepsize::DeepSizeOf;
pub struct SlimBoxedSlice<T> {
pub ptr: Option<std::ptr::NonNull<i32>>,
pub _marker: PhantomData<T>,
}
#[inline]
fn div_round_up(a: usize, b: usize) -> usize {
(a + b - 1) / b
}
#[inline]
fn ideal_capacity<T>(len: usize) -> usize {
let extra_capacity = div_round_up(std::mem::size_of::<i32>(), std::mem::size_of::<T>());
len + extra_capacity
}
#[inline]
fn unideal_min_capacity<T>(len: usize) -> usize {
let extra_capacity = div_round_up(std::mem::size_of::<(i32, u32)>(), std::mem::size_of::<T>());
len + extra_capacity
}
impl<T> SlimBoxedSlice<T> {
#[inline]
pub(crate) fn from_vec(mut vec: Vec<T>) -> Self {
if vec.is_empty() {
return Self {
ptr: None,
_marker: PhantomData,
};
}
let ideal_cap = ideal_capacity::<T>(vec.len());
vec.reserve_exact(ideal_cap.saturating_sub(vec.len()));
if vec.capacity() == ideal_cap {
let ptr = vec.as_mut_ptr();
let len = vec.len();
std::mem::forget(vec);
unsafe {
let ptr_to_end = ptr.add(len) as *mut () as *mut i32;
*ptr_to_end = len as i32;
Self {
ptr: Some(NonNull::new_unchecked(ptr_to_end)),
_marker: PhantomData,
}
}
} else {
let unideal_cap = unideal_min_capacity::<T>(vec.len());
vec.reserve_exact(unideal_cap.saturating_sub(vec.len()));
if vec.capacity() < unideal_cap {
panic!("Unideal capacity too small");
}
let ptr = vec.as_mut_ptr();
let len = vec.len();
let cap = vec.capacity();
std::mem::forget(vec);
unsafe {
let ptr_to_end = ptr.add(len) as *mut () as *mut (i32, u32);
*ptr_to_end = (-(len as i32), cap as u32);
Self {
ptr: Some(NonNull::new_unchecked(ptr_to_end as *mut i32)),
_marker: PhantomData,
}
}
}
}
#[inline]
fn into_vec_raw_parts(&self) -> (*mut T, usize, usize) {
if let Some(ptr) = self.ptr {
let ptr = ptr.as_ptr();
let len = unsafe { *ptr };
if len >= 0 {
let len = len as usize;
let ptr_to_end = ptr as *mut T;
let ptr_to_start = unsafe { ptr_to_end.sub(len) };
(ptr_to_start, len, ideal_capacity::<T>(len))
} else {
unsafe {
let ptr = ptr as *mut (i32, u32);
let len = (-(*ptr).0) as usize;
let cap = (*ptr).1 as usize;
let ptr_to_end = ptr as *mut T;
let ptr_to_start = ptr_to_end.sub(len);
(ptr_to_start, len, cap)
}
}
} else {
(NonNull::dangling().as_ptr(), 0, 0)
}
}
#[inline]
pub fn into_vec(self) -> Vec<T> {
unsafe {
let (ptr, len, cap) = self.into_vec_raw_parts();
std::mem::forget(self);
Vec::from_raw_parts(ptr, len, cap)
}
}
#[inline]
unsafe fn as_slice_parts(&self) -> (*mut T, usize) {
slim_box_parts_for_slice::<T>(self.ptr)
}
#[inline]
pub(crate) fn into_raw(self) -> *mut () {
let ptr = self.ptr;
std::mem::forget(self);
ptr.map(NonNull::as_ptr).unwrap_or(std::ptr::null_mut()) as *mut ()
}
#[inline]
pub(crate) unsafe fn from_raw(ptr: *mut ()) -> Self {
Self {
ptr: NonNull::new(ptr as *mut i32),
_marker: PhantomData,
}
}
}
pub(crate) unsafe fn slim_box_parts_for_slice<T>(ptr: Option<NonNull<i32>>) -> (*mut T, usize) {
if let Some(ptr) = ptr {
let ptr = ptr.as_ptr();
let len = (*ptr).unsigned_abs() as usize;
let ptr_to_end = ptr as *mut T;
let ptr_to_start = ptr_to_end.sub(len);
(ptr_to_start, len)
} else {
(NonNull::dangling().as_ptr(), 0)
}
}
impl<T> AsRef<[T]> for SlimBoxedSlice<T> {
#[inline]
fn as_ref(&self) -> &[T] {
unsafe {
let (ptr, len) = self.as_slice_parts();
std::slice::from_raw_parts(ptr, len)
}
}
}
impl<T> AsMut<[T]> for SlimBoxedSlice<T> {
#[inline]
fn as_mut(&mut self) -> &mut [T] {
unsafe {
let (ptr, len) = self.as_slice_parts();
std::slice::from_raw_parts_mut(ptr, len)
}
}
}
impl<T> Drop for SlimBoxedSlice<T> {
#[inline]
fn drop(&mut self) {
let (ptr, len, cap) = self.into_vec_raw_parts();
let _ = unsafe { Vec::from_raw_parts(ptr, len, cap) };
}
}
impl<T: Debug> Debug for SlimBoxedSlice<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_ref().fmt(f)
}
}
impl<T: PartialEq> PartialEq for SlimBoxedSlice<T> {
fn eq(&self, other: &Self) -> bool {
self.as_ref().eq(other.as_ref())
}
}
impl<T: Clone> Clone for SlimBoxedSlice<T> {
fn clone(&self) -> Self {
Self::from_vec(self.as_ref().to_vec())
}
}
impl<T: Hash> Hash for SlimBoxedSlice<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().hash(state)
}
}
impl<T: Eq> Eq for SlimBoxedSlice<T> {}
impl<T: DeepSizeOf> DeepSizeOf for SlimBoxedSlice<T> {
fn deep_size_of_children(&self, context: &mut deepsize::Context) -> usize {
let (_, _, cap) = self.into_vec_raw_parts();
self.as_ref()
.iter()
.map(|child| child.deep_size_of_children(context))
.sum::<usize>()
+ cap * std::mem::size_of::<T>()
}
}
pub struct SlimBoxedString(pub(crate) SlimBoxedSlice<u8>);
impl SlimBoxedString {
#[inline]
pub fn from_string(string: String) -> Self {
Self(SlimBoxedSlice::from_vec(string.into_bytes()))
}
#[inline]
pub fn from_str(s: &str) -> Self {
Self(SlimBoxedSlice::from_vec(s.to_owned().into_bytes()))
}
#[inline]
pub fn into_string(self) -> String {
unsafe { String::from_utf8_unchecked(self.0.into_vec()) }
}
#[inline]
pub(crate) fn into_raw(self) -> *mut () {
self.0.into_raw()
}
#[inline]
pub(crate) unsafe fn from_raw(ptr: *mut ()) -> Self {
Self(SlimBoxedSlice::from_raw(ptr))
}
}
impl AsRef<str> for SlimBoxedString {
#[inline]
fn as_ref(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) }
}
}
impl Debug for SlimBoxedString {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self.as_ref(), f)
}
}
impl PartialEq for SlimBoxedString {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl Eq for SlimBoxedString {}
impl DeepSizeOf for SlimBoxedString {
fn deep_size_of_children(&self, context: &mut deepsize::Context) -> usize {
self.0.deep_size_of_children(context)
}
}