nimix 0.1.2

An allocator designed to be use by a GC
Documentation
use super::block::Block;
use super::error::AllocError;
use super::constants::{FREE_MARK, LARGE_OBJECT_MIN};

use std::alloc::Layout;
use std::num::NonZero;
use std::sync::atomic::{AtomicU8, Ordering};
use std::ptr::write;

pub struct LargeBlock {
    block: Block,
    mark: *const AtomicU8
}

impl LargeBlock {
    pub fn new(obj_layout: Layout) -> Result<Self, AllocError> {
        debug_assert!(obj_layout.size() >= LARGE_OBJECT_MIN);

        let mark_layout = Layout::new::<AtomicU8>();
        let (obj_mark_layout, mark_offset) = obj_layout.extend(mark_layout)?;
        let block_layout = obj_mark_layout.pad_to_align();
        let block = Block::new(block_layout)?;
        let mark = unsafe { 
            let mark = block.as_ptr().add(mark_offset) as *const AtomicU8;
            write(mark as *mut AtomicU8, AtomicU8::new(FREE_MARK));
            mark
        };

        let large_block = Self {
            block,
            mark
        };

        Ok(large_block)
    }

    pub unsafe fn mark(ptr: *const u8, obj_layout: Layout, mark: NonZero<u8>) -> Result<(), AllocError> {
        let mark_layout = Layout::new::<AtomicU8>();
        let (_, mark_offset) = obj_layout.extend(mark_layout)?;
        let block_mark: *const AtomicU8 = ptr.add(mark_offset) as *const AtomicU8;

        (&*block_mark).store(mark.into(), Ordering::Relaxed);

        Ok(())
    }

    pub fn is_marked(&self, mark: NonZero<u8>) -> bool {
        unsafe { (&*self.mark).load(Ordering::Relaxed) == mark.into() }
    }

    pub fn get_size(&self) -> usize {
        self.block.get_size()
    }

    pub fn as_ptr(&self) -> *const u8 {
        self.block.as_ptr()
    }
}

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

    #[test]
    fn new_large_block() {
        let align = 8;
        let block =  LargeBlock::new(Layout::from_size_align(LARGE_OBJECT_MIN, align).unwrap()).unwrap();

        assert!(block.get_size() > LARGE_OBJECT_MIN);
        assert_eq!(block.as_ptr() as usize % align, 0);
        assert_eq!(block.is_marked(NonZero::new(1).unwrap()), false);
    }

    #[test]
    fn mark_large() {
        let data = [0u8; LARGE_OBJECT_MIN];
        let align = 8;
        let layout = Layout::from_size_align(LARGE_OBJECT_MIN, align).unwrap();
        let block =  LargeBlock::new(layout).unwrap();

        unsafe { 
            write(block.as_ptr() as *mut [u8; LARGE_OBJECT_MIN], data);
            LargeBlock::mark(block.as_ptr(), layout, NonZero::new(1).unwrap()).unwrap();

            assert_eq!(&*(block.as_ptr() as *mut [u8; LARGE_OBJECT_MIN]), &data);
        }

        assert!(block.is_marked(NonZero::new(1).unwrap()));
    }
}