1use std::fmt::Debug;
4
5use bytes::Bytes;
6
7use crate::bmt::SPAN_SIZE;
8use crate::chunk::encryption::{EncryptedChunkRef, EncryptionKey, decrypt_chunk_data};
9use crate::chunk::{BmtChunk, Chunk, ChunkAddress, ContentChunk};
10use crate::store::{SyncChunkGet, SyncChunkPut};
11
12use super::constants::{ENCRYPTED_REF_SIZE, REF_SIZE, compute_spans_inline, subspan_for_spans};
13use super::error::{FileError, Result};
14
15fn chunk_creation_error(e: crate::error::PrimitivesError) -> FileError {
17 match e {
18 crate::error::PrimitivesError::Chunk(c) => FileError::Chunk(c),
19 other => FileError::Store(Box::new(other)),
20 }
21}
22
23#[inline]
25fn create_chunk<const BS: usize>(data: Bytes) -> Result<ContentChunk<BS>> {
26 ContentChunk::<BS>::try_from(data).map_err(chunk_creation_error)
27}
28
29fn store_chunk<const BS: usize, S: SyncChunkPut<BS>>(
31 chunk: ContentChunk<BS>,
32 store: &S,
33) -> Result<ChunkAddress> {
34 let address = *chunk.address();
35 store.put(chunk.into()).map_err(FileError::store)?;
36 Ok(address)
37}
38
39pub trait JoinMode: Sized + 'static {
41 const REF_SIZE: usize;
43
44 type RootRef: Clone + Debug + Send + Sync;
46
47 type JoinerContext: Clone + Debug + Send + Sync;
49
50 #[inline]
52 fn refs_per_chunk(body_size: usize) -> usize {
53 body_size / Self::REF_SIZE
54 }
55
56 #[inline]
58 fn levels(length: u64, chunk_size: usize) -> usize {
59 super::constants::tree_depth(length, chunk_size, Self::REF_SIZE)
60 }
61
62 #[inline]
64 fn subspan_size<const BS: usize>(span: u64) -> u64 {
65 let spans = compute_spans_inline(BS / Self::REF_SIZE);
66 subspan_for_spans::<BS>(span, &spans)
67 }
68
69 #[inline]
71 fn child_span<const BS: usize>(parent_span: u64, subspan: u64, child_index: usize) -> u64 {
72 let branches = Self::refs_per_chunk(BS);
73 if child_index == branches - 1 {
74 let preceding = child_index as u64 * subspan;
75 parent_span.saturating_sub(preceding)
76 } else {
77 subspan.min(parent_span.saturating_sub(child_index as u64 * subspan))
78 }
79 }
80
81 fn root_address(input: &Self::RootRef) -> ChunkAddress;
83
84 fn init_from_chunk<const BS: usize>(
86 input: Self::RootRef,
87 chunk: ContentChunk<BS>,
88 ) -> Result<(ChunkAddress, u64, Self::JoinerContext)>;
89
90 fn decode_body<const BS: usize>(
92 chunk: ContentChunk<BS>,
93 context: &Self::JoinerContext,
94 span: u64,
95 ) -> Result<Bytes>;
96
97 fn parse_child_ref(
99 body: &[u8],
100 ref_start: usize,
101 ) -> Result<(ChunkAddress, Self::JoinerContext)>;
102}
103
104pub(crate) fn joiner_init<M: JoinMode, G: SyncChunkGet<BS>, const BS: usize>(
106 getter: &G,
107 input: M::RootRef,
108) -> Result<(ChunkAddress, u64, M::JoinerContext)> {
109 let addr = M::root_address(&input);
110 let any = getter.get(&addr).map_err(FileError::getter)?;
111 let chunk = any.into_content().ok_or(FileError::InvalidChunkType {
112 type_name: "non-content",
113 })?;
114 M::init_from_chunk::<BS>(input, chunk)
115}
116
117pub(crate) async fn joiner_init_async<
119 M: JoinMode + Send + Sync,
120 G: crate::store::ChunkGet<BS>,
121 const BS: usize,
122>(
123 getter: &G,
124 input: M::RootRef,
125) -> Result<(ChunkAddress, u64, M::JoinerContext)> {
126 let addr = M::root_address(&input);
127 let any = getter.get(&addr).await.map_err(FileError::getter)?;
128 let chunk = any.into_content().ok_or(FileError::InvalidChunkType {
129 type_name: "non-content",
130 })?;
131 M::init_from_chunk::<BS>(input, chunk)
132}
133
134#[inline]
136pub(crate) fn read_chunk_body<M: JoinMode, G: SyncChunkGet<BS>, const BS: usize>(
137 getter: &G,
138 address: &ChunkAddress,
139 context: &M::JoinerContext,
140 span: u64,
141) -> Result<Bytes> {
142 let any = getter.get(address).map_err(FileError::getter)?;
143 let chunk = any.into_content().ok_or(FileError::InvalidChunkType {
144 type_name: "non-content",
145 })?;
146 M::decode_body::<BS>(chunk, context, span)
147}
148
149pub(crate) async fn read_chunk_body_async<
151 M: JoinMode + Send + Sync,
152 G: crate::store::ChunkGet<BS>,
153 const BS: usize,
154>(
155 getter: &G,
156 address: &ChunkAddress,
157 context: &M::JoinerContext,
158 span: u64,
159) -> Result<Bytes> {
160 let address = *address;
161 let context = context.clone();
162 let any = getter.get(&address).await.map_err(FileError::getter)?;
163 let chunk = any.into_content().ok_or(FileError::InvalidChunkType {
164 type_name: "non-content",
165 })?;
166 M::decode_body::<BS>(chunk, &context, span)
167}
168
169pub trait SplitMode: JoinMode {
171 type RefBytes: AsRef<[u8]> + AsMut<[u8]> + Clone + Debug + Send + Sync;
173
174 fn prepare_chunk<const BS: usize>(data: Vec<u8>) -> Result<(ContentChunk<BS>, Self::RefBytes)>;
177
178 #[inline]
181 fn process_chunk<const BS: usize, S: SyncChunkPut<BS>>(
182 data: Vec<u8>,
183 store: &S,
184 ) -> Result<Self::RefBytes> {
185 let (chunk, ref_bytes) = Self::prepare_chunk::<BS>(data)?;
186 store.put(chunk.into()).map_err(FileError::store)?;
187 Ok(ref_bytes)
188 }
189
190 fn process_empty<const BS: usize, S: SyncChunkPut<BS>>(store: &S) -> Result<Self::RootRef>;
192
193 fn extract_root(buffer: &[u8]) -> Result<Self::RootRef>;
195}
196
197#[derive(Debug)]
199pub struct PlainMode;
200
201impl JoinMode for PlainMode {
202 const REF_SIZE: usize = REF_SIZE;
203 type RootRef = ChunkAddress;
204 type JoinerContext = ();
205
206 #[inline]
207 fn root_address(input: &ChunkAddress) -> ChunkAddress {
208 *input
209 }
210
211 fn init_from_chunk<const BS: usize>(
212 root: ChunkAddress,
213 chunk: ContentChunk<BS>,
214 ) -> Result<(ChunkAddress, u64, ())> {
215 let span = chunk.span();
216 Ok((root, span, ()))
217 }
218
219 #[inline]
220 fn decode_body<const BS: usize>(
221 chunk: ContentChunk<BS>,
222 _context: &(),
223 _span: u64,
224 ) -> Result<Bytes> {
225 Ok(chunk.data().clone())
226 }
227
228 #[inline]
229 fn parse_child_ref(body: &[u8], ref_start: usize) -> Result<(ChunkAddress, ())> {
230 let ref_end = ref_start + REF_SIZE;
231 let child_addr_bytes: [u8; 32] = body[ref_start..ref_end]
232 .try_into()
233 .map_err(|_| FileError::InvalidReference { level: 0 })?;
234 Ok((ChunkAddress::from(child_addr_bytes), ()))
235 }
236}
237
238impl SplitMode for PlainMode {
239 type RefBytes = [u8; REF_SIZE];
240
241 #[inline]
242 fn prepare_chunk<const BS: usize>(data: Vec<u8>) -> Result<(ContentChunk<BS>, [u8; REF_SIZE])> {
243 let chunk = create_chunk::<BS>(Bytes::from(data))?;
244 let ref_bytes = (*chunk.address()).into();
245 Ok((chunk, ref_bytes))
246 }
247
248 fn process_empty<const BS: usize, S: SyncChunkPut<BS>>(store: &S) -> Result<ChunkAddress> {
249 let chunk = ContentChunk::<BS>::new(Bytes::new()).map_err(chunk_creation_error)?;
252 store_chunk::<BS, S>(chunk, store)
253 }
254
255 fn extract_root(buffer: &[u8]) -> Result<ChunkAddress> {
256 let root_bytes: [u8; 32] = buffer
257 .get(..REF_SIZE)
258 .and_then(|s| s.try_into().ok())
259 .ok_or(FileError::InvalidReference { level: 0 })?;
260 Ok(ChunkAddress::from(root_bytes))
261 }
262}
263
264#[derive(Debug)]
269pub struct EncryptedMode;
270
271impl EncryptedMode {
272 fn decrypt_data_length<const BS: usize>(span: u64) -> usize {
274 if span <= BS as u64 {
275 span as usize
276 } else {
277 let sub = Self::subspan_size::<BS>(span);
278 let num_children = span.div_ceil(sub) as usize;
279 let raw = num_children * ENCRYPTED_REF_SIZE;
280 raw.min(BS)
281 }
282 }
283}
284
285impl JoinMode for EncryptedMode {
286 const REF_SIZE: usize = ENCRYPTED_REF_SIZE;
287 type RootRef = EncryptedChunkRef;
288 type JoinerContext = EncryptionKey;
289
290 fn root_address(input: &EncryptedChunkRef) -> ChunkAddress {
291 *input.address()
292 }
293
294 fn init_from_chunk<const BS: usize>(
295 root_ref: EncryptedChunkRef,
296 chunk: ContentChunk<BS>,
297 ) -> Result<(ChunkAddress, u64, EncryptionKey)> {
298 let encrypted_data: Bytes = chunk.into();
299
300 let span_buf = decrypt_span::<BS>(&encrypted_data, root_ref.key())?;
301 let span = u64::from_le_bytes(span_buf);
302
303 let (address, key) = root_ref.into_parts();
304 Ok((address, span, key))
305 }
306
307 fn decode_body<const BS: usize>(
308 chunk: ContentChunk<BS>,
309 key: &EncryptionKey,
310 span: u64,
311 ) -> Result<Bytes> {
312 let encrypted_data: Bytes = chunk.into();
313
314 let data_length = Self::decrypt_data_length::<BS>(span);
315 let decrypted = decrypt_chunk_data::<BS>(&encrypted_data, key, data_length)?;
316 Ok(Bytes::from(decrypted).slice(SPAN_SIZE..))
317 }
318
319 fn parse_child_ref(body: &[u8], ref_start: usize) -> Result<(ChunkAddress, EncryptionKey)> {
320 let ref_end = ref_start + ENCRYPTED_REF_SIZE;
321 let child_addr_bytes: [u8; 32] = body[ref_start..ref_start + 32]
322 .try_into()
323 .map_err(|_| FileError::InvalidReference { level: 0 })?;
324 let child_key = EncryptionKey::try_from(&body[ref_start + 32..ref_end])?;
325 Ok((ChunkAddress::from(child_addr_bytes), child_key))
326 }
327}
328
329#[cfg(feature = "encryption")]
330impl SplitMode for EncryptedMode {
331 type RefBytes = [u8; ENCRYPTED_REF_SIZE];
332
333 fn prepare_chunk<const BS: usize>(
334 data: Vec<u8>,
335 ) -> Result<(ContentChunk<BS>, [u8; ENCRYPTED_REF_SIZE])> {
336 use crate::chunk::encryption::encrypt_chunk;
337
338 let key = EncryptionKey::generate();
339 let ciphertext = encrypt_chunk::<BS>(&data, &key)?;
340 let chunk = create_chunk::<BS>(Bytes::from(ciphertext))?;
341
342 let mut ref_bytes = [0u8; ENCRYPTED_REF_SIZE];
343 ref_bytes[..32].copy_from_slice(chunk.address().as_bytes());
344 ref_bytes[32..].copy_from_slice(key.as_bytes());
345 Ok((chunk, ref_bytes))
346 }
347
348 fn process_empty<const BS: usize, S: SyncChunkPut<BS>>(store: &S) -> Result<EncryptedChunkRef> {
349 use crate::chunk::encryption::encrypt_chunk;
350
351 let key = EncryptionKey::generate();
352 let chunk_bytes = 0u64.to_le_bytes().to_vec();
353 let ciphertext = encrypt_chunk::<BS>(&chunk_bytes, &key)?;
354 let chunk = create_chunk::<BS>(Bytes::from(ciphertext))?;
355 let address = store_chunk::<BS, S>(chunk, store)?;
356 Ok(EncryptedChunkRef::new(address, key))
357 }
358
359 fn extract_root(buffer: &[u8]) -> Result<EncryptedChunkRef> {
360 let root_ref_bytes = buffer
361 .get(..ENCRYPTED_REF_SIZE)
362 .ok_or(FileError::InvalidReference { level: 0 })?;
363 EncryptedChunkRef::try_from(root_ref_bytes)
364 .map_err(|_| FileError::InvalidReference { level: 0 })
365 }
366}
367
368fn decrypt_span<const BODY_SIZE: usize>(
370 encrypted_data: &[u8],
371 key: &EncryptionKey,
372) -> Result<[u8; SPAN_SIZE]> {
373 use crate::chunk::encryption::transcrypt;
374
375 let expected_len = SPAN_SIZE + BODY_SIZE;
376 if encrypted_data.len() != expected_len {
377 return Err(FileError::Encryption(
378 crate::chunk::encryption::EncryptionError::DataTooShort {
379 len: encrypted_data.len(),
380 min: expected_len,
381 },
382 ));
383 }
384
385 let span_ctr = (BODY_SIZE / EncryptionKey::SIZE) as u32;
386 let mut span_buf = [0u8; SPAN_SIZE];
387 transcrypt(key, span_ctr, &encrypted_data[..SPAN_SIZE], &mut span_buf)
388 .map_err(FileError::Encryption)?;
389 Ok(span_buf)
390}