memur 0.3.4

Arena storage with bells and whistles.
Documentation
use crate::dontdothis;
use crate::dontdothis::next_item_aligned_start;

pub enum PlacementError {
    NotEnoughSpaceInBlock,
    ItemTooBig,
}

struct BlockMetadata {
    next_item_offset: usize,
    previous_block: Option<Block>,
}

impl BlockMetadata {
    pub unsafe fn init_in_slice(slice: &mut [u8]) -> Option<()> {
        if std::mem::size_of::<BlockMetadata>() > slice.len() {
            None
        } else {
            let metadata = BlockMetadata {
                next_item_offset: std::mem::size_of::<BlockMetadata>(),
                previous_block: None,
            };
            let metadata_as_slice = dontdothis::value_as_slice(&metadata);
            for (inbyte, outbyte) in metadata_as_slice.iter().zip(slice.iter_mut()) {
                *outbyte = *inbyte;
            }
            std::mem::forget(metadata);
            Some(())
        }
    }

    #[inline(always)]
    pub unsafe fn reinterpret_from_slice_ptr_mut(slice: &mut [u8]) -> *mut BlockMetadata {
        dontdothis::slice_as_value_ref_mut::<BlockMetadata>(slice)
    }

    #[inline(always)]
    pub unsafe fn reinterpret_from_slice_ptr(slice: &[u8]) -> *const BlockMetadata {
        dontdothis::slice_as_value_ref::<BlockMetadata>(slice)
    }

    #[inline(always)]
    pub unsafe fn reinterpret_from_slice_mut<'a, 'b>(slice: &'a mut [u8]) -> &'b mut BlockMetadata {
        std::mem::transmute::<*mut BlockMetadata, &mut BlockMetadata>(
            BlockMetadata::reinterpret_from_slice_ptr_mut(slice)
        )
    }

    #[inline(always)]
    pub unsafe fn reinterpret_from_slice<'a, 'b>(slice: &'a [u8]) -> &'b BlockMetadata {
        std::mem::transmute::<*const BlockMetadata, &BlockMetadata>(
            BlockMetadata::reinterpret_from_slice_ptr(slice)
        )
    }
}

pub struct Block {
    data: Box<[u8]>,
}

impl Block {
    pub fn new(mut data: Box<[u8]>) -> Block {
        unsafe { BlockMetadata::init_in_slice(&mut *data).expect("init metadata in block") };
        Block {
            data
        }
    }

    pub unsafe fn set_previous_block(&mut self, block: Block) {
        let metadata = BlockMetadata::reinterpret_from_slice_mut(&mut *self.data);
        metadata.previous_block = Some(block);
    }

    pub unsafe fn push<T>(&mut self, value: T) -> Result<*mut T, PlacementError> {
        match self.push_copy(&value) {
            Err(e) => Err(e),
            Ok(ptr) => {
                std::mem::forget(value);
                Ok(ptr)
            },
        }
    }

    pub fn largest_item_size(&self) -> usize {
        self.data.len() - std::mem::size_of::<BlockMetadata>()
    }

    pub unsafe fn push_copy<T>(&mut self, value: &T) -> Result<*mut T, PlacementError> {
        let metadata = BlockMetadata::reinterpret_from_slice_mut(&mut *self.data);
        let aligned = next_item_aligned_start::<T>(metadata.next_item_offset);
        let end = aligned + std::mem::size_of::<T>();
        if end > self.data.len() {
            if std::mem::size_of::<T>() > self.largest_item_size() {
                Err(PlacementError::ItemTooBig)
            } else {
                Err(PlacementError::NotEnoughSpaceInBlock)
            }
        } else {
            let target_slice = &mut self.data[aligned..];
            let source_slice = dontdothis::value_as_slice(value);
            for (inbyte, outbyte) in source_slice.iter().zip(target_slice.iter_mut()) {
                *outbyte = *inbyte;
            }
            metadata.next_item_offset = end;
            Ok(dontdothis::slice_as_value_ref_mut::<T>(target_slice))
        }
    }

    pub unsafe fn into_previous_block_and_data(mut self) -> (Option<Block>, Box<[u8]>) {
        let metadata = BlockMetadata::reinterpret_from_slice_mut(&mut *self.data);
        let mut block = None;
        std::mem::swap(&mut block, &mut metadata.previous_block);
        (block, self.data)
    }

    pub fn remaining_bytes_for_alignment<T>(&self) -> (isize, usize) {
        let metadata = unsafe { BlockMetadata::reinterpret_from_slice(&*self.data) };
        let aligned = next_item_aligned_start::<T>(metadata.next_item_offset);
        (self.data.len() as isize - aligned as isize, aligned)
    }

    pub unsafe fn upload_bytes_unchecked(&mut self, aligned_start: usize, len: usize, value: impl Iterator<Item=u8>) -> *mut u8 {
        let metadata = BlockMetadata::reinterpret_from_slice_mut(&mut *self.data);
        let end = aligned_start + len;
        debug_assert!(end <= self.data.len(), "upload_bytes_unchecked end <= data.len");
        let target_slice = &mut self.data[aligned_start..];
        for (inbyte, outbyte) in value.zip(target_slice.iter_mut()) {
            *outbyte = inbyte;
        }
        metadata.next_item_offset = end;
        target_slice.as_mut_ptr()
    }

    pub unsafe fn upload_bytes_unchecked_uninit(&mut self, aligned_start: usize, len: usize) -> *mut u8 {
        let metadata = BlockMetadata::reinterpret_from_slice_mut(&mut *self.data);
        let end = aligned_start + len;
        debug_assert!(end <= self.data.len(), "upload_bytes_unchecked end <= data.len");
        let target_slice = &mut self.data[aligned_start..];
        metadata.next_item_offset = end;
        target_slice.as_mut_ptr()
    }
}