Skip to content

Commit 8478862

Browse files
authored
test: BTreeMap: add case for deallocating root node with overflows (#213)
Adds a new test case to verify that deallocating the root node is done correctly, even when the root node contains overflow pages. NOTE: The logic in `btreemap.remove` has been slightly modified to be more understandable, but the functionality remains equivalent.
1 parent 4f6b8ae commit 8478862

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

src/btreemap.rs

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,11 +791,12 @@ where
791791
self.root_addr = new_child.address();
792792

793793
// Deallocate the root node.
794-
self.allocator.deallocate(node.address());
794+
node.deallocate(&mut self.allocator);
795795
self.save();
796+
} else {
797+
node.save(self.allocator_mut());
796798
}
797799

798-
node.save(self.allocator_mut());
799800
new_child.save(self.allocator_mut());
800801

801802
// Recursively delete the key.
@@ -3126,4 +3127,70 @@ mod test {
31263127
// All chunks have been deallocated.
31273128
assert_eq!(btree.allocator.num_allocated_chunks(), 0);
31283129
}
3130+
3131+
#[test]
3132+
fn deallocating_root_does_not_leak_memory() {
3133+
let mem = make_memory();
3134+
let mut btree: BTreeMap<Vec<u8>, _, _> = BTreeMap::new(mem.clone());
3135+
3136+
for i in 1..=11 {
3137+
// Large keys are stored so that each node overflows.
3138+
assert_eq!(btree.insert(vec![i; 10_000], ()), None);
3139+
}
3140+
3141+
// Should now split a node.
3142+
assert_eq!(btree.insert(vec![0; 10_000], ()), None);
3143+
3144+
// The btree should look like this:
3145+
// [6]
3146+
// / \
3147+
// [0, 1, 2, 3, 4, 5] [7, 8, 9, 10, 11]
3148+
let root = btree.load_node(btree.root_addr);
3149+
assert_eq!(root.node_type(), NodeType::Internal);
3150+
assert_eq!(root.keys(), vec![vec![6; 10_000]]);
3151+
assert_eq!(root.children_len(), 2);
3152+
3153+
// Remove the element in the root.
3154+
btree.remove(&vec![6; 10_000]);
3155+
3156+
// The btree should look like this:
3157+
// [5]
3158+
// / \
3159+
// [0, 1, 2, 3, 4] [7, 8, 9, 10, 11]
3160+
let root = btree.load_node(btree.root_addr);
3161+
assert_eq!(root.node_type(), NodeType::Internal);
3162+
assert_eq!(root.keys(), vec![vec![5; 10_000]]);
3163+
assert_eq!(root.children_len(), 2);
3164+
3165+
// Remove the element in the root. This triggers the case where the root
3166+
// node is deallocated and the children are merged into a single node.
3167+
btree.remove(&vec![5; 10_000]);
3168+
3169+
// The btree should look like this:
3170+
// [0, 1, 2, 3, 4, 7, 8, 9, 10, 11]
3171+
let root = btree.load_node(btree.root_addr);
3172+
assert_eq!(root.node_type(), NodeType::Leaf);
3173+
assert_eq!(
3174+
root.keys(),
3175+
vec![
3176+
vec![0; 10_000],
3177+
vec![1; 10_000],
3178+
vec![2; 10_000],
3179+
vec![3; 10_000],
3180+
vec![4; 10_000],
3181+
vec![7; 10_000],
3182+
vec![8; 10_000],
3183+
vec![9; 10_000],
3184+
vec![10; 10_000],
3185+
vec![11; 10_000],
3186+
]
3187+
);
3188+
3189+
// Delete everything else.
3190+
for i in 0..=11 {
3191+
btree.remove(&vec![i; 10_000]);
3192+
}
3193+
3194+
assert_eq!(btree.allocator.num_allocated_chunks(), 0);
3195+
}
31293196
}

src/btreemap/node.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,11 @@ impl<K: Storable + Ord + Clone> Node<K> {
366366
.collect()
367367
}
368368

369+
#[cfg(test)]
370+
pub fn keys(&self) -> &[K] {
371+
&self.keys
372+
}
373+
369374
#[cfg(test)]
370375
pub fn overflows(&self) -> &[Address] {
371376
&self.overflows

0 commit comments

Comments
 (0)