hdf5-pure-rust 0.3.2

Pure Rust implementation of the HDF5 file format
Documentation
use crate::error::{Error, Result};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FreeListStats {
    pub regular_bytes: usize,
    pub block_bytes: usize,
    pub array_bytes: usize,
    pub factory_bytes: usize,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FreeListManager {
    regular: Vec<Vec<u8>>,
    blocks: Vec<Vec<u8>>,
    arrays: Vec<Vec<u8>>,
    factories: Vec<Vec<u8>>,
    max_regular: usize,
    max_block: usize,
    max_array: usize,
    max_factory: usize,
}

impl Default for FreeListManager {
    fn default() -> Self {
        Self::new()
    }
}

impl FreeListManager {
    pub fn new() -> Self {
        Self {
            regular: Vec::new(),
            blocks: Vec::new(),
            arrays: Vec::new(),
            factories: Vec::new(),
            max_regular: usize::MAX,
            max_block: usize::MAX,
            max_array: usize::MAX,
            max_factory: usize::MAX,
        }
    }

    pub fn term_package(&mut self) {
        self.garbage_coll();
    }

    pub fn malloc(size: usize) -> Result<Vec<u8>> {
        if size == 0 {
            return Err(Error::InvalidFormat(
                "free-list allocation size is zero".into(),
            ));
        }
        Ok(vec![0; size])
    }

    pub fn reg_malloc(&mut self, size: usize) -> Result<Vec<u8>> {
        self.regular
            .pop()
            .filter(|buf| buf.len() >= size)
            .map_or_else(
                || Self::malloc(size),
                |mut buf| {
                    buf.resize(size, 0);
                    Ok(buf)
                },
            )
    }

    pub fn reg_calloc(&mut self, size: usize) -> Result<Vec<u8>> {
        let mut buf = self.reg_malloc(size)?;
        buf.fill(0);
        Ok(buf)
    }

    pub fn reg_gc_list(&mut self) {
        self.regular.clear();
    }

    pub fn reg_gc(&mut self) {
        self.reg_gc_list();
    }

    pub fn reg_term(&mut self) {
        self.reg_gc();
    }

    pub fn blk_find_list(&mut self, size: usize) -> Option<Vec<u8>> {
        let pos = self.blocks.iter().position(|buf| buf.len() >= size)?;
        Some(self.blocks.remove(pos))
    }

    pub fn blk_create_list(&mut self) {
        self.blocks.clear();
    }

    pub fn blk_init(&mut self) {
        self.blk_create_list();
    }

    pub fn blk_free_block_avail(&self, size: usize) -> bool {
        self.blocks.iter().any(|buf| buf.len() >= size)
    }

    pub fn blk_malloc(&mut self, size: usize) -> Result<Vec<u8>> {
        self.blk_find_list(size).map_or_else(
            || Self::malloc(size),
            |mut buf| {
                buf.resize(size, 0);
                Ok(buf)
            },
        )
    }

    pub fn blk_calloc(&mut self, size: usize) -> Result<Vec<u8>> {
        let mut buf = self.blk_malloc(size)?;
        buf.fill(0);
        Ok(buf)
    }

    pub fn blk_free(&mut self, mut buf: Vec<u8>) {
        if self.blocks.len() < self.max_block {
            buf.fill(0);
            self.blocks.push(buf);
        }
    }

    pub fn blk_realloc(&mut self, mut buf: Vec<u8>, new_size: usize) -> Result<Vec<u8>> {
        if new_size == 0 {
            return Err(Error::InvalidFormat(
                "free-list allocation size is zero".into(),
            ));
        }
        buf.resize(new_size, 0);
        Ok(buf)
    }

    pub fn blk_gc_list(&mut self) {
        self.blocks.clear();
    }

    pub fn blk_gc(&mut self) {
        self.blk_gc_list();
    }

    pub fn blk_term(&mut self) {
        self.blk_gc();
    }

    pub fn arr_init(&mut self) {
        self.arrays.clear();
    }

    pub fn arr_free(&mut self, mut buf: Vec<u8>) {
        if self.arrays.len() < self.max_array {
            buf.fill(0);
            self.arrays.push(buf);
        }
    }

    pub fn arr_malloc(&mut self, count: usize, elem_size: usize) -> Result<Vec<u8>> {
        let size = count
            .checked_mul(elem_size)
            .ok_or_else(|| Error::InvalidFormat("free-list array size overflow".into()))?;
        Self::malloc(size)
    }

    pub fn arr_calloc(&mut self, count: usize, elem_size: usize) -> Result<Vec<u8>> {
        self.arr_malloc(count, elem_size)
    }

    pub fn arr_realloc(
        &mut self,
        mut buf: Vec<u8>,
        count: usize,
        elem_size: usize,
    ) -> Result<Vec<u8>> {
        let size = count
            .checked_mul(elem_size)
            .ok_or_else(|| Error::InvalidFormat("free-list array size overflow".into()))?;
        buf.resize(size, 0);
        Ok(buf)
    }

    pub fn arr_gc_list(&mut self) {
        self.arrays.clear();
    }

    pub fn arr_gc(&mut self) {
        self.arr_gc_list();
    }

    pub fn arr_term(&mut self) {
        self.arr_gc();
    }

    pub fn seq_free(&mut self, buf: Vec<u8>) {
        self.arr_free(buf);
    }

    pub fn seq_malloc(&mut self, count: usize, elem_size: usize) -> Result<Vec<u8>> {
        self.arr_malloc(count, elem_size)
    }

    pub fn seq_calloc(&mut self, count: usize, elem_size: usize) -> Result<Vec<u8>> {
        self.arr_calloc(count, elem_size)
    }

    pub fn seq_realloc(&mut self, buf: Vec<u8>, count: usize, elem_size: usize) -> Result<Vec<u8>> {
        self.arr_realloc(buf, count, elem_size)
    }

    pub fn fac_init(&mut self) {
        self.factories.clear();
    }

    pub fn fac_free(&mut self, mut buf: Vec<u8>) {
        if self.factories.len() < self.max_factory {
            buf.fill(0);
            self.factories.push(buf);
        }
    }

    pub fn fac_malloc(&mut self, size: usize) -> Result<Vec<u8>> {
        self.factories
            .pop()
            .filter(|buf| buf.len() >= size)
            .map_or_else(
                || Self::malloc(size),
                |mut buf| {
                    buf.resize(size, 0);
                    Ok(buf)
                },
            )
    }

    pub fn fac_calloc(&mut self, size: usize) -> Result<Vec<u8>> {
        let mut buf = self.fac_malloc(size)?;
        buf.fill(0);
        Ok(buf)
    }

    pub fn fac_gc_list(&mut self) {
        self.factories.clear();
    }

    pub fn fac_gc(&mut self) {
        self.fac_gc_list();
    }

    pub fn fac_term(&mut self) {
        self.fac_gc();
    }

    pub fn fac_term_all(&mut self) {
        self.fac_term();
    }

    pub fn garbage_coll(&mut self) {
        self.reg_gc();
        self.blk_gc();
        self.arr_gc();
        self.fac_gc();
    }

    pub fn set_free_list_limits(
        &mut self,
        regular: usize,
        block: usize,
        array: usize,
        factory: usize,
    ) {
        self.max_regular = regular;
        self.max_block = block;
        self.max_array = array;
        self.max_factory = factory;
        self.regular.truncate(regular);
        self.blocks.truncate(block);
        self.arrays.truncate(array);
        self.factories.truncate(factory);
    }

    pub fn get_free_list_sizes(&self) -> FreeListStats {
        FreeListStats {
            regular_bytes: self.regular.iter().map(Vec::len).sum(),
            block_bytes: self.blocks.iter().map(Vec::len).sum(),
            array_bytes: self.arrays.iter().map(Vec::len).sum(),
            factory_bytes: self.factories.iter().map(Vec::len).sum(),
        }
    }
}

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

    #[test]
    fn block_free_list_reuses_buffers() {
        let mut lists = FreeListManager::new();
        let buf = lists.blk_malloc(8).unwrap();
        lists.blk_free(buf);
        assert!(lists.blk_free_block_avail(8));
        let reused = lists.blk_malloc(4).unwrap();
        assert_eq!(reused.len(), 4);
    }
}