crdtosphere/traits/
bounded.rs1use crate::error::CRDTResult;
7use crate::memory::MemoryConfig;
8use crate::traits::CRDT;
9
10pub trait BoundedCRDT<C: MemoryConfig>: CRDT<C> {
15 const MAX_SIZE_BYTES: usize;
17
18 const ALIGNMENT: usize = C::MEMORY_ALIGNMENT;
20
21 const MAX_ELEMENTS: usize;
23
24 fn memory_usage(&self) -> usize;
26
27 fn remaining_capacity(&self) -> usize {
29 Self::MAX_SIZE_BYTES.saturating_sub(self.memory_usage())
30 }
31
32 fn is_at_capacity(&self) -> bool {
34 self.memory_usage() >= Self::MAX_SIZE_BYTES
35 }
36
37 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 fn can_add_element(&self) -> bool {
49 self.element_count() < Self::MAX_ELEMENTS && !self.is_at_capacity()
50 }
51
52 fn element_count(&self) -> usize;
54
55 fn max_elements(&self) -> usize {
57 Self::MAX_ELEMENTS
58 }
59
60 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 fn compact(&mut self) -> CRDTResult<usize>;
75
76 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct MemoryStats {
91 pub current_usage: usize,
93 pub max_capacity: usize,
95 pub element_count: usize,
97 pub max_elements: usize,
99 pub utilization_percent: u8,
101}
102
103impl MemoryStats {
104 pub fn remaining_capacity(&self) -> usize {
106 self.max_capacity.saturating_sub(self.current_usage)
107 }
108
109 pub fn remaining_elements(&self) -> usize {
111 self.max_elements.saturating_sub(self.element_count)
112 }
113
114 pub fn is_at_capacity(&self) -> bool {
116 self.current_usage >= self.max_capacity || self.element_count >= self.max_elements
117 }
118
119 pub fn is_nearly_full(&self) -> bool {
121 self.utilization_percent >= 90
122 }
123}
124
125pub trait MemoryPressureHandler<C: MemoryConfig>: BoundedCRDT<C> {
130 fn handle_memory_pressure(&mut self) -> CRDTResult<usize>;
134
135 fn memory_pressure_level(&self) -> u8 {
137 self.utilization_percent()
138 }
139
140 fn is_under_pressure(&self) -> bool {
142 self.memory_pressure_level() >= 80
143 }
144
145 fn set_pressure_threshold(&mut self, threshold: u8);
147
148 fn pressure_threshold(&self) -> u8;
150}
151
152pub trait GarbageCollectable<C: MemoryConfig>: BoundedCRDT<C> {
157 fn garbage_collect(&mut self) -> CRDTResult<usize>;
161
162 fn garbage_size(&self) -> usize;
164
165 fn needs_gc(&self) -> bool {
167 self.garbage_size() > 0 || self.utilization_percent() >= 80
168 }
169
170 fn set_gc_threshold(&mut self, threshold: usize);
172
173 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 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 let base_size = core::mem::size_of::<usize>(); 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 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 for i in 0..5 {
273 assert!(crdt.add(i));
274 }
275
276 assert_eq!(crdt.element_count(), 5);
277 assert!(crdt.utilization_percent() < 100);
279 assert!(crdt.can_add_element()); 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}