//! [TSM.ID].[11031972] — Platform X Ecosystem
//! xcu-memory-pool — Zero-Copy Slab Memory Allocator
#![deny(warnings)]
#![allow(dead_code)]

pub mod pool;
pub mod allocator;

#[derive(Debug)]
pub enum PoolError {
    OutOfMemory,
    InvalidBlockSize,
    DoubleFree,
    InvalidPointer,
    PoolExhausted,
}

impl std::fmt::Display for PoolError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::OutOfMemory => write!(f, "Out of memory"),
            Self::InvalidBlockSize => write!(f, "Invalid block size"),
            Self::DoubleFree => write!(f, "Double free detected"),
            Self::InvalidPointer => write!(f, "Invalid pointer"),
            Self::PoolExhausted => write!(f, "Pool exhausted"),
        }
    }
}
impl std::error::Error for PoolError {}
pub type Result<T> = std::result::Result<T, PoolError>;

pub struct PoolConfig {
    pub block_size: usize,
    pub num_blocks: usize,
    pub alignment: usize,
}

impl Default for PoolConfig {
    fn default() -> Self {
        Self { block_size: 4096, num_blocks: 256, alignment: 8 }
    }
}

pub struct PoolStats {
    pub total_blocks: usize,
    pub used_blocks: usize,
    pub free_blocks: usize,
    pub total_bytes: usize,
    pub used_bytes: usize,
    pub peak_usage: usize,
    pub alloc_count: u64,
    pub free_count: u64,
}

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

    #[test]
    fn test_alloc_dealloc() {
        let config = PoolConfig { block_size: 64, num_blocks: 4, alignment: 8 };
        let mut slab = SlabAllocator::new(&config).expect("init");
        let (idx, buf) = slab.allocate().expect("alloc");
        buf[0] = 42;
        assert_eq!(buf[0], 42);
        slab.deallocate(idx).expect("dealloc");
        let stats = slab.stats();
        assert_eq!(stats.used_blocks, 0);
        assert_eq!(stats.alloc_count, 1);
        assert_eq!(stats.free_count, 1);
    }

    #[test]
    fn test_double_free() {
        let config = PoolConfig::default();
        let mut slab = SlabAllocator::new(&config).expect("init");
        let (idx, _) = slab.allocate().expect("alloc");
        slab.deallocate(idx).expect("first free");
        assert!(slab.deallocate(idx).is_err());
    }

    #[test]
    fn test_pool_exhaustion() {
        let config = PoolConfig { block_size: 32, num_blocks: 2, alignment: 8 };
        let mut slab = SlabAllocator::new(&config).expect("init");
        let _ = slab.allocate().expect("1");
        let _ = slab.allocate().expect("2");
        assert!(slab.allocate().is_err());
    }
}
