use crate::error::{GpuAdvancedError, Result};
use parking_lot::{Mutex, RwLock};
use std::collections::{BTreeMap, HashMap};
use std::ops::Range;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use wgpu::{Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Device, Queue};
#[derive(Debug, Clone)]
struct MemoryBlock {
offset: u64,
size: u64,
is_free: bool,
allocation_id: Option<u64>,
movable: bool,
ref_count: u32,
}
#[derive(Debug, Clone)]
pub struct DefragMove {
pub allocation_id: u64,
pub src_offset: u64,
pub dst_offset: u64,
pub size: u64,
}
#[derive(Debug, Clone, Default)]
pub struct DefragmentationPlan {
pub moves: Vec<DefragMove>,
pub total_bytes: u64,
pub expected_fragmentation: f64,
pub current_fragmentation: f64,
}
impl DefragmentationPlan {
pub fn is_worthwhile(&self, min_improvement: f64) -> bool {
if self.moves.is_empty() {
return false;
}
let improvement = self.current_fragmentation - self.expected_fragmentation;
improvement >= min_improvement
}
pub fn move_count(&self) -> usize {
self.moves.len()
}
}
#[derive(Debug, Clone)]
pub struct DefragmentationResult {
pub performed: bool,
pub blocks_moved: usize,
pub bytes_moved: u64,
pub fragmentation_before: f64,
pub fragmentation_after: f64,
pub duration: Duration,
pub unmovable_blocks: usize,
}
impl Default for DefragmentationResult {
fn default() -> Self {
Self {
performed: false,
blocks_moved: 0,
bytes_moved: 0,
fragmentation_before: 0.0,
fragmentation_after: 0.0,
duration: Duration::ZERO,
unmovable_blocks: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct DefragConfig {
pub min_fragmentation_threshold: f64,
pub min_improvement: f64,
pub max_moves_per_pass: usize,
pub skip_unmovable: bool,
pub compaction_alignment: u64,
}
impl Default for DefragConfig {
fn default() -> Self {
Self {
min_fragmentation_threshold: 0.2,
min_improvement: 0.1,
max_moves_per_pass: 100,
skip_unmovable: true,
compaction_alignment: 256,
}
}
}
pub struct MemoryPool {
device: Arc<Device>,
buffer: Arc<Mutex<Option<Buffer>>>,
pool_size: u64,
blocks: Arc<RwLock<BTreeMap<u64, MemoryBlock>>>,
next_alloc_id: Arc<Mutex<u64>>,
usage: BufferUsages,
current_usage: Arc<Mutex<u64>>,
peak_usage: Arc<Mutex<u64>>,
allocation_count: Arc<Mutex<u64>>,
deallocation_count: Arc<Mutex<u64>>,
defrag_count: Arc<Mutex<u64>>,
allocation_offsets: Arc<RwLock<HashMap<u64, u64>>>,
defrag_config: RwLock<DefragConfig>,
last_defrag_time: Arc<Mutex<Option<Instant>>>,
total_bytes_defragged: Arc<AtomicU64>,
}
pub struct MemoryAllocation {
id: u64,
original_offset: u64,
size: u64,
pool: Arc<MemoryPool>,
}
impl MemoryPool {
pub fn new(device: Arc<Device>, pool_size: u64, usage: BufferUsages) -> Result<Self> {
Self::with_config(device, pool_size, usage, DefragConfig::default())
}
pub fn with_config(
device: Arc<Device>,
pool_size: u64,
usage: BufferUsages,
defrag_config: DefragConfig,
) -> Result<Self> {
let usage_with_copy = usage | BufferUsages::COPY_SRC | BufferUsages::COPY_DST;
let buffer = device.create_buffer(&BufferDescriptor {
label: Some("Memory Pool"),
size: pool_size,
usage: usage_with_copy,
mapped_at_creation: false,
});
let mut blocks = BTreeMap::new();
blocks.insert(
0,
MemoryBlock {
offset: 0,
size: pool_size,
is_free: true,
allocation_id: None,
movable: true,
ref_count: 0,
},
);
Ok(Self {
device,
buffer: Arc::new(Mutex::new(Some(buffer))),
pool_size,
blocks: Arc::new(RwLock::new(blocks)),
next_alloc_id: Arc::new(Mutex::new(0)),
usage: usage_with_copy,
current_usage: Arc::new(Mutex::new(0)),
peak_usage: Arc::new(Mutex::new(0)),
allocation_count: Arc::new(Mutex::new(0)),
deallocation_count: Arc::new(Mutex::new(0)),
defrag_count: Arc::new(Mutex::new(0)),
allocation_offsets: Arc::new(RwLock::new(HashMap::new())),
defrag_config: RwLock::new(defrag_config),
last_defrag_time: Arc::new(Mutex::new(None)),
total_bytes_defragged: Arc::new(AtomicU64::new(0)),
})
}
pub fn allocate(self: &Arc<Self>, size: u64, alignment: u64) -> Result<MemoryAllocation> {
let aligned_size = Self::align_up(size, alignment);
let alloc_id = {
let mut next_id = self.next_alloc_id.lock();
let id = *next_id;
*next_id = next_id.wrapping_add(1);
id
};
let (offset, block_offset) = {
let blocks = self.blocks.read();
let mut found: Option<(u64, u64)> = None;
for (blk_offset, block) in blocks.iter() {
if block.is_free && block.size >= aligned_size {
let aligned_offset = Self::align_up(*blk_offset, alignment);
let waste = aligned_offset - blk_offset;
if block.size >= aligned_size + waste {
found = Some((aligned_offset, *blk_offset));
break;
}
}
}
found.ok_or_else(|| GpuAdvancedError::AllocationFailed {
size: aligned_size,
available: self.get_available_memory(),
})?
};
{
let mut blocks = self.blocks.write();
let block = blocks
.remove(&block_offset)
.ok_or_else(|| GpuAdvancedError::memory_pool_error("Block not found"))?;
let waste = offset - block_offset;
if waste > 0 {
blocks.insert(
block_offset,
MemoryBlock {
offset: block_offset,
size: waste,
is_free: true,
allocation_id: None,
movable: true,
ref_count: 0,
},
);
}
blocks.insert(
offset,
MemoryBlock {
offset,
size: aligned_size,
is_free: false,
allocation_id: Some(alloc_id),
movable: true,
ref_count: 1,
},
);
let remainder = block.size - aligned_size - waste;
if remainder > 0 {
blocks.insert(
offset + aligned_size,
MemoryBlock {
offset: offset + aligned_size,
size: remainder,
is_free: true,
allocation_id: None,
movable: true,
ref_count: 0,
},
);
}
}
{
let mut offsets = self.allocation_offsets.write();
offsets.insert(alloc_id, offset);
}
{
let mut usage = self.current_usage.lock();
*usage = usage.saturating_add(aligned_size);
let mut peak = self.peak_usage.lock();
*peak = (*peak).max(*usage);
let mut count = self.allocation_count.lock();
*count = count.saturating_add(1);
}
Ok(MemoryAllocation {
id: alloc_id,
original_offset: offset,
size: aligned_size,
pool: Arc::clone(self),
})
}
pub fn get_allocation_offset(&self, alloc_id: u64) -> Option<u64> {
self.allocation_offsets.read().get(&alloc_id).copied()
}
pub fn pin_allocation(&self, alloc_id: u64) -> Result<()> {
let offsets = self.allocation_offsets.read();
let offset = offsets
.get(&alloc_id)
.copied()
.ok_or_else(|| GpuAdvancedError::memory_pool_error("Allocation not found"))?;
drop(offsets);
let mut blocks = self.blocks.write();
if let Some(block) = blocks.get_mut(&offset) {
block.movable = false;
Ok(())
} else {
Err(GpuAdvancedError::memory_pool_error(
"Block not found for allocation",
))
}
}
pub fn unpin_allocation(&self, alloc_id: u64) -> Result<()> {
let offsets = self.allocation_offsets.read();
let offset = offsets
.get(&alloc_id)
.copied()
.ok_or_else(|| GpuAdvancedError::memory_pool_error("Allocation not found"))?;
drop(offsets);
let mut blocks = self.blocks.write();
if let Some(block) = blocks.get_mut(&offset) {
block.movable = true;
Ok(())
} else {
Err(GpuAdvancedError::memory_pool_error(
"Block not found for allocation",
))
}
}
pub fn set_defrag_config(&self, config: DefragConfig) {
*self.defrag_config.write() = config;
}
pub fn get_defrag_config(&self) -> DefragConfig {
self.defrag_config.read().clone()
}
fn deallocate(&self, allocation: &MemoryAllocation) -> Result<()> {
let current_offset = self
.allocation_offsets
.read()
.get(&allocation.id)
.copied()
.unwrap_or(allocation.original_offset);
let mut blocks = self.blocks.write();
if let Some(block) = blocks.get_mut(¤t_offset) {
if block.allocation_id == Some(allocation.id) {
block.is_free = true;
block.allocation_id = None;
block.ref_count = 0;
} else {
return Err(GpuAdvancedError::memory_pool_error("Invalid allocation ID"));
}
} else {
return Err(GpuAdvancedError::memory_pool_error("Block not found"));
}
{
let mut offsets = self.allocation_offsets.write();
offsets.remove(&allocation.id);
}
{
let mut usage = self.current_usage.lock();
*usage = usage.saturating_sub(allocation.size);
let mut count = self.deallocation_count.lock();
*count = count.saturating_add(1);
}
self.coalesce_free_blocks(&mut blocks);
Ok(())
}
fn coalesce_free_blocks(&self, blocks: &mut BTreeMap<u64, MemoryBlock>) {
let mut to_merge: Vec<u64> = Vec::new();
let mut prev_offset: Option<u64> = None;
for (offset, block) in blocks.iter() {
if block.is_free {
if let Some(prev_off) = prev_offset {
if let Some(prev_block) = blocks.get(&prev_off) {
if prev_block.is_free && prev_block.offset + prev_block.size == *offset {
to_merge.push(*offset);
}
}
}
prev_offset = Some(*offset);
} else {
prev_offset = None;
}
}
for offset in to_merge {
if let Some(block) = blocks.remove(&offset) {
let prev_offset = blocks.range(..offset).next_back().map(|(k, _)| *k);
if let Some(prev_off) = prev_offset {
if let Some(prev_block) = blocks.get_mut(&prev_off) {
if prev_block.is_free {
prev_block.size += block.size;
}
}
}
}
}
}
pub fn plan_defragmentation(&self) -> DefragmentationPlan {
let config = self.defrag_config.read().clone();
let blocks = self.blocks.read();
let current_fragmentation = self.calculate_fragmentation_internal(&blocks);
let mut allocated_blocks: Vec<_> = blocks
.iter()
.filter(|(_, b)| !b.is_free && b.allocation_id.is_some())
.map(|(offset, b)| (*offset, b.clone()))
.collect();
allocated_blocks.sort_by_key(|(offset, _)| *offset);
let mut moves = Vec::new();
let mut total_bytes = 0u64;
let mut next_offset = 0u64;
for (current_offset, block) in &allocated_blocks {
let aligned_offset = Self::align_up(next_offset, config.compaction_alignment);
if aligned_offset < *current_offset && block.movable {
if let Some(alloc_id) = block.allocation_id {
moves.push(DefragMove {
allocation_id: alloc_id,
src_offset: *current_offset,
dst_offset: aligned_offset,
size: block.size,
});
total_bytes += block.size;
}
next_offset = aligned_offset + block.size;
} else {
next_offset = current_offset + block.size;
}
}
let expected_fragmentation = if moves.is_empty() {
current_fragmentation
} else {
let unmovable_count = allocated_blocks.iter().filter(|(_, b)| !b.movable).count();
if unmovable_count == 0 {
0.0
} else {
(unmovable_count as f64 / allocated_blocks.len().max(1) as f64) * 0.5
}
};
DefragmentationPlan {
moves,
total_bytes,
expected_fragmentation,
current_fragmentation,
}
}
pub fn defragment(&self) -> Result<DefragmentationResult> {
let start = Instant::now();
let plan = self.plan_defragmentation();
if plan.moves.is_empty() {
return Ok(DefragmentationResult {
performed: false,
fragmentation_before: plan.current_fragmentation,
fragmentation_after: plan.current_fragmentation,
duration: start.elapsed(),
..Default::default()
});
}
let config = self.defrag_config.read().clone();
if !plan.is_worthwhile(config.min_improvement) {
return Ok(DefragmentationResult {
performed: false,
fragmentation_before: plan.current_fragmentation,
fragmentation_after: plan.current_fragmentation,
duration: start.elapsed(),
..Default::default()
});
}
let result = self.execute_defrag_plan_logical(&plan, &config)?;
{
let mut count = self.defrag_count.lock();
*count = count.saturating_add(1);
}
self.total_bytes_defragged
.fetch_add(result.bytes_moved, Ordering::Relaxed);
*self.last_defrag_time.lock() = Some(Instant::now());
Ok(DefragmentationResult {
duration: start.elapsed(),
..result
})
}
pub fn defragment_with_queue(&self, queue: &Queue) -> Result<DefragmentationResult> {
let start = Instant::now();
let plan = self.plan_defragmentation();
if plan.moves.is_empty() {
return Ok(DefragmentationResult {
performed: false,
fragmentation_before: plan.current_fragmentation,
fragmentation_after: plan.current_fragmentation,
duration: start.elapsed(),
..Default::default()
});
}
let config = self.defrag_config.read().clone();
if plan.current_fragmentation < config.min_fragmentation_threshold {
return Ok(DefragmentationResult {
performed: false,
fragmentation_before: plan.current_fragmentation,
fragmentation_after: plan.current_fragmentation,
duration: start.elapsed(),
..Default::default()
});
}
if !plan.is_worthwhile(config.min_improvement) {
return Ok(DefragmentationResult {
performed: false,
fragmentation_before: plan.current_fragmentation,
fragmentation_after: plan.current_fragmentation,
duration: start.elapsed(),
..Default::default()
});
}
let result = self.execute_defrag_plan_gpu(&plan, &config, queue)?;
{
let mut count = self.defrag_count.lock();
*count = count.saturating_add(1);
}
self.total_bytes_defragged
.fetch_add(result.bytes_moved, Ordering::Relaxed);
*self.last_defrag_time.lock() = Some(Instant::now());
Ok(DefragmentationResult {
duration: start.elapsed(),
..result
})
}
fn execute_defrag_plan_logical(
&self,
plan: &DefragmentationPlan,
config: &DefragConfig,
) -> Result<DefragmentationResult> {
let mut blocks = self.blocks.write();
let mut allocation_offsets = self.allocation_offsets.write();
let mut blocks_moved = 0usize;
let mut bytes_moved = 0u64;
let mut unmovable_blocks = 0usize;
let moves_to_execute: Vec<_> = plan
.moves
.iter()
.take(config.max_moves_per_pass)
.cloned()
.collect();
for defrag_move in &moves_to_execute {
let block = match blocks.remove(&defrag_move.src_offset) {
Some(b) => b,
None => {
if config.skip_unmovable {
unmovable_blocks += 1;
continue;
} else {
return Err(GpuAdvancedError::memory_pool_error(
"Block not found during defragmentation",
));
}
}
};
if !block.movable {
blocks.insert(defrag_move.src_offset, block);
unmovable_blocks += 1;
continue;
}
let new_block = MemoryBlock {
offset: defrag_move.dst_offset,
size: block.size,
is_free: false,
allocation_id: block.allocation_id,
movable: block.movable,
ref_count: block.ref_count,
};
blocks.insert(defrag_move.dst_offset, new_block);
if let Some(alloc_id) = block.allocation_id {
allocation_offsets.insert(alloc_id, defrag_move.dst_offset);
}
blocks_moved += 1;
bytes_moved += defrag_move.size;
}
drop(blocks);
drop(allocation_offsets);
self.rebuild_free_blocks()?;
let blocks = self.blocks.read();
let fragmentation_after = self.calculate_fragmentation_internal(&blocks);
Ok(DefragmentationResult {
performed: blocks_moved > 0,
blocks_moved,
bytes_moved,
fragmentation_before: plan.current_fragmentation,
fragmentation_after,
duration: Duration::ZERO, unmovable_blocks,
})
}
fn execute_defrag_plan_gpu(
&self,
plan: &DefragmentationPlan,
config: &DefragConfig,
queue: &Queue,
) -> Result<DefragmentationResult> {
let buffer_guard = self.buffer.lock();
let buffer = buffer_guard
.as_ref()
.ok_or_else(|| GpuAdvancedError::memory_pool_error("Pool buffer not available"))?;
let staging_buffer = self.device.create_buffer(&BufferDescriptor {
label: Some("Defrag Staging Buffer"),
size: plan.total_bytes,
usage: BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let moves_to_execute: Vec<_> = plan
.moves
.iter()
.take(config.max_moves_per_pass)
.cloned()
.collect();
let mut encoder = self
.device
.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Defrag Copy to Staging"),
});
let mut staging_offset = 0u64;
let mut staging_map: Vec<(DefragMove, u64)> = Vec::new();
for defrag_move in &moves_to_execute {
encoder.copy_buffer_to_buffer(
buffer,
defrag_move.src_offset,
&staging_buffer,
staging_offset,
defrag_move.size,
);
staging_map.push((defrag_move.clone(), staging_offset));
staging_offset += defrag_move.size;
}
queue.submit(std::iter::once(encoder.finish()));
let mut encoder = self
.device
.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Defrag Copy from Staging"),
});
for (defrag_move, staging_off) in &staging_map {
encoder.copy_buffer_to_buffer(
&staging_buffer,
*staging_off,
buffer,
defrag_move.dst_offset,
defrag_move.size,
);
}
queue.submit(std::iter::once(encoder.finish()));
drop(buffer_guard);
let mut blocks = self.blocks.write();
let mut allocation_offsets = self.allocation_offsets.write();
let mut blocks_moved = 0usize;
let mut bytes_moved = 0u64;
let mut unmovable_blocks = 0usize;
for defrag_move in &moves_to_execute {
let block = match blocks.remove(&defrag_move.src_offset) {
Some(b) => b,
None => {
unmovable_blocks += 1;
continue;
}
};
let new_block = MemoryBlock {
offset: defrag_move.dst_offset,
size: block.size,
is_free: false,
allocation_id: block.allocation_id,
movable: block.movable,
ref_count: block.ref_count,
};
blocks.insert(defrag_move.dst_offset, new_block);
if let Some(alloc_id) = block.allocation_id {
allocation_offsets.insert(alloc_id, defrag_move.dst_offset);
}
blocks_moved += 1;
bytes_moved += defrag_move.size;
}
drop(blocks);
drop(allocation_offsets);
self.rebuild_free_blocks()?;
let blocks = self.blocks.read();
let fragmentation_after = self.calculate_fragmentation_internal(&blocks);
Ok(DefragmentationResult {
performed: blocks_moved > 0,
blocks_moved,
bytes_moved,
fragmentation_before: plan.current_fragmentation,
fragmentation_after,
duration: Duration::ZERO, unmovable_blocks,
})
}
fn rebuild_free_blocks(&self) -> Result<()> {
let mut blocks = self.blocks.write();
let allocated_ranges: Vec<(u64, u64)> = blocks
.iter()
.filter(|(_, b)| !b.is_free)
.map(|(offset, b)| (*offset, b.size))
.collect();
let offsets_to_remove: Vec<u64> = blocks
.iter()
.filter(|(_, b)| b.is_free)
.map(|(offset, _)| *offset)
.collect();
for offset in offsets_to_remove {
blocks.remove(&offset);
}
let mut last_end = 0u64;
for (offset, size) in &allocated_ranges {
if *offset > last_end {
blocks.insert(
last_end,
MemoryBlock {
offset: last_end,
size: offset - last_end,
is_free: true,
allocation_id: None,
movable: true,
ref_count: 0,
},
);
}
last_end = offset + size;
}
if last_end < self.pool_size {
blocks.insert(
last_end,
MemoryBlock {
offset: last_end,
size: self.pool_size - last_end,
is_free: true,
allocation_id: None,
movable: true,
ref_count: 0,
},
);
}
self.coalesce_free_blocks(&mut blocks);
Ok(())
}
fn calculate_fragmentation_internal(&self, blocks: &BTreeMap<u64, MemoryBlock>) -> f64 {
let free_blocks: Vec<u64> = blocks
.values()
.filter(|b| b.is_free)
.map(|b| b.size)
.collect();
self.calculate_fragmentation(&free_blocks)
}
pub fn needs_defragmentation(&self) -> bool {
let config = self.defrag_config.read();
let stats = self.get_stats();
stats.fragmentation >= config.min_fragmentation_threshold
}
pub fn get_fragmentation(&self) -> f64 {
let blocks = self.blocks.read();
self.calculate_fragmentation_internal(&blocks)
}
pub fn get_total_bytes_defragged(&self) -> u64 {
self.total_bytes_defragged.load(Ordering::Relaxed)
}
pub fn time_since_last_defrag(&self) -> Option<Duration> {
self.last_defrag_time
.lock()
.map(|instant| instant.elapsed())
}
pub fn buffer(&self) -> Option<Buffer> {
self.buffer.lock().as_ref().map(|_b| {
self.device.create_buffer(&BufferDescriptor {
label: Some("Memory Pool Access"),
size: self.pool_size,
usage: self.usage,
mapped_at_creation: false,
})
})
}
pub fn get_available_memory(&self) -> u64 {
let blocks = self.blocks.read();
blocks
.values()
.filter(|block| block.is_free)
.map(|block| block.size)
.sum()
}
pub fn get_current_usage(&self) -> u64 {
*self.current_usage.lock()
}
pub fn get_peak_usage(&self) -> u64 {
*self.peak_usage.lock()
}
pub fn get_stats(&self) -> MemoryPoolStats {
let blocks = self.blocks.read();
let free_blocks: Vec<_> = blocks
.values()
.filter(|b| b.is_free)
.map(|b| b.size)
.collect();
let allocated_blocks: Vec<_> = blocks
.values()
.filter(|b| !b.is_free)
.map(|b| b.size)
.collect();
MemoryPoolStats {
pool_size: self.pool_size,
current_usage: *self.current_usage.lock(),
peak_usage: *self.peak_usage.lock(),
available: self.get_available_memory(),
allocation_count: *self.allocation_count.lock(),
deallocation_count: *self.deallocation_count.lock(),
defrag_count: *self.defrag_count.lock(),
free_block_count: free_blocks.len(),
allocated_block_count: allocated_blocks.len(),
largest_free_block: free_blocks.iter().max().copied().unwrap_or(0),
fragmentation: self.calculate_fragmentation(&free_blocks),
}
}
fn calculate_fragmentation(&self, free_blocks: &[u64]) -> f64 {
if free_blocks.is_empty() {
return 0.0;
}
let total_free: u64 = free_blocks.iter().sum();
let largest = free_blocks.iter().max().copied().unwrap_or(0);
if total_free == 0 {
return 0.0;
}
1.0 - (largest as f64 / total_free as f64)
}
fn align_up(value: u64, alignment: u64) -> u64 {
if alignment == 0 {
return value;
}
value.div_ceil(alignment) * alignment
}
pub fn print_stats(&self) {
let stats = self.get_stats();
println!("\nMemory Pool Statistics:");
println!(" Pool size: {} bytes", stats.pool_size);
println!(
" Current usage: {} bytes ({:.1}%)",
stats.current_usage,
(stats.current_usage as f64 / stats.pool_size as f64) * 100.0
);
println!(
" Peak usage: {} bytes ({:.1}%)",
stats.peak_usage,
(stats.peak_usage as f64 / stats.pool_size as f64) * 100.0
);
println!(" Available: {} bytes", stats.available);
println!(" Allocations: {}", stats.allocation_count);
println!(" Deallocations: {}", stats.deallocation_count);
println!(" Defragmentations: {}", stats.defrag_count);
println!(" Free blocks: {}", stats.free_block_count);
println!(" Allocated blocks: {}", stats.allocated_block_count);
println!(" Largest free block: {} bytes", stats.largest_free_block);
println!(" Fragmentation: {:.1}%", stats.fragmentation * 100.0);
}
}
#[derive(Debug, Clone)]
pub struct MemoryPoolStats {
pub pool_size: u64,
pub current_usage: u64,
pub peak_usage: u64,
pub available: u64,
pub allocation_count: u64,
pub deallocation_count: u64,
pub defrag_count: u64,
pub free_block_count: usize,
pub allocated_block_count: usize,
pub largest_free_block: u64,
pub fragmentation: f64,
}
impl MemoryAllocation {
pub fn offset(&self) -> u64 {
self.pool
.get_allocation_offset(self.id)
.unwrap_or(self.original_offset)
}
pub fn size(&self) -> u64 {
self.size
}
pub fn range(&self) -> Range<u64> {
let offset = self.offset();
offset..(offset + self.size)
}
pub fn id(&self) -> u64 {
self.id
}
}
impl Drop for MemoryAllocation {
fn drop(&mut self) {
let _ = self.pool.deallocate(self);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_align_up() {
assert_eq!(MemoryPool::align_up(0, 256), 0);
assert_eq!(MemoryPool::align_up(1, 256), 256);
assert_eq!(MemoryPool::align_up(256, 256), 256);
assert_eq!(MemoryPool::align_up(257, 256), 512);
}
#[test]
fn test_memory_block() {
let block = MemoryBlock {
offset: 0,
size: 1024,
is_free: true,
allocation_id: None,
movable: true,
ref_count: 0,
};
assert!(block.is_free);
assert_eq!(block.size, 1024);
assert!(block.movable);
assert_eq!(block.ref_count, 0);
}
}