1use async_trait::async_trait;
4use std::collections::HashMap;
5use std::sync::{Arc, RwLock};
6
7use crate::types::{to_hex, Hash};
8
9#[derive(Debug, Clone, Default)]
11pub struct StoreStats {
12 pub count: u64,
14 pub bytes: u64,
16 pub pinned_count: u64,
18 pub pinned_bytes: u64,
20}
21
22#[derive(Debug, Clone, Default, PartialEq, Eq)]
24pub struct PutManyReport {
25 pub total: usize,
27 pub inserted: usize,
29 pub inserted_bytes: u64,
31 pub inserted_hashes: Vec<Hash>,
33}
34
35#[async_trait]
37pub trait Store: Send + Sync {
38 async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError>;
41
42 async fn put_many(&self, items: Vec<(Hash, Vec<u8>)>) -> Result<usize, StoreError> {
45 let mut inserted = 0usize;
46 for (hash, data) in items {
47 if self.put(hash, data).await? {
48 inserted += 1;
49 }
50 }
51 Ok(inserted)
52 }
53
54 async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError>;
57
58 async fn has(&self, hash: &Hash) -> Result<bool, StoreError>;
60
61 async fn delete(&self, hash: &Hash) -> Result<bool, StoreError>;
64
65 fn set_max_bytes(&self, _max: u64) {}
71
72 fn max_bytes(&self) -> Option<u64> {
74 None
75 }
76
77 async fn stats(&self) -> StoreStats {
79 StoreStats::default()
80 }
81
82 async fn evict_if_needed(&self) -> Result<u64, StoreError> {
85 Ok(0)
86 }
87
88 async fn pin(&self, _hash: &Hash) -> Result<(), StoreError> {
94 Ok(())
95 }
96
97 async fn unpin(&self, _hash: &Hash) -> Result<(), StoreError> {
99 Ok(())
100 }
101
102 fn pin_count(&self, _hash: &Hash) -> u32 {
104 0
105 }
106
107 fn is_pinned(&self, hash: &Hash) -> bool {
109 self.pin_count(hash) > 0
110 }
111}
112
113#[derive(Debug, thiserror::Error)]
115pub enum StoreError {
116 #[error("IO error: {0}")]
117 Io(#[from] std::io::Error),
118 #[error("Store error: {0}")]
119 Other(String),
120}
121
122#[derive(Debug, Default)]
123struct BufferedStoreInner {
124 pending: HashMap<Hash, Vec<u8>>,
125 order: Vec<Hash>,
126}
127
128#[derive(Debug, Clone, Copy)]
129struct BufferedStoreOptions {
130 check_base_on_put: bool,
131}
132
133impl Default for BufferedStoreOptions {
134 fn default() -> Self {
135 Self {
136 check_base_on_put: true,
137 }
138 }
139}
140
141#[derive(Debug, Clone)]
143pub struct BufferedStore<S: Store> {
144 base: Arc<S>,
145 inner: Arc<RwLock<BufferedStoreInner>>,
146 options: BufferedStoreOptions,
147}
148
149impl<S: Store> BufferedStore<S> {
150 pub fn new(base: Arc<S>) -> Self {
151 Self::with_options(base, BufferedStoreOptions::default())
152 }
153
154 pub fn new_optimistic(base: Arc<S>) -> Self {
155 Self::with_options(
156 base,
157 BufferedStoreOptions {
158 check_base_on_put: false,
159 },
160 )
161 }
162
163 fn with_options(base: Arc<S>, options: BufferedStoreOptions) -> Self {
164 Self {
165 base,
166 inner: Arc::new(RwLock::new(BufferedStoreInner::default())),
167 options,
168 }
169 }
170
171 pub async fn flush(&self) -> Result<usize, StoreError> {
172 let items = {
173 let mut inner = self.inner.write().unwrap();
174 if inner.order.is_empty() {
175 return Ok(0);
176 }
177
178 let order = std::mem::take(&mut inner.order);
179 let mut items = Vec::with_capacity(order.len());
180 for hash in order {
181 if let Some(data) = inner.pending.remove(&hash) {
182 items.push((hash, data));
183 }
184 }
185 items
186 };
187
188 self.base.put_many(items).await
189 }
190}
191
192#[async_trait]
193impl<S: Store> Store for BufferedStore<S> {
194 async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError> {
195 {
196 let inner = self.inner.read().unwrap();
197 if inner.pending.contains_key(&hash) {
198 return Ok(false);
199 }
200 }
201
202 if self.options.check_base_on_put && self.base.has(&hash).await? {
203 return Ok(false);
204 }
205
206 let mut inner = self.inner.write().unwrap();
207 if inner.pending.contains_key(&hash) {
208 return Ok(false);
209 }
210 inner.order.push(hash);
211 inner.pending.insert(hash, data);
212 Ok(true)
213 }
214
215 async fn put_many(&self, items: Vec<(Hash, Vec<u8>)>) -> Result<usize, StoreError> {
216 let mut inserted = 0usize;
217 for (hash, data) in items {
218 if self.put(hash, data).await? {
219 inserted += 1;
220 }
221 }
222 Ok(inserted)
223 }
224
225 async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError> {
226 if let Some(data) = self.inner.read().unwrap().pending.get(hash).cloned() {
227 return Ok(Some(data));
228 }
229 self.base.get(hash).await
230 }
231
232 async fn has(&self, hash: &Hash) -> Result<bool, StoreError> {
233 if self.inner.read().unwrap().pending.contains_key(hash) {
234 return Ok(true);
235 }
236 self.base.has(hash).await
237 }
238
239 async fn delete(&self, hash: &Hash) -> Result<bool, StoreError> {
240 let removed = {
241 let mut inner = self.inner.write().unwrap();
242 let removed = inner.pending.remove(hash).is_some();
243 if removed {
244 inner.order.retain(|queued| queued != hash);
245 }
246 removed
247 };
248
249 if removed {
250 return Ok(true);
251 }
252
253 self.base.delete(hash).await
254 }
255
256 async fn stats(&self) -> StoreStats {
257 let mut stats = self.base.stats().await;
258 let pending_bytes = self
259 .inner
260 .read()
261 .unwrap()
262 .pending
263 .values()
264 .map(|data| data.len() as u64)
265 .sum::<u64>();
266 stats.count += self.inner.read().unwrap().pending.len() as u64;
267 stats.bytes += pending_bytes;
268 stats
269 }
270
271 async fn evict_if_needed(&self) -> Result<u64, StoreError> {
272 self.base.evict_if_needed().await
273 }
274
275 async fn pin(&self, hash: &Hash) -> Result<(), StoreError> {
276 self.base.pin(hash).await
277 }
278
279 async fn unpin(&self, hash: &Hash) -> Result<(), StoreError> {
280 self.base.unpin(hash).await
281 }
282
283 fn pin_count(&self, hash: &Hash) -> u32 {
284 self.base.pin_count(hash)
285 }
286}
287
288#[derive(Debug, Clone)]
290struct MemoryEntry {
291 data: Vec<u8>,
292 order: u64,
294}
295
296#[derive(Debug, Default)]
298struct MemoryStoreInner {
299 data: HashMap<String, MemoryEntry>,
300 pins: HashMap<String, u32>,
301 next_order: u64,
302 max_bytes: Option<u64>,
303}
304
305#[derive(Debug, Clone, Default)]
307pub struct MemoryStore {
308 inner: Arc<RwLock<MemoryStoreInner>>,
309}
310
311impl MemoryStore {
312 pub fn new() -> Self {
313 Self {
314 inner: Arc::new(RwLock::new(MemoryStoreInner::default())),
315 }
316 }
317
318 pub fn with_max_bytes(max_bytes: u64) -> Self {
320 Self {
321 inner: Arc::new(RwLock::new(MemoryStoreInner {
322 max_bytes: if max_bytes > 0 { Some(max_bytes) } else { None },
323 ..Default::default()
324 })),
325 }
326 }
327
328 pub fn size(&self) -> usize {
330 self.inner.read().unwrap().data.len()
331 }
332
333 pub fn total_bytes(&self) -> usize {
335 self.inner
336 .read()
337 .unwrap()
338 .data
339 .values()
340 .map(|e| e.data.len())
341 .sum()
342 }
343
344 pub fn clear(&self) {
346 self.inner.write().unwrap().data.clear();
347 }
348
349 pub fn keys(&self) -> Vec<Hash> {
351 self.inner
352 .read()
353 .unwrap()
354 .data
355 .keys()
356 .filter_map(|hex| {
357 let bytes = hex::decode(hex).ok()?;
358 if bytes.len() != 32 {
359 return None;
360 }
361 let mut hash = [0u8; 32];
362 hash.copy_from_slice(&bytes);
363 Some(hash)
364 })
365 .collect()
366 }
367
368 fn evict_to_target(&self, target_bytes: u64) -> u64 {
370 let mut inner = self.inner.write().unwrap();
371
372 let current_bytes: u64 = inner.data.values().map(|e| e.data.len() as u64).sum();
373 if current_bytes <= target_bytes {
374 return 0;
375 }
376
377 let mut unpinned: Vec<(String, u64, u64)> = inner
379 .data
380 .iter()
381 .filter(|(key, _)| inner.pins.get(*key).copied().unwrap_or(0) == 0)
382 .map(|(key, entry)| (key.clone(), entry.order, entry.data.len() as u64))
383 .collect();
384
385 unpinned.sort_by_key(|(_, order, _)| *order);
386
387 let mut freed = 0u64;
388 let to_free = current_bytes - target_bytes;
389
390 for (key, _, size) in unpinned {
391 if freed >= to_free {
392 break;
393 }
394 inner.data.remove(&key);
395 freed += size;
396 }
397
398 freed
399 }
400}
401
402#[async_trait]
403impl Store for MemoryStore {
404 async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError> {
405 let key = to_hex(&hash);
406 let mut inner = self.inner.write().unwrap();
407 if inner.data.contains_key(&key) {
408 return Ok(false);
409 }
410 let order = inner.next_order;
411 inner.next_order += 1;
412 inner.data.insert(key, MemoryEntry { data, order });
413 Ok(true)
414 }
415
416 async fn put_many(&self, items: Vec<(Hash, Vec<u8>)>) -> Result<usize, StoreError> {
417 let mut inserted = 0usize;
418 let mut inner = self.inner.write().unwrap();
419 for (hash, data) in items {
420 let key = to_hex(&hash);
421 if inner.data.contains_key(&key) {
422 continue;
423 }
424 let order = inner.next_order;
425 inner.next_order += 1;
426 inner.data.insert(key, MemoryEntry { data, order });
427 inserted += 1;
428 }
429 Ok(inserted)
430 }
431
432 async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError> {
433 let key = to_hex(hash);
434 let inner = self.inner.read().unwrap();
435 Ok(inner.data.get(&key).map(|e| e.data.clone()))
436 }
437
438 async fn has(&self, hash: &Hash) -> Result<bool, StoreError> {
439 let key = to_hex(hash);
440 Ok(self.inner.read().unwrap().data.contains_key(&key))
441 }
442
443 async fn delete(&self, hash: &Hash) -> Result<bool, StoreError> {
444 let key = to_hex(hash);
445 let mut inner = self.inner.write().unwrap();
446 inner.pins.remove(&key);
448 Ok(inner.data.remove(&key).is_some())
449 }
450
451 fn set_max_bytes(&self, max: u64) {
452 self.inner.write().unwrap().max_bytes = if max > 0 { Some(max) } else { None };
453 }
454
455 fn max_bytes(&self) -> Option<u64> {
456 self.inner.read().unwrap().max_bytes
457 }
458
459 async fn stats(&self) -> StoreStats {
460 let inner = self.inner.read().unwrap();
461 let mut count = 0u64;
462 let mut bytes = 0u64;
463 let mut pinned_count = 0u64;
464 let mut pinned_bytes = 0u64;
465
466 for (key, entry) in &inner.data {
467 count += 1;
468 bytes += entry.data.len() as u64;
469 if inner.pins.get(key).copied().unwrap_or(0) > 0 {
470 pinned_count += 1;
471 pinned_bytes += entry.data.len() as u64;
472 }
473 }
474
475 StoreStats {
476 count,
477 bytes,
478 pinned_count,
479 pinned_bytes,
480 }
481 }
482
483 async fn evict_if_needed(&self) -> Result<u64, StoreError> {
484 let max = match self.inner.read().unwrap().max_bytes {
485 Some(m) => m,
486 None => return Ok(0), };
488
489 let current: u64 = self
490 .inner
491 .read()
492 .unwrap()
493 .data
494 .values()
495 .map(|e| e.data.len() as u64)
496 .sum();
497
498 if current <= max {
499 return Ok(0);
500 }
501
502 let target = max * 9 / 10;
504 Ok(self.evict_to_target(target))
505 }
506
507 async fn pin(&self, hash: &Hash) -> Result<(), StoreError> {
508 let key = to_hex(hash);
509 let mut inner = self.inner.write().unwrap();
510 *inner.pins.entry(key).or_insert(0) += 1;
511 Ok(())
512 }
513
514 async fn unpin(&self, hash: &Hash) -> Result<(), StoreError> {
515 let key = to_hex(hash);
516 let mut inner = self.inner.write().unwrap();
517 if let Some(count) = inner.pins.get_mut(&key) {
518 if *count > 0 {
519 *count -= 1;
520 }
521 if *count == 0 {
522 inner.pins.remove(&key);
523 }
524 }
525 Ok(())
526 }
527
528 fn pin_count(&self, hash: &Hash) -> u32 {
529 let key = to_hex(hash);
530 self.inner
531 .read()
532 .unwrap()
533 .pins
534 .get(&key)
535 .copied()
536 .unwrap_or(0)
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543 use crate::hash::sha256;
544
545 #[tokio::test]
546 async fn test_put_returns_true_for_new() {
547 let store = MemoryStore::new();
548 let data = vec![1u8, 2, 3];
549 let hash = sha256(&data);
550
551 let result = store.put(hash, data).await.unwrap();
552 assert!(result);
553 }
554
555 #[tokio::test]
556 async fn test_put_returns_false_for_duplicate() {
557 let store = MemoryStore::new();
558 let data = vec![1u8, 2, 3];
559 let hash = sha256(&data);
560
561 store.put(hash, data.clone()).await.unwrap();
562 let result = store.put(hash, data).await.unwrap();
563 assert!(!result);
564 }
565
566 #[tokio::test]
567 async fn test_put_many_counts_only_new_items() {
568 let store = MemoryStore::new();
569 let data1 = vec![1u8, 2, 3];
570 let data2 = vec![4u8, 5, 6];
571 let hash1 = sha256(&data1);
572 let hash2 = sha256(&data2);
573
574 store.put(hash1, data1.clone()).await.unwrap();
575 let inserted = store
576 .put_many(vec![(hash1, data1), (hash2, data2.clone())])
577 .await
578 .unwrap();
579
580 assert_eq!(inserted, 1);
581 assert_eq!(store.get(&hash2).await.unwrap(), Some(data2));
582 }
583
584 #[tokio::test]
585 async fn test_buffered_store_flushes_pending_writes() {
586 let base = std::sync::Arc::new(MemoryStore::new());
587 let buffered = BufferedStore::new(std::sync::Arc::clone(&base));
588 let data = vec![9u8, 8, 7];
589 let hash = sha256(&data);
590
591 assert!(buffered.put(hash, data.clone()).await.unwrap());
592 assert_eq!(buffered.get(&hash).await.unwrap(), Some(data.clone()));
593 assert_eq!(base.get(&hash).await.unwrap(), None);
594
595 let flushed = buffered.flush().await.unwrap();
596
597 assert_eq!(flushed, 1);
598 assert_eq!(base.get(&hash).await.unwrap(), Some(data));
599 }
600
601 #[tokio::test]
602 async fn test_optimistic_buffered_store_avoids_base_probe_but_preserves_contents() {
603 let base = std::sync::Arc::new(MemoryStore::new());
604 let buffered = BufferedStore::new_optimistic(std::sync::Arc::clone(&base));
605 let data = vec![4u8, 5, 6];
606 let hash = sha256(&data);
607
608 base.put(hash, data.clone()).await.unwrap();
609
610 assert!(buffered.put(hash, data.clone()).await.unwrap());
611 assert_eq!(buffered.get(&hash).await.unwrap(), Some(data.clone()));
612
613 let flushed = buffered.flush().await.unwrap();
614
615 assert_eq!(flushed, 0);
616 assert_eq!(base.get(&hash).await.unwrap(), Some(data));
617 }
618
619 #[tokio::test]
620 async fn test_get_returns_data() {
621 let store = MemoryStore::new();
622 let data = vec![1u8, 2, 3];
623 let hash = sha256(&data);
624
625 store.put(hash, data.clone()).await.unwrap();
626 let result = store.get(&hash).await.unwrap();
627
628 assert_eq!(result, Some(data));
629 }
630
631 #[tokio::test]
632 async fn test_get_returns_none_for_missing() {
633 let store = MemoryStore::new();
634 let hash = [0u8; 32];
635
636 let result = store.get(&hash).await.unwrap();
637 assert!(result.is_none());
638 }
639
640 #[tokio::test]
641 async fn test_has_returns_true() {
642 let store = MemoryStore::new();
643 let data = vec![1u8, 2, 3];
644 let hash = sha256(&data);
645
646 store.put(hash, data).await.unwrap();
647 assert!(store.has(&hash).await.unwrap());
648 }
649
650 #[tokio::test]
651 async fn test_has_returns_false() {
652 let store = MemoryStore::new();
653 let hash = [0u8; 32];
654
655 assert!(!store.has(&hash).await.unwrap());
656 }
657
658 #[tokio::test]
659 async fn test_delete_returns_true() {
660 let store = MemoryStore::new();
661 let data = vec![1u8, 2, 3];
662 let hash = sha256(&data);
663
664 store.put(hash, data).await.unwrap();
665 let result = store.delete(&hash).await.unwrap();
666
667 assert!(result);
668 assert!(!store.has(&hash).await.unwrap());
669 }
670
671 #[tokio::test]
672 async fn test_delete_returns_false() {
673 let store = MemoryStore::new();
674 let hash = [0u8; 32];
675
676 let result = store.delete(&hash).await.unwrap();
677 assert!(!result);
678 }
679
680 #[tokio::test]
681 async fn test_size() {
682 let store = MemoryStore::new();
683 assert_eq!(store.size(), 0);
684
685 let data1 = vec![1u8];
686 let data2 = vec![2u8];
687 let hash1 = sha256(&data1);
688 let hash2 = sha256(&data2);
689
690 store.put(hash1, data1).await.unwrap();
691 store.put(hash2, data2).await.unwrap();
692
693 assert_eq!(store.size(), 2);
694 }
695
696 #[tokio::test]
697 async fn test_total_bytes() {
698 let store = MemoryStore::new();
699 assert_eq!(store.total_bytes(), 0);
700
701 let data1 = vec![1u8, 2, 3];
702 let data2 = vec![4u8, 5];
703 let hash1 = sha256(&data1);
704 let hash2 = sha256(&data2);
705
706 store.put(hash1, data1).await.unwrap();
707 store.put(hash2, data2).await.unwrap();
708
709 assert_eq!(store.total_bytes(), 5);
710 }
711
712 #[tokio::test]
713 async fn test_clear() {
714 let store = MemoryStore::new();
715 let data = vec![1u8, 2, 3];
716 let hash = sha256(&data);
717
718 store.put(hash, data).await.unwrap();
719 store.clear();
720
721 assert_eq!(store.size(), 0);
722 assert!(!store.has(&hash).await.unwrap());
723 }
724
725 #[tokio::test]
726 async fn test_keys() {
727 let store = MemoryStore::new();
728 assert!(store.keys().is_empty());
729
730 let data1 = vec![1u8];
731 let data2 = vec![2u8];
732 let hash1 = sha256(&data1);
733 let hash2 = sha256(&data2);
734
735 store.put(hash1, data1).await.unwrap();
736 store.put(hash2, data2).await.unwrap();
737
738 let keys = store.keys();
739 assert_eq!(keys.len(), 2);
740
741 let mut hex_keys: Vec<_> = keys.iter().map(to_hex).collect();
742 hex_keys.sort();
743 let mut expected: Vec<_> = vec![to_hex(&hash1), to_hex(&hash2)];
744 expected.sort();
745 assert_eq!(hex_keys, expected);
746 }
747
748 #[tokio::test]
749 async fn test_pin_and_unpin() {
750 let store = MemoryStore::new();
751 let data = vec![1u8, 2, 3];
752 let hash = sha256(&data);
753
754 store.put(hash, data).await.unwrap();
755
756 assert!(!store.is_pinned(&hash));
758 assert_eq!(store.pin_count(&hash), 0);
759
760 store.pin(&hash).await.unwrap();
762 assert!(store.is_pinned(&hash));
763 assert_eq!(store.pin_count(&hash), 1);
764
765 store.unpin(&hash).await.unwrap();
767 assert!(!store.is_pinned(&hash));
768 assert_eq!(store.pin_count(&hash), 0);
769 }
770
771 #[tokio::test]
772 async fn test_pin_count_ref_counting() {
773 let store = MemoryStore::new();
774 let data = vec![1u8, 2, 3];
775 let hash = sha256(&data);
776
777 store.put(hash, data).await.unwrap();
778
779 store.pin(&hash).await.unwrap();
781 store.pin(&hash).await.unwrap();
782 store.pin(&hash).await.unwrap();
783 assert_eq!(store.pin_count(&hash), 3);
784
785 store.unpin(&hash).await.unwrap();
787 assert_eq!(store.pin_count(&hash), 2);
788 assert!(store.is_pinned(&hash));
789
790 store.unpin(&hash).await.unwrap();
792 store.unpin(&hash).await.unwrap();
793 assert_eq!(store.pin_count(&hash), 0);
794 assert!(!store.is_pinned(&hash));
795
796 store.unpin(&hash).await.unwrap();
798 assert_eq!(store.pin_count(&hash), 0);
799 }
800
801 #[tokio::test]
802 async fn test_stats() {
803 let store = MemoryStore::new();
804
805 let data1 = vec![1u8, 2, 3]; let data2 = vec![4u8, 5]; let hash1 = sha256(&data1);
808 let hash2 = sha256(&data2);
809
810 store.put(hash1, data1).await.unwrap();
811 store.put(hash2, data2).await.unwrap();
812
813 store.pin(&hash1).await.unwrap();
815
816 let stats = store.stats().await;
817 assert_eq!(stats.count, 2);
818 assert_eq!(stats.bytes, 5);
819 assert_eq!(stats.pinned_count, 1);
820 assert_eq!(stats.pinned_bytes, 3);
821 }
822
823 #[tokio::test]
824 async fn test_max_bytes() {
825 let store = MemoryStore::new();
826 assert!(store.max_bytes().is_none());
827
828 store.set_max_bytes(1000);
829 assert_eq!(store.max_bytes(), Some(1000));
830
831 store.set_max_bytes(0);
833 assert!(store.max_bytes().is_none());
834 }
835
836 #[tokio::test]
837 async fn test_with_max_bytes() {
838 let store = MemoryStore::with_max_bytes(500);
839 assert_eq!(store.max_bytes(), Some(500));
840
841 let store_unlimited = MemoryStore::with_max_bytes(0);
842 assert!(store_unlimited.max_bytes().is_none());
843 }
844
845 #[tokio::test]
846 async fn test_eviction_respects_pins() {
847 let store = MemoryStore::with_max_bytes(10);
849
850 let data1 = vec![1u8, 1, 1]; let data2 = vec![2u8, 2, 2];
853 let data3 = vec![3u8, 3, 3]; let hash1 = sha256(&data1);
855 let hash2 = sha256(&data2);
856 let hash3 = sha256(&data3);
857
858 store.put(hash1, data1).await.unwrap();
859 store.put(hash2, data2).await.unwrap();
860 store.put(hash3, data3).await.unwrap();
861
862 store.pin(&hash1).await.unwrap();
864
865 let data4 = vec![4u8, 4, 4];
867 let hash4 = sha256(&data4);
868 store.put(hash4, data4).await.unwrap();
869
870 let freed = store.evict_if_needed().await.unwrap();
872 assert!(freed > 0);
873
874 assert!(store.has(&hash1).await.unwrap());
876 assert!(!store.has(&hash2).await.unwrap());
878 assert!(store.has(&hash3).await.unwrap());
880 assert!(store.has(&hash4).await.unwrap());
881 }
882
883 #[tokio::test]
884 async fn test_eviction_lru_order() {
885 let store = MemoryStore::with_max_bytes(15);
887
888 let data1 = vec![1u8; 5]; let data2 = vec![2u8; 5];
891 let data3 = vec![3u8; 5];
892 let data4 = vec![4u8; 5]; let hash1 = sha256(&data1);
894 let hash2 = sha256(&data2);
895 let hash3 = sha256(&data3);
896 let hash4 = sha256(&data4);
897
898 store.put(hash1, data1).await.unwrap();
899 store.put(hash2, data2).await.unwrap();
900 store.put(hash3, data3).await.unwrap();
901 store.put(hash4, data4).await.unwrap();
902
903 assert_eq!(store.total_bytes(), 20);
905
906 let freed = store.evict_if_needed().await.unwrap();
908 assert!(freed >= 5); assert!(!store.has(&hash1).await.unwrap());
912 assert!(store.has(&hash4).await.unwrap());
914 }
915
916 #[tokio::test]
917 async fn test_no_eviction_when_under_limit() {
918 let store = MemoryStore::with_max_bytes(100);
919
920 let data = vec![1u8, 2, 3];
921 let hash = sha256(&data);
922 store.put(hash, data).await.unwrap();
923
924 let freed = store.evict_if_needed().await.unwrap();
925 assert_eq!(freed, 0);
926 assert!(store.has(&hash).await.unwrap());
927 }
928
929 #[tokio::test]
930 async fn test_no_eviction_without_limit() {
931 let store = MemoryStore::new();
932
933 for i in 0..100u8 {
935 let data = vec![i; 100];
936 let hash = sha256(&data);
937 store.put(hash, data).await.unwrap();
938 }
939
940 let freed = store.evict_if_needed().await.unwrap();
941 assert_eq!(freed, 0);
942 assert_eq!(store.size(), 100);
943 }
944
945 #[tokio::test]
946 async fn test_delete_removes_pin() {
947 let store = MemoryStore::new();
948 let data = vec![1u8, 2, 3];
949 let hash = sha256(&data);
950
951 store.put(hash, data).await.unwrap();
952 store.pin(&hash).await.unwrap();
953 assert!(store.is_pinned(&hash));
954
955 store.delete(&hash).await.unwrap();
956 assert_eq!(store.pin_count(&hash), 0);
958 }
959}