diff --git a/src/overlay_zipper.rs b/src/overlay_zipper.rs index 211c8aa3..3cb88a2d 100644 --- a/src/overlay_zipper.rs +++ b/src/overlay_zipper.rs @@ -159,6 +159,10 @@ impl ZipperMoving #[inline] fn focus_byte(&self) -> Option { + //The sources may be rooted at different paths, so at the root their bytes differ + if self.depth() == 0 { + return None; + } let byte = self.a.focus_byte(); debug_assert_eq!(byte, self.b.focus_byte()); byte @@ -196,27 +200,37 @@ impl ZipperMoving fn descend_to_val>(&mut self, path: K) -> usize { let path = path.as_ref(); - let depth_a = self.a.descend_to_val(path); - let depth_o = self.b.descend_to_val(path); - if depth_a < depth_o { - if self.a.is_val() { - self.b.ascend(depth_o - depth_a); - depth_a - } else { - self.a.descend_to(&path[depth_a..depth_o]); - depth_o - } - } else if depth_o < depth_a { - if self.b.is_val() { - self.a.ascend(depth_a - depth_o); - depth_o + let mut descended = 0; + while descended < path.len() { + let remaining = &path[descended..]; + let depth_a = self.a.descend_to_val(remaining); + let depth_b = self.b.descend_to_val(remaining); + // A source at a value can return zero without finding a new value along the path. + let advanced = if depth_a < depth_b { + if depth_a > 0 && self.a.is_val() { + self.b.ascend(depth_b - depth_a); + depth_a + } else { + self.a.descend_to(&remaining[depth_a..depth_b]); + depth_b + } + } else if depth_b < depth_a { + if depth_b > 0 && self.b.is_val() { + self.a.ascend(depth_a - depth_b); + depth_b + } else { + self.b.descend_to(&remaining[depth_b..depth_a]); + depth_a + } } else { - self.a.descend_to(&path[depth_o..depth_a]); depth_a + }; + descended += advanced; + if advanced == 0 || self.is_val() { + break; } - } else { - depth_a } + descended } fn descend_to_byte(&mut self, k: u8) { @@ -410,6 +424,7 @@ mod tests { zipper_moving_tests, ZipperMoving, ZipperPath, + ZipperValues, OverlayZipper }, }; @@ -577,4 +592,65 @@ mod tests { assert_eq!(moved, true); assert_eq!(observed, oz.path(), "observer must match the resulting path"); } + + /// Sources rooted at different paths: no focus byte, and no sibling step, at the root + #[test] + fn overlay_sources_at_different_roots() { + use crate::zipper::ZipperIteration; + let mut a = PathMap::::new(); + for p in [&[1u8, 5][..], &[1, 6], &[2, 5, 1]] { a.set_val_at(p, 1); } + let mut z = OverlayZipper::new(a.read_zipper_at_path(&[1u8]), a.read_zipper_at_path(&[2u8])); + assert_eq!(z.focus_byte(), None); + assert_eq!(z.to_next_sibling_byte(), None); + assert_eq!(z.to_prev_sibling_byte(), None); + let mut steps = vec![]; + while z.to_next_step() { steps.push(z.path().to_vec()); assert!(steps.len() < 16); } + assert_eq!(steps, vec![vec![5], vec![5, 1], vec![6]]); + let mut o = Vec::new(); + while z.to_next_val_observed(&mut o) { assert_eq!(&o[..], z.path()); } + } + + /// `descend_to_val` keeps both sources at the same place when the second one stops first + #[test] + fn overlay_descend_to_val_second_stops_first() { + let mut a = PathMap::::new(); + a.set_val_at(&[1u8, 2, 3], 1); + let mut b = PathMap::::new(); + b.set_val_at(&[1u8, 7], 2); + for (x, y) in [(&a, &b), (&b, &a)] { + let mut z = OverlayZipper::new(x.read_zipper(), y.read_zipper()); + assert_eq!(z.descend_to_val(&[1u8, 2, 3, 4, 5]), 3); + assert_eq!(z.depth(), 3); + assert_eq!(z.path(), &[1u8, 2, 3]); + assert_eq!(z.ascend(3), 3); + assert_eq!(z.depth(), 0); + } + } + + #[test] + fn overlay_descend_to_val_skips_values_filtered_by_mapping() { + fn only_a<'a>(a: Option<&'a u64>, _: Option<&'a u64>) -> Option<&'a u64> { a } + fn only_b<'a>(_: Option<&'a u64>, b: Option<&'a u64>) -> Option<&'a u64> { b } + + let mut a = PathMap::::new(); + a.set_val_at(&[1u8, 2, 3], 3); + let mut b = PathMap::::new(); + b.set_val_at(&[1u8], 1); + + let mut z = OverlayZipper::with_mapping(a.read_zipper(), b.read_zipper(), only_a); + assert_eq!(z.descend_to_val(&[1u8, 2, 3]), 3); + assert_eq!(z.path(), &[1u8, 2, 3]); + assert_eq!(z.val(), Some(&3)); + + let mut a = PathMap::::new(); + a.set_val_at(&[1u8], 1); + a.set_val_at(&[1u8, 2], 2); + let mut b = PathMap::::new(); + b.set_val_at(&[1u8, 2, 3], 3); + + let mut z = OverlayZipper::with_mapping(a.read_zipper(), b.read_zipper(), only_b); + assert_eq!(z.descend_to_val(&[1u8, 2, 3]), 3); + assert_eq!(z.path(), &[1u8, 2, 3]); + assert_eq!(z.val(), Some(&3)); + } }