crdtosphere/traits/
bounded.rs

1//! Bounded CRDT trait definition
2//!
3//! This module defines traits for CRDTs that have bounded memory usage
4//! and can verify their resource constraints at compile time.
5
6use crate::error::CRDTResult;
7use crate::memory::MemoryConfig;
8use crate::traits::CRDT;
9
10/// Trait for CRDTs with bounded memory usage
11///
12/// This trait ensures that CRDTs have predictable memory usage that can be
13/// verified at compile time, which is essential for embedded systems.
14pub trait BoundedCRDT<C: MemoryConfig>: CRDT<C> {
15    /// Maximum size in bytes this CRDT can occupy
16    const MAX_SIZE_BYTES: usize;
17
18    /// Memory alignment requirement in bytes
19    const ALIGNMENT: usize = C::MEMORY_ALIGNMENT;
20
21    /// Maximum number of elements this CRDT can contain
22    const MAX_ELEMENTS: usize;
23
24    /// Returns the current memory usage in bytes
25    fn memory_usage(&self) -> usize;
26
27    /// Returns the remaining memory capacity in bytes
28    fn remaining_capacity(&self) -> usize {
29        Self::MAX_SIZE_BYTES.saturating_sub(self.memory_usage())
30    }
31
32    /// Checks if the CRDT is at its memory limit
33    fn is_at_capacity(&self) -> bool {
34        self.memory_usage() >= Self::MAX_SIZE_BYTES
35    }
36
37    /// Returns the memory utilization as a percentage (0-100)
38    fn utilization_percent(&self) -> u8 {
39        if Self::MAX_SIZE_BYTES == 0 {
40            return 100;
41        }
42
43        let utilization = (self.memory_usage() * 100) / Self::MAX_SIZE_BYTES;
44        utilization.min(100) as u8
45    }
46
47    /// Checks if adding an element would exceed memory bounds
48    fn can_add_element(&self) -> bool {
49        self.element_count() < Self::MAX_ELEMENTS && !self.is_at_capacity()
50    }
51
52    /// Returns the current number of elements
53    fn element_count(&self) -> usize;
54
55    /// Returns the maximum number of elements that can be stored
56    fn max_elements(&self) -> usize {
57        Self::MAX_ELEMENTS
58    }
59
60    /// Validates that the CRDT is within its memory bounds
61    fn validate_bounds(&self) -> CRDTResult<()> {
62        if self.memory_usage() > Self::MAX_SIZE_BYTES {
63            return Err(crate::error::CRDTError::BufferOverflow);
64        }
65
66        if self.element_count() > Self::MAX_ELEMENTS {
67            return Err(crate::error::CRDTError::ConfigurationExceeded);
68        }
69
70        Ok(())
71    }
72
73    /// Compacts the CRDT to reduce memory usage if possible
74    fn compact(&mut self) -> CRDTResult<usize>;
75
76    /// Returns memory statistics for this CRDT
77    fn memory_stats(&self) -> MemoryStats {
78        MemoryStats {
79            current_usage: self.memory_usage(),
80            max_capacity: Self::MAX_SIZE_BYTES,
81            element_count: self.element_count(),
82            max_elements: Self::MAX_ELEMENTS,
83            utilization_percent: self.utilization_percent(),
84        }
85    }
86}
87
88/// Memory statistics for bounded CRDTs
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct MemoryStats {
91    /// Current memory usage in bytes
92    pub current_usage: usize,
93    /// Maximum memory capacity in bytes
94    pub max_capacity: usize,
95    /// Current number of elements
96    pub element_count: usize,
97    /// Maximum number of elements
98    pub max_elements: usize,
99    /// Memory utilization percentage (0-100)
100    pub utilization_percent: u8,
101}
102
103impl MemoryStats {
104    /// Returns the remaining memory capacity
105    pub fn remaining_capacity(&self) -> usize {
106        self.max_capacity.saturating_sub(self.current_usage)
107    }
108
109    /// Returns the remaining element capacity
110    pub fn remaining_elements(&self) -> usize {
111        self.max_elements.saturating_sub(self.element_count)
112    }
113
114    /// Checks if the CRDT is at capacity
115    pub fn is_at_capacity(&self) -> bool {
116        self.current_usage >= self.max_capacity || self.element_count >= self.max_elements
117    }
118
119    /// Checks if the CRDT is nearly full (>90% utilization)
120    pub fn is_nearly_full(&self) -> bool {
121        self.utilization_percent >= 90
122    }
123}
124
125/// Trait for CRDTs that support memory pressure handling
126///
127/// This trait provides methods for CRDTs to handle memory pressure situations
128/// and implement strategies for memory management.
129pub trait MemoryPressureHandler<C: MemoryConfig>: BoundedCRDT<C> {
130    /// Handles memory pressure by freeing up space
131    ///
132    /// Returns the number of bytes freed
133    fn handle_memory_pressure(&mut self) -> CRDTResult<usize>;
134
135    /// Returns the memory pressure level (0-100)
136    fn memory_pressure_level(&self) -> u8 {
137        self.utilization_percent()
138    }
139
140    /// Checks if the CRDT is under memory pressure
141    fn is_under_pressure(&self) -> bool {
142        self.memory_pressure_level() >= 80
143    }
144
145    /// Sets the memory pressure threshold (0-100)
146    fn set_pressure_threshold(&mut self, threshold: u8);
147
148    /// Returns the current memory pressure threshold
149    fn pressure_threshold(&self) -> u8;
150}
151
152/// Trait for CRDTs that support garbage collection
153///
154/// This trait provides methods for CRDTs to perform garbage collection
155/// to reclaim unused memory.
156pub trait GarbageCollectable<C: MemoryConfig>: BoundedCRDT<C> {
157    /// Performs garbage collection
158    ///
159    /// Returns the number of bytes freed
160    fn garbage_collect(&mut self) -> CRDTResult<usize>;
161
162    /// Returns the amount of garbage (reclaimable memory) in bytes
163    fn garbage_size(&self) -> usize;
164
165    /// Checks if garbage collection is needed
166    fn needs_gc(&self) -> bool {
167        self.garbage_size() > 0 || self.utilization_percent() >= 80
168    }
169
170    /// Sets the garbage collection threshold
171    fn set_gc_threshold(&mut self, threshold: usize);
172
173    /// Returns the current garbage collection threshold
174    fn gc_threshold(&self) -> usize;
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use crate::error::CRDTError;
181    use crate::memory::DefaultConfig;
182
183    // Mock bounded CRDT for testing
184    struct MockBoundedCRDT {
185        elements: [Option<u32>; 10],
186        count: usize,
187    }
188
189    impl MockBoundedCRDT {
190        fn new() -> Self {
191            Self {
192                elements: [None; 10],
193                count: 0,
194            }
195        }
196
197        fn add(&mut self, value: u32) -> bool {
198            if self.count < 10 {
199                self.elements[self.count] = Some(value);
200                self.count += 1;
201                true
202            } else {
203                false
204            }
205        }
206    }
207
208    impl CRDT<DefaultConfig> for MockBoundedCRDT {
209        type Error = CRDTError;
210
211        fn merge(&mut self, other: &Self) -> CRDTResult<()> {
212            for &element in other.elements.iter().flatten() {
213                if !self.add(element) {
214                    return Err(CRDTError::BufferOverflow);
215                }
216            }
217            Ok(())
218        }
219
220        fn eq(&self, other: &Self) -> bool {
221            self.elements == other.elements
222        }
223
224        fn size_bytes(&self) -> usize {
225            core::mem::size_of::<Self>()
226        }
227
228        fn validate(&self) -> CRDTResult<()> {
229            Ok(())
230        }
231
232        fn state_hash(&self) -> u32 {
233            self.count as u32
234        }
235
236        fn can_merge(&self, other: &Self) -> bool {
237            self.count + other.count <= 10
238        }
239    }
240
241    impl BoundedCRDT<DefaultConfig> for MockBoundedCRDT {
242        const MAX_SIZE_BYTES: usize = 64;
243        const MAX_ELEMENTS: usize = 10;
244
245        fn memory_usage(&self) -> usize {
246            // XXX: Return memory usage based on actual elements stored
247            let base_size = core::mem::size_of::<usize>(); // for count field
248            let element_size = self.count * core::mem::size_of::<Option<u32>>();
249            base_size + element_size
250        }
251
252        fn element_count(&self) -> usize {
253            self.count
254        }
255
256        fn compact(&mut self) -> CRDTResult<usize> {
257            // No compaction needed for this simple example
258            Ok(0)
259        }
260    }
261
262    #[test]
263    fn test_bounded_crdt() {
264        let mut crdt = MockBoundedCRDT::new();
265
266        assert_eq!(crdt.element_count(), 0);
267        assert_eq!(crdt.max_elements(), 10);
268        assert!(crdt.can_add_element());
269        assert!(!crdt.is_at_capacity());
270
271        // Add some elements
272        for i in 0..5 {
273            assert!(crdt.add(i));
274        }
275
276        assert_eq!(crdt.element_count(), 5);
277        // With 5 elements, memory usage should be reasonable (not 100%)
278        assert!(crdt.utilization_percent() < 100);
279        assert!(crdt.can_add_element()); // Should still be able to add more elements
280
281        let stats = crdt.memory_stats();
282        assert_eq!(stats.element_count, 5);
283        assert_eq!(stats.max_elements, 10);
284        assert_eq!(stats.remaining_elements(), 5);
285    }
286
287    #[test]
288    fn test_memory_stats() {
289        let stats = MemoryStats {
290            current_usage: 32,
291            max_capacity: 64,
292            element_count: 5,
293            max_elements: 10,
294            utilization_percent: 50,
295        };
296
297        assert_eq!(stats.remaining_capacity(), 32);
298        assert_eq!(stats.remaining_elements(), 5);
299        assert!(!stats.is_at_capacity());
300        assert!(!stats.is_nearly_full());
301    }
302}