nectar_primitives/file/
mod.rs1mod constants;
49pub mod entry_ref;
50pub mod error;
51mod fold;
52mod frontier;
53mod helpers;
54#[cfg(test)]
55#[macro_use]
56mod joiner_tests;
57#[cfg(test)]
58#[macro_use]
59mod splitter_tests;
60mod joiner;
61pub mod mode;
62mod read_at;
63mod splitter;
64mod splitter_parallel;
65mod tree;
66mod windowed;
67mod write_at;
68
69use crate::chunk::ChunkAddress;
70#[cfg(feature = "encryption")]
71use crate::chunk::encryption::EncryptedChunkRef;
72use crate::store::{ChunkPut, MaybeSend, MaybeSync};
73
74#[cfg(feature = "encryption")]
76pub use joiner::EncryptedJoiner;
77#[cfg(feature = "tokio")]
78pub use joiner::JoinerReader;
79pub use joiner::{GenericJoiner, Joiner};
80#[cfg(feature = "tokio")]
81pub use windowed::WindowedJoinerReader;
82pub use windowed::WindowedReader;
83pub use read_at::ReadAt;
85#[cfg(feature = "encryption")]
86pub use splitter::EncryptedSplitter;
87pub use splitter::Splitter;
88#[cfg(feature = "encryption")]
89pub use splitter_parallel::EncryptedParallelSplitter;
90pub use splitter_parallel::ParallelSplitter;
91pub use write_at::WriteAt;
92
93pub use entry_ref::EntryRef;
94pub use error::FileError;
95pub use tree::{ChunkRange, TreeParams};
96
97mod join_ref_sealed {
98 pub trait Sealed {}
99 impl Sealed for crate::ChunkAddress {}
100 #[cfg(feature = "encryption")]
101 impl Sealed for crate::EncryptedChunkRef {}
102}
103
104pub trait JoinRef: join_ref_sealed::Sealed + Clone + Send + Sync + 'static {
107 type Mode: mode::JoinMode + Send + Sync;
109
110 fn into_root_ref(self) -> <Self::Mode as mode::JoinMode>::RootRef;
112}
113
114impl JoinRef for ChunkAddress {
115 type Mode = mode::PlainMode;
116
117 fn into_root_ref(self) -> Self {
118 self
119 }
120}
121
122#[cfg(feature = "encryption")]
123impl JoinRef for EncryptedChunkRef {
124 type Mode = mode::EncryptedMode;
125
126 fn into_root_ref(self) -> Self {
127 self
128 }
129}
130
131pub(crate) fn resolve_seek_position(
133 pos: std::io::SeekFrom,
134 current: u64,
135 span: u64,
136) -> std::io::Result<u64> {
137 use std::io::{Error, ErrorKind::InvalidInput, SeekFrom};
138 let to_i64 = |v: u64, msg: &str| i64::try_from(v).map_err(|_| Error::new(InvalidInput, msg));
139 let new_pos = match pos {
140 SeekFrom::Start(off) => to_i64(off, "seek offset exceeds i64::MAX")?,
141 SeekFrom::End(off) => to_i64(span, "file span exceeds i64::MAX")? + off,
142 SeekFrom::Current(off) => to_i64(current, "current position exceeds i64::MAX")? + off,
143 };
144 if new_pos < 0 {
145 return Err(Error::new(InvalidInput, "seek to negative position"));
146 }
147 Ok(new_pos as u64)
148}
149
150pub async fn join<R, G, const BODY_SIZE: usize>(getter: G, root: R) -> error::Result<Vec<u8>>
154where
155 R: JoinRef,
156 G: crate::store::ChunkGet<BODY_SIZE>,
157{
158 GenericJoiner::<G, R::Mode, BODY_SIZE>::new(getter, root.into_root_ref())
159 .await?
160 .read_all()
161 .await
162}
163
164pub fn split<const BODY_SIZE: usize>(
170 data: &[u8],
171) -> error::Result<(ChunkAddress, crate::store::MemoryStore<BODY_SIZE>)> {
172 let (root, chunks) = ParallelSplitter::<BODY_SIZE>::split_to_vec(&data)?;
173 Ok((root, crate::store::MemoryStore::from_chunks(chunks)))
174}
175
176#[cfg(feature = "encryption")]
178pub fn split_encrypted<const BODY_SIZE: usize>(
179 data: &[u8],
180) -> error::Result<(EncryptedChunkRef, crate::store::MemoryStore<BODY_SIZE>)> {
181 let (root_ref, chunks) = EncryptedParallelSplitter::<BODY_SIZE>::split_to_vec(&data)?;
182 Ok((root_ref, crate::store::MemoryStore::from_chunks(chunks)))
183}
184
185#[cfg(test)]
187pub(crate) const fn levels(length: u64, chunk_size: usize) -> usize {
188 constants::tree_depth(length, chunk_size, constants::REF_SIZE)
189}
190
191pub trait ChunkGetExt<const BODY_SIZE: usize>: crate::store::ChunkGet<BODY_SIZE> {
197 fn joiner<R: JoinRef>(
199 self,
200 root: R,
201 ) -> impl std::future::Future<Output = error::Result<GenericJoiner<Self, R::Mode, BODY_SIZE>>>
202 + MaybeSend
203 where
204 Self: Sized + Clone + MaybeSend + MaybeSync + 'static,
205 {
206 GenericJoiner::new(self, root.into_root_ref())
207 }
208
209 fn read_file<R: JoinRef>(
211 self,
212 root: R,
213 ) -> impl std::future::Future<Output = error::Result<Vec<u8>>> + MaybeSend
214 where
215 Self: Sized + Clone + MaybeSend + MaybeSync + 'static,
216 {
217 join(self, root)
218 }
219}
220
221impl<T, const BODY_SIZE: usize> ChunkGetExt<BODY_SIZE> for T where
222 T: crate::store::ChunkGet<BODY_SIZE>
223{
224}
225
226pub trait ChunkPutExt<const BODY_SIZE: usize>: ChunkPut<BODY_SIZE> {
243 fn write_file<D: ReadAt + Sync>(
248 &self,
249 data: D,
250 ) -> impl std::future::Future<Output = error::Result<ChunkAddress>> + MaybeSend
251 where
252 Self: Sized + MaybeSync,
253 {
254 let split = ParallelSplitter::<BODY_SIZE>::split_to_vec(&data);
255 async move {
256 let (root, chunks) = split?;
257 for chunk in chunks {
258 self.put(chunk).await.map_err(FileError::store)?;
259 }
260 Ok(root)
261 }
262 }
263
264 #[cfg(feature = "encryption")]
266 fn write_encrypted_file<D: ReadAt + Sync>(
267 &self,
268 data: D,
269 ) -> impl std::future::Future<Output = error::Result<EncryptedChunkRef>> + MaybeSend
270 where
271 Self: Sized + MaybeSync,
272 {
273 let split = EncryptedParallelSplitter::<BODY_SIZE>::split_to_vec(&data);
274 async move {
275 let (root_ref, chunks) = split?;
276 for chunk in chunks {
277 self.put(chunk).await.map_err(FileError::store)?;
278 }
279 Ok(root_ref)
280 }
281 }
282}
283
284impl<T, const BODY_SIZE: usize> ChunkPutExt<BODY_SIZE> for T where T: ChunkPut<BODY_SIZE> {}
285
286#[cfg(test)]
287mod tests;