nectar_primitives/store/
memory.rs1use std::collections::HashMap;
4
5use parking_lot::RwLock;
6
7use crate::bmt::DEFAULT_BODY_SIZE;
8use crate::chunk::{AnyChunk, ChunkAddress};
9
10use super::ChunkStoreError;
11use super::typed::{ChunkGet, ChunkHas, ChunkPut};
12
13#[derive(Debug)]
18pub struct MemoryStore<const BODY_SIZE: usize = DEFAULT_BODY_SIZE> {
19 chunks: RwLock<HashMap<ChunkAddress, AnyChunk<BODY_SIZE>>>,
20}
21
22impl<const BODY_SIZE: usize> Clone for MemoryStore<BODY_SIZE> {
23 fn clone(&self) -> Self {
24 Self {
25 chunks: RwLock::new(self.chunks.read().clone()),
26 }
27 }
28}
29
30impl<const BODY_SIZE: usize> Default for MemoryStore<BODY_SIZE> {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl<const BODY_SIZE: usize> MemoryStore<BODY_SIZE> {
37 pub fn new() -> Self {
39 Self {
40 chunks: RwLock::new(HashMap::new()),
41 }
42 }
43
44 pub fn from_chunks(chunks: impl IntoIterator<Item = AnyChunk<BODY_SIZE>>) -> Self {
46 Self {
47 chunks: RwLock::new(chunks.into_iter().map(|c| (*c.address(), c)).collect()),
48 }
49 }
50
51 pub fn get(&self, address: &ChunkAddress) -> Option<AnyChunk<BODY_SIZE>> {
53 self.chunks.read().get(address).cloned()
54 }
55
56 pub fn len(&self) -> usize {
58 self.chunks.read().len()
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.chunks.read().is_empty()
64 }
65
66 pub fn into_chunks(self) -> HashMap<ChunkAddress, AnyChunk<BODY_SIZE>> {
68 self.chunks.into_inner()
69 }
70}
71
72impl<const BODY_SIZE: usize> ChunkPut<BODY_SIZE> for MemoryStore<BODY_SIZE> {
73 type Error = std::convert::Infallible;
74
75 async fn put(&self, chunk: AnyChunk<BODY_SIZE>) -> Result<(), Self::Error> {
76 self.chunks.write().insert(*chunk.address(), chunk);
77 Ok(())
78 }
79}
80
81impl<const BODY_SIZE: usize> ChunkGet<BODY_SIZE> for MemoryStore<BODY_SIZE> {
82 type Error = ChunkStoreError;
83
84 async fn get(&self, address: &ChunkAddress) -> Result<AnyChunk<BODY_SIZE>, Self::Error> {
85 self.chunks
86 .read()
87 .get(address)
88 .cloned()
89 .ok_or_else(|| ChunkStoreError::not_found(address))
90 }
91}
92
93impl<const BODY_SIZE: usize> ChunkHas<BODY_SIZE> for MemoryStore<BODY_SIZE> {
94 async fn has(&self, address: &ChunkAddress) -> bool {
95 self.chunks.read().contains_key(address)
96 }
97}
98
99impl<const BODY_SIZE: usize> ChunkGet<BODY_SIZE> for HashMap<ChunkAddress, AnyChunk<BODY_SIZE>> {
100 type Error = ChunkStoreError;
101
102 async fn get(&self, address: &ChunkAddress) -> Result<AnyChunk<BODY_SIZE>, Self::Error> {
103 self.get(address)
104 .cloned()
105 .ok_or_else(|| ChunkStoreError::not_found(address))
106 }
107}
108
109impl<const BODY_SIZE: usize> ChunkHas<BODY_SIZE> for HashMap<ChunkAddress, AnyChunk<BODY_SIZE>> {
110 async fn has(&self, address: &ChunkAddress) -> bool {
111 self.contains_key(address)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::chunk::{Chunk, ContentChunk};
119 use futures::executor::block_on;
120
121 #[test]
122 fn test_memory_store() {
123 let store = MemoryStore::<DEFAULT_BODY_SIZE>::new();
124 assert!(store.is_empty());
125
126 let chunk = ContentChunk::new(b"hello".as_slice()).unwrap();
127 let addr = *chunk.address();
128 let any: AnyChunk = chunk.into();
129
130 block_on(ChunkPut::put(&store, any.clone())).unwrap();
131 assert_eq!(store.len(), 1);
132 assert!(block_on(ChunkHas::has(&store, &addr)));
133 assert_eq!(store.get(&addr), Some(any));
134 }
135}