use super::*;
use crate::{
btreemap::{
node::v2::{OVERFLOW_ADDRESS_OFFSET, PAGE_OVERFLOW_NEXT_OFFSET},
Allocator,
},
types::NULL,
};
use proptest::collection::btree_map as pmap;
use proptest::collection::vec as pvec;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
use test_strategy::{proptest, Arbitrary};
fn make_memory() -> Rc<RefCell<Vec<u8>>> {
Rc::new(RefCell::new(Vec::new()))
}
#[derive(Arbitrary, Debug)]
struct NodeV1Data {
#[strategy(0..1_000u32)]
max_key_size: u32,
#[strategy(0..1_000u32)]
max_value_size: u32,
#[strategy(
pmap(
pvec(0..u8::MAX, 0..=#max_key_size as usize),
pvec(0..u8::MAX, 0..=#max_value_size as usize),
1..CAPACITY
)
)]
entries: BTreeMap<Vec<u8>, Vec<u8>>,
node_type: NodeType,
}
impl NodeV1Data {
fn get(&self, address: Address) -> Node<Vec<u8>> {
let mut node = Node::new_v1(
address,
self.node_type,
DerivedPageSize {
max_key_size: self.max_key_size,
max_value_size: self.max_value_size,
},
);
for entry in self.entries.clone().into_iter() {
node.push_entry(entry);
}
for child in self.children() {
node.push_child(child);
}
node
}
fn children(&self) -> Vec<Address> {
match self.node_type {
NodeType::Leaf => vec![],
NodeType::Internal => (0..=self.entries.len())
.map(|i| Address::from(i as u64))
.collect(),
}
}
}
#[derive(Arbitrary, Debug)]
struct NodeV2Data {
#[strategy(128..10_000_u32)]
page_size: u32,
#[strategy(
pmap(
pvec(0..u8::MAX, 0..1000),
pvec(0..u8::MAX, 0..1000),
1..CAPACITY
)
)]
entries: BTreeMap<Vec<u8>, Vec<u8>>,
node_type: NodeType,
}
impl NodeV2Data {
fn get(&self, address: Address) -> Node<Vec<u8>> {
let mut node = Node::new_v2(address, self.node_type, PageSize::Value(self.page_size));
for entry in self.entries.clone().into_iter() {
node.push_entry(entry);
}
for child in self.children() {
node.push_child(child);
}
node
}
fn children(&self) -> Vec<Address> {
match self.node_type {
NodeType::Leaf => vec![],
NodeType::Internal => (0..=self.entries.len())
.map(|i| Address::from(i as u64))
.collect(),
}
}
}
#[proptest]
fn saving_and_loading_v1_preserves_data(node_data: NodeV1Data) {
let mem = make_memory();
let node_addr = Address::from(0);
let node = node_data.get(node_addr);
node.save_v1(&mem);
let node = Node::load(
node_addr,
PageSize::Derived(DerivedPageSize {
max_key_size: node_data.max_key_size,
max_value_size: node_data.max_value_size,
}),
&mem,
);
assert_eq!(node.children, node_data.children());
assert_eq!(
node.entries(&mem),
node_data.entries.into_iter().collect::<Vec<_>>()
);
}
#[proptest]
fn saving_and_loading_v2_preserves_data(node_data: NodeV2Data) {
let mem = make_memory();
let allocator_addr = Address::from(0);
let mut allocator = Allocator::new(
mem.clone(),
allocator_addr,
Bytes::from(node_data.page_size as u64),
);
let node_addr = allocator.allocate();
let mut node = node_data.get(node_addr);
node.save_v2(&mut allocator);
let node = Node::load(node_addr, PageSize::Value(node_data.page_size), &mem);
assert_eq!(node.children, node_data.children());
assert_eq!(
node.entries(&mem),
node_data.entries.into_iter().collect::<Vec<_>>()
);
}
#[proptest]
fn migrating_v1_nodes_to_v2(node_data: NodeV1Data) {
let v1_size = v1::size_v1(node_data.max_key_size, node_data.max_value_size);
let mem = make_memory();
let allocator_addr = Address::from(0);
let mut allocator = Allocator::new(mem.clone(), allocator_addr, v1_size);
let node_addr = allocator.allocate();
let node = node_data.get(node_addr);
node.save_v1(allocator.memory());
let mut node = Node::<Vec<u8>>::load(
node_addr,
PageSize::Derived(DerivedPageSize {
max_key_size: node_data.max_key_size,
max_value_size: node_data.max_value_size,
}),
allocator.memory(),
);
node.save_v2(&mut allocator);
let node = Node::load(
node_addr,
PageSize::Derived(DerivedPageSize {
max_key_size: node_data.max_key_size,
max_value_size: node_data.max_value_size,
}),
&mem,
);
assert_eq!(node.children, node_data.children());
assert_eq!(
node.entries(&mem),
node_data.entries.into_iter().collect::<Vec<_>>()
);
}
#[test]
fn growing_and_shrinking_entries_does_not_leak_memory() {
let mem = make_memory();
let allocator_addr = Address::from(0);
let mut allocator = Allocator::new(mem, allocator_addr, PageSize::Value(500).get().into());
let mut node = Node::new_v2(allocator.allocate(), NodeType::Leaf, PageSize::Value(500));
node.push_entry((vec![1, 2, 3], vec![0; 10000]));
node.save(&mut allocator);
let num_allocated_chunks = allocator.num_allocated_chunks();
assert!(num_allocated_chunks > 1);
node.swap_entry(0, (vec![1, 2, 3], vec![1; 10000]), allocator.memory());
node.save(&mut allocator);
assert_eq!(num_allocated_chunks, allocator.num_allocated_chunks());
node.swap_entry(0, (vec![1, 2, 3], vec![2; 20000]), allocator.memory());
node.save(&mut allocator);
assert!(num_allocated_chunks < allocator.num_allocated_chunks());
node.swap_entry(0, (vec![1, 2, 3], vec![3; 10000]), allocator.memory());
node.save(&mut allocator);
assert_eq!(num_allocated_chunks, allocator.num_allocated_chunks());
}
#[test]
fn overflows_end_with_null_after_nodes_growing_and_shrinking() {
let mem = make_memory();
let allocator_addr = Address::from(0);
let page_size = PageSize::Value(500);
let mut allocator = Allocator::new(mem.clone(), allocator_addr, page_size.get().into());
let node_addr = allocator.allocate();
let mut node = Node::new_v2(node_addr, NodeType::Leaf, page_size);
node.push_entry((vec![1, 2, 3], vec![13; 1000]));
node.save(&mut allocator);
assert_eq!(node.overflows().len(), 2);
assert_eq!(
read_u64(&mem, node.overflows()[1] + PAGE_OVERFLOW_NEXT_OFFSET),
NULL.get()
);
node.swap_entry(0, (vec![1, 2, 3], vec![7; 500]), allocator.memory());
node.save(&mut allocator);
assert_eq!(node.overflows().len(), 1);
assert_eq!(
read_u64(&mem, node.overflows()[0] + PAGE_OVERFLOW_NEXT_OFFSET),
NULL.get()
);
node.swap_entry(0, (vec![1, 2, 3], vec![]), allocator.memory());
node.save(&mut allocator);
assert_eq!(node.overflows().len(), 0);
assert_eq!(
read_u64(&mem, node_addr + OVERFLOW_ADDRESS_OFFSET),
NULL.get()
);
}
#[test]
fn can_call_node_value_multiple_times_on_same_index() {
let mem = make_memory();
let allocator_addr = Address::from(0);
let page_size = PageSize::Value(500);
let mut allocator = Allocator::new(mem.clone(), allocator_addr, page_size.get().into());
let node_addr = allocator.allocate();
let mut node = Node::new_v2(node_addr, NodeType::Leaf, page_size);
node.push_entry((1u32, vec![1, 2, 3]));
let value1 = node.value(0, &mem);
let value2 = node.value(0, &mem);
assert_eq!(value1, value2);
}