litl-val 0.2.0

A memory efficient representation of JSON values
Documentation
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>,
}

// Stores a boxed slice with length and potential capacity appended to data
// Allows seamless conversion from vecs, by putting the len and capacity after the end of the vec
// In the ideal case (cap == ideal_cap), we only store one i32 after the data
// otherwise we store a negative i32 and a u32 for capacity

#[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()))
    }

    // TODO: preallocate right size, just copy once (instead of growing)
    #[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)
    }
}