use crate::error::CRDTResult;
use crate::memory::MemoryConfig;
use crate::traits::CRDT;
pub trait BoundedCRDT<C: MemoryConfig>: CRDT<C> {
const MAX_SIZE_BYTES: usize;
const ALIGNMENT: usize = C::MEMORY_ALIGNMENT;
const MAX_ELEMENTS: usize;
fn memory_usage(&self) -> usize;
fn remaining_capacity(&self) -> usize {
Self::MAX_SIZE_BYTES.saturating_sub(self.memory_usage())
}
fn is_at_capacity(&self) -> bool {
self.memory_usage() >= Self::MAX_SIZE_BYTES
}
fn utilization_percent(&self) -> u8 {
if Self::MAX_SIZE_BYTES == 0 {
return 100;
}
let utilization = (self.memory_usage() * 100) / Self::MAX_SIZE_BYTES;
utilization.min(100) as u8
}
fn can_add_element(&self) -> bool {
self.element_count() < Self::MAX_ELEMENTS && !self.is_at_capacity()
}
fn element_count(&self) -> usize;
fn max_elements(&self) -> usize {
Self::MAX_ELEMENTS
}
fn validate_bounds(&self) -> CRDTResult<()> {
if self.memory_usage() > Self::MAX_SIZE_BYTES {
return Err(crate::error::CRDTError::BufferOverflow);
}
if self.element_count() > Self::MAX_ELEMENTS {
return Err(crate::error::CRDTError::ConfigurationExceeded);
}
Ok(())
}
fn compact(&mut self) -> CRDTResult<usize>;
fn memory_stats(&self) -> MemoryStats {
MemoryStats {
current_usage: self.memory_usage(),
max_capacity: Self::MAX_SIZE_BYTES,
element_count: self.element_count(),
max_elements: Self::MAX_ELEMENTS,
utilization_percent: self.utilization_percent(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryStats {
pub current_usage: usize,
pub max_capacity: usize,
pub element_count: usize,
pub max_elements: usize,
pub utilization_percent: u8,
}
impl MemoryStats {
pub fn remaining_capacity(&self) -> usize {
self.max_capacity.saturating_sub(self.current_usage)
}
pub fn remaining_elements(&self) -> usize {
self.max_elements.saturating_sub(self.element_count)
}
pub fn is_at_capacity(&self) -> bool {
self.current_usage >= self.max_capacity || self.element_count >= self.max_elements
}
pub fn is_nearly_full(&self) -> bool {
self.utilization_percent >= 90
}
}
pub trait MemoryPressureHandler<C: MemoryConfig>: BoundedCRDT<C> {
fn handle_memory_pressure(&mut self) -> CRDTResult<usize>;
fn memory_pressure_level(&self) -> u8 {
self.utilization_percent()
}
fn is_under_pressure(&self) -> bool {
self.memory_pressure_level() >= 80
}
fn set_pressure_threshold(&mut self, threshold: u8);
fn pressure_threshold(&self) -> u8;
}
pub trait GarbageCollectable<C: MemoryConfig>: BoundedCRDT<C> {
fn garbage_collect(&mut self) -> CRDTResult<usize>;
fn garbage_size(&self) -> usize;
fn needs_gc(&self) -> bool {
self.garbage_size() > 0 || self.utilization_percent() >= 80
}
fn set_gc_threshold(&mut self, threshold: usize);
fn gc_threshold(&self) -> usize;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::CRDTError;
use crate::memory::DefaultConfig;
struct MockBoundedCRDT {
elements: [Option<u32>; 10],
count: usize,
}
impl MockBoundedCRDT {
fn new() -> Self {
Self {
elements: [None; 10],
count: 0,
}
}
fn add(&mut self, value: u32) -> bool {
if self.count < 10 {
self.elements[self.count] = Some(value);
self.count += 1;
true
} else {
false
}
}
}
impl CRDT<DefaultConfig> for MockBoundedCRDT {
type Error = CRDTError;
fn merge(&mut self, other: &Self) -> CRDTResult<()> {
for &element in other.elements.iter().flatten() {
if !self.add(element) {
return Err(CRDTError::BufferOverflow);
}
}
Ok(())
}
fn eq(&self, other: &Self) -> bool {
self.elements == other.elements
}
fn size_bytes(&self) -> usize {
core::mem::size_of::<Self>()
}
fn validate(&self) -> CRDTResult<()> {
Ok(())
}
fn state_hash(&self) -> u32 {
self.count as u32
}
fn can_merge(&self, other: &Self) -> bool {
self.count + other.count <= 10
}
}
impl BoundedCRDT<DefaultConfig> for MockBoundedCRDT {
const MAX_SIZE_BYTES: usize = 64;
const MAX_ELEMENTS: usize = 10;
fn memory_usage(&self) -> usize {
let base_size = core::mem::size_of::<usize>(); let element_size = self.count * core::mem::size_of::<Option<u32>>();
base_size + element_size
}
fn element_count(&self) -> usize {
self.count
}
fn compact(&mut self) -> CRDTResult<usize> {
Ok(0)
}
}
#[test]
fn test_bounded_crdt() {
let mut crdt = MockBoundedCRDT::new();
assert_eq!(crdt.element_count(), 0);
assert_eq!(crdt.max_elements(), 10);
assert!(crdt.can_add_element());
assert!(!crdt.is_at_capacity());
for i in 0..5 {
assert!(crdt.add(i));
}
assert_eq!(crdt.element_count(), 5);
assert!(crdt.utilization_percent() < 100);
assert!(crdt.can_add_element());
let stats = crdt.memory_stats();
assert_eq!(stats.element_count, 5);
assert_eq!(stats.max_elements, 10);
assert_eq!(stats.remaining_elements(), 5);
}
#[test]
fn test_memory_stats() {
let stats = MemoryStats {
current_usage: 32,
max_capacity: 64,
element_count: 5,
max_elements: 10,
utilization_percent: 50,
};
assert_eq!(stats.remaining_capacity(), 32);
assert_eq!(stats.remaining_elements(), 5);
assert!(!stats.is_at_capacity());
assert!(!stats.is_nearly_full());
}
}