dynvec 0.1.4

This crate provides the `DynVec` type that acts like a vector to store any datatype.
Documentation
use super::block::Block;
use super::Region;

use std::alloc::Layout;

/// Reallocates new chuncks when it gets too small to allocate new objects.
pub struct Chunks<H> {
    /// The size of the chunks.
    min_chunk_size: usize,
    /// The inner blocks that will store our data.
    chunks: Vec<Block<H>>,
}

impl<H> Chunks<H> {
    /// Creates a new instance of `Chunks`. No chunks will be allocated until
    /// an object is allocated.
    ///
    /// Note that chunks may need to allocate more memory than `min_chunk_size`
    /// to store allocate a large number of bytes.
    pub fn new(min_chunk_size: usize) -> Self {
        Self {
            min_chunk_size,
            chunks: Vec::new(),
        }
    }

    /// Creates a new instance of `Chuncks`. This function allocates `chunk_count`
    /// chunks in advance.
    pub fn with_chuncks(min_chunk_size: usize, chunk_count: usize) -> Self {
        Self {
            min_chunk_size,
            chunks: (0..chunk_count)
                .map(|_| Block::new(min_chunk_size))
                .collect(),
        }
    }
}

impl<H> Region<H> for Chunks<H> {
    type Pointers = std::iter::Flatten<std::vec::IntoIter<<Block<H> as Region<H>>::Pointers>>;

    fn allocate(&mut self, layout: Layout, mut header: H) -> Result<*mut u8, H> {
        for chunk in &mut self.chunks {
            match chunk.allocate(layout, header) {
                Ok(ptr) => return Ok(ptr),
                Err(h) => header = h,
            }
        }

        // no chunk could take care of the allocation
        // we need to allocate a new one.
        let size = self.min_chunk_size.max(layout.size() + layout.align() - 1);
        self.chunks.push(Block::new(size));
        self.chunks.last_mut().unwrap().allocate(layout, header)
    }

    fn deallocate(&mut self, ptr: *mut u8) -> Option<H> {
        for chunk in &mut self.chunks {
            if let Some(header) = chunk.deallocate(ptr) {
                return Some(header);
            }
        }

        None
    }

    fn has_allocated(&self, ptr: *mut u8) -> bool {
        self.chunks.iter().any(|chunk| chunk.has_allocated(ptr))
    }

    fn count(&self) -> usize {
        self.chunks.iter().map(|chunk| chunk.count()).sum()
    }

    fn allocations(&self) -> Self::Pointers {
        self.chunks
            .iter()
            .map(|chunk| chunk.allocations())
            .collect::<Vec<<Block<H> as Region<H>>::Pointers>>()
            .into_iter()
            .flatten()
    }
}

#[cfg(test)]
#[test]
fn allocate_new_chunk() {
    let mut chunks = Chunks::new(5);

    assert_eq!(chunks.chunks.len(), 0);

    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();

    assert_eq!(chunks.chunks.len(), 1);

    chunks.allocate(Layout::new::<u8>(), ()).unwrap();

    assert_eq!(chunks.chunks.len(), 2);
}

#[cfg(test)]
#[test]
fn allocate_chunk_large_enough() {
    let mut chunks = Chunks::new(2);

    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u32>(), ()).unwrap();
}

#[cfg(test)]
#[test]
fn has_allocated() {
    let mut chunks = Chunks::new(4);

    let vec: Vec<*mut u8> = (0..100)
        .map(|_| chunks.allocate(Layout::new::<u8>(), ()).unwrap())
        .collect();

    for ptr in vec {
        assert!(chunks.has_allocated(ptr));
    }
}

#[cfg(test)]
#[test]
fn allocations_count() {
    let mut chunks = Chunks::new(4);

    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u8>(), ()).unwrap();
    chunks.allocate(Layout::new::<u32>(), ()).unwrap();
    chunks.allocate(Layout::new::<u32>(), ()).unwrap();
    chunks.allocate(Layout::new::<u32>(), ()).unwrap();

    assert_eq!(chunks.count(), 11);
}