1use std::{future::Future, result};
3
4use blake3;
5use bytes::Bytes;
6use serde::{Deserialize, Serialize};
7use smallvec::SmallVec;
8
9use super::{sync::Outboard, EncodeError, Leaf, Parent};
10use crate::{
11 iter::BaoChunk, rec::truncate_ranges, split_inner, ChunkNum, ChunkRangesRef, HashMode, TreeNode,
12};
13
14#[derive(Debug, Serialize, Deserialize)]
16pub enum EncodedItem {
17 Size(u64),
19 Parent(Parent),
21 Leaf(Leaf),
23 Error(EncodeError),
25 Done,
27}
28
29impl From<Leaf> for EncodedItem {
30 fn from(l: Leaf) -> Self {
31 Self::Leaf(l)
32 }
33}
34
35impl From<Parent> for EncodedItem {
36 fn from(p: Parent) -> Self {
37 Self::Parent(p)
38 }
39}
40
41impl From<EncodeError> for EncodedItem {
42 fn from(e: EncodeError) -> Self {
43 Self::Error(e)
44 }
45}
46
47pub trait Sender {
49 type Error;
51 fn send(
53 &mut self,
54 item: EncodedItem,
55 ) -> impl Future<Output = std::result::Result<(), Self::Error>> + '_;
56}
57
58impl Sender for tokio::sync::mpsc::Sender<EncodedItem> {
59 type Error = tokio::sync::mpsc::error::SendError<EncodedItem>;
60 fn send(
61 &mut self,
62 item: EncodedItem,
63 ) -> impl Future<Output = std::result::Result<(), Self::Error>> + '_ {
64 tokio::sync::mpsc::Sender::send(self, item)
65 }
66}
67
68pub async fn traverse_ranges_validated<D, O, F>(
76 data: D,
77 outboard: O,
78 ranges: &ChunkRangesRef,
79 send: &mut F,
80) -> std::result::Result<(), F::Error>
81where
82 D: ReadBytesAt,
83 O: Outboard,
84 F: Sender,
85{
86 traverse_ranges_validated_impl(data, outboard, ranges, send, HashMode::Standard).await
87}
88
89pub async fn keyed_traverse_ranges_validated<D, O, F>(
97 data: D,
98 outboard: O,
99 ranges: &ChunkRangesRef,
100 send: &mut F,
101 key: &[u8; 32],
102) -> std::result::Result<(), F::Error>
103where
104 D: ReadBytesAt,
105 O: Outboard,
106 F: Sender,
107{
108 traverse_ranges_validated_impl(data, outboard, ranges, send, HashMode::Keyed(*key)).await
109}
110
111async fn traverse_ranges_validated_impl<D, O, F>(
112 data: D,
113 outboard: O,
114 ranges: &ChunkRangesRef,
115 send: &mut F,
116 mode: HashMode,
117) -> std::result::Result<(), F::Error>
118where
119 D: ReadBytesAt,
120 O: Outboard,
121 F: Sender,
122{
123 send.send(EncodedItem::Size(outboard.tree().size())).await?;
124 let res: result::Result<std::result::Result<(), F::Error>, EncodeError> = async {
125 if ranges.is_empty() {
126 return Ok(Ok(()));
127 }
128 let mut stack: SmallVec<[_; 10]> = SmallVec::<[blake3::Hash; 10]>::new();
129 stack.push(outboard.root());
130 let data = data;
131 let tree = outboard.tree();
132 let ranges = truncate_ranges(ranges, tree.size());
134 for item in tree.ranges_pre_order_chunks_iter_ref(ranges, 0) {
135 match item {
136 BaoChunk::Parent {
137 is_root,
138 left,
139 right,
140 node,
141 ..
142 } => {
143 let (l_hash, r_hash) = outboard.load(node)?.unwrap();
144 let actual = mode.parent_cv(&l_hash, &r_hash, is_root);
145 let expected = stack.pop().unwrap();
146 if actual != expected {
147 return Err(EncodeError::ParentHashMismatch(node));
148 }
149 if right {
150 stack.push(r_hash);
151 }
152 if left {
153 stack.push(l_hash);
154 }
155 let item = Parent {
156 node,
157 pair: (l_hash, r_hash),
158 };
159 if let Err(e) = send.send(item.into()).await {
160 return Ok(Err(e));
161 }
162 }
163 BaoChunk::Leaf {
164 start_chunk,
165 size,
166 is_root,
167 ranges,
168 ..
169 } => {
170 let expected = stack.pop().unwrap();
171 let start = start_chunk.to_bytes();
172 let buffer = data.read_bytes_at(start, size)?;
173 if !ranges.is_all() {
174 let mut out_buf = Vec::new();
179 let actual = traverse_selected_rec_impl(
180 start_chunk,
181 buffer,
182 is_root,
183 ranges,
184 tree.block_size.to_u32(),
185 true,
186 &mut out_buf,
187 mode,
188 );
189 if actual != expected {
190 return Err(EncodeError::LeafHashMismatch(start_chunk));
191 }
192 for item in out_buf.into_iter() {
193 if let Err(e) = send.send(item).await {
194 return Ok(Err(e));
195 }
196 }
197 } else {
198 let actual = mode.hash_subtree(start_chunk.0, &buffer, is_root);
199 #[allow(clippy::redundant_slicing)]
200 if actual != expected {
201 return Err(EncodeError::LeafHashMismatch(start_chunk));
202 }
203 let item = Leaf {
204 data: buffer,
205 offset: start_chunk.to_bytes(),
206 };
207 if let Err(e) = send.send(item.into()).await {
208 return Ok(Err(e));
209 }
210 };
211 }
212 }
213 }
214 Ok(Ok(()))
215 }
216 .await;
217 let res = match res {
218 Ok(Ok(())) => EncodedItem::Done,
219 Err(cause) => EncodedItem::Error(cause),
220 Ok(Err(err)) => return Err(err),
221 };
222 send.send(res).await
223}
224
225pub fn traverse_selected_rec(
244 start_chunk: ChunkNum,
245 data: Bytes,
246 is_root: bool,
247 query: &ChunkRangesRef,
248 min_level: u32,
249 emit_data: bool,
250 res: &mut Vec<EncodedItem>,
251) -> blake3::Hash {
252 traverse_selected_rec_impl(
253 start_chunk,
254 data,
255 is_root,
256 query,
257 min_level,
258 emit_data,
259 res,
260 HashMode::Standard,
261 )
262}
263
264#[allow(clippy::too_many_arguments)]
266pub fn keyed_traverse_selected_rec(
267 start_chunk: ChunkNum,
268 data: Bytes,
269 is_root: bool,
270 query: &ChunkRangesRef,
271 min_level: u32,
272 emit_data: bool,
273 res: &mut Vec<EncodedItem>,
274 key: &[u8; 32],
275) -> blake3::Hash {
276 traverse_selected_rec_impl(
277 start_chunk,
278 data,
279 is_root,
280 query,
281 min_level,
282 emit_data,
283 res,
284 HashMode::Keyed(*key),
285 )
286}
287
288#[allow(clippy::too_many_arguments)]
289fn traverse_selected_rec_impl(
290 start_chunk: ChunkNum,
291 data: Bytes,
292 is_root: bool,
293 query: &ChunkRangesRef,
294 min_level: u32,
295 emit_data: bool,
296 res: &mut Vec<EncodedItem>,
297 mode: HashMode,
298) -> blake3::Hash {
299 use blake3::CHUNK_LEN;
300 if data.len() <= CHUNK_LEN {
301 if emit_data && !query.is_empty() {
302 res.push(
303 Leaf {
304 data: data.clone(),
305 offset: start_chunk.to_bytes(),
306 }
307 .into(),
308 );
309 }
310 mode.hash_subtree(start_chunk.0, &data, is_root)
311 } else {
312 let chunks = data.len() / CHUNK_LEN + (data.len() % CHUNK_LEN != 0) as usize;
313 let chunks = chunks.next_power_of_two();
314 let level = chunks.trailing_zeros() - 1;
315 let mid = chunks / 2;
316 let mid_bytes = mid * CHUNK_LEN;
317 let mid_chunk = start_chunk + (mid as u64);
318 let (l_ranges, r_ranges) = split_inner(query, start_chunk, mid_chunk);
319 let full = query.is_all();
325 let emit_parent = !query.is_empty() && (!full || level >= min_level);
326 let hash_offset = if emit_parent {
327 let pair = Parent {
329 node: TreeNode(0),
330 pair: ([0; 32].into(), [0; 32].into()),
331 };
332 res.push(pair.into());
333 Some(res.len() - 1)
334 } else {
335 None
336 };
337 let left = traverse_selected_rec_impl(
339 start_chunk,
340 data.slice(..mid_bytes),
341 false,
342 l_ranges,
343 min_level,
344 emit_data,
345 res,
346 mode,
347 );
348 let right = traverse_selected_rec_impl(
349 mid_chunk,
350 data.slice(mid_bytes..),
351 false,
352 r_ranges,
353 min_level,
354 emit_data,
355 res,
356 mode,
357 );
358 if let Some(o) = hash_offset {
360 let node = TreeNode(0);
362 res[o] = Parent {
363 node,
364 pair: (left, right),
365 }
366 .into();
367 }
368 mode.parent_cv(&left, &right, is_root)
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375 use crate::{
376 io::{
377 outboard::PreOrderMemOutboard,
378 sync::{encode_ranges_validated, keyed_encode_ranges_validated},
379 },
380 BlockSize, ChunkRanges,
381 };
382
383 fn flatten(items: Vec<EncodedItem>) -> Vec<u8> {
384 let mut res = Vec::new();
385 for item in items {
386 match item {
387 EncodedItem::Leaf(Leaf { data, .. }) => res.extend_from_slice(&data),
388 EncodedItem::Parent(Parent { pair: (l, r), .. }) => {
389 res.extend_from_slice(l.as_bytes());
390 res.extend_from_slice(r.as_bytes());
391 }
392 _ => {}
393 }
394 }
395 res
396 }
397
398 #[tokio::test]
399 async fn smoke() {
400 let data = [0u8; 100000];
401 let outboard = PreOrderMemOutboard::create(data, BlockSize::from_chunk_log(4));
402 let (mut tx, mut rx) = tokio::sync::mpsc::channel(10);
403 let mut encoded = Vec::new();
404 encode_ranges_validated(&data[..], &outboard, &ChunkRanges::empty(), &mut encoded).unwrap();
405 tokio::spawn(async move {
406 traverse_ranges_validated(&data[..], &outboard, &ChunkRanges::empty(), &mut tx)
407 .await
408 .unwrap();
409 });
410 let mut res = Vec::new();
411 while let Some(item) = rx.recv().await {
412 res.push(item);
413 }
414 println!("{res:?}");
415 let encoded2 = flatten(res);
416 assert_eq!(encoded, encoded2);
417 }
418
419 #[tokio::test]
420 async fn keyed_smoke() {
421 let data = [0u8; 100000];
422 let key = blake3::derive_key("bao-tree.test", b"mixed");
423 let outboard = PreOrderMemOutboard::create_keyed(data, BlockSize::from_chunk_log(4), &key);
424 let (mut tx, mut rx) = tokio::sync::mpsc::channel(10);
425 let mut encoded = Vec::new();
426 keyed_encode_ranges_validated(
427 &data[..],
428 &outboard,
429 &ChunkRanges::all(),
430 &mut encoded,
431 &key,
432 )
433 .unwrap();
434 tokio::spawn(async move {
435 keyed_traverse_ranges_validated(
436 &data[..],
437 &outboard,
438 &ChunkRanges::all(),
439 &mut tx,
440 &key,
441 )
442 .await
443 .unwrap();
444 });
445 let mut res = Vec::new();
446 while let Some(item) = rx.recv().await {
447 res.push(item);
448 }
449 let encoded2 = flatten(res);
450 assert_eq!(encoded, encoded2);
451 }
452}
453
454pub trait ReadBytesAt {
458 fn read_bytes_at(&self, offset: u64, size: usize) -> std::io::Result<Bytes>;
460}
461
462mod impls {
463 use std::io;
464
465 use bytes::Bytes;
466
467 use super::ReadBytesAt;
468
469 macro_rules! impl_read_bytes_at_generic {
471 ($($t:ty),*) => {
472 $(
473 impl ReadBytesAt for $t {
474 fn read_bytes_at(&self, offset: u64, size: usize) -> io::Result<Bytes> {
475 let mut buf = vec![0; size];
476 ::positioned_io::ReadAt::read_exact_at(self, offset, &mut buf)?;
477 Ok(buf.into())
478 }
479 }
480 )*
481 };
482}
483
484 macro_rules! impl_read_bytes_at_special {
486 ($($t:ty),*) => {
487 $(
488 impl ReadBytesAt for $t {
489 fn read_bytes_at(&self, offset: u64, size: usize) -> io::Result<Bytes> {
490 let offset = offset as usize;
491 if offset + size > self.len() {
492 return Err(io::Error::new(
493 io::ErrorKind::UnexpectedEof,
494 "Read past end of buffer",
495 ));
496 }
497 Ok(self.slice(offset..offset + size))
498 }
499 }
500 )*
501 };
502}
503
504 impl_read_bytes_at_generic!(&[u8], Vec<u8>);
506 impl_read_bytes_at_special!(Bytes, &Bytes, &mut Bytes);
507 #[cfg(feature = "fs")]
508 impl_read_bytes_at_generic!(std::fs::File);
509}