From ae26e31034c9ab56a199e0b1b5640623957c9725 Mon Sep 17 00:00:00 2001 From: Igor Malovitsa Date: Thu, 17 Sep 2026 03:39:59 +0000 Subject: [PATCH] Fix get_val_with_witness at an owned zipper's root With no root value and the zipper root at the root node, the lookup used an unset parent key and sliced out of range. The value there is just root_val. --- src/zipper.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/zipper.rs b/src/zipper.rs index fcede695..af8cc067 100644 --- a/src/zipper.rs +++ b/src/zipper.rs @@ -2586,7 +2586,8 @@ pub(crate) mod read_zipper_core { if let Some((parent, _iter_tok, _prefix_offset)) = self.ancestors.last() { parent.node_get_val(self.parent_key()) } else { - if self.root_val.is_some() { + if self.root_val.is_some() || self.root_parent_key_start == usize::MAX { + //No parent key: the zipper root is the root node itself, and its value is `root_val` self.root_val } else { //We know the node in the witness and the node in self.root_node are the same, @@ -3396,6 +3397,21 @@ pub(crate) mod read_zipper_core { } } + /// `get_val_with_witness` agrees with `val` on owned read zippers, including at a root without a value + #[test] + fn read_zipper_owned_get_val_with_witness() { + let mut map = PathMap::::new(); + for p in [&[1u8][..], &[1, 2], &[3, 4, 5]] { map.set_val_at(p, p.len() as u64); } + for root in [&[][..], &[1u8], &[3u8], &[3u8, 4], &[9u8]] { + let mut z = map.clone().into_read_zipper(root); + loop { + let w = z.witness(); + assert_eq!(z.get_val_with_witness(&w), z.val(), "{root:?} {:?}", z.path()); + if !z.to_next_step() { break } + } + } + } + /// Validate we don't accidentially reallocate the path buffer when we don't need to #[test] fn read_zipper_reserve_buffer_test() {