peckr 0.6.1

Rust pointer type that allows packing additional data along with the underlying memory offset.
Documentation
//! Heap allocated memory location.

extern crate alloc;

use alloc::alloc::{alloc as malloc, dealloc, Layout};
use core::mem;
use core::ops::{Deref, DerefMut, Drop};
use core::ptr::NonNull;

use crate::raw::PackedPtr;

/// Convenience struct to return the [`PackedPtr`] type associated
/// with some [`PackedBox`].
trait PackedPtrType {
    /// The [`PackedPtr`] type.
    type Ptr;
}

impl<const CAPACITY: u32, T> PackedPtrType for PackedBox<CAPACITY, T> {
    type Ptr = PackedPtr<CAPACITY, T>;
}

/// Heap allocated pointer type with the ability to store up to `CAPACITY`
/// arbitrary bits inline with the underlying memory offset.
///
/// The variance guarantees are the same as that of
/// a [`NonNull`] pointer.
#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[repr(transparent)]
pub struct PackedBox<const CAPACITY: u32, T> {
    packed: PackedPtr<CAPACITY, T>,
}

/// Failure condition met upon allocating a [`PackedBox`] value.
#[derive(Clone, Debug)]
pub enum AllocationError {
    /// Invalid layout resulting from the requested
    /// capacity bits.
    InvalidLayout,
    /// The allocator returned a null pointer.
    NullPtr,
}

unsafe impl<const CAPACITY: u32, T: Send> Send for PackedBox<CAPACITY, T> {}

unsafe impl<const CAPACITY: u32, T: Sync> Sync for PackedBox<CAPACITY, T> {}

impl<const CAPACITY: u32, T> Clone for PackedBox<CAPACITY, T>
where
    T: Clone,
{
    #[inline]
    fn clone(&self) -> Self {
        Self::new_with_bitfield(self.as_ref().clone(), self.bitfield()).unwrap()
    }

    #[inline]
    fn clone_from(&mut self, source: &Self) {
        let this: &mut T = self;
        let source: &T = source;

        this.clone_from(source);
    }
}

impl<const CAPACITY: u32, T> Deref for PackedBox<CAPACITY, T> {
    type Target = PackedPtr<CAPACITY, T>;

    fn deref(&self) -> &Self::Target {
        &self.packed
    }
}

impl<const CAPACITY: u32, T> DerefMut for PackedBox<CAPACITY, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.packed
    }
}

impl<const CAPACITY: u32, T> Drop for PackedBox<CAPACITY, T> {
    fn drop(&mut self) {
        if mem::size_of::<T>() == 0 {
            return;
        }

        let layout = Self::layout().unwrap();
        let offset = self.packed.masked_pointer().cast();

        unsafe {
            // SAFETY: The bit field has been properly masked away,
            // and the layout is correct, so the call to `dealloc`
            // should be semantically correct.
            dealloc(offset, layout);
        }
    }
}

impl<const CAPACITY: u32, T> PackedBox<CAPACITY, T> {
    /// The memory layout required to represent `T` along with
    /// up to `CAPACITY` of arbitrary storage bits.
    #[inline]
    fn layout() -> Result<Layout, AllocationError> {
        let alignment = 2usize
            .checked_pow(
                CAPACITY
                    .checked_sub(1)
                    .ok_or(AllocationError::InvalidLayout)?,
            )
            .ok_or(AllocationError::InvalidLayout)?;
        Layout::new::<T>()
            .align_to(alignment)
            .map_err(|_| AllocationError::InvalidLayout)
    }

    /// Allocates a new value of type `T` using the global
    /// allocator.
    #[inline]
    pub fn new(value: T) -> Result<Self, AllocationError> {
        Self::new_with_bitfield(value, 0)
    }

    /// Allocates a new value of type `T` using the global
    /// allocator. The bit field of the returned pointer is
    /// pre-set to `bitfield`.
    pub fn new_with_bitfield(value: T, bitfield: usize) -> Result<Self, AllocationError> {
        if CAPACITY == 0 || CAPACITY >= (mem::size_of::<usize>() * 8) as u32 {
            return Err(AllocationError::InvalidLayout);
        }
        if mem::size_of::<T>() == 0 {
            let mask = <Self as PackedPtrType>::Ptr::mask();
            let packed = unsafe {
                // SAFETY: The pointer is non-null, well aligned and contains
                // at least `CAPACITY` free bits to store arbitrary data.
                PackedPtr::new_unchecked_raw((!mask | (bitfield & mask)) as *mut _)
            };
            return Ok(PackedBox { packed });
        }
        let layout = Self::layout()?;
        let offset: *mut T = unsafe {
            // SAFETY: The requested layout for `T` must be
            // correct at this point.
            malloc(layout).cast()
        };
        let pointer = NonNull::new(offset).ok_or(AllocationError::NullPtr)?;
        debug_assert_eq!(offset as usize & <Self as PackedPtrType>::Ptr::mask(), 0);
        unsafe {
            // SAFETY: The pointer returned by the global allocator
            // should point to a valid memory location.
            pointer.as_ptr().write(value);
        }
        let mut packed = unsafe {
            // SAFETY: The pointer is properly aligned, and has at
            // least `CAPACITY` of free bits to store arbitrary data.
            PackedPtr::new_unchecked(pointer)
        };
        if bitfield != 0 {
            packed.set_bitfield(bitfield);
        }
        Ok(PackedBox { packed })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Check that allocating a pointer with zero capacity fails.
    #[test]
    fn zero_capacity_fails() {
        let result: Result<PackedBox<0, _>, _> = PackedBox::new(1234);
        assert!(matches!(result, Err(AllocationError::InvalidLayout)));
    }

    /// Test that allocating bit fields larger than or equal to the
    /// platform's [`usize`] length in bits is invalid.
    #[test]
    fn large_bitfields_invalid() {
        const MAX_WIDTH: u32 = (mem::size_of::<usize>() * 8) as u32;
        let result: Result<PackedBox<{ MAX_WIDTH }, _>, _> = PackedBox::new("test");
        assert!(matches!(result, Err(AllocationError::InvalidLayout)));
    }

    /// Test initializing a ptr with a bitfield.
    #[test]
    fn new_with_bitfield() {
        let ptr: PackedBox<4, _> = PackedBox::new_with_bitfield(1234, 0b1010).unwrap();
        assert_eq!(ptr.bitfield(), 0b1010);
        assert_eq!(*ptr.as_ref(), 1234);

        let ptr: PackedBox<4, _> = PackedBox::new_with_bitfield((), 0b0101).unwrap();
        assert_eq!(ptr.bitfield(), 0b0101);
        assert_eq!(*ptr.as_ref(), ());
    }

    /// Test cloning a pointer.
    #[test]
    fn clone_pointer() {
        let ptr: PackedBox<4, _> =
            PackedBox::new_with_bitfield(String::from("test"), 0b1010).unwrap();
        assert_eq!(ptr.bitfield(), 0b1010);
        assert_eq!(*ptr.as_ref(), "test");

        let cloned = ptr.clone();
        assert_eq!(ptr.bitfield(), cloned.bitfield());
        assert_eq!(ptr.as_ref(), cloned.as_ref());
        assert!(!core::ptr::eq(ptr.as_ref(), cloned.as_ref()));

        // --------------

        let ptr: PackedBox<4, _> = PackedBox::new_with_bitfield((), 0b0101).unwrap();
        assert_eq!(ptr.bitfield(), 0b0101);
        assert_eq!(*ptr.as_ref(), ());

        let cloned = ptr.clone();
        assert_eq!(ptr.bitfield(), cloned.bitfield());
        assert_eq!(ptr.as_ref(), cloned.as_ref());
        assert!(core::ptr::eq(ptr.as_ref(), cloned.as_ref()));
    }

    /// Test attempting to allocate zero sized types results in no
    /// allocation. Additionally, test that writing values is indeed
    /// a no-op. In other words, no segmentation faults should occur
    /// from writing to pointer types allocating zero sized types.
    #[test]
    fn zero_size_types() {
        let mut ptr: PackedBox<63, _> = PackedBox::new(()).unwrap();
        assert_eq!(
            ptr.as_ref() as *const _ as usize,
            !PackedPtr::<63, ()>::mask()
        );
        *ptr.as_mut() = ();
    }

    /// Test that the `clone_from` method keeps the same mem buffer.
    #[test]
    fn clone_from_same_buf() {
        let mut ptr1: PackedBox<1, i32> = PackedBox::new(4).unwrap();
        let ptr2: PackedBox<1, i32> = PackedBox::new(2).unwrap();

        let ptr1_ref1: &i32 = ptr1.as_ref();
        let ptr1_off1: usize = ptr1_ref1 as *const _ as usize;

        ptr1.clone_from(&ptr2);
        drop(ptr2);

        let ptr1_ref2: &i32 = ptr1.as_ref();
        let ptr1_off2: usize = ptr1_ref2 as *const _ as usize;

        assert_eq!(ptr1_off1, ptr1_off2);
    }
}