mod alloc;
mod pool;
mod types;
mod volume;
pub use types::{
Alert, AlertType, AllocationPolicy, BlockMapping, BlockState, DEFAULT_BLOCK_SIZE,
DEFAULT_CRITICAL_THRESHOLD, DEFAULT_EMERGENCY_THRESHOLD, DEFAULT_WARN_THRESHOLD,
MAX_BLOCK_SIZE, MIN_BLOCK_SIZE, MappingFlags, OvercommitPolicy, PhysicalBlock, PoolConfig,
PoolStats, ThinError, ThinResult, ThresholdLevel, Thresholds, VirtualBlock, VolumeConfig,
VolumeStats,
};
pub use alloc::{BlockAllocator, Extent, ExtentTree, FreeBitmap};
pub use volume::{IoContext, ThinVolume};
pub use pool::{
AlertCallback, CapacitySummary, ThinPool, clear_pools, create_pool, delete_pool, get_pool_id,
get_pool_name, list_pools, pool_count, pool_exists, pool_exists_by_name, with_pool,
};
pub fn quick_pool(name: &str, capacity_gb: u64) -> ThinResult<u64> {
let config = PoolConfig::new(name, capacity_gb * 1024 * 1024 * 1024);
create_pool(config)
}
pub fn quick_volume(pool_id: u64, name: &str, size_gb: u64) -> ThinResult<u64> {
with_pool(pool_id, |pool| {
let config = VolumeConfig::new(name, size_gb * 1024 * 1024 * 1024);
pool.create_volume(config)
})?
}
pub fn pool_usage(pool_id: u64) -> ThinResult<u8> {
with_pool(pool_id, |pool| pool.usage_percent())
}
pub fn pool_overcommit(pool_id: u64) -> ThinResult<f64> {
with_pool(pool_id, |pool| pool.overcommit_ratio())
}
#[cfg(test)]
mod tests {
use super::*;
fn setup() {
clear_pools();
}
#[test]
fn test_quick_pool() {
setup();
let pool_id = quick_pool("test-pool", 100).unwrap();
assert!(pool_exists(pool_id));
}
#[test]
fn test_quick_volume() {
let pool_name = "vol-test-unique-112233";
let pool_id = quick_pool(pool_name, 100).unwrap();
let vol_id = quick_volume(pool_id, "vol1", 50).unwrap();
with_pool(pool_id, |pool| {
assert!(pool.get_volume(vol_id).is_some());
})
.unwrap();
}
#[test]
fn test_pool_usage() {
setup();
let pool_id = quick_pool("usage-test", 10).unwrap();
let usage = pool_usage(pool_id).unwrap();
assert!(usage < 10);
}
#[test]
fn test_pool_overcommit() {
let pool_name = "overcommit-test-unique-54321";
let pool_id = quick_pool(pool_name, 10).unwrap();
let ratio = with_pool(pool_id, |pool| {
for i in 0..10 {
let vol_name = ::alloc::format!("vol{}", i);
let config = VolumeConfig::new(&vol_name, 10 * 1024 * 1024 * 1024);
pool.create_volume(config).unwrap();
}
pool.overcommit_ratio()
})
.unwrap();
assert!(ratio > 9.0, "Expected >9x overcommit, got {}", ratio);
}
#[test]
fn test_exports_accessible() {
let _ = BlockState::Unallocated;
let _ = ThresholdLevel::Normal;
let _ = AlertType::PoolFull;
let _ = AllocationPolicy::FirstFit;
let _ = OvercommitPolicy::StopAtCritical;
}
#[test]
fn test_virtual_block_operations() {
let vb = VirtualBlock::new(100);
assert_eq!(vb.block(), 100);
let pb = PhysicalBlock::new(5000);
assert_eq!(pb.block(), 5000);
}
#[test]
fn test_threshold_defaults() {
let t = Thresholds::default();
assert_eq!(t.warning, DEFAULT_WARN_THRESHOLD);
assert_eq!(t.critical, DEFAULT_CRITICAL_THRESHOLD);
assert_eq!(t.emergency, DEFAULT_EMERGENCY_THRESHOLD);
}
#[test]
fn test_volume_config_builder() {
let config = VolumeConfig::new("test", 1024 * 1024 * 1024)
.with_block_size(256 * 1024)
.with_compression()
.with_reservation(100 * 1024 * 1024);
assert_eq!(config.block_size, 256 * 1024);
assert!(config.compression);
assert_eq!(config.reservation, 100 * 1024 * 1024);
}
#[test]
fn test_pool_config_builder() {
let config = PoolConfig::new("test", 100 * 1024 * 1024 * 1024)
.with_block_size(64 * 1024)
.with_thresholds(Thresholds::new(70, 85, 95))
.with_overcommit_policy(OvercommitPolicy::StopAtCritical);
assert_eq!(config.block_size, 64 * 1024);
assert_eq!(config.thresholds.warning, 70);
assert!(matches!(
config.overcommit_policy,
OvercommitPolicy::StopAtCritical
));
}
#[test]
fn test_extent_operations() {
let e = Extent::new(100, 50);
assert_eq!(e.start, 100);
assert_eq!(e.count, 50);
assert_eq!(e.end(), 150);
assert!(e.contains(125));
assert!(!e.contains(150));
}
#[test]
fn test_free_bitmap() {
let mut bitmap = FreeBitmap::new(1000);
assert_eq!(bitmap.free_count(), 1000);
assert!(bitmap.is_free(500));
bitmap.allocate(500);
assert!(!bitmap.is_free(500));
assert_eq!(bitmap.free_count(), 999);
bitmap.free(500);
assert!(bitmap.is_free(500));
}
#[test]
fn test_extent_tree() {
let mut tree = ExtentTree::with_extent(0, 1000);
let block = tree.allocate_first_fit(100).unwrap();
assert_eq!(block, 0);
assert_eq!(tree.free_blocks(), 900);
tree.free_range(0, 100);
assert_eq!(tree.free_blocks(), 1000);
}
#[test]
fn test_block_allocator() {
let mut alloc = BlockAllocator::new(10000, 4096, 100);
let block = alloc.allocate().unwrap();
assert!(block.0 >= 100);
alloc.free(block);
}
#[test]
fn test_thin_volume() {
let config = VolumeConfig::new("test-vol", 1024 * 1024 * 1024);
let mut vol = ThinVolume::new(1, config);
assert_eq!(vol.id(), 1);
assert_eq!(vol.name(), "test-vol");
let vb = VirtualBlock::new(100);
let pb = PhysicalBlock::new(5000);
vol.map_block(vb, pb);
assert!(vol.is_allocated(vb));
assert_eq!(vol.get_physical(vb), Some(pb));
}
#[test]
fn test_thin_pool() {
let config = PoolConfig::new("test-pool", 10 * 1024 * 1024 * 1024);
let mut pool = ThinPool::new(1, config);
assert_eq!(pool.name(), "test-pool");
assert_eq!(pool.volume_count(), 0);
let vol_config = VolumeConfig::new("vol1", 1024 * 1024 * 1024);
let vol_id = pool.create_volume(vol_config).unwrap();
assert_eq!(pool.volume_count(), 1);
assert!(pool.get_volume(vol_id).is_some());
}
#[test]
fn test_snapshot() {
let config = PoolConfig::new("snap-test", 10 * 1024 * 1024 * 1024);
let mut pool = ThinPool::new(1, config);
let vol_config = VolumeConfig::new("vol1", 1024 * 1024 * 1024);
let vol_id = pool.create_volume(vol_config).unwrap();
let snap_id = pool.create_snapshot(vol_id, "snap1".into()).unwrap();
assert!(pool.get_volume(snap_id).unwrap().is_snapshot());
assert_eq!(pool.stats().snapshot_count, 1);
}
#[test]
fn test_io_context() {
let config = VolumeConfig::new("io-test", 1024 * 1024);
let mut vol = ThinVolume::new(1, config);
let mut alloc = BlockAllocator::new(1000, 128 * 1024, 0);
{
let mut ctx = IoContext::new(&mut vol, &mut alloc);
let pblock = ctx.write_block(VirtualBlock::new(5)).unwrap();
assert!(pblock.0 < 1000);
}
assert!(vol.is_allocated(VirtualBlock::new(5)));
}
}