diff --git a/benches/binary_keys.rs b/benches/binary_keys.rs index c7f97cd2..91f9ed44 100644 --- a/benches/binary_keys.rs +++ b/benches/binary_keys.rs @@ -178,8 +178,8 @@ fn binary_k_path_iter(bencher: Bencher, n: u64) { //NOTE: 30 was found empirically and has no special meaning. It's just a number that is deep enough // that there happens not to be any non-unique paths at that depth, given the RNG I tested. If this // test fails, make that number smaller. - zipper.descend_first_k_path(KEY_LENGTH-30); - while zipper.to_next_k_path(KEY_LENGTH-30) { + zipper.descend_first_k_path(KEY_LENGTH-30, &mut ()); + while zipper.to_next_k_path(KEY_LENGTH-30, &mut ()) { count += 1; } assert_eq!(count, n); @@ -196,7 +196,7 @@ fn binary_zipper_iter(bencher: Bencher, n: u64) { bencher.bench_local(|| { let mut count = 0; let mut zipper = map.read_zipper(); - while zipper.to_next_val() { + while zipper.to_next_val(&mut ()) { count += 1; } assert_eq!(count, n); diff --git a/benches/parallel.rs b/benches/parallel.rs index d28db571..292c5301 100644 --- a/benches/parallel.rs +++ b/benches/parallel.rs @@ -315,7 +315,7 @@ fn parallel_copy_traverse(bencher: Bencher, (elements, thread_cnt): (usize, &str Ok((mut reader_z, mut writer_z)) => { //We got the zippers, do the stuff let witness = reader_z.witness(); - while let Some(val) = reader_z.to_next_get_val_with_witness(&witness) { + while let Some(val) = reader_z.to_next_get_val_with_witness(&witness, &mut ()) { writer_z.move_to_path(reader_z.path()); writer_z.set_val(*val); @@ -363,7 +363,7 @@ fn parallel_copy_traverse(bencher: Bencher, (elements, thread_cnt): (usize, &str let mut writer_z = unsafe{ zipper_head.write_zipper_at_exclusive_path_unchecked(&[b'o', b'u', b't', 0]) }; let mut reader_z = unsafe{ zipper_head.read_zipper_at_path_unchecked(&[b'i', b'n', 0]) }; let witness = reader_z.witness(); - while let Some(val) = reader_z.to_next_get_val_with_witness(&witness) { + while let Some(val) = reader_z.to_next_get_val_with_witness(&witness, &mut ()) { writer_z.move_to_path(reader_z.path()); writer_z.set_val(*val); sanity_counter += 1; diff --git a/benches/sparse_keys.rs b/benches/sparse_keys.rs index 2fb43d96..89b6bc39 100644 --- a/benches/sparse_keys.rs +++ b/benches/sparse_keys.rs @@ -296,8 +296,8 @@ fn sparse_k_path_iter(bencher: Bencher, n: u64) { bencher.bench_local(|| { let mut zipper = map.read_zipper(); let mut count = 1; - zipper.descend_first_k_path(5); - while zipper.to_next_k_path(5) { + zipper.descend_first_k_path(5, &mut ()); + while zipper.to_next_k_path(5, &mut ()) { count += 1; } assert_eq!(count, n); @@ -318,7 +318,7 @@ fn sparse_zipper_cursor(bencher: Bencher, n: u64) { bencher.bench_local(|| { let mut count = 0; let mut zipper = map.read_zipper(); - while zipper.to_next_val() { + while zipper.to_next_val(&mut ()) { count += 1; } assert_eq!(count, n); diff --git a/benches/superdense_keys.rs b/benches/superdense_keys.rs index 58766f37..9e429216 100644 --- a/benches/superdense_keys.rs +++ b/benches/superdense_keys.rs @@ -256,8 +256,8 @@ fn superdense_k_path_iter(bencher: Bencher, n: u64) { let mut zipper = map.read_zipper(); let mut count = 1; - zipper.descend_first_k_path(2); - while zipper.to_next_k_path(2) { + zipper.descend_first_k_path(2, &mut ()); + while zipper.to_next_k_path(2, &mut ()) { count += 1; } assert_eq!(count, n); @@ -291,7 +291,7 @@ fn superdense_zipper_cursor(bencher: Bencher, n: u64) { bencher.bench_local(|| { let mut count = 0; let mut zipper = map.read_zipper(); - while zipper.to_next_val() { + while zipper.to_next_val(&mut ()) { count += 1; } assert_eq!(count, n); diff --git a/pathmap-derive/src/lib.rs b/pathmap-derive/src/lib.rs index 3bbc959e..c89e53ef 100644 --- a/pathmap-derive/src/lib.rs +++ b/pathmap-derive/src/lib.rs @@ -15,6 +15,7 @@ enum PolyZipperTrait { ZipperMoving, ZipperIteration, ZipperConcrete, + ZipperPath, ZipperAbsolutePath, ZipperPathBuffer, ZipperSubtries, @@ -33,6 +34,7 @@ impl PolyZipperTrait { "ZipperMoving" => Some(Self::ZipperMoving), "ZipperIteration" => Some(Self::ZipperIteration), "ZipperConcrete" => Some(Self::ZipperConcrete), + "ZipperPath" => Some(Self::ZipperPath), "ZipperAbsolutePath" => Some(Self::ZipperAbsolutePath), "ZipperPathBuffer" => Some(Self::ZipperPathBuffer), "ZipperSubtries" => Some(Self::ZipperSubtries), @@ -54,6 +56,7 @@ fn all_poly_zipper_traits() -> BTreeSet { ZipperMoving, ZipperIteration, ZipperConcrete, + ZipperPath, ZipperAbsolutePath, ZipperPathBuffer, ZipperSubtries, @@ -98,6 +101,11 @@ fn add_trait_dependencies(traits: &mut BTreeSet) { } } if traits.contains(&ZipperAbsolutePath) { + if traits.insert(ZipperPath) { + changed = true; + } + } + if traits.contains(&ZipperPath) { if traits.insert(ZipperMoving) { changed = true; } @@ -459,19 +467,16 @@ fn derive_poly_zipper_with_traits( quote! {} }; Some(quote! { - impl #impl_generics pathmap::zipper::ZipperPath for #enum_name #ty_generics + impl #impl_generics pathmap::zipper::ZipperMoving for #enum_name #ty_generics #zipper_moving_where { - fn path(&self) -> &[u8] { + #[inline] + fn depth(&self) -> usize { match self { - #(#variant_arms => inner.path(),)* + #(#variant_arms => inner.depth(),)* } } - } - impl #impl_generics pathmap::zipper::ZipperMoving for #enum_name #ty_generics - #zipper_moving_where - { fn at_root(&self) -> bool { match self { #(#variant_arms => inner.at_root(),)* @@ -572,6 +577,33 @@ fn derive_poly_zipper_with_traits( None }; + // Generate ZipperPath trait implementation + let zipper_path_impl = if traits.contains(&PolyZipperTrait::ZipperPath) { + let variant_arms = &variant_arms; + let zipper_path_where = if include_where_clause { + quote! { + where + #(#inner_types: pathmap::zipper::ZipperPath,)* + #where_clause + } + } else { + quote! {} + }; + Some(quote! { + impl #impl_generics pathmap::zipper::ZipperPath for #enum_name #ty_generics + #zipper_path_where + { + fn path(&self) -> &[u8] { + match self { + #(#variant_arms => inner.path(),)* + } + } + } + }) + } else { + None + }; + // Generate ZipperAbsolutePath trait implementation let zipper_absolute_path_impl = if traits.contains(&PolyZipperTrait::ZipperAbsolutePath) { let variant_arms = &variant_arms; @@ -660,27 +692,27 @@ fn derive_poly_zipper_with_traits( impl #impl_generics pathmap::zipper::ZipperIteration for #enum_name #ty_generics #zipper_iteration_where { - fn to_next_val(&mut self) -> bool { + fn to_next_val(&mut self, obs: &mut Obs) -> bool { match self { - #(#variant_arms => inner.to_next_val(),)* + #(#variant_arms => inner.to_next_val(obs),)* } } - fn descend_last_path(&mut self) -> bool { + fn descend_last_path(&mut self, obs: &mut Obs) -> bool { match self { - #(#variant_arms => inner.descend_last_path(),)* + #(#variant_arms => inner.descend_last_path(obs),)* } } - fn descend_first_k_path(&mut self, k: usize) -> bool { + fn descend_first_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { match self { - #(#variant_arms => inner.descend_first_k_path(k),)* + #(#variant_arms => inner.descend_first_k_path(k, obs),)* } } - fn to_next_k_path(&mut self, k: usize) -> bool { + fn to_next_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { match self { - #(#variant_arms => inner.to_next_k_path(k),)* + #(#variant_arms => inner.to_next_k_path(k, obs),)* } } } @@ -705,9 +737,9 @@ fn derive_poly_zipper_with_traits( impl #impl_generics pathmap::zipper::ZipperReadOnlyIteration<'trie, V> for #enum_name #ty_generics #zipper_read_only_iteration_where { - fn to_next_get_val(&mut self) -> Option<&'trie V> { + fn to_next_get_val(&mut self, obs: &mut Obs) -> Option<&'trie V> { match self { - #(#variant_arms => inner.to_next_get_val(),)* + #(#variant_arms => inner.to_next_get_val(obs),)* } } } @@ -731,9 +763,9 @@ fn derive_poly_zipper_with_traits( impl #impl_generics pathmap::zipper::ZipperReadOnlyConditionalIteration<'trie, V> for #enum_name #ty_generics #zipper_read_only_conditional_iteration_where { - fn to_next_get_val_with_witness<'w>(&mut self, witness: &'w Self::WitnessT) -> Option<&'w V> where 'trie: 'w { + fn to_next_get_val_with_witness<'w, Obs: pathmap::zipper::PathObserver>(&mut self, witness: &'w Self::WitnessT, obs: &mut Obs) -> Option<&'w V> where 'trie: 'w { match (self, witness) { - #((Self::#variant_names(inner), #witness_enum_name::#variant_names(w)) => inner.to_next_get_val_with_witness(w),)* + #((Self::#variant_names(inner), #witness_enum_name::#variant_names(w)) => inner.to_next_get_val_with_witness(w, obs),)* _ => { debug_assert!(false, "Witness variant must match zipper variant"); None @@ -847,6 +879,7 @@ fn derive_poly_zipper_with_traits( // #zipper_forking_impl #zipper_moving_impl #zipper_concrete_impl + #zipper_path_impl #zipper_absolute_path_impl #zipper_path_buffer_impl #zipper_iteration_impl diff --git a/src/arena_compact.rs b/src/arena_compact.rs index 9e15bc97..cfcc732a 100644 --- a/src/arena_compact.rs +++ b/src/arena_compact.rs @@ -2525,6 +2525,9 @@ where Storage: AsRef<[u8]> impl<'tree, Storage, Value> ZipperMoving for ACTZipper<'tree, Storage, Value> where Storage: AsRef<[u8]> { + #[inline] + fn depth(&self) -> usize { self.path.len().saturating_sub(self.origin_depth) } + /// Returns `true` if the zipper cannot ascend further, otherwise returns `false` fn at_root(&self) -> bool { self.path.len() <= self.origin_depth } @@ -2554,7 +2557,7 @@ where Storage: AsRef<[u8]> if zipper.is_val() { count += 1; } - while zipper.to_next_val() { + while zipper.to_next_val(&mut ()) { count += 1; } count @@ -2782,7 +2785,7 @@ where Storage: AsRef<[u8]> } // default - // fn to_next_step(&mut self) -> bool; + // fn to_next_step(&mut self, obs: &mut Obs) -> bool; } impl ZipperIteration for ACTZipper<'_, Storage, Value> @@ -2792,8 +2795,8 @@ where Storage: AsRef<[u8]> /// order /// /// Returns a reference to the value or `None` if the zipper has encountered the root. - fn to_next_val(&mut self) -> bool { - while self.to_next_step() { + fn to_next_val(&mut self, obs: &mut Obs) -> bool { + while self.to_next_step(obs) { if self.is_val() { return true; } @@ -2811,11 +2814,15 @@ where Storage: AsRef<[u8]> /// below the zipper's focus. Although a typical cost is `order log n` or better. /// /// See: [to_next_k_path](ZipperIteration::to_next_k_path) - fn descend_first_k_path(&mut self, k: usize) -> bool { + fn descend_first_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { for ii in 0..k { - if self.descend_first_byte().is_none() { - self.ascend(ii); - return false; + match self.descend_first_byte() { + Some(byte) => obs.descend_to_byte(byte), + None => { + self.ascend(ii); + obs.ascend(ii); + return false; + } } } return true; @@ -2832,35 +2839,46 @@ where Storage: AsRef<[u8]> /// below the zipper's focus. Although a typical cost is `order log n` or better. /// /// See: [descend_first_k_path](ZipperIteration::descend_first_k_path) - fn to_next_k_path(&mut self, k: usize) -> bool { + fn to_next_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { let mut depth = k; 'outer: loop { while depth > 0 && self.child_count() <= 1 { if self.ascend(1) == 0 { break 'outer; } + obs.ascend(1); depth -= 1; } let stack = self.stack.last_mut().unwrap(); let idx = stack.child_index + 1; if idx >= stack.child_count { - if depth == 0 || self.ascend(1) == 0 { + if depth == 0 { break 'outer; } + if self.ascend(1) == 0 { + break 'outer; + } + obs.ascend(1); depth -= 1; continue 'outer; } - assert!(self.descend_indexed_byte(idx).is_some()); + //The loops above already ascended, so this is a plain descent of one byte + match self.descend_indexed_byte(idx) { + Some(byte) => obs.descend_to_byte(byte), + None => unreachable!("idx was bounds-checked against child_count above"), + } depth += 1; for _ii in 0..k - depth { - if self.descend_first_byte().is_none() { - continue 'outer; + match self.descend_first_byte() { + Some(byte) => obs.descend_to_byte(byte), + None => continue 'outer, } depth += 1; } return true; } self.ascend(depth); + obs.ascend(depth); false } } @@ -2912,14 +2930,18 @@ mod tests { let mut btm_zipper = btm.read_zipper(); let mut act_zipper = act.read_zipper_u64(); + let mut btm_observed = Vec::::new(); + let mut act_observed = Vec::::new(); loop { - btm_zipper.to_next_val(); - act_zipper.to_next_val(); + btm_zipper.to_next_val(&mut btm_observed); + act_zipper.to_next_val(&mut act_observed); let btm_val = btm_zipper.val().copied(); let act_val = act_zipper.val().copied(); assert_eq!(btm_zipper.path(), act_zipper.path()); + assert_eq!(btm_observed, act_observed); + assert_eq!(&btm_observed[..], btm_zipper.path()); assert_eq!(btm_val, act_val); if act_val.is_none() { @@ -3012,11 +3034,15 @@ mod tests { let btm = PathMap::from_iter(items.iter().map(|&(k, v)| (k, v))); let mut bz = btm.read_zipper(); let mut az = act.read_zipper_u64(); + let mut b_observed = Vec::::new(); + let mut a_observed = Vec::::new(); loop { - let more_b = bz.to_next_val(); - let more_a = az.to_next_val(); + let more_b = bz.to_next_val(&mut b_observed); + let more_a = az.to_next_val(&mut a_observed); assert_eq!(more_b, more_a, "walks end together"); assert_eq!(bz.path(), az.path()); + assert_eq!(b_observed, a_observed); + assert_eq!(&b_observed[..], bz.path()); assert_eq!(bz.val().copied(), az.val().copied()); if !more_a { break; @@ -3197,11 +3223,15 @@ mod tests { let act = ArenaCompactTree::from_zipper(btm.read_zipper(), |&v| v); let mut cata_zipper = act.read_zipper_u64(); let mut stream_zipper = tree.read_zipper_u64(); + let mut cata_observed = Vec::::new(); + let mut stream_observed = Vec::::new(); loop { - let cata_next = cata_zipper.to_next_val(); - let stream_next = stream_zipper.to_next_val(); + let cata_next = cata_zipper.to_next_val(&mut cata_observed); + let stream_next = stream_zipper.to_next_val(&mut stream_observed); assert_eq!(cata_next, stream_next); assert_eq!(cata_zipper.path(), stream_zipper.path()); + assert_eq!(cata_observed, stream_observed); + assert_eq!(&cata_observed[..], cata_zipper.path()); assert_eq!(cata_zipper.get_val(), stream_zipper.get_val()); if !cata_next { break; @@ -3319,10 +3349,14 @@ mod tests { fn assert_act_matches_map(map: &PathMap, tree: &ArenaCompactTree) { let mut map_zipper = map.read_zipper(); let mut act_zipper = tree.read_zipper_u64(); + let mut map_observed = Vec::::new(); + let mut act_observed = Vec::::new(); loop { - let map_next = map_zipper.to_next_val(); - assert_eq!(map_next, act_zipper.to_next_val()); + let map_next = map_zipper.to_next_val(&mut map_observed); + assert_eq!(map_next, act_zipper.to_next_val(&mut act_observed)); assert_eq!(map_zipper.path(), act_zipper.path()); + assert_eq!(map_observed, act_observed); + assert_eq!(&map_observed[..], map_zipper.path()); assert_eq!(map_zipper.val().copied(), act_zipper.val().copied()); if !map_next { break } } @@ -3376,7 +3410,7 @@ mod tests { let map = random_pathmap(0xAC7_0002, 5000); let mut items: Vec<(Vec, u64)> = Vec::with_capacity(map.val_count()); let mut zipper = map.read_zipper(); - while zipper.to_next_val() { + while zipper.to_next_val(&mut ()) { items.push((zipper.path().to_vec(), *zipper.val().unwrap())); } diff --git a/src/counters.rs b/src/counters.rs index 78baf39e..cdf62a3e 100644 --- a/src/counters.rs +++ b/src/counters.rs @@ -103,8 +103,8 @@ impl Counters { counters.count_node(map.root().unwrap().as_tagged(), 0); let mut zipper = map.read_zipper(); - while zipper.to_next_step() { - let depth = zipper.path().len(); + while zipper.to_next_step(&mut ()) { + let depth = zipper.depth(); counters.run_counter_update(depth); if let Some(focus_node) = zipper.get_focus().try_as_tagged() { @@ -187,11 +187,11 @@ impl Counters { } } -pub fn print_traversal<'a, V: 'a + Clone + Unpin, Z: ZipperIteration + Clone>(zipper: &Z) { +pub fn print_traversal<'a, V: 'a + Clone + Unpin, Z: ZipperIteration + ZipperPath + Clone>(zipper: &Z) { let mut zipper = zipper.clone(); println!("{:?}", zipper.path()); - while zipper.to_next_val() { + while zipper.to_next_val(&mut ()) { println!("{:?}", zipper.path()); } } diff --git a/src/dependent_zipper.rs b/src/dependent_zipper.rs index e9a874da..b3106d50 100644 --- a/src/dependent_zipper.rs +++ b/src/dependent_zipper.rs @@ -33,7 +33,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath, - SecondaryZ: ZipperMoving + ZipperPath, + SecondaryZ: ZipperMoving, { /// Creates a new `DependentProductZipperG` from the provided enroll function pub fn new_enroll(primary: PrimaryZ, enroll_payload: C, enroll: F) -> Self @@ -61,7 +61,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], /// Actual focus factor calculation. /// Returns a valid index into `self.factor_paths`, truncating to parents if requested. fn factor_idx(&self, truncate_up: bool) -> Option { - let len = self.path().len(); + let len = self.depth(); let mut factor = self.factor_paths.len().checked_sub(1)?; while truncate_up && self.factor_paths[factor] == len { factor = factor.checked_sub(1)?; @@ -75,7 +75,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], /// The returned slice will have a length of [`focus_factor`](Self::focus_factor), so the factor /// containing the current focus has will not be included. /// - /// Indices will be offsets into the buffer returned by [path](ZipperMoving::path). To get an offset into + /// Indices will be offsets into the buffer returned by [path](ZipperPath::path). To get an offset into /// [origin_path](ZipperAbsolutePath::origin_path), add the length of the prefix path from /// [root_prefix_path](ZipperAbsolutePath::root_prefix_path). pub fn path_indices(&self) -> &[usize] { @@ -94,7 +94,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], /// Remove top factors if they are at root fn exit_factors(&mut self) -> bool { - let len = self.path().len(); + let len = self.depth(); let mut exited = false; while self.factor_paths.last() == Some(&len) { self.factor_paths.pop(); @@ -106,7 +106,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], /// Enter factors at current location if we're on the end of the factor's path fn enter_factors(&mut self) -> bool { - let len = self.path().len(); + let len = self.depth(); // enter the next factor if we can let mut entered = false; if self.is_path_end() { @@ -125,7 +125,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], /// A combination between `ascend_until` and `ascend_until_branch`. /// if `allow_stop_on_val` is `true`, behaves as `ascend_until` fn ascend_cond(&mut self, allow_stop_on_val: bool) -> usize { - let mut plen = self.path().len(); + let mut plen = self.depth(); let mut ascended = 0; loop { while self.factor_paths.last() == Some(&plen) { @@ -181,7 +181,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperAbsolutePath, - SecondaryZ: ZipperMoving + ZipperPath, + SecondaryZ: ZipperMoving, { fn origin_path(&self) -> &[u8] { self.primary.origin_path() } fn root_prefix_path(&self) -> &[u8] { self.primary.root_prefix_path() } @@ -192,7 +192,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath + ZipperConcrete, - SecondaryZ: ZipperMoving + ZipperPath + ZipperConcrete, + SecondaryZ: ZipperMoving + ZipperConcrete, { fn shared_node_id(&self) -> Option { if let Some(idx) = self.factor_idx(true) { @@ -215,7 +215,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath + ZipperPathBuffer, - SecondaryZ: ZipperMoving + ZipperPath + ZipperPathBuffer, + SecondaryZ: ZipperMoving + ZipperPathBuffer, { unsafe fn origin_path_assert_len(&self, len: usize) -> &[u8] { unsafe{ self.primary.origin_path_assert_len(len) } } fn prepare_buffers(&mut self) { self.primary.prepare_buffers() } @@ -227,7 +227,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath + ZipperValues, - SecondaryZ: ZipperMoving + ZipperPath + ZipperValues, + SecondaryZ: ZipperMoving + ZipperValues, { fn val(&self) -> Option<&V> { if let Some(idx) = self.factor_idx(true) { @@ -250,7 +250,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath + ZipperReadOnlyValues<'trie, V>, - SecondaryZ: ZipperMoving + ZipperPath + ZipperReadOnlyValues<'trie, V>, + SecondaryZ: ZipperMoving + ZipperReadOnlyValues<'trie, V>, { fn get_val(&self) -> Option<&'trie V> { if let Some(idx) = self.factor_idx(true) { @@ -273,7 +273,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath + ZipperReadOnlyConditionalValues<'trie, V>, - SecondaryZ: ZipperMoving + ZipperPath + ZipperReadOnlyConditionalValues<'trie, V>, + SecondaryZ: ZipperMoving + ZipperReadOnlyConditionalValues<'trie, V>, { type WitnessT = (PrimaryZ::WitnessT, Vec); fn witness<'w>(&self) -> Self::WitnessT { @@ -294,7 +294,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath + Zipper, - SecondaryZ: ZipperMoving + ZipperPath + Zipper, + SecondaryZ: ZipperMoving + Zipper, { fn path_exists(&self) -> bool { if let Some(idx) = self.factor_idx(true) { @@ -330,10 +330,11 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath, - SecondaryZ: ZipperMoving + ZipperPath, + SecondaryZ: ZipperMoving, { - fn at_root(&self) -> bool { - self.path().is_empty() + #[inline] + fn depth(&self) -> usize { + self.primary.depth() } #[inline] fn focus_byte(&self) -> Option { @@ -428,7 +429,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], while remaining > 0 { self.exit_factors(); if let Some(idx) = self.factor_idx(false) { - let len = self.path().len() - self.factor_paths[idx]; + let len = self.depth() - self.factor_paths[idx]; let delta = len.min(remaining); self.secondary[idx].ascend(delta); self.primary.ascend(delta); @@ -457,7 +458,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath, - SecondaryZ: ZipperMoving + ZipperPath, + SecondaryZ: ZipperMoving, { #[inline] fn path(&self) -> &[u8] { @@ -469,7 +470,8 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], for DependentProductZipperG<'trie, PrimaryZ, SecondaryZ, V, C, F> where V: Clone + Send + Sync, - PrimaryZ: ZipperIteration, + //The `enroll` closure receives the path traversed so far, which comes from the primary + PrimaryZ: ZipperIteration + ZipperPath, SecondaryZ: ZipperIteration, { } //Use the default impl for all methods @@ -477,7 +479,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V: Clone + Send + Sync + Unpin, C, F : Clone + where V: Clone + Send + Sync, PrimaryZ: ZipperMoving + ZipperPath + ZipperValues, - SecondaryZ: ZipperMoving + ZipperPath + ZipperValues, + SecondaryZ: ZipperMoving + ZipperValues, { fn native_subtries(&self) -> bool { false } fn try_make_map(&self) -> Option> { None } @@ -501,7 +503,9 @@ mod tests { }); let mut full = String::new(); - while dpz.to_next_val() { + let mut observed = Vec::::new(); + while dpz.to_next_val(&mut observed) { + assert_eq!(&observed[..], dpz.path()); if dpz.child_count() == 0 { full.push_str(std::str::from_utf8(dpz.path()).unwrap()); full.push('\n'); @@ -532,7 +536,9 @@ rubicundus.postfix }); let mut full = String::new(); - while dpz.to_next_val() { + let mut observed = Vec::::new(); + while dpz.to_next_val(&mut observed) { + assert_eq!(&observed[..], dpz.path()); if dpz.child_count() == 0 { full.push_str(std::str::from_utf8(dpz.path()).unwrap()); full.push('\n'); diff --git a/src/empty_zipper.rs b/src/empty_zipper.rs index 294b7d36..3f34a1bb 100644 --- a/src/empty_zipper.rs +++ b/src/empty_zipper.rs @@ -32,7 +32,7 @@ impl Zipper for EmptyZipper { } impl ZipperMoving for EmptyZipper { - fn at_root(&self) -> bool { self.path.len() == self.path_start_idx } + #[inline] fn depth(&self) -> usize { self.path.len() - self.path_start_idx } #[inline] fn focus_byte(&self) -> Option { self.path.last().cloned() } fn reset(&mut self) { self.path.truncate(self.path_start_idx) } @@ -82,9 +82,10 @@ impl ZipperAbsolutePath for EmptyZipper { } impl ZipperIteration for EmptyZipper { - fn to_next_val(&mut self) -> bool { false } - fn descend_first_k_path(&mut self, _k: usize) -> bool { false } - fn to_next_k_path(&mut self, _k: usize) -> bool { false } + fn to_next_val(&mut self, _obs: &mut Obs) -> bool { false } + fn descend_last_path(&mut self, _obs: &mut Obs) -> bool { false } + fn descend_first_k_path(&mut self, _k: usize, _obs: &mut Obs) -> bool { false } + fn to_next_k_path(&mut self, _k: usize, _obs: &mut Obs) -> bool { false } } impl ZipperValues for EmptyZipper { @@ -109,11 +110,11 @@ impl<'a, V: Clone + Send + Sync> ZipperReadOnlyConditionalValues<'a, V> for Empt } impl<'a, V: Clone + Send + Sync> ZipperReadOnlyIteration<'a, V> for EmptyZipper { - fn to_next_get_val(&mut self) -> Option<&'a V> { None } + fn to_next_get_val(&mut self, _obs: &mut Obs) -> Option<&'a V> { None } } impl<'a, V: Clone + Send + Sync> ZipperReadOnlyConditionalIteration<'a, V> for EmptyZipper { - fn to_next_get_val_with_witness<'w>(&mut self, _witness: &'w Self::WitnessT) -> Option<&'w V> where 'a: 'w { None } + fn to_next_get_val_with_witness<'w, Obs: PathObserver>(&mut self, _witness: &'w Self::WitnessT, _obs: &mut Obs) -> Option<&'w V> where 'a: 'w { None } } impl ZipperPathBuffer for EmptyZipper { diff --git a/src/experimental.rs b/src/experimental.rs index c0db7ee3..18355b7d 100644 --- a/src/experimental.rs +++ b/src/experimental.rs @@ -45,7 +45,7 @@ impl ZipperPathBuffer for FullZipper { } impl ZipperMoving for FullZipper { - fn at_root(&self) -> bool { self.path.len() == 0 } + #[inline] fn depth(&self) -> usize { self.path.len() } #[inline] fn focus_byte(&self) -> Option { self.path.last().cloned() } fn reset(&mut self) { self.path.clear() } @@ -118,7 +118,7 @@ impl Zipper for NullZipper { } impl ZipperMoving for NullZipper { - fn at_root(&self) -> bool { true } + #[inline] fn depth(&self) -> usize { 0 } #[inline] fn focus_byte(&self) -> Option { None } fn reset(&mut self) {} diff --git a/src/morphisms.rs b/src/morphisms.rs index ba0aa70d..c23fdb12 100644 --- a/src/morphisms.rs +++ b/src/morphisms.rs @@ -2189,7 +2189,7 @@ mod tests { let mut it = check.iter(); let mut rz = btm.read_zipper(); - while let Some(range) = rz.to_next_get_val() { + while let Some(range) = rz.to_next_get_val(&mut ()) { for language_idx in range.clone().into_iter() { let greeting = std::str::from_utf8(rz.path()).unwrap(); let language = &greetings_vec[language_idx][rz.path().len()+1..]; @@ -2263,7 +2263,7 @@ mod tests { println!("test"); let mut rz = counted.read_zipper(); - while let Some(v) = rz.to_next_get_val() { + while let Some(v) = rz.to_next_get_val(&mut ()) { // todo write out useful print function, that shows the count submaps println!("v: {}, p: {}", v, std::str::from_utf8(rz.path()).unwrap()); } diff --git a/src/overlay_zipper.rs b/src/overlay_zipper.rs index 937cea76..e1a0788e 100644 --- a/src/overlay_zipper.rs +++ b/src/overlay_zipper.rs @@ -137,6 +137,13 @@ impl ZipperMoving BZipper: ZipperMoving + ZipperPath + ZipperValues, Mapping: for<'a> Fn(Option<&'a AV>, Option<&'a BV>) -> Option<&'a OutV>, { + #[inline] + fn depth(&self) -> usize { + let depth = self.a.depth(); + debug_assert_eq!(depth, self.b.depth()); + depth + } + fn at_root(&self) -> bool { self.a.at_root() || self.b.at_root() } diff --git a/src/path_tracker.rs b/src/path_tracker.rs index 27544733..c7136631 100644 --- a/src/path_tracker.rs +++ b/src/path_tracker.rs @@ -60,6 +60,7 @@ impl Zipper for PathTracker { } impl ZipperMoving for PathTracker { + #[inline] fn depth(&self) -> usize { self.path.len() - self.origin_len } #[inline] fn at_root(&self) -> bool { self.zipper.at_root() } fn reset(&mut self) { self.zipper.reset(); @@ -140,6 +141,9 @@ impl ZipperMoving for PathTracker { self.path.truncate(self.path.len() - ascended); ascended } + fn to_next_step(&mut self, obs: &mut Obs) -> bool { + self.zipper.to_next_step(&mut (&mut self.path, &mut *obs)) + } fn to_next_sibling_byte(&mut self) -> Option { let byte = self.zipper.to_next_sibling_byte()?; *self.path.last_mut().expect("path must not be empty") = byte; @@ -153,7 +157,22 @@ impl ZipperMoving for PathTracker { } -impl ZipperIteration for PathTracker { } +impl ZipperIteration for PathTracker { + //Each method delegates to the wrapped zipper, so it can use its own native implementation, and + //fans the reported movements out to this tracker's path buffer as well as the caller's observer + fn to_next_val(&mut self, obs: &mut Obs) -> bool { + self.zipper.to_next_val(&mut (&mut self.path, &mut *obs)) + } + fn descend_last_path(&mut self, obs: &mut Obs) -> bool { + self.zipper.descend_last_path(&mut (&mut self.path, &mut *obs)) + } + fn descend_first_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + self.zipper.descend_first_k_path(k, &mut (&mut self.path, &mut *obs)) + } + fn to_next_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + self.zipper.to_next_k_path(k, &mut (&mut self.path, &mut *obs)) + } +} impl ZipperPath for PathTracker { fn path(&self) -> &[u8] { &self.path[self.origin_len..] } diff --git a/src/paths_serialization.rs b/src/paths_serialization.rs index 3f6e05df..73d295aa 100644 --- a/src/paths_serialization.rs +++ b/src/paths_serialization.rs @@ -49,7 +49,7 @@ pub struct DeserializationStats { pub fn serialize_paths<'a, V, W, RZ>(rz: RZ, target: &mut W) -> std::io::Result where V: TrieValue, - RZ: ZipperReadOnlyConditionalIteration<'a, V>, + RZ: ZipperReadOnlyConditionalIteration<'a, V> + ZipperPath, W: std::io::Write { serialize_paths_with_auxdata(rz, target, |_, _, _| {}) @@ -78,7 +78,7 @@ pub fn serialize_paths_with_auxdata<'a, V : TrieValue, RZ : ZipperValues + Zi // } // } // }) - serialize_paths_from_funcs(target, &mut rz, |rz| Ok(rz.to_next_val()), |rz| { + serialize_paths_from_funcs(target, &mut rz, |rz| Ok(rz.to_next_val(&mut ())), |rz| { let path = rz.path(); fv(k, path, rz.val().unwrap()); k += 1; @@ -300,12 +300,12 @@ mod test { println!("de {} {} {}", c, bw, pw); let mut lrz = restored_btm.read_zipper(); - while lrz.to_next_val() { + while lrz.to_next_val(&mut ()) { assert!(btm.contains(lrz.path()), "{}", std::str::from_utf8(lrz.path()).unwrap()); } let mut rrz = btm.read_zipper(); - while rrz.to_next_val() { + while rrz.to_next_val(&mut ()) { assert!(restored_btm.contains(rrz.path())); } } @@ -340,12 +340,12 @@ mod test { println!("de {} {} {}", c, bw, pw); let mut lrz = restored_btm.read_zipper(); - while lrz.to_next_val() { + while lrz.to_next_val(&mut ()) { assert!(btm.contains(lrz.path()), "{}", std::str::from_utf8(lrz.path()).unwrap()); } let mut rrz = btm.read_zipper(); - while rrz.to_next_val() { + while rrz.to_next_val(&mut ()) { assert!(restored_btm.contains(rrz.path())); } } @@ -377,12 +377,12 @@ mod test { println!("de {} {} {}", c, bw, pw); let mut lrz = restored_btm.read_zipper(); - while lrz.to_next_val() { + while lrz.to_next_val(&mut ()) { assert_eq!(btm.get_val_at(lrz.path()), Some(lrz.val().unwrap())); } let mut rrz = btm.read_zipper(); - while rrz.to_next_val() { + while rrz.to_next_val(&mut ()) { assert_eq!(restored_btm.get_val_at(rrz.path()), Some(rrz.val().unwrap())); } } @@ -429,7 +429,7 @@ mod test { } let mut rz = restored.read_zipper(); let mut restored_count = 0; - while rz.to_next_val() { restored_count += 1; } + while rz.to_next_val(&mut ()) { restored_count += 1; } assert_eq!(restored_count, lengths.len(), "no extra paths may appear"); } @@ -464,7 +464,7 @@ mod test { } let mut rz = restored.read_zipper(); let mut restored_count = 0; - while rz.to_next_val() { restored_count += 1; } + while rz.to_next_val(&mut ()) { restored_count += 1; } assert_eq!(restored_count, lengths.len(), "no extra paths may appear"); } } diff --git a/src/poly_zipper.rs b/src/poly_zipper.rs index 7eb19fab..4655cec0 100644 --- a/src/poly_zipper.rs +++ b/src/poly_zipper.rs @@ -13,6 +13,7 @@ use crate::zipper::*; /// * [`ZipperConcrete`] /// * [`ZipperIteration`] /// * [`ZipperMoving`] +/// * [`ZipperPath`] /// * [`ZipperPathBuffer`] /// * [`ZipperReadOnlyConditionalIteration`] /// * [`ZipperReadOnlyConditionalValues`] @@ -142,7 +143,7 @@ mod tests { // ====================================================================================== // Cocktail of recursive zipper madness #[derive(PolyZipperExplicit)] - #[poly_zipper_explicit(traits(Zipper, ZipperValues, ZipperMoving, ZipperIteration))] + #[poly_zipper_explicit(traits(Zipper, ZipperValues, ZipperMoving, ZipperPath, ZipperIteration))] pub enum ExprFactor<'trie, V: Clone + Send + Sync + Unpin + 'static = ()> { Specific(ReadZipperOwned), Generic(PrefixZipper<'trie, diff --git a/src/prefix_zipper.rs b/src/prefix_zipper.rs index 778c7b6a..7bacd948 100644 --- a/src/prefix_zipper.rs +++ b/src/prefix_zipper.rs @@ -97,7 +97,7 @@ impl<'prefix, Z> PrefixZipper<'prefix, Z> /// Sets the portion of the zipper's `prefix` to treat as the [`root_prefix_path`](ZipperAbsolutePath::root_prefix_path) /// - /// The remaining portion of the `prefix` will be part of the [`path`](ZipperMoving::path). + /// The remaining portion of the `prefix` will be part of the [`path`](ZipperPath::path). /// This method resets the zipper, and typically it is called immediately after creating the `PrefixZipper`. pub fn set_root_prefix_path(&mut self, root_prefix_path: &[u8]) -> Result<(), &'static str> { if !starts_with(&*self.prefix, root_prefix_path) { @@ -117,6 +117,23 @@ impl<'prefix, Z> PrefixZipper<'prefix, Z> }; } + /// Descends over whatever remains of the `prefix`, leaving the focus at the source's root + /// + /// Returns `true` if the focus moved. The descended bytes are appended to this zipper's path + /// buffer and reported to `obs`. Does nothing if the focus is already within the source. + fn consume_prefix(&mut self, obs: &mut Obs) -> bool { + match self.position.prefixed_depth() { + Some(prefixed_depth) => { + let prefix_rest = &self.prefix[self.origin_depth + prefixed_depth..]; + self.path.extend_from_slice(prefix_rest); + obs.descend_to(prefix_rest); + self.position = PrefixPos::Source; + true + }, + None => false, + } + } + fn ascend_n(&mut self, mut steps: usize) -> Result<(), usize> { if let PrefixPos::PrefixOff { valid, mut invalid } = self.position { if invalid > steps { @@ -342,6 +359,11 @@ impl<'prefix, Z> ZipperMoving for PrefixZipper<'prefix, Z> where Z: ZipperMoving { + #[inline] + fn depth(&self) -> usize { + self.path.len() - self.origin_depth + } + fn at_root(&self) -> bool { match self.position { PrefixPos::Prefix { valid } => valid == 0, @@ -432,15 +454,7 @@ impl<'prefix, Z> ZipperMoving for PrefixZipper<'prefix, Z> } //Consuming the remainder of the prefix is itself a movement, so it must be reflected in the // return value even when the source zipper can't descend any further - let descended_prefix = if let Some(prefixed_depth) = self.position.prefixed_depth() { - let prefix_rest = &self.prefix[self.origin_depth + prefixed_depth..]; - self.path.extend_from_slice(prefix_rest); - obs.descend_to(prefix_rest); - self.position = PrefixPos::Source; - true - } else { - false - }; + let descended_prefix = self.consume_prefix(obs); //Fan the descended bytes out to our own path buffer as well as the caller's observer descended_prefix | self.source.descend_until(&mut (&mut self.path, &mut *obs)) } @@ -518,22 +532,85 @@ impl<'prefix, Z> ZipperAbsolutePath for PrefixZipper<'prefix, Z> impl<'prefix, Z> ZipperIteration for PrefixZipper<'prefix, Z> where Z: ZipperIteration { - //TODO: The default impls are highly sub-optimal. However we need "blind" versions of `ZipperIteration` to make this do the right thing - // fn to_next_val(&mut self) -> bool { todo!() } - // fn descend_first_k_path(&mut self, k: usize) -> bool { todo!() } - // fn to_next_k_path(&mut self, k: usize) -> bool { todo!() } -} + fn to_next_val(&mut self, obs: &mut Obs) -> bool { + if self.position.is_invalid() { + return false; + } + //Values only exist within the source, and the source's own root may hold one, so the prefix + //is consumed without descending any further before handing off + self.consume_prefix(obs); + self.source.to_next_val(&mut (&mut self.path, &mut *obs)) + } -impl<'prefix, 'a, V, Z> ZipperReadOnlyIteration<'a, V> for PrefixZipper<'prefix, Z> where Z: ZipperReadOnlyIteration<'a, V>, Self: ZipperReadOnlyValues<'a, V> + ZipperIteration { - //TODO: same as above. Default impls are highly sub-optimal - // fn to_next_get_val(&mut self) -> Option<&'a V> { todo!() } -} + fn descend_last_path(&mut self, obs: &mut Obs) -> bool { + if self.position.is_invalid() { + return false; + } + //As in `descend_until`, consuming the prefix is movement in its own right + let descended_prefix = self.consume_prefix(obs); + descended_prefix | self.source.descend_last_path(&mut (&mut self.path, &mut *obs)) + } -impl<'prefix, 'a, V, Z> ZipperReadOnlyConditionalIteration<'a, V> for PrefixZipper<'prefix, Z> where Z: ZipperReadOnlyConditionalIteration<'a, V>, Self: ZipperReadOnlyConditionalValues<'a, V, WitnessT = Z::WitnessT> + ZipperIteration { - //TODO: same as above. Default impls are highly sub-optimal - // fn to_next_get_val_with_witness<'w>(&mut self, witness: &'w Self::WitnessT) -> Option<&'w V> where 'a: 'w { todo!() } + fn descend_first_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + if self.position.is_invalid() { + return false; + } + //The prefix is a single forced path, so the bytes it contributes always exist and never + //branch. Descend as much of `k` as the prefix covers, then ask the source for the rest. + let prefix_rest = match self.position.prefixed_depth() { + Some(prefixed_depth) => self.prefix.len() - self.origin_depth - prefixed_depth, + None => 0, + }; + if k <= prefix_rest { + let taken = &self.prefix[self.path.len()..self.path.len() + k]; + self.path.extend_from_slice(taken); + obs.descend_to(taken); + self.set_valid(self.path.len() - self.origin_depth); + return true + } + self.consume_prefix(obs); + if self.source.descend_first_k_path(k - prefix_rest, &mut (&mut self.path, &mut *obs)) { + true + } else { + self.ascend(prefix_rest); + obs.ascend(prefix_rest); + false + } + } + + fn to_next_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + if self.position.is_invalid() || self.depth() < k { + return false; + } + //Only the portion of `k` inside the source can have alternatives to step to, so `k` is + //clamped to the source's depth. Iterating at the clamped depth visits exactly the same + //positions, because the prefix above it is a single forced path. + let source_depth = self.source.depth(); + if source_depth == 0 { + //Entirely within the prefix, which offers no alternatives + return false + } + let clamped = k.min(source_depth); + if self.source.to_next_k_path(clamped, &mut (&mut self.path, &mut *obs)) { + true + } else { + //The source rewound to its root; ascend the rest of `k` back up through the prefix + let remaining = k - clamped; + if remaining > 0 { + self.ascend(remaining); + obs.ascend(remaining); + } + false + } + } } +//The default impl is built on `to_next_val`, which is implemented natively above, so it picks up +//that implementation without needing its own +impl<'prefix, 'a, V, Z> ZipperReadOnlyIteration<'a, V> for PrefixZipper<'prefix, Z> where Z: ZipperReadOnlyIteration<'a, V>, Self: ZipperReadOnlyValues<'a, V> + ZipperIteration { } + +impl<'prefix, 'a, V, Z> ZipperReadOnlyConditionalIteration<'a, V> for PrefixZipper<'prefix, Z> where Z: ZipperReadOnlyConditionalIteration<'a, V>, Self: ZipperReadOnlyConditionalValues<'a, V, WitnessT = Z::WitnessT> + ZipperIteration { } + impl<'prefix, Z, V> ZipperForking for PrefixZipper<'prefix, Z> where Z: ZipperIteration + ZipperForking @@ -678,9 +755,97 @@ mod tests { use crate::zipper::Zipper; use crate::zipper::ZipperMoving; use crate::zipper::ZipperAbsolutePath; + use crate::zipper::ZipperIteration; use crate::zipper::ZipperPath; use crate::zipper::ZipperReadOnlyValues; use crate::zipper::ZipperValues; + + //The whole prefix is the root prefix, so these run the shared suites against a `PrefixZipper` + //whose focus begins in the source + crate::zipper::zipper_moving_tests::zipper_moving_tests!(prefix_zipper, + |keys: &[&[u8]]| { + keys.iter().map(|k| (k, ())).collect::>() + }, + |btm: &mut PathMap<()>, path: &[u8]| -> _ { + let mut z = PrefixZipper::new(path.to_vec(), btm.read_zipper_at_path(path)); + z.set_root_prefix_path(path).unwrap(); + z + } + ); + + crate::zipper::zipper_iteration_tests::zipper_iteration_tests!(prefix_zipper, + |keys: &[&[u8]]| { + keys.iter().map(|k| (k, ())).collect::>() + }, + |btm: &mut PathMap<()>, path: &[u8]| -> _ { + let mut z = PrefixZipper::new(path.to_vec(), btm.read_zipper_at_path(path)); + z.set_root_prefix_path(path).unwrap(); + z + } + ); + + /// Iteration when part of the `prefix` lies below the zipper's root, so the focus starts inside + /// the prefix and the methods must cross the seam into the source + #[test] + fn prefix_zipper_iter_across_seam() { + let keys: &[&[u8]] = &[b"pre.fix.aa", b"pre.fix.ab", b"pre.fix.b"]; + let btm: PathMap<()> = keys.iter().map(|k| (k, ())).collect(); + + let make = || { + let mut z = PrefixZipper::new(b"pre.fix.".to_vec(), btm.read_zipper_at_path(b"pre.fix.")); + //Only `pre.` is the root prefix, so `fix.` remains part of the zipper's own path + z.set_root_prefix_path(b"pre.").unwrap(); + z + }; + + //`to_next_val` must traverse the rest of the prefix before reaching source values + let mut z = make(); + let mut obs = Vec::::new(); + let mut found = vec![]; + while z.to_next_val(&mut obs) { + assert_eq!(&obs[..], z.path(), "observer diverged from path"); + found.push(z.path().to_vec()); + } + assert_eq!(found, vec![b"fix.aa".to_vec(), b"fix.ab".to_vec(), b"fix.b".to_vec()]); + + //`descend_last_path` crosses the seam and lands on the last path by sort order + let mut z = make(); + let mut obs = Vec::::new(); + assert!(z.descend_last_path(&mut obs)); + assert_eq!(z.path(), b"fix.b"); + assert_eq!(&obs[..], z.path()); + + //k_path where `k` spans the seam: 5 bytes covers `fix.` plus one source byte + let mut z = make(); + let mut obs = Vec::::new(); + assert!(z.descend_first_k_path(5, &mut obs)); + assert_eq!(z.path(), b"fix.a"); + assert_eq!(&obs[..], z.path()); + assert!(z.to_next_k_path(5, &mut obs)); + assert_eq!(z.path(), b"fix.b"); + assert_eq!(&obs[..], z.path()); + assert!(!z.to_next_k_path(5, &mut obs)); + assert_eq!(&obs[..], z.path()); + + //k_path where `k` lands inside the prefix, which is a single forced path with no siblings + let mut z = make(); + let mut obs = Vec::::new(); + assert!(z.descend_first_k_path(2, &mut obs)); + assert_eq!(z.path(), b"fi"); + assert_eq!(&obs[..], z.path()); + assert!(!z.to_next_k_path(2, &mut obs)); + assert_eq!(&obs[..], z.path()); + + //`k` deeper than anything the source can supply, so the prefix bytes descended on the way + //must be rewound and the focus left where it started + let mut z = make(); + let mut obs = Vec::::new(); + assert_eq!(z.path(), b""); + assert!(!z.descend_first_k_path(99, &mut obs)); + assert_eq!(z.path(), b"", "a failed descent must leave the focus unmoved"); + assert_eq!(&obs[..], z.path()); + } + const PATHS1: &[(&[u8], u64)] = &[ (b"0000", 0), (b"00000", 1), diff --git a/src/product_zipper.rs b/src/product_zipper.rs index 22a8903b..5c90f5f1 100644 --- a/src/product_zipper.rs +++ b/src/product_zipper.rs @@ -128,7 +128,7 @@ impl<'factor_z, 'trie, V: Clone + Send + Sync + Unpin, A: Allocator> ProductZipp self.z.deregularize(); self.z.push_node(secondary_root); - self.factor_paths.push(self.path().len()); + self.factor_paths.push(self.depth()); } /// Internal method to descend across the boundary between two factor zippers if the focus is on a value /// @@ -140,7 +140,7 @@ impl<'factor_z, 'trie, V: Clone + Send + Sync + Unpin, A: Allocator> ProductZipp //We don't want to push the same factor on the stack twice if let Some(factor_path_len) = self.factor_paths.last() { - if *factor_path_len == self.path().len() { + if *factor_path_len == self.depth() { return } } @@ -152,7 +152,7 @@ impl<'factor_z, 'trie, V: Clone + Send + Sync + Unpin, A: Allocator> ProductZipp #[inline] fn fix_after_ascend(&mut self) { while let Some(factor_path_start) = self.factor_paths.last() { - if self.z.path().len() < *factor_path_start { + if self.z.depth() < *factor_path_start { self.factor_paths.pop(); } else { break @@ -162,8 +162,9 @@ impl<'factor_z, 'trie, V: Clone + Send + Sync + Unpin, A: Allocator> ProductZipp } impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperMoving for ProductZipper<'_, 'trie, V, A> { - fn at_root(&self) -> bool { - self.path().len() == 0 + #[inline] + fn depth(&self) -> usize { + self.z.depth() } #[inline] fn focus_byte(&self) -> Option { @@ -189,7 +190,7 @@ impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> Zipper descended += this_step; if self.has_next_factor() { - if self.z.child_count() == 0 && self.factor_paths.last().map(|l| *l).unwrap_or(0) < self.path().len() { + if self.z.child_count() == 0 && self.factor_paths.last().map(|l| *l).unwrap_or(0) < self.depth() { self.enroll_next_factor(); } } else { @@ -220,7 +221,7 @@ impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> Zipper if self.z.child_count() == 0 { if self.has_next_factor() { if self.z.path_exists() { - debug_assert!(self.factor_paths.last().map(|l| *l).unwrap_or(0) < self.path().len()); + debug_assert!(self.factor_paths.last().map(|l| *l).unwrap_or(0) < self.depth()); self.enroll_next_factor(); if self.z.node_key().len() > 0 { self.z.regularize(); @@ -234,7 +235,7 @@ impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> Zipper let descended = self.z.descend_to_existing_byte(k); if descended && self.z.child_count() == 0 { if self.has_next_factor() { - debug_assert!(self.factor_paths.last().map(|l| *l).unwrap_or(0) < self.path().len()); + debug_assert!(self.factor_paths.last().map(|l| *l).unwrap_or(0) < self.depth()); self.enroll_next_factor(); if self.z.node_key().len() > 0 { self.z.regularize(); @@ -265,7 +266,7 @@ impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> Zipper moved } fn to_next_sibling_byte(&mut self) -> Option { - if self.factor_paths.last().cloned().unwrap_or(0) == self.path().len() { + if self.factor_paths.last().cloned().unwrap_or(0) == self.depth() { self.factor_paths.pop(); } let moved = self.z.to_next_sibling_byte(); @@ -273,7 +274,7 @@ impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> Zipper moved } fn to_prev_sibling_byte(&mut self) -> Option { - if self.factor_paths.last().cloned().unwrap_or(0) == self.path().len() { + if self.factor_paths.last().cloned().unwrap_or(0) == self.depth() { self.factor_paths.pop(); } let moved = self.z.to_prev_sibling_byte(); @@ -395,8 +396,8 @@ pub struct ProductZipperG<'trie, PrimaryZ, SecondaryZ, V> impl<'trie, PrimaryZ, SecondaryZ, V> ProductZipperG<'trie, PrimaryZ, SecondaryZ, V> where V: Clone + Send + Sync, - PrimaryZ: ZipperMoving + ZipperPath, - SecondaryZ: ZipperMoving + ZipperPath, + PrimaryZ: ZipperMoving, + SecondaryZ: ZipperMoving, { /// Creates a new `ProductZipper` from the provided zippers pub fn new(primary: PrimaryZ, other_zippers: ZipperList) -> Self @@ -416,7 +417,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V> ProductZipperG<'trie, PrimaryZ, SecondaryZ, /// Actual focus factor calculation. /// Returns a valid index into `self.factor_paths`, truncating to parents if requested. fn factor_idx(&self, truncate_up: bool) -> Option { - let len = self.path().len(); + let len = self.depth(); let mut factor = self.factor_paths.len().checked_sub(1)?; while truncate_up && self.factor_paths[factor] == len { factor = factor.checked_sub(1)?; @@ -436,7 +437,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V> ProductZipperG<'trie, PrimaryZ, SecondaryZ, /// Remove top factors if they are at root fn exit_factors(&mut self) -> bool { - let len = self.path().len(); + let len = self.depth(); let mut exited = false; while self.factor_paths.last() == Some(&len) { self.factor_paths.pop(); @@ -447,7 +448,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V> ProductZipperG<'trie, PrimaryZ, SecondaryZ, /// Enter factors at current location if we're on the end of the factor's path fn enter_factors(&mut self) -> bool { - let len = self.path().len(); + let len = self.depth(); // enter the next factor if we can let mut entered = false; if self.factor_paths.len() < self.secondary.len() && self.is_path_end() { @@ -460,7 +461,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V> ProductZipperG<'trie, PrimaryZ, SecondaryZ, /// A combination between `ascend_until` and `ascend_until_branch`. /// if `allow_stop_on_val` is `true`, behaves as `ascend_until` fn ascend_cond(&mut self, allow_stop_on_val: bool) -> usize { - let mut plen = self.path().len(); + let mut plen = self.depth(); let mut ascended = 0; loop { while self.factor_paths.last() == Some(&plen) { @@ -627,8 +628,8 @@ impl<'trie, PrimaryZ, SecondaryZ, V> ZipperReadOnlyConditionalValues<'trie, V> impl<'trie, PrimaryZ, SecondaryZ, V> Zipper for ProductZipperG<'trie, PrimaryZ, SecondaryZ, V> where V: Clone + Send + Sync, - PrimaryZ: ZipperMoving + ZipperPath + Zipper, - SecondaryZ: ZipperMoving + ZipperPath + Zipper, + PrimaryZ: ZipperMoving + Zipper, + SecondaryZ: ZipperMoving + Zipper, { fn path_exists(&self) -> bool { if let Some(idx) = self.factor_idx(true) { @@ -663,11 +664,12 @@ impl<'trie, PrimaryZ, SecondaryZ, V> Zipper for ProductZipperG<'trie, PrimaryZ, impl<'trie, PrimaryZ, SecondaryZ, V> ZipperMoving for ProductZipperG<'trie, PrimaryZ, SecondaryZ, V> where V: Clone + Send + Sync, - PrimaryZ: ZipperMoving + ZipperPath, - SecondaryZ: ZipperMoving + ZipperPath, + PrimaryZ: ZipperMoving, + SecondaryZ: ZipperMoving, { - fn at_root(&self) -> bool { - self.path().is_empty() + #[inline] + fn depth(&self) -> usize { + self.primary.depth() } #[inline] fn focus_byte(&self) -> Option { @@ -765,7 +767,7 @@ impl<'trie, PrimaryZ, SecondaryZ, V> ZipperMoving for ProductZipperG<'trie, Prim while remaining > 0 { self.exit_factors(); if let Some(idx) = self.factor_idx(false) { - let len = self.path().len() - self.factor_paths[idx]; + let len = self.depth() - self.factor_paths[idx]; let delta = len.min(remaining); self.secondary[idx].ascend(delta); self.primary.ascend(delta); @@ -834,7 +836,7 @@ pub trait ZipperProduct : ZipperMoving + Zipper + ZipperAbsolutePath + ZipperIte /// The returned slice will have a length of [`focus_factor`](ZipperProduct::focus_factor), so the factor /// containing the current focus has will not be included. /// - /// Indices will be offsets into the buffer returned by [path](ZipperMoving::path). To get an offset into + /// Indices will be offsets into the buffer returned by [path](ZipperPath::path). To get an offset into /// [origin_path](ZipperAbsolutePath::origin_path), add the length of the prefix path from /// [root_prefix_path](ZipperAbsolutePath::root_prefix_path). fn path_indices(&self) -> &[usize]; @@ -848,7 +850,7 @@ impl<'trie, V: Clone + Send + Sync + Unpin, A: Allocator> ZipperProduct for Prod match self.factor_paths.last() { Some(factor_path_len) => { let factor_idx = self.factor_paths.len(); - if *factor_path_len < self.path().len() { + if *factor_path_len < self.depth() { factor_idx } else { factor_idx - 1 @@ -1473,8 +1475,11 @@ mod tests { z.descend_to([3, 196, 50, 193, 52, 3, 196, 50, 194, 49, 54]); assert_eq!(z.path_exists(), true); - assert_eq!(z.to_next_k_path(2), true); + //This steps across a factor boundary, so it checks the reporting through the seam + let mut observed = z.path().to_vec(); + assert_eq!(z.to_next_k_path(2, &mut observed), true); assert_eq!(z.path_exists(), true); + assert_eq!(&observed[..], z.path()); assert_eq!(z.child_mask(), ByteMask::from_iter([3])); } diff --git a/src/random.rs b/src/random.rs index b3fbea1a..3bfabffe 100644 --- a/src/random.rs +++ b/src/random.rs @@ -78,7 +78,7 @@ impl Distribution<(Vec, T)> for FairTrieValue { let size = rz.val_count(); let target = rng.random_range(0..size); let mut i = 0; - while let Some(t) = rz.to_next_get_val() { + while let Some(t) = rz.to_next_get_val(&mut ()) { if i == target { return (rz.path().to_vec(), t.clone()) } i += 1; } diff --git a/src/trie_map.rs b/src/trie_map.rs index 3343fa58..31317234 100644 --- a/src/trie_map.rs +++ b/src/trie_map.rs @@ -53,7 +53,7 @@ impl core::fmt: let mut contains_all_ascii = true; let mut dbg_map = f.debug_map(); let mut path_cnt = 0; - while rz.to_next_val() && path_cnt < MAX_DEBUG_PATHS { + while rz.to_next_val(&mut ()) && path_cnt < MAX_DEBUG_PATHS { if let Some(key) = crate::utils::debug::render_debug_path(rz.path(), crate::utils::debug::PathRenderMode::RequireAscii) { dbg_map.entry(&key, rz.val().unwrap()); path_cnt += 1; @@ -70,7 +70,7 @@ impl core::fmt: rz.reset(); let mut dbg_struct = f.debug_struct("PathMap"); let mut path_cnt = 0; - while rz.to_next_val() && path_cnt < MAX_DEBUG_PATHS { + while rz.to_next_val(&mut ()) && path_cnt < MAX_DEBUG_PATHS { let key = crate::utils::debug::render_debug_path(rz.path(), crate::utils::debug::PathRenderMode::ByteList).unwrap(); dbg_struct.field(&key, rz.val().unwrap()); path_cnt += 1; @@ -1290,7 +1290,7 @@ mod tests { let expected = [3, 5, 4]; let mut i = 0; let witness = zipper.witness(); - while let Some(val) = zipper.to_next_get_val_with_witness(&witness) { + while let Some(val) = zipper.to_next_get_val_with_witness(&witness, &mut ()) { assert_eq!(*val, expected[i]); i += 1; } diff --git a/src/utils/debug/diff_zipper.rs b/src/utils/debug/diff_zipper.rs index 5192c67a..f2346e52 100644 --- a/src/utils/debug/diff_zipper.rs +++ b/src/utils/debug/diff_zipper.rs @@ -46,6 +46,12 @@ impl Zipper for DiffZipper impl ZipperMoving for DiffZipper { + fn depth(&self) -> usize { + let a = self.a.depth(); + let b = self.b.depth(); + assert_eq!(a, b); + a + } fn at_root(&self) -> bool { let a = self.a.at_root(); let b = self.b.at_root(); @@ -243,27 +249,41 @@ impl ZipperPathBuffe impl ZipperIteration for DiffZipper { - fn to_next_val(&mut self) -> bool { - let a = self.a.to_next_val(); - let b = self.b.to_next_val(); + fn to_next_val(&mut self, obs: &mut Obs) -> bool { + let a = self.a.to_next_val(obs); + let b = self.b.to_next_val(&mut ()); if self.log_moves { println!("DiffZipper: to_next_val") } assert_eq!(a, b); a } - fn descend_first_k_path(&mut self, k: usize) -> bool { - let a = self.a.descend_first_k_path(k); - let b = self.b.descend_first_k_path(k); + fn descend_last_path(&mut self, obs: &mut Obs) -> bool { + //`a`'s descent is forwarded to the caller as it happens, since a digest can't reconstruct + //the bytes afterwards. `b`'s is only hashed, and the two digests are compared. + let mut hash_a = HashObserver::default(); + let mut hash_b = HashObserver::default(); + let a = self.a.descend_last_path(&mut (&mut hash_a, &mut *obs)); + let b = self.b.descend_last_path(&mut hash_b); + if self.log_moves { + println!("DiffZipper: descend_last_path") + } + assert_eq!(a, b); + assert_eq!(hash_a, hash_b); + a + } + fn descend_first_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + let a = self.a.descend_first_k_path(k, obs); + let b = self.b.descend_first_k_path(k, &mut ()); if self.log_moves { println!("DiffZipper: descend_first_k_path k={k}") } assert_eq!(a, b); a } - fn to_next_k_path(&mut self, k: usize) -> bool { - let a = self.a.to_next_k_path(k); - let b = self.b.to_next_k_path(k); + fn to_next_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + let a = self.a.to_next_k_path(k, obs); + let b = self.b.to_next_k_path(k, &mut ()); if self.log_moves { println!("DiffZipper: to_next_k_path k={k}") } diff --git a/src/viz.rs b/src/viz.rs index 85f60ecf..2fb7276f 100644 --- a/src/viz.rs +++ b/src/viz.rs @@ -233,7 +233,7 @@ enum AsciiEdgeTarget { Value(*const V), } -fn build_ascii_graph_logical + ZipperMoving + ZipperIteration + ZipperValues>( +fn build_ascii_graph_logical + ZipperMoving + ZipperPath + ZipperIteration + ZipperValues>( mut z: Z, dc: &DrawConfig, ds: &mut DrawState, @@ -259,7 +259,7 @@ fn build_ascii_graph_logical(node: &TrieNode } } -fn viz_zipper_logical + ZipperMoving + ZipperIteration + ZipperValues>(mut z: Z, dc: &DrawConfig, ds: &mut DrawState) { +fn viz_zipper_logical + ZipperMoving + ZipperPath + ZipperIteration + ZipperValues>(mut z: Z, dc: &DrawConfig, ds: &mut DrawState) { let root_focus = z.get_focus(); let root_node = root_focus.borrow().unwrap(); let root_node_id = hash_pair(root_node.shared_node_id(), &[]); @@ -563,7 +563,7 @@ fn viz_zipper_logical ZipperInfallibleSubt } impl<'a, 'path, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperMoving for WriteZipperTracked<'a, 'path, V, A> { + #[inline] fn depth(&self) -> usize { self.z.depth() } fn at_root(&self) -> bool { self.z.at_root() } #[inline] fn focus_byte(&self) -> Option { self.z.focus_byte() } fn reset(&mut self) { self.z.reset() } @@ -581,6 +582,7 @@ impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperInfallibleSubt } impl<'a, 'path, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperMoving for WriteZipperUntracked<'a, 'path, V, A> { + #[inline] fn depth(&self) -> usize { self.z.depth() } fn at_root(&self) -> bool { self.z.at_root() } #[inline] fn focus_byte(&self) -> Option { self.z.focus_byte() } fn reset(&mut self) { self.z.reset() } @@ -869,7 +871,7 @@ impl Iterator return Some((self.zipper.path().to_vec(), val)) } } - if self.zipper.to_next_val() { + if self.zipper.to_next_val(&mut ()) { match self.zipper.remove_val(true) { Some(val) => return Some((self.zipper.path().to_vec(), val)), None => None @@ -1007,6 +1009,11 @@ impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> WriteZipperCore<'a, } impl<'a, 'path, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperMoving for WriteZipperCore<'a, 'path, V, A> { + #[inline] + fn depth(&self) -> usize { + self.key.prefix_buf.len().saturating_sub(self.key.origin_path.len()) + } + #[inline] fn at_root(&self) -> bool { self.key.prefix_buf.len() <= self.key.origin_path.len() @@ -2882,7 +2889,7 @@ mod tests { let mut visited = vec![]; let mut z = map.read_zipper(); - while z.to_next_val() { + while z.to_next_val(&mut ()) { visited.push((z.path().to_vec(), *z.val().unwrap())); assert!(visited.len() <= 1, "iteration must terminate"); } @@ -2923,7 +2930,7 @@ mod tests { let mut visited = vec![]; let mut z = map.read_zipper(); - while z.to_next_val() { + while z.to_next_val(&mut ()) { visited.push((z.path().to_vec(), *z.val().unwrap())); assert!(visited.len() <= 2, "iteration must terminate"); } @@ -3066,7 +3073,7 @@ mod tests { let mut writer_z = unsafe{ zipper_head.write_zipper_at_exclusive_path_unchecked(b"out\0") }; let mut reader_z = unsafe{ zipper_head.read_zipper_at_path_unchecked(b"in\0") }; let witness = reader_z.witness(); - while let Some(val) = reader_z.to_next_get_val_with_witness(&witness) { + while let Some(val) = reader_z.to_next_get_val_with_witness(&witness, &mut ()) { writer_z.descend_to(reader_z.path()); writer_z.set_val(*val * 65536); writer_z.reset(); @@ -3768,7 +3775,7 @@ mod tests { let mut rz = r.read_zipper(); let mut count = 0; use crate::zipper::ZipperIteration; - while rz.to_next_val() { count += 1; } + while rz.to_next_val(&mut ()) { count += 1; } assert!(count > 0); } diff --git a/src/zipper.rs b/src/zipper.rs index 1bfc3dcc..1613b76c 100644 --- a/src/zipper.rs +++ b/src/zipper.rs @@ -134,8 +134,17 @@ pub trait ZipperSubtries: Zi /// on how the zipper was created, and the origin will never be below the root. The origin of a given zipper /// will never change. pub trait ZipperMoving: Zipper { + /// Returns the number of bytes between the zipper's root and its current focus + /// + /// This is equal to `self.path().len()` for zippers that implement [`ZipperPath`], although + /// implementations are usually able to supply it more cheaply than by measuring a path. Zippers + /// that do not retain a contiguous path buffer must still be able to report their depth. + fn depth(&self) -> usize; + /// Returns `true` if the zipper's focus is at its root, and it cannot ascend further, otherwise returns `false` - fn at_root(&self) -> bool; + fn at_root(&self) -> bool { + self.depth() == 0 + } /// Returns the byte that was last descended to reach the zipper's focus, or `None` if that /// byte is unavailable @@ -358,8 +367,8 @@ pub trait ZipperMoving: Zipper { /// Moves the zipper's focus to the next sibling byte with the same parent /// - /// Returns `true` if the focus was moved. If the focus is already on the last byte among its siblings, - /// this method returns false, leving the focus unmodified. + /// Returns `Some(byte)` if the focus was moved. If the focus is already on the last byte among its siblings, + /// this method returns None, leving the focus unmodified. /// /// This method is equivalent to calling [ZipperMoving::ascend] with `1`, followed by [ZipperMoving::descend_indexed_byte] /// where the index passed is 1 more than the index of the current focus position. @@ -384,8 +393,8 @@ pub trait ZipperMoving: Zipper { /// Moves the zipper's focus to the previous sibling byte with the same parent /// - /// Returns `true` if the focus was moved. If the focus is already on the first byte among its siblings, - /// this method returns false, leving the focus unmodified. + /// Returns `Some(byte)` if the focus was moved. If the focus is already on the first byte among its siblings, + /// this method returns None, leving the focus unmodified. /// /// This method is equivalent to calling [Self::ascend] with `1`, followed by [Self::descend_indexed_byte] /// where the index passed is 1 less than the index of the current focus position. @@ -413,18 +422,38 @@ pub trait ZipperMoving: Zipper { /// /// Returns `true` if the position of the zipper has moved, or `false` if the zipper has returned /// to the root - fn to_next_step(&mut self) -> bool { + /// `obs` is notified of every movement made, so a caller can track the zipper's location without + /// the zipper needing to maintain a path buffer of its own. Pass `&mut ()` to discard them. + fn to_next_step(&mut self, obs: &mut Obs) -> bool { //If we're at a leaf ascend until we're not and jump to the next sibling if self.child_count() == 0 { //We can stop ascending when we succeed in moving to a sibling - while self.to_next_sibling_byte().is_none() { - if !self.ascend_byte() { - return false; + loop { + match self.to_next_sibling_byte() { + Some(byte) => { + //A sibling step replaces the last byte rather than adding one, so the + //observer sees the old byte retracted before the new one arrives. + obs.ascend(1); + obs.descend_to_byte(byte); + break + }, + None => { + if !self.ascend_byte() { + return false; + } + obs.ascend(1); + } } } } else { - return self.descend_first_byte().is_some() + return match self.descend_first_byte() { + Some(byte) => { + obs.descend_to_byte(byte); + true + }, + None => false + } } true } @@ -719,8 +748,11 @@ pub trait ZipperReadOnlyConditionalValues<'a, V>: ZipperValues { pub trait ZipperReadOnlyIteration<'a, V>: ZipperReadOnlyValues<'a, V> + ZipperIteration { /// Advances to the next value with behavior identical to [ZipperIteration::to_next_val], but returns /// a reference to the value or `None` if the zipper has encountered the root - fn to_next_get_val(&mut self) -> Option<&'a V> { - if self.to_next_val() { + /// + /// `obs` is notified of every movement made, so a caller can track the zipper's location without + /// the zipper needing to maintain a path buffer of its own. Pass `&mut ()` to discard them. + fn to_next_get_val(&mut self, obs: &mut Obs) -> Option<&'a V> { + if self.to_next_val(obs) { let val = self.get_val(); debug_assert!(val.is_some()); val @@ -731,8 +763,8 @@ pub trait ZipperReadOnlyIteration<'a, V>: ZipperReadOnlyValues<'a, V> + ZipperIt /// Deprecated alias for [ZipperReadOnlyIteration::to_next_get_val] #[deprecated] //GOAT-old-names - fn to_next_get_value(&mut self) -> Option<&'a V> { - self.to_next_get_val() + fn to_next_get_value(&mut self, obs: &mut Obs) -> Option<&'a V> { + self.to_next_get_val(obs) } } @@ -740,8 +772,11 @@ pub trait ZipperReadOnlyIteration<'a, V>: ZipperReadOnlyValues<'a, V> + ZipperIt /// why this trait exists pub trait ZipperReadOnlyConditionalIteration<'a, V>: ZipperReadOnlyConditionalValues<'a, V> + ZipperIteration { /// See [ZipperReadOnlyIteration::to_next_get_val] and [`witness`](ZipperReadOnlyConditionalValues::witness) - fn to_next_get_val_with_witness<'w>(&mut self, witness: &'w Self::WitnessT) -> Option<&'w V> where 'a: 'w { - if self.to_next_val() { + /// + /// `obs` is notified of every movement made, so a caller can track the zipper's location without + /// the zipper needing to maintain a path buffer of its own. Pass `&mut ()` to discard them. + fn to_next_get_val_with_witness<'w, Obs: PathObserver>(&mut self, witness: &'w Self::WitnessT, obs: &mut Obs) -> Option<&'w V> where 'a: 'w { + if self.to_next_val(obs) { let val = self.get_val_with_witness(witness); debug_assert!(val.is_some()); val @@ -819,35 +854,44 @@ pub trait ZipperReadOnlySubtries<'a, V: Clone + Send + Sync, A: Allocator = Glob /// An interface for advanced [Zipper] movements used for various types of iteration; such as iterating /// every value, or iterating all paths descending from a common root at a certain depth /// -/// NOTE: the [ZipperPath] bound will be removed when these methods are changed to take a -/// [PathObserver], as [ZipperMoving::descend_until] does. Until then, the `k_path` methods locate -/// themselves by measuring [`path`](ZipperPath::path), so a zipper must maintain one to iterate. -pub trait ZipperIteration: ZipperMoving + ZipperPath { +/// Every method takes a [PathObserver], so these movements are available to "blind" zippers that do +/// not maintain a contiguous path buffer of their own. +pub trait ZipperIteration: ZipperMoving { /// Systematically advances to the next value accessible from the zipper, traversing in a depth-first /// order /// /// Returns `true` if the zipper is positioned at the next value, or `false` if the zipper has /// encountered the root. - fn to_next_val(&mut self) -> bool { + /// + /// `obs` is notified of every movement made, so a caller can track the zipper's location without + /// the zipper needing to maintain a path buffer of its own. Pass `&mut ()` to discard them. + fn to_next_val(&mut self, obs: &mut Obs) -> bool { loop { - if self.descend_first_byte().is_some() { + if let Some(byte) = self.descend_first_byte() { + obs.descend_to_byte(byte); if self.is_val() { return true } - if self.descend_until(&mut ()) { + if self.descend_until(obs) { if self.is_val() { return true } } } else { 'ascending: loop { - if self.to_next_sibling_byte().is_some() { + if let Some(byte) = self.to_next_sibling_byte() { + //A sibling step replaces the last byte rather than adding one, so the + //observer sees the old byte retracted before the new one arrives. + obs.ascend(1); + obs.descend_to_byte(byte); if self.is_val() { return true } break 'ascending } else { - self.ascend_byte(); + if self.ascend_byte() { + obs.ascend(1); + } if self.at_root() { return false } @@ -865,11 +909,15 @@ pub trait ZipperIteration: ZipperMoving + ZipperPath { /// Returns `true` if the zipper has sucessfully descended, or `false` if the zipper was already /// at the end of a path. If this method returns `false` then the zipper will be in its original /// position. - fn descend_last_path(&mut self) -> bool { + /// + /// `obs` is notified of every movement made, so a caller can track the zipper's location without + /// the zipper needing to maintain a path buffer of its own. Pass `&mut ()` to discard them. + fn descend_last_path(&mut self, obs: &mut Obs) -> bool { let mut any = false; - while self.descend_last_byte().is_some() { + while let Some(byte) = self.descend_last_byte() { any = true; - self.descend_until(&mut ()); + obs.descend_to_byte(byte); + self.descend_until(obs); } any } @@ -883,9 +931,12 @@ pub trait ZipperIteration: ZipperMoving + ZipperPath { /// WARNING: This is not a constant-time operation, and may be as bad as `order n` with respect to the paths /// below the zipper's focus. Although a typical cost is `order log n` or better. /// + /// `obs` is notified of every movement made, so a caller can track the zipper's location without + /// the zipper needing to maintain a path buffer of its own. Pass `&mut ()` to discard them. + /// /// See: [to_next_k_path](ZipperIteration::to_next_k_path) - fn descend_first_k_path(&mut self, k: usize) -> bool { - k_path_default_internal(self, k, self.path().len()) + fn descend_first_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + k_path_default_internal(self, k, self.depth(), obs) } /// Moves the zipper's focus to the next location with the same path length as the current focus, @@ -898,38 +949,52 @@ pub trait ZipperIteration: ZipperMoving + ZipperPath { /// WARNING: This is not a constant-time operation, and may be as bad as `order n` with respect to the paths /// below the zipper's focus. Although a typical cost is `order log n` or better. /// + /// `obs` is notified of every movement made, so a caller can track the zipper's location without + /// the zipper needing to maintain a path buffer of its own. Pass `&mut ()` to discard them. + /// /// See: [descend_first_k_path](ZipperIteration::descend_first_k_path) - fn to_next_k_path(&mut self, k: usize) -> bool { - let base_idx = if self.path().len() >= k { - self.path().len() - k + fn to_next_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { + let depth = self.depth(); + let base_idx = if depth >= k { + depth - k } else { return false }; - k_path_default_internal(self, k, base_idx) + k_path_default_internal(self, k, base_idx, obs) } } /// The default implementation of both [ZipperIteration::to_next_k_path] and [ZipperIteration::descend_first_k_path] /// -/// NOTE: `base_idx` is an absolute depth, compared against the length of [`path`](ZipperPath::path) -/// to tell how far the traversal has descended. When the [ZipperIteration] methods are changed to -/// take a [PathObserver], the depth will be tracked directly and the [ZipperPath] bound will go away. +/// `base_idx` is the [`depth`](ZipperMoving::depth) of the common root the traversal explores from, and +/// the traversal is finished when the depth returns to it. #[inline] -fn k_path_default_internal(z: &mut Z, k: usize, base_idx: usize) -> bool { +fn k_path_default_internal(z: &mut Z, k: usize, base_idx: usize, obs: &mut Obs) -> bool { loop { - if z.path().len() < base_idx + k { - while z.descend_first_byte().is_some() { - if z.path().len() == base_idx + k { return true } + if z.depth() < base_idx + k { + while let Some(byte) = z.descend_first_byte() { + obs.descend_to_byte(byte); + if z.depth() == base_idx + k { return true } } } - if z.to_next_sibling_byte().is_some() { - if z.path().len() == base_idx + k { return true } + //A sibling step replaces the last byte rather than adding one, so the observer sees the old + //byte retracted before the new one arrives + if let Some(byte) = z.to_next_sibling_byte() { + obs.ascend(1); + obs.descend_to_byte(byte); + if z.depth() == base_idx + k { return true } continue } - while z.path().len() > base_idx { - z.ascend_byte(); - if z.path().len() == base_idx { return false } - if z.to_next_sibling_byte().is_some() { break } + while z.depth() > base_idx { + if z.ascend_byte() { + obs.ascend(1); + } + if z.depth() == base_idx { return false } + if let Some(byte) = z.to_next_sibling_byte() { + obs.ascend(1); + obs.descend_to_byte(byte); + break + } } } } @@ -1068,9 +1133,10 @@ use zipper_priv::*; macro_rules! zipper_impl_lens { (ZipperIteration $s: ident => $e:expr) => { - fn to_next_val(&mut $s) -> bool { $e.to_next_val() } - fn descend_first_k_path(&mut $s, k: usize) -> bool { $e.descend_first_k_path(k) } - fn to_next_k_path(&mut $s, k: usize) -> bool { $e.to_next_k_path(k) } + fn to_next_val(&mut $s, obs: &mut Obs) -> bool { $e.to_next_val(obs) } + fn descend_last_path(&mut $s, obs: &mut Obs) -> bool { $e.descend_last_path(obs) } + fn descend_first_k_path(&mut $s, k: usize, obs: &mut Obs) -> bool { $e.descend_first_k_path(k, obs) } + fn to_next_k_path(&mut $s, k: usize, obs: &mut Obs) -> bool { $e.to_next_k_path(k, obs) } }; (ZipperAbsolutePath $s: ident => $e:expr) => { fn origin_path(&$s) -> &[u8] { $e.origin_path() } @@ -1100,6 +1166,7 @@ macro_rules! zipper_impl_lens { fn alloc(&$s) -> A { $e.alloc() } }; (ZipperMoving $s: ident => $e:expr) => { + #[inline] fn depth(&$s) -> usize { $e.depth() } fn at_root(&$s) -> bool { $e.at_root() } #[inline] fn focus_byte(&$s) -> Option { $e.focus_byte() } fn reset(&mut $s) { $e.reset() } @@ -1120,7 +1187,7 @@ macro_rules! zipper_impl_lens { fn ascend_byte(&mut $s) -> bool { $e.ascend_byte() } fn ascend_until(&mut $s) -> usize { $e.ascend_until() } fn ascend_until_branch(&mut $s) -> usize { $e.ascend_until_branch() } - fn to_next_step(&mut $s) -> bool { $e.to_next_step() } + fn to_next_step(&mut $s, obs: &mut Obs) -> bool { $e.to_next_step(obs) } }; (ZipperInfallibleSubtries $s: ident => $e:expr) => { fn make_map(&$s) -> crate::PathMap { $e.make_map() } @@ -1138,10 +1205,10 @@ macro_rules! zipper_impl_lens { fn get_val_with_witness<'w>(&$s, witness: &'w Self::WitnessT) -> Option<&'w V> where 'a: 'w { $e.get_val_with_witness(witness) } }; (ZipperReadOnlyIteration $s: ident => $e:expr) => { - fn to_next_get_val(&mut $s) -> Option<&'a V> { $e.to_next_get_val() } + fn to_next_get_val(&mut $s, obs: &mut Obs) -> Option<&'a V> { $e.to_next_get_val(obs) } }; (ZipperReadOnlyConditionalIteration $s: ident => $e:expr) => { - fn to_next_get_val_with_witness<'w>(&mut $s, witness: &'w Self::WitnessT) -> Option<&'w V> where 'a: 'w { $e.to_next_get_val_with_witness(witness) } + fn to_next_get_val_with_witness<'w, Obs: PathObserver>(&mut $s, witness: &'w Self::WitnessT, obs: &mut Obs) -> Option<&'w V> where 'a: 'w { $e.to_next_get_val_with_witness(witness, obs) } }; (ZipperReadOnlySubtries $s: ident => $e:expr) => { type TrieRefT = >::TrieRefT; @@ -1376,7 +1443,7 @@ impl<'a, V: Clone + Send + Sync + Unpin + 'a, A: Allocator + 'a> ZipperReadOnlyP impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperReadOnlyIteration<'trie, V> for ReadZipperUntracked<'trie, '_, V, A> { - fn to_next_get_val(&mut self) -> Option<&'trie V> { unsafe{ self.z.to_next_get_val() } } + fn to_next_get_val(&mut self, obs: &mut Obs) -> Option<&'trie V> { unsafe{ self.z.to_next_get_val(obs) } } } impl<'a, 'path, V: Clone + Send + Sync + Unpin + 'a, A: Allocator + 'a> ReadZipperUntracked<'a, 'path, V, A> { @@ -1825,6 +1892,11 @@ pub(crate) mod read_zipper_core { impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperMoving for ReadZipperCore<'trie, '_, V, A> { + #[inline] + fn depth(&self) -> usize { + self.prefix_buf.len().saturating_sub(self.origin_path.len()) + } + fn at_root(&self) -> bool { self.prefix_buf.len() <= self.origin_path.len() } @@ -2393,28 +2465,28 @@ pub(crate) mod read_zipper_core { //Then I need to port all the iter() conveniences over to use that new method impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperIteration for ReadZipperCore<'trie, '_, V, A> { - fn to_next_val(&mut self) -> bool { - unsafe{ self.to_next_get_val() }.is_some() + fn to_next_val(&mut self, obs: &mut Obs) -> bool { + unsafe{ self.to_next_get_val(obs) }.is_some() } - fn descend_first_k_path(&mut self, k: usize) -> bool { + fn descend_first_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { self.prepare_buffers(); debug_assert!(self.is_regularized()); let cur_tok = self.focus_node.iter_token_for_path(self.node_key()); self.focus_iter_token = cur_tok; - self.k_path_internal(k, self.prefix_buf.len()) + self.k_path_internal(k, self.prefix_buf.len(), obs) } - fn to_next_k_path(&mut self, k: usize) -> bool { + fn to_next_k_path(&mut self, k: usize, obs: &mut Obs) -> bool { let base_idx = if self.path_len() >= k { self.prefix_buf.len() - k } else { - self.origin_path.len() + return false }; //De-regularize the zipper debug_assert!(self.is_regularized()); self.deregularize(); - self.k_path_internal(k, base_idx) + self.k_path_internal(k, base_idx, obs) } } @@ -2643,7 +2715,12 @@ pub(crate) mod read_zipper_core { } /// See [ReadZipperCore::get_val] for explanation as to why this is unsafe - pub(crate) unsafe fn to_next_get_val(&mut self) -> Option<&'a V> { + /// + /// `obs` is notified of every movement made. This zipper advances by rewinding `prefix_buf` + /// to a node boundary and then extending it by a whole key run, so the movements reported are + /// multi-byte and a descent may re-tread bytes the observer already held. The net position is + /// always correct; only the granularity differs from a byte-at-a-time traversal. + pub(crate) unsafe fn to_next_get_val(&mut self, obs: &mut Obs) -> Option<&'a V> { self.prepare_buffers(); loop { if self.focus_iter_token == NODE_ITER_INVALID { @@ -2670,13 +2747,25 @@ pub(crate) mod read_zipper_core { let unmodifiable_len = origin_path_len - key_start; let unmodifiable_subkey = &self.prefix_buf[key_start..origin_path_len]; if unmodifiable_len > key_bytes.len() || &key_bytes[..unmodifiable_len] != unmodifiable_subkey { + obs.ascend(self.prefix_buf.len() - origin_path_len); self.prefix_buf.truncate(origin_path_len); return None } } + //The truncate may rewind past `origin_path_len`, but the bytes below the root are + //re-supplied identically by `key_bytes` (checked above), so the focus stays at or + //below the root. The observer tracks the path from the root, so `key_bytes` is + //reported from the point where the rewind lands relative to it. + //`get` rather than indexing, so the slicing carries no panic path and can be + //optimized away entirely when `obs` discards what it is given + let obs_skip = origin_path_len.saturating_sub(key_start); + obs.ascend(self.prefix_buf.len() - key_start - obs_skip); self.prefix_buf.truncate(key_start); self.prefix_buf.extend(key_bytes); + if let Some(reported) = key_bytes.get(obs_skip..) { + obs.descend_to(reported); + } match child_node { None => {}, @@ -2696,10 +2785,12 @@ pub(crate) mod read_zipper_core { if let Some((focus_node, iter_tok, prefix_offset)) = self.ancestors.pop() { *self.focus_node = focus_node; self.focus_iter_token = iter_tok; + obs.ascend(self.prefix_buf.len() - prefix_offset); self.prefix_buf.truncate(prefix_offset); } else { let new_len = self.origin_path.len(); self.focus_iter_token = NODE_ITER_INVALID; + obs.ascend(self.prefix_buf.len() - new_len); self.prefix_buf.truncate(new_len); return None } @@ -2829,8 +2920,13 @@ pub(crate) mod read_zipper_core { } /// Internal method that implements both `k_path...` methods above + /// + /// `obs` is notified of every movement made. As in [Self::to_next_get_val], the movements are + /// multi-byte because this zipper advances by rewinding `prefix_buf` to a node boundary and + /// extending it by a whole key run. #[inline] - fn k_path_internal(&mut self, k: usize, base_idx: usize) -> bool { + fn k_path_internal(&mut self, k: usize, base_idx: usize, obs: &mut Obs) -> bool { + let obs_floor = self.origin_path.len(); loop { //If either of these trip, the caller is probably misusing the API and likely didn't call // `descend_first_k_path` before calling `to_next_k_path` @@ -2858,6 +2954,7 @@ pub(crate) mod read_zipper_core { //Have we reached the root of this k_path iteration? if self.node_key_start() <= base_idx { self.focus_iter_token = NODE_ITER_FINISHED; + obs.ascend(self.prefix_buf.len().saturating_sub(base_idx.max(obs_floor))); self.prefix_buf.truncate(base_idx); return false } @@ -2865,10 +2962,12 @@ pub(crate) mod read_zipper_core { if let Some((focus_node, iter_tok, prefix_offset)) = self.ancestors.pop() { *self.focus_node = focus_node; self.focus_iter_token = iter_tok; + obs.ascend(self.prefix_buf.len().saturating_sub(prefix_offset.max(obs_floor))); self.prefix_buf.truncate(prefix_offset); } else { let new_len = self.origin_path.len(); self.focus_iter_token = NODE_ITER_INVALID; + obs.ascend(self.prefix_buf.len().saturating_sub(new_len)); self.prefix_buf.truncate(new_len); return false } @@ -2884,6 +2983,7 @@ pub(crate) mod read_zipper_core { if key_start < base_idx { let base_key_len = base_idx - key_start; //The number of bytes we should not modify if base_key_len > key_bytes.len() || &key_bytes[..base_key_len] != &self.prefix_buf[key_start..base_idx] { + obs.ascend(self.prefix_buf.len().saturating_sub(base_idx.max(obs_floor))); self.prefix_buf.truncate(base_idx); return false; } @@ -2894,8 +2994,17 @@ pub(crate) mod read_zipper_core { //If we got here, it means we're either going to continue to descend, or return a // result at `path_len == base_idx+k` let key_start = self.node_key_start(); + //`key_bytes` re-supplies everything from `key_start`, so the observer retreats to + //that point (or the origin, whichever is lower) and re-descends + //`get` rather than indexing, so the slicing carries no panic path and can be + //optimized away entirely when `obs` discards what it is given + let obs_skip = obs_floor.saturating_sub(key_start); + obs.ascend(self.prefix_buf.len().saturating_sub(key_start.max(obs_floor))); self.prefix_buf.truncate(key_start); self.prefix_buf.extend(key_bytes); + if let Some(reported) = key_bytes.get(obs_skip..) { + obs.descend_to(reported); + } if self.prefix_buf.len() <= k+base_idx { match child_node { @@ -2907,6 +3016,8 @@ pub(crate) mod read_zipper_core { }, } } else { + //The descent overshot `k`, so retract the excess from the observer as well + obs.ascend(self.prefix_buf.len() - (k+base_idx)); self.prefix_buf.truncate(k+base_idx); } @@ -3193,7 +3304,7 @@ impl<'a, V: Clone + Send + Sync + Unpin + 'a, A: Allocator + 'a> Iterator for Re } if let Some(zipper) = &mut self.zipper { //SAFETY: we only allow ReadZipperUntracked to become a `ReadZipperIter` - match unsafe{ zipper.to_next_get_val() } { + match unsafe{ zipper.to_next_get_val(&mut ()) } { Some(val) => return Some((zipper.path().to_vec(), val)), None => self.zipper = None } @@ -3223,7 +3334,7 @@ impl<'a, V: Clone + Send + Sync + Unpin + 'a, A: Allocator + 'a> Iterator for Re fn next(&mut self) -> Option> { if let Some(zipper) = &mut self.zipper { - while zipper.to_next_step() { + while zipper.to_next_step(&mut ()) { if zipper.path_exists() && zipper.child_count() == 0 { return Some(zipper.path().to_vec()); } @@ -3540,6 +3651,7 @@ pub(crate) mod zipper_moving_tests { assert_in_list(zipper.path(), &[b"roma", b"romu"]); assert_eq!(zipper.to_prev_sibling_byte(), Some(39)); // focus = rom' (we stepped back to where we began) assert_eq!(zipper.path(), b"rom'"); + assert_eq!(zipper.depth(), zipper.path().len()); assert_eq!(zipper.child_mask().iter().collect::>(), vec![b'i']); assert_eq!(zipper.ascend(1), 1); // focus = rom assert_eq!(zipper.child_mask().iter().collect::>(), vec![b'\'', b'a', b'u']); // all three options we visited @@ -3573,6 +3685,8 @@ pub(crate) mod zipper_moving_tests { assert_eq!(zipper.child_count(), 3); zipper.descend_to(b"an"); assert_eq!(zipper.path(), b"man"); + //`depth` is relative to the zipper's root, not the map root + assert_eq!(zipper.depth(), zipper.path().len()); assert_eq!(zipper.child_count(), 2); zipper.descend_to(b"e"); assert_eq!(zipper.path(), b"mane"); @@ -3589,6 +3703,7 @@ pub(crate) mod zipper_moving_tests { assert_eq!(zipper.child_count(), 3); assert_eq!(zipper.ascend_until(), 1); assert_eq!(zipper.path(), b""); + assert_eq!(zipper.depth(), 0); assert_eq!(zipper.child_count(), 1); assert_eq!(zipper.at_root(), true); assert_eq!(zipper.ascend_until(), 0); @@ -3763,12 +3878,16 @@ pub(crate) mod zipper_moving_tests { zip.descend_to(b"AAaDDd"); assert!(!zip.path_exists()); assert_eq!(zip.path(), b"AAaDDd"); + assert_eq!(zip.depth(), zip.path().len()); assert_eq!(zip.ascend_until(), 3); assert_eq!(zip.path(), b"AAa"); + assert_eq!(zip.depth(), zip.path().len()); assert_eq!(zip.ascend_until(), 1); assert_eq!(zip.path(), b"AA"); + assert_eq!(zip.depth(), zip.path().len()); assert_eq!(zip.ascend_until(), 2); assert_eq!(zip.path(), b""); + assert_eq!(zip.depth(), 0); assert_eq!(zip.ascend_until(), 0); } @@ -4010,7 +4129,7 @@ pub(crate) mod zipper_moving_tests { assert_eq!(zipper.child_mask(), [0, 1<<(b'a'-64) | 1<<(b'b'-64) | 1<<(b'c'-64) | 1<<(b'r'-64), 0, 0]); let mut i = 0; - while zipper.to_next_step() { + while zipper.to_next_step(&mut ()) { match i { //'r' descending from 'a' in "arrow" 0 => assert_eq!(zipper.child_mask(), [0, 1<<(b'r'-64), 0, 0]), @@ -4077,7 +4196,9 @@ pub(crate) mod zipper_moving_tests { pub fn to_next_step_test1(mut zipper: Z) { let mut i = 0; - while zipper.to_next_step() { + let mut observed = Vec::::new(); + while zipper.to_next_step(&mut observed) { + assert_eq!(&observed[..], zipper.path(), "observer disagreed with path at step {i}"); match i { 0 => assert_eq!(zipper.path(), b"a"), 4 => assert_eq!(zipper.path(), b"arrow"), @@ -4351,15 +4472,17 @@ pub(crate) mod zipper_iteration_tests { pub const ZIPPER_ITER_TEST1_KEYS: &[&[u8]] = &[b"arrow", b"bow", b"cannon", b"rom'i", b"roman", b"romane", b"romanus", b"romulus", b"rubens", b"ruber", b"rubicon", b"rubicundus"]; /// Simply calls `to_next_val` over the whole trie, ensuring all paths are visited exactly once - pub fn zipper_iter_test1<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn zipper_iter_test1<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { let keys = ZIPPER_ITER_TEST1_KEYS; //Test iteration of the whole tree let mut idx = 0; + let mut observed = Vec::::new(); assert_eq!(zipper.is_val(), false); - while zipper.to_next_val() { + while zipper.to_next_val(&mut observed) { println!("{idx} {}", std::str::from_utf8(zipper.path()).unwrap()); assert_eq!(keys[idx], zipper.path()); + assert_eq!(&observed[..], zipper.path(), "observer disagreed with path at value {idx}"); idx += 1; } assert_eq!(idx, keys.len()); @@ -4372,13 +4495,15 @@ pub(crate) mod zipper_iteration_tests { }).collect() } - pub fn zipper_iter_test2<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn zipper_iter_test2<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { //Test iterating using a zipper that has a root that is not the map root let mut count: usize = 0; - while zipper.to_next_val() { + let mut observed = Vec::::new(); + while zipper.to_next_val(&mut observed) { assert_eq!(zipper.is_val(), true); assert_eq!(zipper.path(), count.to_be_bytes()); + assert_eq!(&observed[..], zipper.path(), "observer disagreed with path at value {count}"); count += 1; } assert_eq!(count, ZIPPER_ITER_TEST2_COUNT); @@ -4397,7 +4522,7 @@ pub(crate) mod zipper_iteration_tests { b":9:muckymuck:5:raker:", ]; - pub fn k_path_test1<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test1<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { //This is a cheesy way to encode lengths, but it's is more readable than unprintable chars assert!(zipper.descend_indexed_byte(0).is_some()); @@ -4408,36 +4533,37 @@ pub(crate) mod zipper_iteration_tests { assert!(zipper.descend_indexed_byte(0).is_some()); assert_eq!(zipper.child_count(), 6); + //The observer starts in sync with the zipper, and must track it through every k_path call + let mut observed = zipper.path().to_vec(); + //Start iterating over all the symbols of length=sym_len - assert_eq!(zipper.descend_first_k_path(sym_len+1), true); + assert_eq!(zipper.descend_first_k_path(sym_len+1, &mut observed), true); //This should have taken us to "above:" assert_eq!(zipper.path(), b"5:above:"); + assert_eq!(&observed[..], zipper.path()); //Go to the next symbol. // Two interesting things will happen. First, we blow past "err" because its path length is // shorter than `k`. Second, we will stop in the middle of "erronious". // These situations would be caused by an encode bug. Which hopefully we won't have in real // paths. But the parser should recognize the last digit of the path isn't ':' - assert_eq!(zipper.to_next_k_path(sym_len+1), true); + assert_eq!(zipper.to_next_k_path(sym_len+1, &mut observed), true); assert_eq!(zipper.path(), b"5:erroni"); + assert_eq!(&observed[..], zipper.path()); assert_ne!(zipper.path().last(), Some(&b':')); //Now we'll move on to some correctly formed symbols - assert_eq!(zipper.to_next_k_path(sym_len+1), true); - assert_eq!(zipper.path(), b"5:error:"); - assert_eq!(zipper.to_next_k_path(sym_len+1), true); - assert_eq!(zipper.path(), b"5:hello:"); - assert_eq!(zipper.to_next_k_path(sym_len+1), true); - assert_eq!(zipper.path(), b"5:mucky:"); - assert_eq!(zipper.to_next_k_path(sym_len+1), true); - assert_eq!(zipper.path(), b"5:roger:"); - assert_eq!(zipper.to_next_k_path(sym_len+1), true); - assert_eq!(zipper.path(), b"5:zebra:"); + for expected in [b"5:error:", b"5:hello:", b"5:mucky:", b"5:roger:", b"5:zebra:"] { + assert_eq!(zipper.to_next_k_path(sym_len+1, &mut observed), true); + assert_eq!(zipper.path(), expected); + assert_eq!(&observed[..], zipper.path()); + } //The last step should return false, and put us back to where we began - assert_eq!(zipper.to_next_k_path(sym_len+1), false); + assert_eq!(zipper.to_next_k_path(sym_len+1, &mut observed), false); assert_eq!(zipper.path(), b"5:"); + assert_eq!(&observed[..], zipper.path()); assert_eq!(zipper.child_count(), 6); } @@ -4449,11 +4575,13 @@ pub(crate) mod zipper_iteration_tests { }).collect() } - pub fn k_path_test2<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test2<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { - zipper.descend_first_k_path(5); + let mut observed = Vec::::new(); + zipper.descend_first_k_path(5, &mut observed); let mut count = 1; - while zipper.to_next_k_path(5) { + while zipper.to_next_k_path(5, &mut observed) { + assert_eq!(&observed[..], zipper.path()); count += 1; } assert_eq!(count, K_PATH_TEST2_COUNT); @@ -4461,77 +4589,87 @@ pub(crate) mod zipper_iteration_tests { pub const K_PATH_TEST3_KEYS: &[&[u8]] = &[b":1a1A", b":1a1B", b":1a1C", b":1b1A", b":1b1B", b":1b1C", b":1c1A"]; - pub fn k_path_test3<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test3<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { //Scan over the first symbols in the path (lower case letters) zipper.descend_to(b"1"); assert_eq!(zipper.path_exists(), true); - assert_eq!(zipper.descend_first_k_path(1), true); + assert_eq!(zipper.descend_first_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1b"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1c"); - assert_eq!(zipper.to_next_k_path(1), false); + assert_eq!(zipper.to_next_k_path(1, &mut ()), false); assert_eq!(zipper.path(), b"1"); //Scan over the nested second symbols in the path (upper case letters) zipper.reset(); zipper.descend_to(b"1a1"); assert!(zipper.path_exists()); - assert_eq!(zipper.descend_first_k_path(1), true); + assert_eq!(zipper.descend_first_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a1A"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a1B"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a1C"); - assert_eq!(zipper.to_next_k_path(1), false); + assert_eq!(zipper.to_next_k_path(1, &mut ()), false); assert_eq!(zipper.path(), b"1a1"); //Recursively scan nested symbols within a scan of the first outer symbols + //A single observer tracks this whole nested sequence, since the only movements are k_path calls zipper.reset(); zipper.descend_to(b"1"); assert!(zipper.path_exists()); - assert_eq!(zipper.descend_first_k_path(1), true); + let mut observed = zipper.path().to_vec(); + assert_eq!(zipper.descend_first_k_path(1, &mut observed), true); assert_eq!(zipper.path(), b"1a"); - assert_eq!(zipper.descend_first_k_path(2), true); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.descend_first_k_path(2, &mut observed), true); assert_eq!(zipper.path(), b"1a1A"); - assert_eq!(zipper.to_next_k_path(2), true); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(2, &mut observed), true); assert_eq!(zipper.path(), b"1a1B"); - assert_eq!(zipper.to_next_k_path(2), true); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(2, &mut observed), true); assert_eq!(zipper.path(), b"1a1C"); - assert_eq!(zipper.to_next_k_path(2), false); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(2, &mut observed), false); assert_eq!(zipper.path(), b"1a"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(1, &mut observed), true); assert_eq!(zipper.path(), b"1b"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(1, &mut observed), true); assert_eq!(zipper.path(), b"1c"); - assert_eq!(zipper.to_next_k_path(1), false); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(1, &mut observed), false); assert_eq!(zipper.path(), b"1"); + assert_eq!(&observed[..], zipper.path()); //Similar to above, but inter-operating with `descend_indexed_byte` zipper.reset(); zipper.descend_to(b"1"); assert!(zipper.path_exists()); - assert_eq!(zipper.descend_first_k_path(1), true); + assert_eq!(zipper.descend_first_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a"); assert!(zipper.descend_indexed_byte(0).is_some()); assert_eq!(zipper.path(), b"1a1"); - assert_eq!(zipper.descend_first_k_path(1), true); + assert_eq!(zipper.descend_first_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a1A"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a1B"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1a1C"); - assert_eq!(zipper.to_next_k_path(1), false); + assert_eq!(zipper.to_next_k_path(1, &mut ()), false); assert_eq!(zipper.path(), b"1a1"); assert_eq!(zipper.ascend(1), 1); assert_eq!(zipper.path(), b"1a"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1b"); - assert_eq!(zipper.to_next_k_path(1), true); + assert_eq!(zipper.to_next_k_path(1, &mut ()), true); assert_eq!(zipper.path(), b"1c"); - assert_eq!(zipper.to_next_k_path(1), false); + assert_eq!(zipper.to_next_k_path(1, &mut ()), false); assert_eq!(zipper.path(), b"1"); } @@ -4588,11 +4726,13 @@ pub(crate) mod zipper_iteration_tests { &[192, 215, 69, 171, 218, 187, 202, 120, 92, 33, 14, 77, 34, 46, 40, 93, 135, 117, 152], ]; - pub fn k_path_test4<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test4<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { - zipper.descend_first_k_path(5); + let mut observed = Vec::::new(); + zipper.descend_first_k_path(5, &mut observed); let mut count = 1; - while zipper.to_next_k_path(5) { + while zipper.to_next_k_path(5, &mut observed) { + assert_eq!(&observed[..], zipper.path()); count += 1; } assert_eq!(count, K_PATH_TEST4_KEYS.len()); @@ -4608,13 +4748,17 @@ pub(crate) mod zipper_iteration_tests { &[3, 193, 4, 194, 1, 43, 3, 193, 34, 193], ]; - pub fn k_path_test5<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test5<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { zipper.descend_to(&[3, 193, 4, 194, 1, 43, 3, 193, 8, 194, 1, 45, 194]); assert!(zipper.path_exists()); - assert_eq!(zipper.descend_first_k_path(2), true); + //This straddles a node boundary, so it exercises the multi-byte movement reporting + let mut observed = zipper.path().to_vec(); + assert_eq!(zipper.descend_first_k_path(2, &mut observed), true); assert_eq!(zipper.path(), &[3, 193, 4, 194, 1, 43, 3, 193, 8, 194, 1, 45, 194, 1, 46]); - assert_eq!(zipper.to_next_k_path(2), false); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(2, &mut observed), false); assert_eq!(zipper.path(), &[3, 193, 4, 194, 1, 43, 3, 193, 8, 194, 1, 45, 194]); + assert_eq!(&observed[..], zipper.path()); } pub const K_PATH_TEST6_KEYS: &[&[u8]] = &[ @@ -4624,40 +4768,40 @@ pub(crate) mod zipper_iteration_tests { /// This tests the k_path methods in the context of using them recursively, to shake out /// bugs caused by invalidating the iter token - pub fn k_path_test6<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test6<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { - fn test_loop<'a, Z: ZipperMoving + ZipperIteration, AscendF: Fn(&mut Z, usize), DescendF: Fn(&mut Z, &[u8])>(zipper: &mut Z, descend_f: DescendF, ascend_f: AscendF) { + fn test_loop<'a, Z: ZipperMoving + ZipperIteration + ZipperPath, AscendF: Fn(&mut Z, usize), DescendF: Fn(&mut Z, &[u8])>(zipper: &mut Z, descend_f: DescendF, ascend_f: AscendF) { zipper.reset(); //L0 descent descend_f(zipper, &[2, 197, 97, 120, 105, 111, 109, 3, 193, 61, 4, 193, 97, 192, 192, 3, 193]); - assert!(zipper.descend_first_k_path(1)); + assert!(zipper.descend_first_k_path(1, &mut ())); assert_eq!(zipper.path(), &[2, 197, 97, 120, 105, 111, 109, 3, 193, 61, 4, 193, 97, 192, 192, 3, 193, 75]); //L1 descent descend_f(zipper, &[192, 3, 193, 84, 192, 3, 193]); - assert!(zipper.descend_first_k_path(1)); + assert!(zipper.descend_first_k_path(1, &mut ())); assert_eq!(zipper.path(), &[2, 197, 97, 120, 105, 111, 109, 3, 193, 61, 4, 193, 97, 192, 192, 3, 193, 75, 192, 3, 193, 84, 192, 3, 193, 75]); //L2 descent descend_f(zipper, &[128, 131, 193]); - assert!(zipper.descend_first_k_path(1)); + assert!(zipper.descend_first_k_path(1, &mut ())); assert_eq!(zipper.path(), &[2, 197, 97, 120, 105, 111, 109, 3, 193, 61, 4, 193, 97, 192, 192, 3, 193, 75, 192, 3, 193, 84, 192, 3, 193, 75, 128, 131, 193, 49]); //L2 next and ascent - assert!(!zipper.to_next_k_path(1)); + assert!(!zipper.to_next_k_path(1, &mut ())); assert_eq!(zipper.path(), &[2, 197, 97, 120, 105, 111, 109, 3, 193, 61, 4, 193, 97, 192, 192, 3, 193, 75, 192, 3, 193, 84, 192, 3, 193, 75, 128, 131, 193]); ascend_f(zipper, 3); //L1 next and ascent - assert!(!zipper.to_next_k_path(1)); + assert!(!zipper.to_next_k_path(1, &mut ())); assert_eq!(zipper.path(), &[2, 197, 97, 120, 105, 111, 109, 3, 193, 61, 4, 193, 97, 192, 192, 3, 193, 75, 192, 3, 193, 84, 192, 3, 193]); ascend_f(zipper, 7); //L0 next - assert!(zipper.to_next_k_path(1)); + assert!(zipper.to_next_k_path(1, &mut ())); assert_eq!(zipper.path(), &[2, 197, 97, 120, 105, 111, 109, 3, 193, 61, 4, 193, 97, 192, 192, 3, 193, 84]); ascend_f(zipper, 17); @@ -4708,20 +4852,24 @@ pub(crate) mod zipper_iteration_tests { ]; /// This uses the k_path methods to descend and then re-ascend a trie, one step at a time. - pub fn k_path_test7<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test7<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { let keys = K_PATH_TEST7_KEYS; + //Only k_path calls move this zipper, so one observer tracks the whole descent and re-ascent + let mut observed = Vec::::new(); for i in 0..keys[0].len() { assert_eq!(zipper.path(), &keys[0][..i]); - assert!(zipper.descend_first_k_path(1)); + assert_eq!(&observed[..], zipper.path()); + assert!(zipper.descend_first_k_path(1, &mut observed)); } for i in (0..keys[0].len()).rev() { assert_eq!(zipper.path(), &keys[0][..=i]); + assert_eq!(&observed[..], zipper.path()); if i != 17 { - assert!(!zipper.to_next_k_path(1)); + assert!(!zipper.to_next_k_path(1, &mut observed)); } else { - assert!(zipper.to_next_k_path(1)); - assert!(!zipper.to_next_k_path(1)); + assert!(zipper.to_next_k_path(1, &mut observed)); + assert!(!zipper.to_next_k_path(1, &mut observed)); } } } @@ -4729,15 +4877,18 @@ pub(crate) mod zipper_iteration_tests { pub const K_PATH_TEST8_KEYS: &[&[u8]] = &[b"ABCDEFGHIJKLMNOPQRSTUVWXYZ", b"ab",]; /// Tests `..k_path` after `descend_to_byte` - pub fn k_path_test8<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test8<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { zipper.reset(); zipper.descend_to_byte(b'A'); assert_eq!(zipper.path_exists(), true); - assert_eq!(zipper.descend_first_k_path(1), true); + let mut observed = zipper.path().to_vec(); + assert_eq!(zipper.descend_first_k_path(1, &mut observed), true); assert_eq!(zipper.path(), b"AB"); - assert_eq!(zipper.to_next_k_path(1), false); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(1, &mut observed), false); assert_eq!(zipper.path(), b"A"); + assert_eq!(&observed[..], zipper.path()); } pub const K_PATH_TEST9_KEYS: &[&[u8]] = &[ @@ -4747,13 +4898,16 @@ pub(crate) mod zipper_iteration_tests { ]; /// Tests `..k_path` in a subtrie without attitional branches to descend, when the outer trie does have branches - pub fn k_path_test9<'a, Z: ZipperIteration>(mut zipper: Z) { + pub fn k_path_test9<'a, Z: ZipperIteration + ZipperPath>(mut zipper: Z) { zipper.reset(); - assert_eq!(zipper.descend_first_k_path(1), true); + let mut observed = Vec::::new(); + assert_eq!(zipper.descend_first_k_path(1, &mut observed), true); assert_eq!(zipper.path(), &[1]); - assert_eq!(zipper.to_next_k_path(1), false); + assert_eq!(&observed[..], zipper.path()); + assert_eq!(zipper.to_next_k_path(1, &mut observed), false); assert_eq!(zipper.path(), &[]); + assert_eq!(&observed[..], zipper.path()); } } @@ -4979,7 +5133,7 @@ mod tests { //Fork a sub-zipper, and test iteration of that subtree zipper.descend_to(b"rub"); let mut sub_zipper = zipper.fork_read_zipper(); - while let Some(&val) = sub_zipper.to_next_get_val() { + while let Some(&val) = sub_zipper.to_next_get_val(&mut ()) { // println!("{val} {} = {}", std::str::from_utf8(sub_zipper.path()).unwrap(), std::str::from_utf8(&rs[val].as_bytes()[3..]).unwrap()); assert_eq!(&rs[val].as_bytes()[3..], sub_zipper.path()); } @@ -4997,9 +5151,12 @@ mod tests { //This tests iteration over an empty map, with no activity at all let mut map = PathMap::::new(); + let mut observed = Vec::::new(); let mut zipper = map.read_zipper(); - assert_eq!(zipper.to_next_val(), false); - assert_eq!(zipper.to_next_val(), false); + assert_eq!(zipper.to_next_val(&mut observed), false); + assert_eq!(zipper.to_next_val(&mut observed), false); + //A `false` return means the zipper came back to its root, so the observer must be empty + assert_eq!(&observed[..], b""); drop(zipper); //Now test some operations that create nodes, but not values @@ -5008,9 +5165,11 @@ mod tests { drop(_wz); drop(map_head); + let mut observed = Vec::::new(); let mut zipper = map.read_zipper(); - assert_eq!(zipper.to_next_val(), false); - assert_eq!(zipper.to_next_val(), false); + assert_eq!(zipper.to_next_val(&mut observed), false); + assert_eq!(zipper.to_next_val(&mut observed), false); + assert_eq!(&observed[..], b""); } /// Tests iterating a subtrie created by a descended WriteZipper @@ -5031,7 +5190,7 @@ mod tests { let mut reader_z = map.read_zipper_at_path(b"in"); assert_eq!(reader_z.val_count(), N); let mut count = 0; - while let Some(val) = reader_z.to_next_get_val() { + while let Some(val) = reader_z.to_next_get_val(&mut ()) { assert_eq!(reader_z.get_val(), Some(val)); assert_eq!(reader_z.get_val(), Some(&count)); assert_eq!(reader_z.path(), count.to_be_bytes()); @@ -5095,7 +5254,7 @@ mod tests { let mut reader_z = map.read_zipper_at_path([b'i', b'n', 1]); let mut sanity_counter = 0; - while reader_z.to_next_val() { + while reader_z.to_next_val(&mut ()) { sanity_counter += 1; } assert_eq!(sanity_counter, 1); @@ -5150,7 +5309,7 @@ mod tests { full_parent_path.extend(parent_zipper.path()); assert!(family.path_exists_at(&full_parent_path)); - while parent_zipper.to_next_val() { + while parent_zipper.to_next_val(&mut ()) { let mut full_parent_path = parent_path.as_bytes().to_vec(); full_parent_path.extend(parent_zipper.path()); assert!(family.contains(&full_parent_path)); @@ -5169,14 +5328,18 @@ mod tests { zipper.descend_to(b"n"); assert_eq!(format!("roma{}", std::str::from_utf8(zipper.path()).unwrap()), "roman"); assert_eq!(std::str::from_utf8(zipper.origin_path()).unwrap(), "roman"); - zipper.to_next_val(); + let mut observed = zipper.path().to_vec(); + zipper.to_next_val(&mut observed); assert_eq!(format!("roma{}", std::str::from_utf8(zipper.path()).unwrap()), "romane"); assert_eq!(std::str::from_utf8(zipper.origin_path()).unwrap(), "romane"); - zipper.to_next_val(); + assert_eq!(&observed[..], zipper.path()); + zipper.to_next_val(&mut observed); assert_eq!(format!("roma{}", std::str::from_utf8(zipper.path()).unwrap()), "romanus"); assert_eq!(std::str::from_utf8(zipper.origin_path()).unwrap(), "romanus"); - zipper.to_next_val(); + assert_eq!(&observed[..], zipper.path()); + zipper.to_next_val(&mut observed); assert_eq!(zipper.path().len(), 0); + assert_eq!(observed.len(), 0); } #[test] @@ -5208,7 +5371,7 @@ mod tests { let mut rz = map.read_zipper(); let mut shared_cnt = 0; - while rz.to_next_step() { + while rz.to_next_step(&mut ()) { if rz.is_shared() { // println!("{}", String::from_utf8_lossy(rz.path())); shared_cnt += 1; @@ -5263,7 +5426,7 @@ mod tests { let mut rz = map.read_zipper(); let mut shared_cnt = 0; - while rz.to_next_step() { + while rz.to_next_step(&mut ()) { if rz.is_shared() { // println!("{}", String::from_utf8_lossy(rz.path())); shared_cnt += 1; @@ -5337,7 +5500,9 @@ mod tests { let btm: PathMap = rs.into_iter().enumerate().map(|(i, k)| (k, i as u64)).collect(); let mut rz = btm.read_zipper(); - assert!(rz.descend_last_path()); + let mut observed = Vec::::new(); + assert!(rz.descend_last_path(&mut observed)); assert_eq!(rz.path(), b"rubicundus"); + assert_eq!(&observed[..], rz.path()); } } diff --git a/src/zipper_head.rs b/src/zipper_head.rs index 0b8305e3..bf47bb68 100644 --- a/src/zipper_head.rs +++ b/src/zipper_head.rs @@ -559,7 +559,7 @@ mod tests { Ok((mut reader_z, mut writer_z)) => { //We got the zippers, do the stuff let witness = reader_z.witness(); - while let Some(val) = reader_z.to_next_get_val_with_witness(&witness) { + while let Some(val) = reader_z.to_next_get_val_with_witness(&witness, &mut ()) { writer_z.descend_to(reader_z.path()); writer_z.set_val(*val); writer_z.reset(); diff --git a/src/zipper_tracking.rs b/src/zipper_tracking.rs index ce046f36..c69d1f90 100644 --- a/src/zipper_tracking.rs +++ b/src/zipper_tracking.rs @@ -135,9 +135,9 @@ impl Conflict { /* at this point zipper is either focued on the given path (when it exists) , or the procedure broke out early, because it was determined that the path does not exist */ { - if zipper.path().len() == path.len() { + if zipper.depth() == path.len() { let mut subtree = zipper.fork_read_zipper(); - match subtree.to_next_val() { + match subtree.to_next_val(&mut ()) { false => Ok(()), true => Err(conflict_f(subtree.origin_path())), } @@ -157,9 +157,9 @@ impl Conflict { let mut zipper = all_paths.read_zipper(); match Conflict::check_for_lock_along_path(path, &mut zipper) { None => { - if zipper.path().len() == path.len() { + if zipper.depth() == path.len() { let mut subtree = zipper.fork_read_zipper(); - match subtree.to_next_get_val() { + match subtree.to_next_get_val(&mut ()) { None => Ok(()), Some(lock) => Err(conflict_f( *lock,