1use shadow_core::error::{Result, ShadowError};
4use crate::chunking::{Chunker, ChunkInfo};
5use crate::erasure::ErasureEncoder;
6use bytes::Bytes;
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9
10#[derive(Debug, Clone)]
12pub struct StorageConfig {
13 pub chunk_size: usize,
15 pub data_shards: usize,
17 pub parity_shards: usize,
19 pub encrypt: bool,
21 pub max_storage: usize,
23}
24
25impl Default for StorageConfig {
26 fn default() -> Self {
27 Self {
28 chunk_size: 256 * 1024, data_shards: 3,
30 parity_shards: 2,
31 encrypt: true,
32 max_storage: 1024 * 1024 * 1024, }
34 }
35}
36
37pub struct ContentStore {
39 config: StorageConfig,
41 chunks: Arc<RwLock<HashMap<[u8; 32], Bytes>>>,
43 metadata: Arc<RwLock<HashMap<[u8; 32], Vec<ChunkInfo>>>>,
45 current_size: Arc<RwLock<usize>>,
47}
48
49impl ContentStore {
50 pub fn new(config: StorageConfig) -> Self {
52 Self {
53 config,
54 chunks: Arc::new(RwLock::new(HashMap::new())),
55 metadata: Arc::new(RwLock::new(HashMap::new())),
56 current_size: Arc::new(RwLock::new(0)),
57 }
58 }
59
60 pub fn store(&self, data: &[u8]) -> Result<[u8; 32]> {
62 let current = *self.current_size.read().unwrap();
64 if current + data.len() > self.config.max_storage {
65 return Err(ShadowError::Storage("Storage full".into()));
66 }
67
68 let chunker = Chunker::new(self.config.chunk_size);
70 let chunks = chunker.chunk(data)?;
71
72 let mut chunk_infos = Vec::new();
74 let mut chunks_map = self.chunks.write().unwrap();
75 let mut size = self.current_size.write().unwrap();
76
77 for (info, chunk_data) in chunks {
78 if !chunks_map.contains_key(&info.hash) {
80 chunks_map.insert(info.hash, chunk_data);
81 *size += info.size;
82 }
83 chunk_infos.push(info);
84 }
85
86 let content_hash = Chunker::content_hash(&chunk_infos);
88
89 self.metadata.write().unwrap().insert(content_hash, chunk_infos);
91
92 Ok(content_hash)
93 }
94
95 pub fn retrieve(&self, content_hash: &[u8; 32]) -> Result<Bytes> {
97 let metadata = self.metadata.read().unwrap();
99 let chunk_infos = metadata.get(content_hash)
100 .ok_or_else(|| ShadowError::Storage("Content not found".into()))?
101 .clone();
102
103 let chunks_map = self.chunks.read().unwrap();
105 let mut chunks = Vec::new();
106
107 for info in chunk_infos {
108 let chunk_data = chunks_map.get(&info.hash)
109 .ok_or_else(|| ShadowError::Storage(format!(
110 "Chunk {} missing", info.index
111 )))?
112 .clone();
113
114 chunks.push((info, chunk_data));
115 }
116
117 let chunker = Chunker::new(self.config.chunk_size);
119 chunker.reassemble(&chunks)
120 }
121
122 pub fn contains(&self, content_hash: &[u8; 32]) -> bool {
124 self.metadata.read().unwrap().contains_key(content_hash)
125 }
126
127 pub fn delete(&self, content_hash: &[u8; 32]) -> Result<()> {
129 let mut metadata = self.metadata.write().unwrap();
130
131 if let Some(chunk_infos) = metadata.remove(content_hash) {
132 let mut chunks_map = self.chunks.write().unwrap();
133 let mut size = self.current_size.write().unwrap();
134
135 for info in chunk_infos {
137 if let Some(chunk) = chunks_map.remove(&info.hash) {
139 *size = size.saturating_sub(chunk.len());
140 }
141 }
142 }
143
144 Ok(())
145 }
146
147 pub fn stats(&self) -> StorageStats {
149 let chunks_count = self.chunks.read().unwrap().len();
150 let files_count = self.metadata.read().unwrap().len();
151 let current_size = *self.current_size.read().unwrap();
152
153 StorageStats {
154 chunks: chunks_count,
155 files: files_count,
156 size_bytes: current_size,
157 max_bytes: self.config.max_storage,
158 }
159 }
160
161 pub fn list(&self) -> Vec<[u8; 32]> {
163 self.metadata.read().unwrap().keys().copied().collect()
164 }
165}
166
167impl Default for ContentStore {
168 fn default() -> Self {
169 Self::new(StorageConfig::default())
170 }
171}
172
173#[derive(Debug, Clone)]
175pub struct StorageStats {
176 pub chunks: usize,
177 pub files: usize,
178 pub size_bytes: usize,
179 pub max_bytes: usize,
180}
181
182impl StorageStats {
183 pub fn usage_percent(&self) -> f64 {
184 (self.size_bytes as f64 / self.max_bytes as f64) * 100.0
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_store_retrieve() {
194 let store = ContentStore::default();
195 let data = b"Hello, Shadow Network Storage!";
196
197 let hash = store.store(data).unwrap();
198 let retrieved = store.retrieve(&hash).unwrap();
199
200 assert_eq!(retrieved.as_ref(), data);
201 }
202
203 #[test]
204 fn test_deduplication() {
205 let store = ContentStore::default();
206 let data = b"Same data";
207
208 let hash1 = store.store(data).unwrap();
209 let initial_size = store.stats().size_bytes;
210
211 let hash2 = store.store(data).unwrap();
212 let final_size = store.stats().size_bytes;
213
214 assert_eq!(hash1, hash2);
215 assert_eq!(initial_size, final_size); }
217
218 #[test]
219 fn test_delete() {
220 let store = ContentStore::default();
221 let data = b"Temporary data";
222
223 let hash = store.store(data).unwrap();
224 assert!(store.contains(&hash));
225
226 store.delete(&hash).unwrap();
227 assert!(!store.contains(&hash));
228 }
229
230 #[test]
231 fn test_stats() {
232 let store = ContentStore::default();
233 let stats1 = store.stats();
234
235 store.store(b"Test data").unwrap();
236
237 let stats2 = store.stats();
238
239 assert!(stats2.size_bytes > stats1.size_bytes);
240 assert!(stats2.files > stats1.files);
241 }
242}