use super::*;
use crate::{VectorMemory, testing::MeteredMemory};
struct Backing(MeteredMemory<VectorMemory>);
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
struct Key(u64);
impl crate::engine::Storable for Key {
const BOUND: crate::engine::storable::Bound = crate::engine::storable::Bound::Bounded {
max_size: 8,
is_fixed_size: true,
};
fn to_bytes(&self) -> std::borrow::Cow<'_, [u8]> {
self.0.to_le_bytes().to_vec().into()
}
fn into_bytes(self) -> Vec<u8> {
self.0.to_le_bytes().to_vec()
}
fn from_bytes(bytes: std::borrow::Cow<'_, [u8]>) -> Self {
Self(u64::from_le_bytes(bytes.as_ref().try_into().unwrap()))
}
}
impl Memory for Backing {
fn size(&self) -> u64 {
crate::Memory::size(&self.0)
}
fn grow(&self, pages: u64) -> i64 {
crate::Memory::grow(&self.0, pages)
}
fn read(&self, offset: u64, dst: &mut [u8]) {
crate::Memory::read(&self.0, offset, dst);
}
unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
unsafe { crate::Memory::read_unsafe(&self.0, offset, dst, count) };
}
fn write(&self, offset: u64, src: &[u8]) {
crate::Memory::write(&self.0, offset, src);
}
}
#[test]
fn empty_values_load_and_extract_without_payload_reads() {
use super::super::{Node, NodeType};
let backing = MeteredMemory::<VectorMemory>::default();
let mut allocator = Allocator::new(
Backing(backing.clone()),
Address::from(0),
Bytes::from(128u32),
);
let address = allocator.allocate();
let mut node = Node::new_v2(address, NodeType::Leaf, PageSize::Value(128));
node.push_entry((Key(1), vec![]));
node.push_entry((Key(2), vec![7, 8, 9]));
node.save(&mut allocator);
let mut node = Node::<Key>::load(address, PageSize::Value(128), allocator.memory());
backing.reset();
assert_eq!(node.value(0, allocator.memory()), [] as [u8; 0]);
assert_eq!(backing.counts().reads, 0);
assert_eq!(node.value(1, allocator.memory()), [7, 8, 9]);
assert_eq!(backing.counts().reads, 1);
assert_eq!(backing.counts().read_bytes, 3);
node = Node::load(address, PageSize::Value(128), allocator.memory());
backing.reset();
assert_eq!(
node.swap_value(0, vec![4], allocator.memory()),
[] as [u8; 0]
);
assert_eq!(backing.counts().reads, 0);
node.save(&mut allocator);
let node = Node::<Key>::load(address, PageSize::Value(128), allocator.memory());
assert_eq!(node.value(0, allocator.memory()), [4]);
assert_eq!(node.value(1, allocator.memory()), [7, 8, 9]);
}
#[test]
fn empty_fields_preserve_logical_extent_without_payload_writes() {
let backing = MeteredMemory::<VectorMemory>::default();
let mut allocator = Allocator::new(
Backing(backing.clone()),
Address::from(0),
Bytes::from(128u32),
);
let address = allocator.allocate();
let mut writer = NodeWriter::new(address, vec![], PageSize::Value(128), &mut allocator);
backing.reset();
for offset in [0, 15, 127, 128] {
writer.write(Address::from(offset), &[]);
assert_eq!(writer.max_offset, offset);
assert_eq!(writer.overflows, []);
}
assert_eq!(backing.counts().writes, 0);
writer.write(Address::from(129), &[]);
assert_eq!(writer.max_offset, 129);
assert_eq!(writer.overflows.len(), 1);
backing.reset();
writer.write(Address::from(15), &[]);
assert_eq!(writer.max_offset, 129);
let overflows = writer.finish();
assert_eq!(overflows.len(), 1);
assert_eq!(backing.counts().writes, 0);
assert_eq!(
crate::engine::read_u64(allocator.memory(), overflows[0] + PAGE_OVERFLOW_NEXT_OFFSET),
NULL.get(),
);
}
#[test]
fn overflow_tails_stay_terminated_without_redundant_finish_writes() {
let backing = MeteredMemory::<VectorMemory>::default();
let mut allocator = Allocator::new(
Backing(backing.clone()),
Address::from(0),
Bytes::from(128u32),
);
let address = allocator.allocate();
let mut overflows = Vec::new();
for (length, expected_pages) in [
(0, 0),
(113, 0),
(114, 1),
(230, 1),
(231, 2),
(500, 4),
(500, 4),
(231, 2),
(230, 1),
(114, 1),
(113, 0),
(500, 4),
(0, 0),
(231, 2),
] {
let old_pages = overflows.len();
let first = overflows.first().copied().unwrap_or(NULL);
let mut writer = NodeWriter::new(address, overflows, PageSize::Value(128), &mut allocator);
writer.write_u64(Address::from(7), first.get());
let payload = vec![0x5a; length];
writer.write(Address::from(15), &payload);
backing.reset();
overflows = writer.finish();
let counts = backing.counts();
assert!(!counts.overflowed);
assert_eq!(overflows.len(), expected_pages);
if expected_pages >= old_pages {
assert_eq!(counts.writes, 0, "unchanged/new tails need no repair");
} else {
assert!(counts.writes > 0, "shortened chains need a new terminator");
}
let mut link = Address::from(crate::engine::read_u64(
allocator.memory(),
address + OVERFLOW_ADDRESS_OFFSET,
));
for page in &overflows {
assert_eq!(link, *page);
let mut magic = [0; 3];
allocator.memory().read(page.get(), &mut magic);
assert_eq!(&magic, OVERFLOW_MAGIC);
link = Address::from(crate::engine::read_u64(
allocator.memory(),
*page + PAGE_OVERFLOW_NEXT_OFFSET,
));
}
assert_eq!(link, NULL);
let reader = NodeReader {
address,
overflows: &overflows,
page_size: PageSize::Value(128),
memory: allocator.memory(),
};
let mut restored = vec![0; length];
reader.read(15, &mut restored);
assert_eq!(restored, payload);
}
}