1use crate::{
6 index::Unordered as UnorderedIndex,
7 journal::contiguous::{Contiguous, Mutable},
8 merkle::{
9 self, batch::MerkleizedBatch as GenericMerkleizedBatch, mem::Mem,
10 storage::Storage as MerkleStorage, Graftable, Location, Position, Readable,
11 },
12 qmdb::{
13 any::{
14 self,
15 batch::{DiffCursors, DiffEntry, Staged as AnyStaged, StagedUpdates},
16 operation::{update, Operation},
17 ValueEncoding,
18 },
19 batch_chain::Bounds,
20 bitmap::Shared,
21 current::{
22 db::{compute_db_root, read_graft_inputs},
23 grafting,
24 },
25 operation::Key,
26 Error,
27 },
28 Context,
29};
30use ahash::AHashMap;
31use commonware_codec::Codec;
32use commonware_cryptography::{Digest, Hasher};
33use commonware_parallel::Strategy;
34use commonware_utils::bitmap::{self, Readable as _};
35use core::ops::Range;
36use std::sync::Arc;
37
38#[derive(Clone, Debug, Default)]
44pub(crate) struct ChunkOverlay<const N: usize> {
45 pub(crate) chunks: AHashMap<usize, [u8; N]>,
49 pub(crate) len: u64,
51}
52
53impl<const N: usize> ChunkOverlay<N> {
54 const CHUNK_BITS: u64 = bitmap::Prunable::<N>::CHUNK_SIZE_BITS;
55
56 fn new(len: u64, capacity: usize) -> Self {
57 Self {
58 chunks: AHashMap::with_capacity(capacity),
59 len,
60 }
61 }
62
63 fn chunk_mut<B: bitmap::Readable<N>>(&mut self, base: &B, idx: usize) -> &mut [u8; N] {
66 self.chunks.entry(idx).or_insert_with(|| {
67 let base_len = base.len();
68 let base_complete = base.complete_chunks();
69 let base_has_partial = !base_len.is_multiple_of(Self::CHUNK_BITS);
70 if idx < base_complete {
71 base.get_chunk(idx)
72 } else if idx == base_complete && base_has_partial {
73 base.last_chunk().0
74 } else {
75 bitmap::BitMap::<N>::EMPTY_CHUNK
76 }
77 })
78 }
79
80 fn set_bit<B: bitmap::Readable<N>>(&mut self, base: &B, loc: u64) {
82 let idx = bitmap::Prunable::<N>::to_chunk_index(loc);
83 let rel = (loc % Self::CHUNK_BITS) as usize;
84 let chunk = self.chunk_mut(base, idx);
85 chunk[rel / 8] |= 1 << (rel % 8);
86 }
87
88 fn clear_bit<B: bitmap::Readable<N>>(&mut self, base: &B, pruned_chunks: usize, loc: u64) {
92 let idx = bitmap::Prunable::<N>::to_chunk_index(loc);
93 if idx < pruned_chunks {
94 return;
95 }
96 let rel = (loc % Self::CHUNK_BITS) as usize;
97 let chunk = self.chunk_mut(base, idx);
98 chunk[rel / 8] &= !(1 << (rel % 8));
99 }
100
101 pub(crate) fn get(&self, idx: usize) -> Option<&[u8; N]> {
103 self.chunks.get(&idx)
104 }
105
106 pub(crate) const fn complete_chunks(&self) -> usize {
108 (self.len / Self::CHUNK_BITS) as usize
109 }
110}
111
112pub(crate) fn next_candidate<F: Graftable, B: bitmap::Readable<N>, const N: usize>(
126 bitmap: &B,
127 floor: Location<F>,
128 tip: u64,
129) -> Option<Location<F>> {
130 let floor = *floor;
131 let bitmap_len = bitmap.len();
132 let committed_end = bitmap_len.min(tip);
133 if floor < committed_end {
134 if let Some(idx) = bitmap.ones_iter_from(floor).next() {
135 if idx < committed_end {
136 return Some(Location::<F>::new(idx));
137 }
138 }
139 }
140 let candidate = floor.max(bitmap_len);
141 (candidate < tip).then(|| Location::<F>::new(candidate))
142}
143
144pub(crate) fn fill_candidates<F: Graftable, B: bitmap::Readable<N>, const N: usize>(
148 bitmap: &B,
149 floor: Location<F>,
150 tip: u64,
151 limit: usize,
152 out: &mut Vec<Location<F>>,
153) -> Location<F> {
154 let mut scan = floor;
155 while out.len() < limit {
156 let Some(candidate) = next_candidate(bitmap, scan, tip) else {
157 break;
158 };
159 out.push(candidate);
160 scan = Location::<F>::new(*candidate + 1);
161 }
162 scan
163}
164
165struct BatchStorageAdapter<
171 'a,
172 F: Graftable,
173 D: Digest,
174 R: Readable<Family = F, Digest = D, Error = merkle::Error<F>>,
175 S: MerkleStorage<F, Digest = D>,
176> {
177 batch: &'a R,
178 base: &'a S,
179 _phantom: core::marker::PhantomData<(F, D)>,
180}
181
182impl<
183 'a,
184 F: Graftable,
185 D: Digest,
186 R: Readable<Family = F, Digest = D, Error = merkle::Error<F>>,
187 S: MerkleStorage<F, Digest = D>,
188 > BatchStorageAdapter<'a, F, D, R, S>
189{
190 const fn new(batch: &'a R, base: &'a S) -> Self {
191 Self {
192 batch,
193 base,
194 _phantom: core::marker::PhantomData,
195 }
196 }
197}
198
199impl<
200 F: Graftable,
201 D: Digest,
202 R: Readable<Family = F, Digest = D, Error = merkle::Error<F>>,
203 S: MerkleStorage<F, Digest = D>,
204 > MerkleStorage<F> for BatchStorageAdapter<'_, F, D, R, S>
205{
206 type Digest = D;
207
208 fn size(&self) -> Position<F> {
209 self.batch.size()
210 }
211 async fn get_node(&self, pos: Position<F>) -> Result<Option<D>, merkle::Error<F>> {
212 if let Some(node) = self.batch.get_node(pos) {
213 return Ok(Some(node));
214 }
215 self.base.get_node(pos).await
216 }
217}
218
219struct BatchOverMem<'a, F: Graftable, D: Digest, S: Strategy> {
224 batch: &'a GenericMerkleizedBatch<F, D, S>,
225 mem: &'a Mem<F, D>,
226}
227
228impl<F: Graftable, D: Digest, S: Strategy> Readable for BatchOverMem<'_, F, D, S> {
229 type Family = F;
230 type Digest = D;
231 type Error = merkle::Error<F>;
232
233 fn size(&self) -> Position<F> {
234 self.batch.size()
235 }
236
237 fn get_node(&self, pos: Position<F>) -> Option<D> {
238 if let Some(d) = self.batch.get_node(pos) {
239 return Some(d);
240 }
241 self.mem.get_node(pos)
242 }
243
244 fn pruning_boundary(&self) -> Location<F> {
245 self.batch.pruning_boundary()
246 }
247}
248
249pub struct UnmerkleizedBatch<F, H, U, const N: usize, S: Strategy>
255where
256 F: Graftable,
257 U: update::Update + Send + Sync,
258 H: Hasher,
259 Operation<F, U>: Codec,
260{
261 inner: any::batch::UnmerkleizedBatch<F, H, U, S>,
263
264 grafted_parent: Arc<merkle::batch::MerkleizedBatch<F, H::Digest, S>>,
266
267 bitmap_parent: BitmapBatch<N>,
269}
270
271pub struct Staged<F, H, U, const N: usize, S: Strategy>
273where
274 F: Graftable,
275 U: update::Update + Send + Sync,
276 H: Hasher,
277 Operation<F, U>: Codec,
278{
279 inner: AnyStaged<F, H, U, S>,
280 grafted_parent: Arc<merkle::batch::MerkleizedBatch<F, H::Digest, S>>,
281 bitmap_parent: BitmapBatch<N>,
282}
283
284pub struct MerkleizedBatch<
322 F: Graftable,
323 D: Digest,
324 U: update::Update + Send + Sync,
325 const N: usize,
326 S: Strategy,
327> where
328 Operation<F, U>: Send + Sync,
329{
330 pub(crate) inner: Arc<any::batch::MerkleizedBatch<F, D, U, S>>,
332
333 pub(crate) grafted: Arc<merkle::batch::MerkleizedBatch<F, D, S>>,
335
336 pub(crate) bitmap: BitmapBatch<N>,
338
339 pub(crate) canonical_root: D,
341}
342
343impl<F, H, U, const N: usize, S: Strategy> UnmerkleizedBatch<F, H, U, N, S>
344where
345 F: Graftable,
346 U: update::Update + Send + Sync,
347 H: Hasher,
348 Operation<F, U>: Codec,
349{
350 pub(super) const fn new(
351 inner: any::batch::UnmerkleizedBatch<F, H, U, S>,
352 grafted_parent: Arc<merkle::batch::MerkleizedBatch<F, H::Digest, S>>,
353 bitmap_parent: BitmapBatch<N>,
354 ) -> Self {
355 Self {
356 inner,
357 grafted_parent,
358 bitmap_parent,
359 }
360 }
361
362 pub fn write(mut self, key: U::Key, value: Option<U::Value>) -> Self {
367 self.inner = self.inner.write(key, value);
368 self
369 }
370
371 pub async fn get<E, C, I>(
373 &self,
374 key: &U::Key,
375 db: &super::db::Db<F, E, C, I, H, U, N, S>,
376 ) -> Result<Option<U::Value>, Error<F>>
377 where
378 E: Context,
379 C: Contiguous<Item = Operation<F, U>>,
380 I: UnorderedIndex<Value = Location<F>> + 'static,
381 {
382 self.inner.get(key, &db.any).await
383 }
384
385 pub async fn get_many<E, C, I>(
389 &self,
390 keys: &[&U::Key],
391 db: &super::db::Db<F, E, C, I, H, U, N, S>,
392 ) -> Result<Vec<Option<U::Value>>, Error<F>>
393 where
394 E: Context,
395 C: Contiguous<Item = Operation<F, U>>,
396 I: UnorderedIndex<Value = Location<F>> + 'static,
397 {
398 self.inner.get_many(keys, &db.any).await
399 }
400
401 pub async fn stage<E, C, I>(
407 self,
408 keys: &[&U::Key],
409 db: &super::db::Db<F, E, C, I, H, U, N, S>,
410 ) -> Result<(Vec<Option<U::Value>>, Staged<F, H, U, N, S>), Error<F>>
411 where
412 E: Context,
413 C: Contiguous<Item = Operation<F, U>>,
414 I: UnorderedIndex<Value = Location<F>> + 'static,
415 {
416 let Self {
417 inner,
418 grafted_parent,
419 bitmap_parent,
420 } = self;
421 let (values, inner) = inner.stage(keys, &db.any).await?;
422 Ok((
423 values,
424 Staged {
425 inner,
426 grafted_parent,
427 bitmap_parent,
428 },
429 ))
430 }
431}
432
433impl<F, H, U, const N: usize, S: Strategy> Staged<F, H, U, N, S>
434where
435 F: Graftable,
436 U: update::Update + Send + Sync,
437 H: Hasher,
438 Operation<F, U>: Codec,
439{
440 pub async fn expand<E, C, I>(
449 self,
450 keys: &[&U::Key],
451 db: &super::db::Db<F, E, C, I, H, U, N, S>,
452 ) -> Result<(Range<usize>, Vec<Option<U::Value>>, Self), Error<F>>
453 where
454 E: Context,
455 C: Contiguous<Item = Operation<F, U>>,
456 I: UnorderedIndex<Value = Location<F>> + 'static,
457 {
458 let Self {
459 inner,
460 grafted_parent,
461 bitmap_parent,
462 } = self;
463 let (range, values, inner) = inner.expand(keys, &db.any).await?;
464 Ok((
465 range,
466 values,
467 Self {
468 inner,
469 grafted_parent,
470 bitmap_parent,
471 },
472 ))
473 }
474}
475
476impl<F, K, V, H, const N: usize, S: Strategy> Staged<F, H, update::Unordered<K, V>, N, S>
477where
478 F: Graftable,
479 K: Key,
480 V: ValueEncoding,
481 H: Hasher,
482 Operation<F, update::Unordered<K, V>>: Codec,
483{
484 #[allow(clippy::type_complexity)]
497 #[tracing::instrument(
498 name = "qmdb.current.unordered.batch.merkleize.staged",
499 level = "info",
500 skip_all,
501 fields(updates = updates.len() as u64, upserts = upserts.len() as u64),
502 )]
503 pub async fn merkleize<E, C, I>(
504 self,
505 updates: Vec<(usize, Option<V::Value>)>,
506 upserts: Vec<(K, Option<V::Value>)>,
507 metadata: Option<V::Value>,
508 db: &super::db::Db<F, E, C, I, H, update::Unordered<K, V>, N, S>,
509 ) -> Result<Arc<MerkleizedBatch<F, H::Digest, update::Unordered<K, V>, N, S>>, Error<F>>
510 where
511 E: Context,
512 C: Mutable<Item = Operation<F, update::Unordered<K, V>>>,
513 I: UnorderedIndex<Value = Location<F>> + 'static,
514 {
515 let Self {
516 inner,
517 grafted_parent,
518 bitmap_parent,
519 } = self;
520
521 let (inner, staged_updates, prefetched) = inner
525 .resolve_updates_prefetched(updates, upserts, &db.any, |floor, tip, limit, out| {
526 fill_candidates(&bitmap_parent, floor, tip, limit, out)
527 })
528 .await?;
529 let inner = inner
530 .merkleize_with_floor_scan(
531 &db.any,
532 metadata,
533 staged_updates,
534 Some(prefetched),
535 |floor, tip, limit, out| fill_candidates(&bitmap_parent, floor, tip, limit, out),
536 )
537 .await?;
538 compute_current_layer(inner, db, &grafted_parent, &bitmap_parent).await
539 }
540}
541
542impl<F, K, V, H, const N: usize, S: Strategy> Staged<F, H, update::Ordered<K, V>, N, S>
543where
544 F: Graftable,
545 K: Key,
546 V: ValueEncoding,
547 H: Hasher,
548 Operation<F, update::Ordered<K, V>>: Codec,
549{
550 #[allow(clippy::type_complexity)]
563 #[tracing::instrument(
564 name = "qmdb.current.ordered.batch.merkleize.staged",
565 level = "info",
566 skip_all,
567 fields(updates = updates.len() as u64, upserts = upserts.len() as u64),
568 )]
569 pub async fn merkleize<E, C, I>(
570 self,
571 updates: Vec<(usize, Option<V::Value>)>,
572 upserts: Vec<(K, Option<V::Value>)>,
573 metadata: Option<V::Value>,
574 db: &super::db::Db<F, E, C, I, H, update::Ordered<K, V>, N, S>,
575 ) -> Result<Arc<MerkleizedBatch<F, H::Digest, update::Ordered<K, V>, N, S>>, Error<F>>
576 where
577 E: Context,
578 C: Mutable<Item = Operation<F, update::Ordered<K, V>>>,
579 I: crate::index::Ordered<Value = Location<F>> + 'static,
580 {
581 let Self {
582 inner,
583 grafted_parent,
584 bitmap_parent,
585 } = self;
586 let (inner, staged_updates) = inner.resolve_updates(updates, upserts, db.any.strategy());
587 let inner = inner
588 .merkleize_with_floor_scan(
589 &db.any,
590 metadata,
591 staged_updates,
592 |floor, tip, limit, out| fill_candidates(&bitmap_parent, floor, tip, limit, out),
593 )
594 .await?;
595 compute_current_layer(inner, db, &grafted_parent, &bitmap_parent).await
596 }
597}
598
599impl<F, K, V, H, const N: usize, S: Strategy> UnmerkleizedBatch<F, H, update::Unordered<K, V>, N, S>
601where
602 F: Graftable,
603 K: Key,
604 V: ValueEncoding,
605 H: Hasher,
606 Operation<F, update::Unordered<K, V>>: Codec,
607{
608 #[allow(clippy::type_complexity)]
610 #[tracing::instrument(
611 name = "qmdb.current.unordered.batch.merkleize",
612 level = "info",
613 skip_all
614 )]
615 pub async fn merkleize<E, C, I>(
616 self,
617 db: &super::db::Db<F, E, C, I, H, update::Unordered<K, V>, N, S>,
618 metadata: Option<V::Value>,
619 ) -> Result<Arc<MerkleizedBatch<F, H::Digest, update::Unordered<K, V>, N, S>>, Error<F>>
620 where
621 E: Context,
622 C: Mutable<Item = Operation<F, update::Unordered<K, V>>>,
623 I: UnorderedIndex<Value = Location<F>> + 'static,
624 {
625 let Self {
626 inner,
627 grafted_parent,
628 bitmap_parent,
629 } = self;
630 let inner = inner
632 .merkleize_with_floor_scan(
633 &db.any,
634 metadata,
635 StagedUpdates::<F, update::Unordered<K, V>>::new(),
636 None,
637 |floor, tip, limit, out| fill_candidates(&bitmap_parent, floor, tip, limit, out),
638 )
639 .await?;
640 compute_current_layer(inner, db, &grafted_parent, &bitmap_parent).await
641 }
642}
643
644impl<F, K, V, H, const N: usize, S: Strategy> UnmerkleizedBatch<F, H, update::Ordered<K, V>, N, S>
646where
647 F: Graftable,
648 K: Key,
649 V: ValueEncoding,
650 H: Hasher,
651 Operation<F, update::Ordered<K, V>>: Codec,
652{
653 #[allow(clippy::type_complexity)]
655 #[tracing::instrument(
656 name = "qmdb.current.ordered.batch.merkleize",
657 level = "info",
658 skip_all
659 )]
660 pub async fn merkleize<E, C, I>(
661 self,
662 db: &super::db::Db<F, E, C, I, H, update::Ordered<K, V>, N, S>,
663 metadata: Option<V::Value>,
664 ) -> Result<Arc<MerkleizedBatch<F, H::Digest, update::Ordered<K, V>, N, S>>, Error<F>>
665 where
666 E: Context,
667 C: Mutable<Item = Operation<F, update::Ordered<K, V>>>,
668 I: crate::index::Ordered<Value = Location<F>> + 'static,
669 {
670 let Self {
671 inner,
672 grafted_parent,
673 bitmap_parent,
674 } = self;
675 let inner = inner
677 .merkleize_with_floor_scan(
678 &db.any,
679 metadata,
680 StagedUpdates::<F, update::Ordered<K, V>>::new(),
681 |floor, tip, limit, out| fill_candidates(&bitmap_parent, floor, tip, limit, out),
682 )
683 .await?;
684 compute_current_layer(inner, db, &grafted_parent, &bitmap_parent).await
685 }
686}
687
688#[allow(clippy::type_complexity)]
698fn build_chunk_overlay<F: Graftable, U, B: bitmap::Readable<N>, const N: usize>(
699 base: &B,
700 batch_len: usize,
701 batch_base: u64,
702 diff: &[(U::Key, DiffEntry<F, U::Value>)],
703 ancestor_diffs: &[Arc<Vec<(U::Key, DiffEntry<F, U::Value>)>>],
704) -> ChunkOverlay<N>
705where
706 U: update::Update,
707{
708 let total_bits = base.len() + batch_len as u64;
709 let appended_chunks = (batch_len as u64).div_ceil(ChunkOverlay::<N>::CHUNK_BITS) as usize;
710 let mut overlay = ChunkOverlay::new(total_bits, diff.len() + appended_chunks + 1);
711 let pruned_chunks = base.pruned_chunks();
712
713 let commit_loc = batch_base + batch_len as u64 - 1;
715 overlay.set_bit(base, commit_loc);
716
717 overlay.clear_bit(base, pruned_chunks, batch_base - 1);
719
720 let mut ancestors = DiffCursors::new(ancestor_diffs.iter().map(|d| d.as_slice()));
723 for (key, entry) in diff {
724 if let Some(loc) = entry.loc() {
726 if *loc >= batch_base && *loc < batch_base + batch_len as u64 {
727 overlay.set_bit(base, *loc);
728 }
729 }
730
731 let mut prev_loc = entry.base_old_loc();
734 if let Some(ancestor_entry) = ancestors.resolve(key) {
735 prev_loc = ancestor_entry.loc();
736 }
737 if let Some(old) = prev_loc {
738 overlay.clear_bit(base, pruned_chunks, *old);
739 }
740 }
741
742 let parent_complete = base.complete_chunks();
746 let new_complete = overlay.complete_chunks();
747 for idx in parent_complete..new_complete {
748 overlay.chunk_mut(base, idx);
749 }
750
751 overlay
752}
753
754async fn compute_current_layer<F, E, U, C, I, H, const N: usize, S>(
760 inner: Arc<any::batch::MerkleizedBatch<F, H::Digest, U, S>>,
761 current_db: &super::db::Db<F, E, C, I, H, U, N, S>,
762 grafted_parent: &Arc<merkle::batch::MerkleizedBatch<F, H::Digest, S>>,
763 bitmap_parent: &BitmapBatch<N>,
764) -> Result<Arc<MerkleizedBatch<F, H::Digest, U, N, S>>, Error<F>>
765where
766 F: Graftable,
767 E: Context,
768 U: update::Update + Send + Sync,
769 C: Contiguous<Item = Operation<F, U>>,
770 I: UnorderedIndex<Value = Location<F>>,
771 H: Hasher,
772 S: Strategy,
773 Operation<F, U>: Codec,
774{
775 let batch_len = inner.journal_batch.items().len();
776 let batch_base = inner.bounds.total_size - batch_len as u64;
777
778 let overlay = build_chunk_overlay::<F, U, _, N>(
780 bitmap_parent,
781 batch_len,
782 batch_base,
783 &inner.diff,
784 &inner.ancestor_diffs,
785 );
786
787 let grafting_height = grafting::height::<N>();
788 let ops_tree_adapter =
789 BatchStorageAdapter::new(&inner.journal_batch, ¤t_db.any.log.merkle);
790
791 let overlay_ops_leaves = Location::<F>::new(inner.bounds.total_size);
794
795 let new_complete_chunks = overlay.complete_chunks();
803 let graftable_overlay = grafting::graftable_chunks::<F>(*overlay_ops_leaves, grafting_height)
804 .min(new_complete_chunks as u64) as usize;
805 let graftable_parent = *grafted_parent.leaves() as usize;
806 let pruned_chunks = bitmap_parent.pruned_chunks();
807 assert!(
808 pruned_chunks <= graftable_parent && graftable_parent <= graftable_overlay && graftable_overlay <= new_complete_chunks,
809 "invariant violated: pruned={pruned_chunks} graftable_parent={graftable_parent} graftable_overlay={graftable_overlay} new_complete={new_complete_chunks}"
810 );
811
812 let mut chunk_indices_to_update: Vec<usize> = overlay
818 .chunks
819 .iter()
820 .filter(|(&idx, _)| idx < graftable_overlay && idx >= pruned_chunks)
821 .map(|(&idx, _)| idx)
822 .collect();
823 chunk_indices_to_update.extend(graftable_parent..graftable_overlay);
824 chunk_indices_to_update.sort_unstable();
825 chunk_indices_to_update.dedup();
826 let chunks_to_update = chunk_indices_to_update.into_iter().map(|idx| {
827 let chunk = overlay
828 .get(idx)
829 .copied()
830 .unwrap_or_else(|| bitmap_parent.get_chunk(idx));
831 (idx, chunk)
832 });
833
834 let graft_inputs = read_graft_inputs::<F, _, N>(&ops_tree_adapter, chunks_to_update).await?;
839 let grafted_hasher = grafting::hasher::<F, H>(grafting_height);
840 let grafted_batch = if graft_inputs.is_empty() {
841 grafted_parent
842 .new_batch()
843 .merkleize(¤t_db.grafted_tree, &grafted_hasher)
844 } else {
845 let grafted_parent = Arc::clone(grafted_parent);
846 let grafted_tree = Arc::clone(¤t_db.grafted_tree);
847 current_db
848 .strategy
849 .clone()
850 .spawn(move |strategy| {
851 let new_leaves = grafting::graft_chunk_digests::<H, _, N>(&strategy, graft_inputs);
852 let mut grafted_batch = grafted_parent.new_batch();
853 let old_grafted_leaves = *grafted_parent.leaves() as usize;
854 for (chunk_idx, digest) in new_leaves {
855 if chunk_idx < old_grafted_leaves {
856 grafted_batch = grafted_batch
857 .update_leaf_digest(Location::<F>::new(chunk_idx as u64), digest)
858 .expect("update_leaf_digest failed");
859 } else {
860 grafted_batch = grafted_batch.add_leaf_digest(digest);
861 }
862 }
863 grafted_batch.merkleize(&grafted_tree, &grafted_hasher)
864 })
865 .await
866 };
867
868 let bitmap_batch = BitmapBatch::Layer(Arc::new(BitmapBatchLayer {
872 parent: bitmap_parent.clone(),
873 overlay: Arc::new(overlay),
874 shared: Arc::clone(bitmap_parent.shared()),
875 }));
876
877 let ops_root = inner.root();
880 let layered = BatchOverMem {
881 batch: &grafted_batch,
882 mem: ¤t_db.grafted_tree,
883 };
884 let grafted_storage =
885 grafting::Storage::<F, H, _, _>::new(&layered, grafting_height, &ops_tree_adapter);
886 let partial = {
892 let rem = bitmap_batch.len() % BitmapBatch::<N>::CHUNK_SIZE_BITS;
893 if rem == 0 {
894 None
895 } else {
896 let idx = new_complete_chunks;
897 let chunk = bitmap_batch.get_chunk(idx);
898 Some((chunk, rem))
899 }
900 };
901 let canonical_root = compute_db_root::<F, H, _, _, N>(
902 &bitmap_batch,
903 &grafted_storage,
904 overlay_ops_leaves,
905 partial,
906 inner.bounds.inactivity_floor,
907 &ops_root,
908 )
909 .await?;
910
911 Ok(Arc::new(MerkleizedBatch {
912 inner,
913 grafted: grafted_batch,
914 bitmap: bitmap_batch,
915 canonical_root,
916 }))
917}
918
919#[derive(Clone, Debug)]
925pub(crate) enum BitmapBatch<const N: usize> {
926 Base(Arc<Shared<N>>),
928 Layer(Arc<BitmapBatchLayer<N>>),
930}
931
932#[derive(Debug)]
934pub(crate) struct BitmapBatchLayer<const N: usize> {
935 pub(crate) parent: BitmapBatch<N>,
936 pub(crate) overlay: Arc<ChunkOverlay<N>>,
938 pub(crate) shared: Arc<Shared<N>>,
941}
942
943impl<const N: usize> BitmapBatch<N> {
944 const CHUNK_SIZE_BITS: u64 = bitmap::Prunable::<N>::CHUNK_SIZE_BITS;
945
946 fn shared(&self) -> &Arc<Shared<N>> {
948 match self {
949 Self::Base(s) => s,
950 Self::Layer(layer) => &layer.shared,
951 }
952 }
953
954 fn trim_committed(&self) -> Self {
958 let shared = self.shared();
959 let committed = bitmap::Readable::<N>::len(shared.as_ref());
960 let mut kept = Vec::new();
961 let mut current = self;
962 while let Self::Layer(layer) = current {
963 if layer.overlay.len <= committed {
964 break;
965 }
966 kept.push(Arc::clone(&layer.overlay));
967 current = &layer.parent;
968 }
969 let mut result = Self::Base(Arc::clone(shared));
970 for overlay in kept.into_iter().rev() {
971 result = Self::Layer(Arc::new(BitmapBatchLayer {
972 parent: result,
973 overlay,
974 shared: Arc::clone(shared),
975 }));
976 }
977 result
978 }
979}
980
981impl<const N: usize> bitmap::Readable<N> for BitmapBatch<N> {
982 fn complete_chunks(&self) -> usize {
983 (self.len() / Self::CHUNK_SIZE_BITS) as usize
984 }
985
986 fn get_chunk(&self, idx: usize) -> [u8; N] {
987 let mut current = self;
990 loop {
991 match current {
992 Self::Base(shared) => return shared.get_chunk(idx),
993 Self::Layer(layer) => {
994 if let Some(&chunk) = layer.overlay.get(idx) {
995 return chunk;
996 }
997 current = &layer.parent;
998 }
999 }
1000 }
1001 }
1002
1003 fn last_chunk(&self) -> ([u8; N], u64) {
1004 let total = self.len();
1005 if total == 0 {
1006 return (bitmap::BitMap::<N>::EMPTY_CHUNK, 0);
1007 }
1008 let rem = total % Self::CHUNK_SIZE_BITS;
1009 let bits_in_last = if rem == 0 { Self::CHUNK_SIZE_BITS } else { rem };
1010 let idx = if rem == 0 {
1011 self.complete_chunks().saturating_sub(1)
1012 } else {
1013 self.complete_chunks()
1014 };
1015 (self.get_chunk(idx), bits_in_last)
1016 }
1017
1018 fn pruned_chunks(&self) -> usize {
1019 self.shared().pruned_chunks()
1020 }
1021
1022 fn len(&self) -> u64 {
1023 match self {
1024 Self::Base(shared) => bitmap::Readable::<N>::len(shared.as_ref()),
1025 Self::Layer(layer) => layer.overlay.len,
1026 }
1027 }
1028}
1029
1030impl<F: Graftable, D: Digest, U: update::Update + Send + Sync, const N: usize, S: Strategy>
1031 MerkleizedBatch<F, D, U, N, S>
1032where
1033 Operation<F, U>: Send + Sync,
1034{
1035 pub const fn root(&self) -> D {
1037 self.canonical_root
1038 }
1039
1040 pub fn ops_root(&self) -> D {
1042 self.inner.root()
1043 }
1044
1045 pub fn bounds(&self) -> &Bounds<F> {
1047 self.inner.bounds()
1048 }
1049
1050 pub fn sync_boundary(&self) -> Location<F> {
1054 super::db::sync_boundary::<F, N>(
1058 *self.inner.bounds().inactivity_floor / bitmap::Prunable::<N>::CHUNK_SIZE_BITS,
1059 self.inner.bounds().total_size,
1060 )
1061 }
1062}
1063
1064impl<F: Graftable, D: Digest, U: update::Update + Send + Sync, const N: usize, S: Strategy>
1065 MerkleizedBatch<F, D, U, N, S>
1066where
1067 Operation<F, U>: Codec,
1068{
1069 pub fn new_batch<H>(self: &Arc<Self>) -> UnmerkleizedBatch<F, H, U, N, S>
1079 where
1080 H: Hasher<Digest = D>,
1081 {
1082 UnmerkleizedBatch::new(
1083 self.inner.new_batch::<H>(),
1084 Arc::clone(&self.grafted),
1085 self.bitmap.trim_committed(),
1086 )
1087 }
1088
1089 pub async fn get<E, C, I, H>(
1094 &self,
1095 key: &U::Key,
1096 db: &super::db::Db<F, E, C, I, H, U, N, S>,
1097 ) -> Result<Option<U::Value>, Error<F>>
1098 where
1099 E: Context,
1100 C: Contiguous<Item = Operation<F, U>>,
1101 I: UnorderedIndex<Value = Location<F>> + 'static,
1102 H: Hasher<Digest = D>,
1103 {
1104 self.inner.get(key, &db.any).await
1105 }
1106
1107 pub async fn get_many<E, C, I, H>(
1111 &self,
1112 keys: &[&U::Key],
1113 db: &super::db::Db<F, E, C, I, H, U, N, S>,
1114 ) -> Result<Vec<Option<U::Value>>, Error<F>>
1115 where
1116 E: Context,
1117 C: Contiguous<Item = Operation<F, U>>,
1118 I: UnorderedIndex<Value = Location<F>> + 'static,
1119 H: Hasher<Digest = D>,
1120 {
1121 self.inner.get_many(keys, &db.any).await
1122 }
1123}
1124
1125impl<F, E, C, I, H, U, const N: usize, S> super::db::Db<F, E, C, I, H, U, N, S>
1126where
1127 F: Graftable,
1128 E: Context,
1129 U: update::Update + Send + Sync,
1130 C: Contiguous<Item = Operation<F, U>>,
1131 I: UnorderedIndex<Value = Location<F>>,
1132 H: Hasher,
1133 S: Strategy,
1134 Operation<F, U>: Codec,
1135{
1136 pub fn to_batch(&self) -> Arc<MerkleizedBatch<F, H::Digest, U, N, S>> {
1142 let grafted = self.grafted_snapshot();
1143 Arc::new(MerkleizedBatch {
1144 inner: self.any.to_batch(),
1145 grafted,
1146 bitmap: BitmapBatch::Base(Arc::clone(&self.any.bitmap)),
1147 canonical_root: self.root,
1148 })
1149 }
1150}
1151
1152#[cfg(any(test, feature = "test-traits"))]
1153mod trait_impls {
1154 use super::*;
1155 use crate::{
1156 journal::contiguous::Mutable,
1157 qmdb::any::traits::{
1158 BatchableDb, MerkleizedBatch as MerkleizedBatchTrait,
1159 UnmerkleizedBatch as UnmerkleizedBatchTrait,
1160 },
1161 };
1162 use std::future::Future;
1163
1164 type CurrentDb<F, E, C, I, H, U, const N: usize, S> =
1165 crate::qmdb::current::db::Db<F, E, C, I, H, U, N, S>;
1166
1167 impl<F, K, V, H, E, C, I, const N: usize, S>
1168 UnmerkleizedBatchTrait<CurrentDb<F, E, C, I, H, update::Unordered<K, V>, N, S>>
1169 for UnmerkleizedBatch<F, H, update::Unordered<K, V>, N, S>
1170 where
1171 F: Graftable,
1172 K: Key,
1173 V: ValueEncoding + 'static,
1174 H: Hasher,
1175 E: Context,
1176 C: Mutable<Item = Operation<F, update::Unordered<K, V>>>,
1177 I: UnorderedIndex<Value = Location<F>> + 'static,
1178 S: Strategy,
1179 Operation<F, update::Unordered<K, V>>: Codec,
1180 {
1181 type Family = F;
1182 type K = K;
1183 type V = V::Value;
1184 type Metadata = V::Value;
1185 type Merkleized = Arc<MerkleizedBatch<F, H::Digest, update::Unordered<K, V>, N, S>>;
1186
1187 fn write(self, key: K, value: Option<V::Value>) -> Self {
1188 Self::write(self, key, value)
1189 }
1190
1191 async fn merkleize(
1192 self,
1193 db: &CurrentDb<F, E, C, I, H, update::Unordered<K, V>, N, S>,
1194 metadata: Option<V::Value>,
1195 ) -> Result<Self::Merkleized, crate::qmdb::Error<F>> {
1196 self.merkleize(db, metadata).await
1197 }
1198 }
1199
1200 impl<F, K, V, H, E, C, I, const N: usize, S>
1201 UnmerkleizedBatchTrait<CurrentDb<F, E, C, I, H, update::Ordered<K, V>, N, S>>
1202 for UnmerkleizedBatch<F, H, update::Ordered<K, V>, N, S>
1203 where
1204 F: Graftable,
1205 K: Key,
1206 V: ValueEncoding + 'static,
1207 H: Hasher,
1208 E: Context,
1209 C: Mutable<Item = Operation<F, update::Ordered<K, V>>>,
1210 I: crate::index::Ordered<Value = Location<F>> + 'static,
1211 S: Strategy,
1212 Operation<F, update::Ordered<K, V>>: Codec,
1213 {
1214 type Family = F;
1215 type K = K;
1216 type V = V::Value;
1217 type Metadata = V::Value;
1218 type Merkleized = Arc<MerkleizedBatch<F, H::Digest, update::Ordered<K, V>, N, S>>;
1219
1220 fn write(self, key: K, value: Option<V::Value>) -> Self {
1221 Self::write(self, key, value)
1222 }
1223
1224 async fn merkleize(
1225 self,
1226 db: &CurrentDb<F, E, C, I, H, update::Ordered<K, V>, N, S>,
1227 metadata: Option<V::Value>,
1228 ) -> Result<Self::Merkleized, crate::qmdb::Error<F>> {
1229 self.merkleize(db, metadata).await
1230 }
1231 }
1232
1233 impl<
1234 F: Graftable,
1235 D: Digest,
1236 U: update::Update + Send + Sync + 'static,
1237 const N: usize,
1238 S: Strategy,
1239 > MerkleizedBatchTrait for Arc<MerkleizedBatch<F, D, U, N, S>>
1240 where
1241 Operation<F, U>: Codec,
1242 {
1243 type Digest = D;
1244
1245 fn root(&self) -> D {
1246 MerkleizedBatch::root(self)
1247 }
1248 }
1249
1250 impl<F, E, K, V, C, I, H, const N: usize, S> BatchableDb
1251 for CurrentDb<F, E, C, I, H, update::Unordered<K, V>, N, S>
1252 where
1253 F: Graftable,
1254 E: Context,
1255 K: Key,
1256 V: ValueEncoding + 'static,
1257 C: Mutable<Item = Operation<F, update::Unordered<K, V>>>,
1258 I: UnorderedIndex<Value = Location<F>> + 'static,
1259 H: Hasher,
1260 S: Strategy,
1261 Operation<F, update::Unordered<K, V>>: Codec,
1262 {
1263 type Family = F;
1264 type K = K;
1265 type V = V::Value;
1266 type Merkleized = Arc<MerkleizedBatch<F, H::Digest, update::Unordered<K, V>, N, S>>;
1267 type Batch = UnmerkleizedBatch<F, H, update::Unordered<K, V>, N, S>;
1268
1269 fn new_batch(&self) -> Self::Batch {
1270 self.new_batch()
1271 }
1272
1273 fn apply_batch(
1274 &mut self,
1275 batch: Self::Merkleized,
1276 ) -> impl Future<Output = Result<core::ops::Range<Location<F>>, crate::qmdb::Error<F>>>
1277 {
1278 self.apply_batch(batch)
1279 }
1280 }
1281
1282 impl<F, E, K, V, C, I, H, const N: usize, S> BatchableDb
1283 for CurrentDb<F, E, C, I, H, update::Ordered<K, V>, N, S>
1284 where
1285 F: Graftable,
1286 E: Context,
1287 K: Key,
1288 V: ValueEncoding + 'static,
1289 C: Mutable<Item = Operation<F, update::Ordered<K, V>>>,
1290 I: crate::index::Ordered<Value = Location<F>> + 'static,
1291 H: Hasher,
1292 S: Strategy,
1293 Operation<F, update::Ordered<K, V>>: Codec,
1294 {
1295 type Family = F;
1296 type K = K;
1297 type V = V::Value;
1298 type Merkleized = Arc<MerkleizedBatch<F, H::Digest, update::Ordered<K, V>, N, S>>;
1299 type Batch = UnmerkleizedBatch<F, H, update::Ordered<K, V>, N, S>;
1300
1301 fn new_batch(&self) -> Self::Batch {
1302 self.new_batch()
1303 }
1304
1305 fn apply_batch(
1306 &mut self,
1307 batch: Self::Merkleized,
1308 ) -> impl Future<Output = Result<core::ops::Range<Location<F>>, crate::qmdb::Error<F>>>
1309 {
1310 self.apply_batch(batch)
1311 }
1312 }
1313}
1314
1315#[cfg(test)]
1316mod tests {
1317 use super::*;
1318 use crate::mmr;
1319 use commonware_utils::bitmap::Prunable as BitMap;
1320
1321 const N: usize = 4;
1323 type Bm = BitMap<N>;
1324 type Location = mmr::Location;
1325
1326 fn make_bitmap(bits: &[bool]) -> Bm {
1327 let mut bm = Bm::new();
1328 for &b in bits {
1329 bm.push(b);
1330 }
1331 bm
1332 }
1333
1334 #[test]
1337 fn chunk_overlay_pushes() {
1338 use crate::qmdb::any::value::FixedEncoding;
1339 use commonware_utils::sequence::FixedBytes;
1340
1341 type K = FixedBytes<4>;
1342 type V = FixedEncoding<u64>;
1343 type U = crate::qmdb::any::operation::update::Unordered<K, V>;
1344
1345 let key1 = FixedBytes::from([1, 0, 0, 0]);
1346 let key2 = FixedBytes::from([2, 0, 0, 0]);
1347
1348 let base = make_bitmap(&[true; 4]);
1353 let mut diff = vec![
1354 (
1355 key1,
1356 DiffEntry::Active {
1357 value: 100u64,
1358 loc: Location::new(4), base_old_loc: None,
1360 },
1361 ),
1362 (
1363 key2,
1364 DiffEntry::Active {
1365 value: 200u64,
1366 loc: Location::new(99), base_old_loc: None,
1368 },
1369 ),
1370 ];
1371 diff.sort_by(|a, b| a.0.cmp(&b.0));
1372
1373 let overlay = build_chunk_overlay::<mmr::Family, U, _, N>(&base, 4, 4, &diff, &[]);
1374
1375 let c0 = overlay.get(0).expect("chunk 0 should be dirty");
1378 assert_ne!(c0[0] & (1 << 4), 0); assert_eq!(c0[0] & (1 << 5), 0); assert_eq!(c0[0] & (1 << 6), 0); assert_ne!(c0[0] & (1 << 7), 0); assert_eq!(c0[0] & (1 << 3), 0); }
1384
1385 #[test]
1386 fn chunk_overlay_clears() {
1387 use crate::qmdb::any::value::FixedEncoding;
1388 use commonware_utils::sequence::FixedBytes;
1389
1390 type K = FixedBytes<4>;
1391 type U = crate::qmdb::any::operation::update::Unordered<K, FixedEncoding<u64>>;
1392
1393 let key1 = FixedBytes::from([1, 0, 0, 0]);
1394 let key2 = FixedBytes::from([2, 0, 0, 0]);
1395 let key3 = FixedBytes::from([3, 0, 0, 0]);
1396
1397 let base = make_bitmap(&[true; 64]);
1399
1400 let mut diff: Vec<(K, DiffEntry<mmr::Family, u64>)> = vec![
1401 (
1402 key1,
1403 DiffEntry::Active {
1404 value: 100,
1405 loc: Location::new(70),
1406 base_old_loc: Some(Location::new(5)),
1407 },
1408 ),
1409 (
1410 key2,
1411 DiffEntry::Deleted {
1412 base_old_loc: Some(Location::new(10)),
1413 },
1414 ),
1415 (
1416 key3,
1417 DiffEntry::Active {
1418 value: 300,
1419 loc: Location::new(71),
1420 base_old_loc: None,
1421 },
1422 ),
1423 ];
1424 diff.sort_by(|a, b| a.0.cmp(&b.0));
1425
1426 let overlay = build_chunk_overlay::<mmr::Family, U, _, N>(&base, 8, 64, &diff, &[]);
1428
1429 let c0 = overlay.get(0).expect("chunk 0 should be dirty");
1431 assert_eq!(c0[0] & (1 << 5), 0); assert_eq!(c0[1] & (1 << 2), 0); assert_eq!(c0[0] & (1 << 4), 1 << 4); assert_eq!(c0[1] & (1 << 3), 1 << 3); }
1438
1439 #[test]
1443 fn chunk_overlay_preserves_partial_parent_chunk() {
1444 use crate::qmdb::any::value::FixedEncoding;
1445 use commonware_utils::sequence::FixedBytes;
1446
1447 type K = FixedBytes<4>;
1448 type U = crate::qmdb::any::operation::update::Unordered<K, FixedEncoding<u64>>;
1449
1450 let base = make_bitmap(&[true; 20]);
1452 assert_eq!(base.complete_chunks(), 0); let key1 = FixedBytes::from([1, 0, 0, 0]);
1458 let mut diff = vec![(
1459 key1,
1460 DiffEntry::Active {
1461 value: 42u64,
1462 loc: Location::new(35),
1463 base_old_loc: None,
1464 },
1465 )];
1466 diff.sort_by(|a, b| a.0.cmp(&b.0));
1467
1468 let overlay = build_chunk_overlay::<mmr::Family, U, _, N>(&base, 20, 20, &diff, &[]);
1469
1470 let c0 = overlay.get(0).expect("chunk 0 should be in overlay");
1472 assert_eq!(c0[0], 0xFF);
1474 assert_eq!(c0[1], 0xFF);
1476 assert_eq!(c0[2], 0x07);
1478 }
1479
1480 #[test]
1483 fn bitmap_scan_all_active() {
1484 let bm = make_bitmap(&[true; 8]);
1485 for i in 0..8 {
1486 assert_eq!(
1487 next_candidate(&bm, Location::new(i), 8),
1488 Some(Location::new(i))
1489 );
1490 }
1491 assert_eq!(next_candidate(&bm, Location::new(8), 8), None);
1492 }
1493
1494 #[test]
1495 fn bitmap_scan_all_inactive() {
1496 let bm = make_bitmap(&[false; 8]);
1497 assert_eq!(next_candidate(&bm, Location::new(0), 8), None);
1498 }
1499
1500 #[test]
1501 fn bitmap_scan_skips_inactive() {
1502 let bm = make_bitmap(&[false, false, true, false, true]);
1504 assert_eq!(
1505 next_candidate(&bm, Location::new(0), 5),
1506 Some(Location::new(2))
1507 );
1508 assert_eq!(
1509 next_candidate(&bm, Location::new(3), 5),
1510 Some(Location::new(4))
1511 );
1512 assert_eq!(next_candidate(&bm, Location::new(5), 5), None);
1513 }
1514
1515 #[test]
1516 fn bitmap_scan_beyond_bitmap_len_returns_candidate() {
1517 let bm = make_bitmap(&[false; 4]);
1520 assert_eq!(
1522 next_candidate(&bm, Location::new(0), 8),
1523 Some(Location::new(4))
1524 );
1525 assert_eq!(
1526 next_candidate(&bm, Location::new(6), 8),
1527 Some(Location::new(6))
1528 );
1529 }
1530
1531 #[test]
1532 fn bitmap_scan_respects_tip() {
1533 let bm = make_bitmap(&[false, false, false, true]);
1534 assert_eq!(next_candidate(&bm, Location::new(0), 3), None);
1536 assert_eq!(
1538 next_candidate(&bm, Location::new(0), 4),
1539 Some(Location::new(3))
1540 );
1541 }
1542
1543 #[test]
1544 fn bitmap_scan_floor_at_tip() {
1545 let bm = make_bitmap(&[true; 4]);
1546 assert_eq!(next_candidate(&bm, Location::new(4), 4), None);
1547 }
1548
1549 #[test]
1550 fn bitmap_scan_empty_bitmap() {
1551 let bm = Bm::new();
1552 assert_eq!(
1554 next_candidate(&bm, Location::new(0), 5),
1555 Some(Location::new(0))
1556 );
1557 assert_eq!(next_candidate(&bm, Location::new(0), 0), None);
1559 }
1560
1561 fn make_chain(shared: &Arc<Shared<N>>, overlay_lens: &[u64]) -> BitmapBatch<N> {
1574 let mut chain = BitmapBatch::Base(Arc::clone(shared));
1575 for &len in overlay_lens {
1576 chain = BitmapBatch::Layer(Arc::new(BitmapBatchLayer {
1577 parent: chain,
1578 overlay: Arc::new(ChunkOverlay::new(len, 0)),
1579 shared: Arc::clone(shared),
1580 }));
1581 }
1582 chain
1583 }
1584
1585 fn chain_overlays(batch: &BitmapBatch<N>) -> Vec<u64> {
1589 let mut lens = Vec::new();
1590 let mut current = batch;
1591 while let BitmapBatch::Layer(layer) = current {
1592 lens.push(layer.overlay.len);
1593 current = &layer.parent;
1594 }
1595 assert!(matches!(current, BitmapBatch::Base(_)));
1596 lens.reverse();
1597 lens
1598 }
1599
1600 #[test]
1605 fn trim_committed_already_base() {
1606 let shared = Arc::new(Shared::<N>::new(make_bitmap(&[true; 64])));
1607 let base = BitmapBatch::Base(Arc::clone(&shared));
1608 let result = base.trim_committed();
1609 match result {
1611 BitmapBatch::Base(s) => assert!(Arc::ptr_eq(&s, &shared)),
1612 BitmapBatch::Layer(_) => panic!("expected Base"),
1613 }
1614 }
1615
1616 #[test]
1622 fn trim_committed_all_committed() {
1623 let shared = Arc::new(Shared::<N>::new(make_bitmap(&[true; 64])));
1625 let chain = make_chain(&shared, &[32]);
1626 let result = chain.trim_committed();
1627 match result {
1629 BitmapBatch::Base(s) => assert!(Arc::ptr_eq(&s, &shared)),
1630 BitmapBatch::Layer(_) => panic!("expected Base after full trim"),
1631 }
1632 }
1633
1634 #[test]
1639 fn trim_committed_none_committed() {
1640 let shared = Arc::new(Shared::<N>::new(make_bitmap(&[true; 32])));
1642 let chain = make_chain(&shared, &[64, 96]);
1643 let result = chain.trim_committed();
1644 assert_eq!(chain_overlays(&result), vec![64, 96]);
1646 }
1647
1648 #[test]
1654 fn trim_committed_exactly_one_uncommitted() {
1655 let shared = Arc::new(Shared::<N>::new(make_bitmap(&[true; 64])));
1657 let chain = make_chain(&shared, &[64, 96]);
1658 let result = chain.trim_committed();
1659 assert_eq!(chain_overlays(&result), vec![96]);
1661 assert!(Arc::ptr_eq(result.shared(), &shared));
1663 }
1664
1665 #[test]
1670 fn trim_committed_multiple_uncommitted() {
1671 let shared = Arc::new(Shared::<N>::new(make_bitmap(&[true; 64])));
1673 let chain = make_chain(&shared, &[64, 96, 128]);
1674 let result = chain.trim_committed();
1675 assert_eq!(chain_overlays(&result), vec![96, 128]);
1677 assert!(Arc::ptr_eq(result.shared(), &shared));
1679 }
1680}