diff --git a/src/trie_node.rs b/src/trie_node.rs index 09fcabe8..01b7003d 100644 --- a/src/trie_node.rs +++ b/src/trie_node.rs @@ -2682,8 +2682,12 @@ where /// Ensures the node is a CellByteNode /// /// Returns `true` if the node was upgraded and `false` if it already was a CellByteNode -pub(crate) fn make_cell_node(node: &mut TrieNodeODRc) -> bool { - if !node.as_tagged().is_cell_node() { +pub(crate) fn make_cell_node(node: &mut TrieNodeODRc, alloc: A) -> bool { + if node.is_empty() { + //The empty sentinel can't be made mutable; there is nothing in it to keep + *node = TrieNodeODRc::new_in(crate::dense_byte_node::CellByteNode::new_in(alloc.clone()), alloc); + true + } else if !node.as_tagged().is_cell_node() { let replacement = node.make_mut().convert_to_cell_node(); *node = replacement; true diff --git a/src/zipper_head.rs b/src/zipper_head.rs index 007bfe3e..f9e50888 100644 --- a/src/zipper_head.rs +++ b/src/zipper_head.rs @@ -343,7 +343,7 @@ pub(crate) fn prepare_exclusive_write_path<'a, 'trie: 'a, 'path: 'a, V: Clone + debug_assert_eq!(z.focus_stack.depth(), 1); z.focus_stack.to_root(); let stack_root = z.focus_stack.root_mut().unwrap(); - make_cell_node(stack_root); + make_cell_node(stack_root, z.alloc.clone()); let root_val = z.root_val.as_mut().unwrap(); return (stack_root, unsafe{ &mut **root_val }) } @@ -386,7 +386,7 @@ pub(crate) fn prepare_exclusive_write_path<'a, 'trie: 'a, 'path: 'a, V: Clone + |node, key| { let new_node = if key.len() > 0 { if let Some(mut remaining) = node.take_node_at_key(key, false) { - make_cell_node(&mut remaining); + make_cell_node(&mut remaining, alloc.clone()); remaining } else { TrieNodeODRc::new_in(CellByteNode::new_in(alloc.clone()), alloc) @@ -419,8 +419,9 @@ pub(crate) fn prepare_exclusive_write_path<'a, 'trie: 'a, 'path: 'a, V: Clone + //If the node on top of the stack is not a cell node, we need to upgrade it if !z.focus_stack.top().unwrap().is_cell_node() { + let alloc = z.alloc.clone(); swap_top_node(&mut z.focus_stack, &z.key, |mut existing_node| { - make_cell_node(&mut existing_node); + make_cell_node(&mut existing_node, alloc); existing_node }); } @@ -439,9 +440,9 @@ fn prepare_node_at_path_end<'a, V: Clone + Send + Sync, A: Allocator>(start_node let mut node_ref = node.make_mut(); let mut new_parent = match node_ref.take_node_at_key(remaining_key, false) { Some(downward_node) => downward_node, - None => TrieNodeODRc::new_in(CellByteNode::new_in(alloc.clone()), alloc) + None => TrieNodeODRc::new_in(CellByteNode::new_in(alloc.clone()), alloc.clone()) }; - make_cell_node(&mut new_parent); + make_cell_node(&mut new_parent, alloc.clone()); let result = node_ref.node_set_branch(remaining_key, new_parent); match result { Ok(_) => { }, @@ -452,7 +453,7 @@ fn prepare_node_at_path_end<'a, V: Clone + Send + Sync, A: Allocator>(start_node node = child_node; } else { //Otherwise just upgrade node - make_cell_node(node); + make_cell_node(node, alloc); } node } @@ -1605,4 +1606,35 @@ mod tests { assert_eq!(map.val_at(&[1u8, 9, 5]), Some(&3)); assert_eq!(map.val_count(), 7); } + + /// Exclusive paths from a head whose focus node is the empty sentinel + #[test] + fn exclusive_path_over_empty_node() { + let setups: [fn(&mut PathMap); 3] = [ + |m| { m.write_zipper_at_path(&[0u8, 0]).remove_branches(false); }, + |m| { let e = PathMap::::new(); m.write_zipper_at_path(&[0u8, 0]).graft(&e.read_zipper()); }, + |m| { m.write_zipper_at_path(&[0u8, 0]).take_map(false); }, + ]; + for (i, setup) in setups.iter().enumerate() { + for paths in [[&[][..], &[5u8][..]], [&[5u8, 6][..], &[][..]], [&[0u8, 0][..], &[1u8][..]]] { + let mut map = PathMap::::new(); + map.set_val_at(&[0u8, 0, 1, 2], 9); + map.set_val_at(&[7u8], 9); + setup(&mut map); + { + let mut wz = map.write_zipper_at_path(&[0u8, 0]); + let zh = wz.zipper_head(); + for (n, p) in paths.iter().enumerate() { + let mut w = zh.write_zipper_at_exclusive_path(p).unwrap(); + w.set_val(n as u64); + } + } + assert_eq!(map.val_at(&[7u8]), Some(&9), "setup {i} {paths:?}"); + for (n, p) in paths.iter().enumerate() { + let full: Vec = [&[0u8, 0][..], p].concat(); + assert_eq!(map.val_at(&full), Some(&(n as u64)), "setup {i} {paths:?}"); + } + } + } + } }