From fc214cdcdacf318b1cdabc0ee05a5590d1d05e41 Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Tue, 22 Aug 2023 11:18:09 +0200 Subject: [PATCH 1/7] feat: BTreeMap v2 beta --- .github/workflows/ci.yml | 2 +- benches/benches.rs | 33 ++++ benches/run-benchmark.sh | 2 +- benchmark-canisters/dfx.json | 4 +- benchmark-canisters/src/btreemap.rs | 277 +++++++++++++++++++++++++-- examples/dfx.json | 2 +- src/btreemap.rs | 279 +++++++++++++++++----------- src/btreemap/node.rs | 29 ++- src/btreemap/node/tests.rs | 6 +- src/btreemap/node/v1.rs | 10 +- src/btreemap/node/v2.rs | 4 +- 11 files changed, 492 insertions(+), 156 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8631182..cf597025 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: - name: Install DFX run: | wget --output-document install-dfx.sh "https://internetcomputer.org/install.sh" - DFX_VERSION=${DFX_VERSION:=0.14.1} bash install-dfx.sh < <(yes Y) + DFX_VERSION=${DFX_VERSION:=0.14.3} bash install-dfx.sh < <(yes Y) rm install-dfx.sh dfx cache install echo "$HOME/bin" >> $GITHUB_PATH diff --git a/benches/benches.rs b/benches/benches.rs index bba5bb4b..b5597a8d 100644 --- a/benches/benches.rs +++ b/benches/benches.rs @@ -177,40 +177,73 @@ pub fn criterion_benchmark(c: &mut Criterion) { // BTree benchmarks bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_4_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_4_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_8_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_8_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_16_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_16_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_32_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_32_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_64_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_64_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_128_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_128_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_256_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_256_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_512_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_512_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_u64_u64"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_u64_u64_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_u64_blob_8"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_u64_blob_8_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_8_u64"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_insert_blob_8_u64_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_4_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_4_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_8_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_8_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_16_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_16_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_32_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_32_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_64_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_64_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_128_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_128_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_u64_u64"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_u64_u64_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_u64_blob_8"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_u64_blob_8_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_8_u64"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_8_u64_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_256_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_256_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_512_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_get_blob_512_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_4_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_4_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_8_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_8_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_16_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_16_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_32_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_32_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_64_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_64_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_128_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_128_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_256_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_256_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_512_1024"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_512_1024_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_u64_u64"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_u64_u64_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_u64_blob_8"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_u64_blob_8_v2"); bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_8_u64"); + bench_function(c, *BENCHMARK_CANISTER, "btreemap_remove_blob_8_u64_v2"); // Vec benchmarks bench_function(c, *BENCHMARK_CANISTER, "vec_insert_blob_4"); diff --git a/benches/run-benchmark.sh b/benches/run-benchmark.sh index 3d298e77..04136859 100755 --- a/benches/run-benchmark.sh +++ b/benches/run-benchmark.sh @@ -4,7 +4,7 @@ set -euo pipefail BENCH_NAME=$1 -FILE=mktemp +FILE=$(mktemp) if ! type "drun" > /dev/null; then echo "drun is not installed. Please add drun to your path from commit d35535c96184be039aaa31f68b48bbe45909494e." diff --git a/benchmark-canisters/dfx.json b/benchmark-canisters/dfx.json index abb570d8..770d2bcf 100644 --- a/benchmark-canisters/dfx.json +++ b/benchmark-canisters/dfx.json @@ -1,5 +1,5 @@ { - "dfx": "0.12.1", + "dfx": "0.14.3", "canisters": { "benchmarks": { "candid": "candid.did", @@ -16,4 +16,4 @@ } }, "version": 1 -} \ No newline at end of file +} diff --git a/benchmark-canisters/src/btreemap.rs b/benchmark-canisters/src/btreemap.rs index 11f8cfe8..e8314c4b 100644 --- a/benchmark-canisters/src/btreemap.rs +++ b/benchmark-canisters/src/btreemap.rs @@ -8,94 +8,195 @@ pub fn btreemap_insert_blob_4_1024() -> u64 { insert_blob_helper::<4, 1024>() } +#[query] +pub fn btreemap_insert_blob_4_1024_v2() -> u64 { + insert_blob_helper_v2::<4, 1024>() +} + #[query] pub fn btreemap_insert_blob_8_1024() -> u64 { insert_blob_helper::<8, 1024>() } +#[query] +pub fn btreemap_insert_blob_8_1024_v2() -> u64 { + insert_blob_helper_v2::<8, 1024>() +} + #[query] pub fn btreemap_insert_blob_16_1024() -> u64 { insert_blob_helper::<16, 1024>() } +#[query] +pub fn btreemap_insert_blob_16_1024_v2() -> u64 { + insert_blob_helper_v2::<16, 1024>() +} + #[query] pub fn btreemap_insert_blob_32_1024() -> u64 { insert_blob_helper::<32, 1024>() } +#[query] +pub fn btreemap_insert_blob_32_1024_v2() -> u64 { + insert_blob_helper_v2::<32, 1024>() +} + #[query] pub fn btreemap_insert_blob_64_1024() -> u64 { insert_blob_helper::<64, 1024>() } +#[query] +pub fn btreemap_insert_blob_64_1024_v2() -> u64 { + insert_blob_helper_v2::<64, 1024>() +} + #[query] pub fn btreemap_insert_blob_128_1024() -> u64 { insert_blob_helper::<128, 1024>() } +#[query] +pub fn btreemap_insert_blob_128_1024_v2() -> u64 { + insert_blob_helper_v2::<128, 1024>() +} + #[query] pub fn btreemap_insert_blob_256_1024() -> u64 { insert_blob_helper::<256, 1024>() } +#[query] +pub fn btreemap_insert_blob_256_1024_v2() -> u64 { + insert_blob_helper_v2::<256, 1024>() +} + #[query] pub fn btreemap_insert_blob_512_1024() -> u64 { insert_blob_helper::<512, 1024>() } +#[query] +pub fn btreemap_insert_blob_512_1024_v2() -> u64 { + insert_blob_helper_v2::<512, 1024>() +} + #[query] pub fn btreemap_insert_blob_1024_4() -> u64 { insert_blob_helper::<1024, 4>() } +#[query] +pub fn btreemap_insert_blob_1024_4_v2() -> u64 { + insert_blob_helper_v2::<1024, 4>() +} + #[query] pub fn btreemap_insert_blob_1024_8() -> u64 { insert_blob_helper::<1024, 8>() } +#[query] +pub fn btreemap_insert_blob_1024_8_v2() -> u64 { + insert_blob_helper_v2::<1024, 8>() +} + #[query] pub fn btreemap_insert_blob_1024_16() -> u64 { insert_blob_helper::<1024, 16>() } +#[query] +pub fn btreemap_insert_blob_1024_16_v2() -> u64 { + insert_blob_helper_v2::<1024, 16>() +} + #[query] pub fn btreemap_insert_blob_1024_32() -> u64 { insert_blob_helper::<1024, 32>() } +#[query] +pub fn btreemap_insert_blob_1024_32_v2() -> u64 { + insert_blob_helper_v2::<1024, 32>() +} + #[query] pub fn btreemap_insert_blob_1024_64() -> u64 { insert_blob_helper::<1024, 64>() } +#[query] +pub fn btreemap_insert_blob_1024_64_v2() -> u64 { + insert_blob_helper_v2::<1024, 64>() +} + #[query] pub fn btreemap_insert_blob_1024_128() -> u64 { insert_blob_helper::<1024, 128>() } +#[query] +pub fn btreemap_insert_blob_1024_128_v2() -> u64 { + insert_blob_helper_v2::<1024, 128>() +} + #[query] pub fn btreemap_insert_blob_1024_256() -> u64 { insert_blob_helper::<1024, 256>() } +#[query] +pub fn btreemap_insert_blob_1024_256_v2() -> u64 { + insert_blob_helper_v2::<1024, 256>() +} + #[query] pub fn btreemap_insert_blob_1024_512() -> u64 { insert_blob_helper::<1024, 512>() } +#[query] +pub fn btreemap_insert_blob_1024_512_v2() -> u64 { + insert_blob_helper_v2::<1024, 512>() +} + #[query] pub fn btreemap_insert_u64_u64() -> u64 { - insert_helper::() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + insert_helper::(btree) +} + +#[query] +pub fn btreemap_insert_u64_u64_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + insert_helper::(btree) } #[query] pub fn btreemap_insert_u64_blob_8() -> u64 { - insert_helper::>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + insert_helper::>(btree) +} + +#[query] +pub fn btreemap_insert_u64_blob_8_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + insert_helper::>(btree) } #[query] pub fn btreemap_insert_blob_8_u64() -> u64 { - insert_helper::, u64>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + insert_helper::, u64>(btree) +} + +#[query] +pub fn btreemap_insert_blob_8_u64_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + insert_helper::, u64>(btree) } /// Benchmarks removing keys from a BTreeMap. @@ -104,54 +205,112 @@ pub fn btreemap_remove_blob_4_1024() -> u64 { remove_blob_helper::<4, 1024>() } +#[query] +pub fn btreemap_remove_blob_4_1024_v2() -> u64 { + remove_blob_helper::<4, 1024>() +} + #[query] pub fn btreemap_remove_blob_8_1024() -> u64 { remove_blob_helper::<8, 1024>() } +#[query] +pub fn btreemap_remove_blob_8_1024_v2() -> u64 { + remove_blob_helper::<8, 1024>() +} + #[query] pub fn btreemap_remove_blob_16_1024() -> u64 { remove_blob_helper::<16, 1024>() } +#[query] +pub fn btreemap_remove_blob_16_1024_v2() -> u64 { + remove_blob_helper_v2::<16, 1024>() +} + #[query] pub fn btreemap_remove_blob_32_1024() -> u64 { remove_blob_helper::<32, 1024>() } +#[query] +pub fn btreemap_remove_blob_32_1024_v2() -> u64 { + remove_blob_helper_v2::<32, 1024>() +} + #[query] pub fn btreemap_remove_blob_64_1024() -> u64 { remove_blob_helper::<64, 1024>() } +#[query] +pub fn btreemap_remove_blob_64_1024_v2() -> u64 { + remove_blob_helper_v2::<64, 1024>() +} + #[query] pub fn btreemap_remove_blob_128_1024() -> u64 { remove_blob_helper::<128, 1024>() } +#[query] +pub fn btreemap_remove_blob_128_1024_v2() -> u64 { + remove_blob_helper_v2::<128, 1024>() +} + #[query] pub fn btreemap_remove_blob_256_1024() -> u64 { remove_blob_helper::<256, 1024>() } +#[query] +pub fn btreemap_remove_blob_256_1024_v2() -> u64 { + remove_blob_helper_v2::<256, 1024>() +} + #[query] pub fn btreemap_remove_blob_512_1024() -> u64 { remove_blob_helper::<512, 1024>() } +#[query] +pub fn btreemap_remove_blob_512_1024_v2() -> u64 { + remove_blob_helper_v2::<512, 1024>() +} + #[query] pub fn btreemap_remove_u64_u64() -> u64 { - remove_helper::() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + remove_helper::(btree) +} +#[query] +pub fn btreemap_remove_u64_u64_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + remove_helper::(btree) } #[query] pub fn btreemap_remove_u64_blob_8() -> u64 { - remove_helper::>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + remove_helper::>(btree) +} +#[query] +pub fn btreemap_remove_u64_blob_8_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + remove_helper::>(btree) } #[query] pub fn btreemap_remove_blob_8_u64() -> u64 { - remove_helper::, u64>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + remove_helper::, u64>(btree) +} +#[query] +pub fn btreemap_remove_blob_8_u64_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + remove_helper::, u64>(btree) } /// Benchmarks getting keys from a BTreeMap. @@ -160,64 +319,132 @@ pub fn btreemap_get_blob_4_1024() -> u64 { get_blob_helper::<4, 1024>() } +#[query] +pub fn btreemap_get_blob_4_1024_v2() -> u64 { + get_blob_helper_v2::<4, 1024>() +} + #[query] pub fn btreemap_get_blob_8_1024() -> u64 { get_blob_helper::<8, 1024>() } +#[query] +pub fn btreemap_get_blob_8_1024_v2() -> u64 { + get_blob_helper_v2::<8, 1024>() +} + #[query] pub fn btreemap_get_blob_16_1024() -> u64 { get_blob_helper::<16, 1024>() } +#[query] +pub fn btreemap_get_blob_16_1024_v2() -> u64 { + get_blob_helper_v2::<16, 1024>() +} + #[query] pub fn btreemap_get_blob_32_1024() -> u64 { get_blob_helper::<32, 1024>() } +#[query] +pub fn btreemap_get_blob_32_1024_v2() -> u64 { + get_blob_helper_v2::<32, 1024>() +} + #[query] pub fn btreemap_get_blob_64_1024() -> u64 { get_blob_helper::<64, 1024>() } +#[query] +pub fn btreemap_get_blob_64_1024_v2() -> u64 { + get_blob_helper_v2::<64, 1024>() +} + #[query] pub fn btreemap_get_blob_128_1024() -> u64 { get_blob_helper::<128, 1024>() } +#[query] +pub fn btreemap_get_blob_128_1024_v2() -> u64 { + get_blob_helper_v2::<128, 1024>() +} + #[query] pub fn btreemap_get_blob_256_1024() -> u64 { get_blob_helper::<256, 1024>() } +#[query] +pub fn btreemap_get_blob_256_1024_v2() -> u64 { + get_blob_helper_v2::<256, 1024>() +} + #[query] pub fn btreemap_get_blob_512_1024() -> u64 { get_blob_helper::<512, 1024>() } +#[query] +pub fn btreemap_get_blob_512_1024_v2() -> u64 { + get_blob_helper_v2::<512, 1024>() +} + #[query] pub fn btreemap_get_u64_u64() -> u64 { - get_helper::() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + get_helper::(btree) +} + +#[query] +pub fn btreemap_get_u64_u64_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + get_helper::(btree) } #[query] pub fn btreemap_get_u64_blob_8() -> u64 { - get_helper::>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + get_helper::>(btree) +} + +#[query] +pub fn btreemap_get_u64_blob_8_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + get_helper::>(btree) } #[query] pub fn btreemap_get_blob_8_u64() -> u64 { - get_helper::, u64>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + get_helper::, u64>(btree) +} + +#[query] +pub fn btreemap_get_blob_8_u64_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + get_helper::, u64>(btree) } // Profiles inserting a large number of random blobs into a btreemap. fn insert_blob_helper() -> u64 { - insert_helper::, Blob>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + insert_helper::, Blob>(btree) +} + +fn insert_blob_helper_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + insert_helper::, Blob>(btree) } // Profiles inserting a large number of random blobs into a btreemap. -fn insert_helper() -> u64 { - let mut btree: BTreeMap = BTreeMap::new(DefaultMemoryImpl::default()); +fn insert_helper( + mut btree: BTreeMap, +) -> u64 { let num_keys = 10_000; let mut rng = Rng::from_seed(0); let mut random_keys = Vec::with_capacity(num_keys); @@ -238,11 +465,18 @@ fn insert_helper() -> // Profiles getting a large number of random blobs from a btreemap. fn get_blob_helper() -> u64 { - get_helper::, Blob>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + get_helper::, Blob>(btree) } -fn get_helper() -> u64 { - let mut btree: BTreeMap = BTreeMap::new(DefaultMemoryImpl::default()); +fn get_blob_helper_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + get_helper::, Blob>(btree) +} + +fn get_helper( + mut btree: BTreeMap, +) -> u64 { let num_keys = 10_000; let mut rng = Rng::from_seed(0); let mut random_keys = Vec::with_capacity(num_keys); @@ -268,11 +502,18 @@ fn get_helper() -> u64 // Inserts a large number of random blobs into a btreemap, then profiles removing them. fn remove_blob_helper() -> u64 { - remove_helper::, Blob>() + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + remove_helper::, Blob>(btree) +} + +fn remove_blob_helper_v2() -> u64 { + let btree = BTreeMap::new_v2(DefaultMemoryImpl::default()); + remove_helper::, Blob>(btree) } -fn remove_helper() -> u64 { - let mut btree: BTreeMap = BTreeMap::new(DefaultMemoryImpl::default()); +fn remove_helper( + mut btree: BTreeMap, +) -> u64 { let num_keys = 10_000; let mut rng = Rng::from_seed(0); let mut random_keys = Vec::with_capacity(num_keys); diff --git a/examples/dfx.json b/examples/dfx.json index e66fd335..d78b3297 100644 --- a/examples/dfx.json +++ b/examples/dfx.json @@ -1,5 +1,5 @@ { - "dfx": "0.14.1", + "dfx": "0.14.3", "canisters": { "quick_start": { "candid": "src/quick_start/candid.did", diff --git a/src/btreemap.rs b/src/btreemap.rs index 1cfca5d9..85b897e5 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -35,7 +35,7 @@ use crate::{ use allocator::Allocator; pub use iter::Iter; use iter::{Cursor, Index}; -use node::{DerivedPageSize, Entry, Node, NodeType, Version}; +use node::{DerivedPageSize, Entry, Node, NodeType, PageSize, Version}; use std::borrow::Cow; use std::marker::PhantomData; use std::ops::{Bound, RangeBounds}; @@ -45,6 +45,7 @@ mod proptests; const MAGIC: &[u8; 3] = b"BTR"; const LAYOUT_VERSION: u8 = 1; +const LAYOUT_VERSION_2: u8 = 2; /// The sum of all the header fields, i.e. size of a packed header. const PACKED_HEADER_SIZE: usize = 28; /// The offset where the allocator begins. @@ -64,11 +65,7 @@ where // is set to NULL. root_addr: Address, - // The maximum size a key can have. - max_key_size: u32, - - // The maximum size a value can have. - max_value_size: u32, + version: Version, // An allocator used for managing memory and allocating nodes. allocator: Allocator, @@ -81,11 +78,8 @@ where } /// The packed header size must be <= ALLOCATOR_OFFSET. -struct BTreeHeaderV1 { - magic: [u8; 3], - version: u8, - max_key_size: u32, - max_value_size: u32, +struct BTreeHeader { + version: Version, root_addr: Address, length: u64, // Reserved bytes for future extensions @@ -141,8 +135,34 @@ where Address::from(ALLOCATOR_OFFSET as u64), Node::::size(max_key_size, max_value_size), ), - max_key_size, - max_value_size, + version: Version::V1(DerivedPageSize { + max_key_size, + max_value_size, + }), + length: 0, + _phantom: PhantomData, + }; + + btree.save(); + btree + } + + /// Create a V2 of the BTree. + /// This is currently exposed only for beta-testing purposes and will be removed in favor of + /// using BTreeMap::new directly once V2 is tested well enough. + pub fn new_v2(memory: M) -> Self { + // TODO: replace this page size with a more clever heuristic if the keys/values + // have certain types. + let page_size = PageSize::Value(500); + + let btree = Self { + root_addr: NULL, + allocator: Allocator::new( + memory, + Address::from(ALLOCATOR_OFFSET as u64), + page_size.get().into(), + ), + version: Version::V2(page_size), length: 0, _phantom: PhantomData, }; @@ -155,43 +175,71 @@ where pub fn load(memory: M) -> Self { // Read the header from memory. let header = Self::read_header(&memory); - assert_eq!(&header.magic, MAGIC, "Bad magic."); - assert_eq!(header.version, LAYOUT_VERSION, "Unsupported version."); - let expected_key_size = header.max_key_size; - assert!( - max_size::() <= expected_key_size, - "max_key_size must be <= {expected_key_size}" - ); - let expected_value_size = header.max_value_size; - assert!( - max_size::() <= expected_value_size, - "max_value_size must be <= {expected_value_size}" - ); + + match header.version { + Version::V1(DerivedPageSize { + max_key_size: expected_key_size, + max_value_size: expected_value_size, + }) => { + assert!( + max_size::() <= expected_key_size, + "max_key_size must be <= {expected_key_size}" + ); + + assert!( + max_size::() <= expected_value_size, + "max_value_size must be <= {expected_value_size}" + ); + } + Version::V2 { .. } => { + // Nothing to assert. + } + } let allocator_addr = Address::from(ALLOCATOR_OFFSET as u64); Self { root_addr: header.root_addr, allocator: Allocator::load(memory, allocator_addr), - max_key_size: header.max_key_size, - max_value_size: header.max_value_size, + version: header.version, length: header.length, _phantom: PhantomData, } } /// Reads the header from the specified memory. - fn read_header(memory: &M) -> BTreeHeaderV1 { + fn read_header(memory: &M) -> BTreeHeader { // Read the header let mut buf = [0; PACKED_HEADER_SIZE]; memory.read(0, &mut buf); - // Deserialize the fields - BTreeHeaderV1 { - magic: buf[0..3].try_into().unwrap(), - version: buf[3], - max_key_size: u32::from_le_bytes(buf[4..8].try_into().unwrap()), - max_value_size: u32::from_le_bytes(buf[8..12].try_into().unwrap()), - root_addr: Address::from(u64::from_le_bytes(buf[12..20].try_into().unwrap())), - length: u64::from_le_bytes(buf[20..28].try_into().unwrap()), + + assert_eq!(&buf[0..3], MAGIC, "Bad magic."); + + match buf[3] { + LAYOUT_VERSION => { + // Deserialize the fields + BTreeHeader { + version: Version::V1(DerivedPageSize { + max_key_size: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + max_value_size: u32::from_le_bytes(buf[8..12].try_into().unwrap()), + }), + root_addr: Address::from(u64::from_le_bytes(buf[12..20].try_into().unwrap())), + length: u64::from_le_bytes(buf[20..28].try_into().unwrap()), + } + } + LAYOUT_VERSION_2 => { + // FIXME: deal with derived page size. + // Deserialize the fields + BTreeHeader { + version: Version::V2(PageSize::Value(u32::from_le_bytes( + buf[4..8].try_into().unwrap(), + ))), + root_addr: Address::from(u64::from_le_bytes(buf[12..20].try_into().unwrap())), + length: u64::from_le_bytes(buf[20..28].try_into().unwrap()), + } + } + version => { + panic!("Unsupported version: {version}."); + } } } @@ -208,19 +256,29 @@ where let key_bytes = key.to_bytes(); let value_bytes = value.to_bytes(); - assert!( - key_bytes.len() <= self.max_key_size as usize, - "Key is too large. Expected <= {} bytes, found {} bytes", - self.max_key_size, - key_bytes.len() - ); + match self.version { + Version::V1(DerivedPageSize { + max_key_size, + max_value_size, + }) => { + assert!( + key_bytes.len() <= max_key_size as usize, + "Key is too large. Expected <= {} bytes, found {} bytes", + max_key_size, + key_bytes.len() + ); - assert!( - value_bytes.len() <= self.max_value_size as usize, - "Value is too large. Expected <= {} bytes, found {} bytes", - self.max_value_size, - value_bytes.len() - ); + assert!( + value_bytes.len() <= max_value_size as usize, + "Value is too large. Expected <= {} bytes, found {} bytes", + max_value_size, + value_bytes.len() + ); + } + Version::V2 { .. } => { + // Nothing to assert. + } + } let value = value_bytes.to_vec(); @@ -238,7 +296,7 @@ where if let Ok(idx) = root.search(&key) { // The key exists. Overwrite it and return the previous value. let (_, previous_value) = root.swap_entry(idx, (key, value), self.memory()); - root.save(self.memory()); + root.save(self.allocator_mut()); return Some(V::from_bytes(Cow::Owned(previous_value))); } @@ -284,7 +342,7 @@ where // Overwrite it and return the previous value. let (_, previous_value) = node.swap_entry(idx, (key, value), self.memory()); - node.save(self.memory()); + node.save(self.allocator_mut()); Some(previous_value) } Err(idx) => { @@ -295,7 +353,7 @@ where // The node is a non-full leaf. // Insert the entry at the proper location. node.insert_entry(idx, (key, value)); - node.save(self.memory()); + node.save(self.allocator_mut()); // Update the length. self.length += 1; @@ -315,7 +373,7 @@ where // The key exists. Overwrite it and return the previous value. let (_, previous_value) = child.swap_entry(idx, (key, value), self.memory()); - child.save(self.memory()); + child.save(self.allocator_mut()); return Some(previous_value); } @@ -373,9 +431,9 @@ where node.insert_entry(full_child_idx, (median_key, median_value)); - sibling.save(self.memory()); - full_child.save(self.memory()); - node.save(self.memory()); + sibling.save(self.allocator_mut()); + full_child.save(self.allocator_mut()); + node.save(self.allocator_mut()); } /// Returns the value associated with the given key if it exists. @@ -457,6 +515,10 @@ where self.allocator.memory() } + fn allocator_mut(&mut self) -> &mut Allocator { + &mut self.allocator + } + /// Removes a key from the map, returning the previous value at the key if it exists. pub fn remove(&mut self, key: &K) -> Option { if self.root_addr == NULL { @@ -498,7 +560,7 @@ where self.allocator.deallocate(node.address()); self.root_addr = NULL; } else { - node.save(self.memory()); + node.save(self.allocator_mut()); } self.save(); @@ -543,7 +605,7 @@ where let (_, old_value) = node.swap_entry(idx, predecessor, self.memory()); // Save the parent node. - node.save(self.memory()); + node.save(self.allocator_mut()); return Some(old_value); } @@ -578,7 +640,7 @@ where let (_, old_value) = node.swap_entry(idx, successor, self.memory()); // Save the parent node. - node.save(self.memory()); + node.save(self.allocator_mut()); return Some(old_value); } @@ -627,8 +689,8 @@ where self.save(); } - node.save(self.memory()); - new_child.save(self.memory()); + node.save(self.allocator_mut()); + new_child.save(self.allocator_mut()); // Recursively delete the key. self.remove_helper(new_child, key) @@ -708,9 +770,9 @@ where assert_eq!(child.node_type(), NodeType::Leaf); } - left_sibling.save(self.memory()); - child.save(self.memory()); - node.save(self.memory()); + left_sibling.save(self.allocator_mut()); + child.save(self.allocator_mut()); + node.save(self.allocator_mut()); return self.remove_helper(child, key); } } @@ -763,9 +825,9 @@ where } } - right_sibling.save(self.memory()); - child.save(self.memory()); - node.save(self.memory()); + right_sibling.save(self.allocator_mut()); + child.save(self.allocator_mut()); + node.save(self.allocator_mut()); return self.remove_helper(child, key); } } @@ -793,7 +855,7 @@ where self.save(); } } else { - node.save(self.memory()); + node.save(self.allocator_mut()); } return self.remove_helper(left_sibling, key); @@ -821,7 +883,7 @@ where self.save(); } } else { - node.save(self.memory()); + node.save(self.allocator_mut()); } return self.remove_helper(right_sibling, key); @@ -1033,38 +1095,26 @@ where fn merge(&mut self, source: Node, mut into: Node, median: Entry) -> Node { let source_address = source.address(); into.merge(source, median, self.memory()); - into.save(self.memory()); + into.save(self.allocator_mut()); self.allocator.deallocate(source_address); into } fn allocate_node(&mut self, node_type: NodeType) -> Node { - Node::new( - self.allocator.allocate(), - node_type, - self.max_key_size, - self.max_value_size, - ) + match self.version { + Version::V1(page_size) => Node::new_v1(self.allocator.allocate(), node_type, page_size), + Version::V2(page_size) => Node::new_v2(self.allocator.allocate(), node_type, page_size), + } } fn load_node(&self, address: Address) -> Node { - Node::load( - address, - self.memory(), - Version::V1(DerivedPageSize { - max_key_size: self.max_key_size, - max_value_size: self.max_value_size, - }), - ) + Node::load(address, self.memory(), self.version) } // Saves the map to memory. fn save(&self) { - let header = BTreeHeaderV1 { - magic: *MAGIC, - version: LAYOUT_VERSION, - max_key_size: self.max_key_size, - max_value_size: self.max_value_size, + let header = BTreeHeader { + version: self.version, root_addr: self.root_addr, length: self.length, }; @@ -1073,13 +1123,24 @@ where } /// Write the layout header to the memory. - fn write_header(header: &BTreeHeaderV1, memory: &M) { + fn write_header(header: &BTreeHeader, memory: &M) { // Serialize the header let mut buf = [0; PACKED_HEADER_SIZE]; - buf[0..3].copy_from_slice(&header.magic); - buf[3] = header.version; - buf[4..8].copy_from_slice(&header.max_key_size.to_le_bytes()); - buf[8..12].copy_from_slice(&header.max_value_size.to_le_bytes()); + buf[0..3].copy_from_slice(MAGIC.as_slice()); + match header.version { + Version::V1(DerivedPageSize { + max_key_size, + max_value_size, + }) => { + buf[3] = LAYOUT_VERSION; + buf[4..8].copy_from_slice(&max_key_size.to_le_bytes()); + buf[8..12].copy_from_slice(&max_value_size.to_le_bytes()); + } + Version::V2(page_size) => { + buf[3] = LAYOUT_VERSION_2; + buf[4..8].copy_from_slice(&(page_size.get()).to_le_bytes()); + } + }; buf[12..20].copy_from_slice(&header.root_addr.get().to_le_bytes()); buf[20..28].copy_from_slice(&header.length.to_le_bytes()); // Write the header @@ -1116,7 +1177,7 @@ impl std::fmt::Display for InsertError { #[cfg(test)] mod test { use super::*; - use crate::storable::{Blob, Bound as StorableBound}; + use crate::storable::Blob; use std::cell::RefCell; use std::rc::Rc; @@ -2462,6 +2523,7 @@ mod test { } } + /* #[test] #[should_panic(expected = "Key is too large. Expected <= 0 bytes, found 4 bytes")] fn panics_if_key_is_too_large() { @@ -2512,7 +2574,7 @@ mod test { let mut btree: BTreeMap<(), V, _> = BTreeMap::init(make_memory()); btree.insert((), V); - } + }*/ // To generate the memory dump file for the current version: // cargo test create_btreemap_dump_file -- --include-ignored @@ -2566,11 +2628,11 @@ mod test { let packed_mem = make_memory(); crate::write_struct(&packed_header, Address::from(0), &packed_mem); - let v1_header = BTreeHeaderV1 { - magic: *MAGIC, - version: LAYOUT_VERSION, - max_key_size: 0x12345678, - max_value_size: 0x87654321, + let v1_header = BTreeHeader { + version: Version::V1(DerivedPageSize { + max_key_size: 0x12345678, + max_value_size: 0x87654321, + }), root_addr: Address::from(0xDEADBEEF), length: 0xA1B2D3C4, }; @@ -2582,10 +2644,19 @@ mod test { let packed_header: BTreePackedHeader = crate::read_struct(Address::from(0), &v1_mem); let v1_header = BTreeMap::, Vec<_>, RefCell>>::read_header(&v1_mem); - assert!(packed_header.magic == v1_header.magic); - assert!(packed_header.version == v1_header.version); - assert!(packed_header.max_key_size == v1_header.max_key_size); - assert!(packed_header.max_value_size == v1_header.max_value_size); + assert!(packed_header.magic == *MAGIC); + assert!(packed_header.version == LAYOUT_VERSION); + match v1_header.version { + Version::V1(DerivedPageSize { + max_key_size, + max_value_size, + }) => { + assert!(packed_header.max_key_size == max_key_size); + assert!(packed_header.max_value_size == max_value_size); + } + _ => unreachable!("version must be v1"), + }; + assert!(packed_header.root_addr == v1_header.root_addr); assert!(packed_header.length == v1_header.length); } diff --git a/src/btreemap/node.rs b/src/btreemap/node.rs index 1643eb51..00ca56a1 100644 --- a/src/btreemap/node.rs +++ b/src/btreemap/node.rs @@ -1,4 +1,5 @@ use crate::{ + btreemap::Allocator, read_struct, read_u32, read_u64, storable::Storable, types::{Address, Bytes}, @@ -64,16 +65,6 @@ pub struct Node { } impl Node { - /// Creates a new node at the given address. - pub fn new( - address: Address, - node_type: NodeType, - max_key_size: u32, - max_value_size: u32, - ) -> Node { - Node::new_v1(address, node_type, max_key_size, max_value_size) - } - /// Loads a node from memory at the given address. pub fn load(address: Address, memory: &M, version: Version) -> Self { match version { @@ -81,14 +72,16 @@ impl Node { max_key_size, max_value_size, }) => Self::load_v1(address, max_key_size, max_value_size, memory), - Version::V2(_) => unreachable!("Only v1 is currently supported."), + Version::V2(page_size) => Self::load_v2(address, page_size, memory), } } /// Saves the node to memory. - pub fn save(&self, memory: &M) { - // NOTE: new versions of `Node` will be introduced. - self.save_v1(memory) + pub fn save(&self, allocator: &mut Allocator) { + match self.version { + Version::V1(_) => self.save_v1(allocator.memory()), + Version::V2(_) => self.save_v2(allocator), + } } /// Returns the address of the node. @@ -436,10 +429,10 @@ pub enum Version { } impl Version { - fn page_size(&self) -> u32 { + pub fn page_size(&self) -> PageSize { match self { - Self::V2(page_size) => page_size.get(), - Self::V1(page_size) => page_size.get(), + Self::V1(page_size) => PageSize::Derived(*page_size), + Self::V2(page_size) => *page_size, } } } @@ -458,7 +451,7 @@ pub enum PageSize { } impl PageSize { - fn get(&self) -> u32 { + pub fn get(&self) -> u32 { match self { Self::Value(page_size) => *page_size, Self::Derived(page_size) => page_size.get(), diff --git a/src/btreemap/node/tests.rs b/src/btreemap/node/tests.rs index 10a4b077..c74dd5de 100644 --- a/src/btreemap/node/tests.rs +++ b/src/btreemap/node/tests.rs @@ -37,8 +37,10 @@ impl NodeV1Data { let mut node = Node::new_v1( address, self.node_type, - self.max_key_size, - self.max_value_size, + DerivedPageSize { + max_key_size: self.max_key_size, + max_value_size: self.max_value_size, + }, ); // Push the entries diff --git a/src/btreemap/node/v1.rs b/src/btreemap/node/v1.rs index 95759b02..c46317a9 100644 --- a/src/btreemap/node/v1.rs +++ b/src/btreemap/node/v1.rs @@ -40,11 +40,10 @@ use super::*; impl Node { /// Creates a new v1 node at the given address. - pub(super) fn new_v1( + pub fn new_v1( address: Address, node_type: NodeType, - max_key_size: u32, - max_value_size: u32, + page_size: DerivedPageSize, ) -> Node { Node { address, @@ -52,10 +51,7 @@ impl Node { keys: vec![], encoded_values: RefCell::default(), children: vec![], - version: Version::V1(DerivedPageSize { - max_key_size, - max_value_size, - }), + version: Version::V1(page_size), overflow: None, } } diff --git a/src/btreemap/node/v2.rs b/src/btreemap/node/v2.rs index 7389e748..b160a24e 100644 --- a/src/btreemap/node/v2.rs +++ b/src/btreemap/node/v2.rs @@ -96,7 +96,7 @@ const MINIMUM_PAGE_SIZE: u32 = 128; impl Node { /// Creates a new v2 node at the given address. - pub(super) fn new_v2(address: Address, node_type: NodeType, page_size: PageSize) -> Node { + pub fn new_v2(address: Address, node_type: NodeType, page_size: PageSize) -> Node { assert!(page_size.get() >= MINIMUM_PAGE_SIZE); Node { @@ -192,7 +192,7 @@ impl Node { // Saves the node to memory. pub(super) fn save_v2(&self, allocator: &mut Allocator) { - let page_size = self.version.page_size(); + let page_size = self.version.page_size().get(); assert!(page_size >= MINIMUM_PAGE_SIZE); assert_eq!(self.keys.len(), self.encoded_values.borrow().len()); From 022eaa0eefd223855f3ee6bfa9534df475d5b306 Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Tue, 22 Aug 2023 12:56:58 +0200 Subject: [PATCH 2/7] update tests --- src/btreemap.rs | 1842 +++++++++++++++++++-------------------- src/btreemap/node/v1.rs | 6 +- 2 files changed, 921 insertions(+), 927 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 85b897e5..c881dcbd 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1177,7 +1177,10 @@ impl std::fmt::Display for InsertError { #[cfg(test)] mod test { use super::*; - use crate::storable::Blob; + use crate::{ + storable::{Blob, Bound as StorableBound}, + VectorMemory, + }; use std::cell::RefCell; use std::rc::Rc; @@ -1194,585 +1197,588 @@ mod test { Blob::<10>::try_from(x).unwrap() } + // A test runner that runs the test using both V1 and V2 btrees. + fn btree_test(f: F) + where + K: Storable + Ord + Clone, + V: Storable, + F: Fn(BTreeMap) -> R, + { + // Run the test with the V1 btree. + let mem = make_memory(); + let btree = BTreeMap::new(mem); + f(btree); + + // Run the test with the V2 btree. + let mem = make_memory(); + let btree = BTreeMap::new_v2(mem); + f(btree); + } + #[test] fn init_preserves_data() { - let mem = make_memory(); - let mut btree = BTreeMap::init(mem.clone()); - assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); - assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + btree_test(|mut btree| { + assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); + assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); - // Reload the btree - let btree = BTreeMap::init(mem); + // Reload the btree + let btree = BTreeMap::init(btree.into_memory()); - // Data still exists. - assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + // Data still exists. + assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + }); } #[test] fn insert_get() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); - assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + btree_test(|mut btree| { + assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); + assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + }); } #[test] fn insert_overwrites_previous_value() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); - assert_eq!( - btree.insert(b(&[1, 2, 3]), b(&[7, 8, 9])), - Some(b(&[4, 5, 6])) - ); - assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[7, 8, 9]))); + btree_test(|mut btree| { + assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); + assert_eq!( + btree.insert(b(&[1, 2, 3]), b(&[7, 8, 9])), + Some(b(&[4, 5, 6])) + ); + assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[7, 8, 9]))); + }); } #[test] fn insert_get_multiple() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); - assert_eq!(btree.insert(b(&[4, 5]), b(&[7, 8, 9, 10])), None); - assert_eq!(btree.insert(b(&[]), b(&[11])), None); - assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); - assert_eq!(btree.get(&b(&[4, 5])), Some(b(&[7, 8, 9, 10]))); - assert_eq!(btree.get(&b(&[])), Some(b(&[11]))); + btree_test(|mut btree| { + assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); + assert_eq!(btree.insert(b(&[4, 5]), b(&[7, 8, 9, 10])), None); + assert_eq!(btree.insert(b(&[]), b(&[11])), None); + assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + assert_eq!(btree.get(&b(&[4, 5])), Some(b(&[7, 8, 9, 10]))); + assert_eq!(btree.get(&b(&[])), Some(b(&[11]))); + }); } #[test] fn insert_overwrite_median_key_in_full_child_node() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); + btree_test(|mut btree| { + for i in 1..=17 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } - for i in 1..=17 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } + // The result should look like this: + // [6] + // / \ + // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] - // The result should look like this: - // [6] - // / \ - // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] - - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(6)]); - assert_eq!(root.children_len(), 2); + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(6)]); + assert_eq!(root.children_len(), 2); - // The right child should now be full, with the median key being "12" - let right_child = btree.load_node(root.child(1)); - assert!(right_child.is_full()); - let median_index = right_child.entries_len() / 2; - assert_eq!(right_child.key(median_index), &b(&[12])); + // The right child should now be full, with the median key being "12" + let right_child = btree.load_node(root.child(1)); + assert!(right_child.is_full()); + let median_index = right_child.entries_len() / 2; + assert_eq!(right_child.key(median_index), &b(&[12])); - // Overwrite the median key. - assert_eq!(btree.insert(b(&[12]), b(&[1, 2, 3])), Some(b(&[]))); + // Overwrite the median key. + assert_eq!(btree.insert(b(&[12]), b(&[1, 2, 3])), Some(b(&[]))); - // The key is overwritten successfully. - assert_eq!(btree.get(&b(&[12])), Some(b(&[1, 2, 3]))); + // The key is overwritten successfully. + assert_eq!(btree.get(&b(&[12])), Some(b(&[1, 2, 3]))); - // The child has not been split and is still full. - let right_child = btree.load_node(root.child(1)); - assert_eq!(right_child.node_type(), NodeType::Leaf); - assert!(right_child.is_full()); + // The child has not been split and is still full. + let right_child = btree.load_node(root.child(1)); + assert_eq!(right_child.node_type(), NodeType::Leaf); + assert!(right_child.is_full()); + }); } #[test] fn insert_overwrite_key_in_full_root_node() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } - // We now have a root that is full and looks like this: - // - // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - let root = btree.load_node(btree.root_addr); - assert!(root.is_full()); + // We now have a root that is full and looks like this: + // + // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + let root = btree.load_node(btree.root_addr); + assert!(root.is_full()); - // Overwrite an element in the root. It should NOT cause the node to be split. - assert_eq!(btree.insert(b(&[6]), b(&[4, 5, 6])), Some(b(&[]))); + // Overwrite an element in the root. It should NOT cause the node to be split. + assert_eq!(btree.insert(b(&[6]), b(&[4, 5, 6])), Some(b(&[]))); - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Leaf); - assert_eq!(btree.get(&b(&[6])), Some(b(&[4, 5, 6]))); - assert_eq!(root.entries_len(), 11); + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Leaf); + assert_eq!(btree.get(&b(&[6])), Some(b(&[4, 5, 6]))); + assert_eq!(root.entries_len(), 11); + }); } #[test] fn allocations() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - // Insert entries until the root node is full. - let mut i = 0; - loop { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - let root = btree.load_node(btree.root_addr); - if root.is_full() { - break; + btree_test(|mut btree| { + // Insert entries until the root node is full. + let mut i = 0; + loop { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + let root = btree.load_node(btree.root_addr); + if root.is_full() { + break; + } + i += 1; } - i += 1; - } - // Only need a single allocation to store up to `CAPACITY` elements. - assert_eq!(btree.allocator.num_allocated_chunks(), 1); + // Only need a single allocation to store up to `CAPACITY` elements. + assert_eq!(btree.allocator.num_allocated_chunks(), 1); - assert_eq!(btree.insert(b(&[255]), b(&[])), None); + assert_eq!(btree.insert(b(&[255]), b(&[])), None); - // The node had to be split into three nodes. - assert_eq!(btree.allocator.num_allocated_chunks(), 3); + // The node had to be split into three nodes. + assert_eq!(btree.allocator.num_allocated_chunks(), 3); + }); } #[test] fn allocations_2() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - assert_eq!(btree.allocator.num_allocated_chunks(), 0); + btree_test(|mut btree| { + assert_eq!(btree.allocator.num_allocated_chunks(), 0); - assert_eq!(btree.insert(b(&[]), b(&[])), None); - assert_eq!(btree.allocator.num_allocated_chunks(), 1); + assert_eq!(btree.insert(b(&[]), b(&[])), None); + assert_eq!(btree.allocator.num_allocated_chunks(), 1); - assert_eq!(btree.remove(&b(&[])), Some(b(&[]))); - assert_eq!(btree.allocator.num_allocated_chunks(), 0); + assert_eq!(btree.remove(&b(&[])), Some(b(&[]))); + assert_eq!(btree.allocator.num_allocated_chunks(), 0); + }); } #[test] fn insert_same_key_multiple() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); + btree_test(|mut btree| { + assert_eq!(btree.insert(b(&[1]), b(&[2])), None); - assert_eq!(btree.insert(b(&[1]), b(&[2])), None); - - for i in 2..10 { - assert_eq!(btree.insert(b(&[1]), b(&[i + 1])), Some(b(&[i]))); - } + for i in 2..10 { + assert_eq!(btree.insert(b(&[1]), b(&[i + 1])), Some(b(&[i]))); + } + }); } #[test] fn insert_split_node() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } - // Should now split a node. - assert_eq!(btree.insert(b(&[12]), b(&[])), None); + // Should now split a node. + assert_eq!(btree.insert(b(&[12]), b(&[])), None); - // The result should look like this: - // [6] - // / \ - // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] + // The result should look like this: + // [6] + // / \ + // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - for i in 1..=12 { - assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); - } + for i in 1..=12 { + assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); + } + }); } #[test] fn insert_split_multiple_nodes() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } - // Should now split a node. - assert_eq!(btree.insert(b(&[12]), b(&[])), None); - - // The result should look like this: - // [6] - // / \ - // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } + // Should now split a node. + assert_eq!(btree.insert(b(&[12]), b(&[])), None); - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(6)]); - assert_eq!(root.children_len(), 2); + // The result should look like this: + // [6] + // / \ + // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![e(1), e(2), e(3), e(4), e(5)] - ); + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(6)]); + assert_eq!(root.children_len(), 2); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![e(7), e(8), e(9), e(10), e(11), e(12)] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![e(1), e(2), e(3), e(4), e(5)] + ); - for i in 1..=12 { - assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); - } + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![e(7), e(8), e(9), e(10), e(11), e(12)] + ); - // Insert more to cause more splitting. - assert_eq!(btree.insert(b(&[13]), b(&[])), None); - assert_eq!(btree.insert(b(&[14]), b(&[])), None); - assert_eq!(btree.insert(b(&[15]), b(&[])), None); - assert_eq!(btree.insert(b(&[16]), b(&[])), None); - assert_eq!(btree.insert(b(&[17]), b(&[])), None); - // Should cause another split - assert_eq!(btree.insert(b(&[18]), b(&[])), None); + for i in 1..=12 { + assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); + } - for i in 1..=18 { - assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); - } + // Insert more to cause more splitting. + assert_eq!(btree.insert(b(&[13]), b(&[])), None); + assert_eq!(btree.insert(b(&[14]), b(&[])), None); + assert_eq!(btree.insert(b(&[15]), b(&[])), None); + assert_eq!(btree.insert(b(&[16]), b(&[])), None); + assert_eq!(btree.insert(b(&[17]), b(&[])), None); + // Should cause another split + assert_eq!(btree.insert(b(&[18]), b(&[])), None); + + for i in 1..=18 { + assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); + } - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(6), e(12)],); - assert_eq!(root.children_len(), 3); + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(6), e(12)],); + assert_eq!(root.children_len(), 3); - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![e(1), e(2), e(3), e(4), e(5)] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![e(1), e(2), e(3), e(4), e(5)] + ); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![e(7), e(8), e(9), e(10), e(11)] - ); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![e(7), e(8), e(9), e(10), e(11)] + ); - let child_2 = btree.load_node(root.child(2)); - assert_eq!(child_2.node_type(), NodeType::Leaf); - assert_eq!( - child_2.entries(btree.memory()), - vec![e(13), e(14), e(15), e(16), e(17), e(18)] - ); + let child_2 = btree.load_node(root.child(2)); + assert_eq!(child_2.node_type(), NodeType::Leaf); + assert_eq!( + child_2.entries(btree.memory()), + vec![e(13), e(14), e(15), e(16), e(17), e(18)] + ); + }); } #[test] fn remove_simple() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); - assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); - assert_eq!(btree.remove(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); - assert_eq!(btree.get(&b(&[1, 2, 3])), None); + btree_test(|mut btree| { + assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); + assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + assert_eq!(btree.remove(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + assert_eq!(btree.get(&b(&[1, 2, 3])), None); + }); } #[test] fn remove_case_2a_and_2c() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem.clone()); - - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } - // Should now split a node. - assert_eq!(btree.insert(b(&[0]), b(&[])), None); + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } + // Should now split a node. + assert_eq!(btree.insert(b(&[0]), b(&[])), None); - // The result should look like this: - // [6] - // / \ - // [0, 1, 2, 3, 4, 5] [7, 8, 9, 10, 11] + // The result should look like this: + // [6] + // / \ + // [0, 1, 2, 3, 4, 5] [7, 8, 9, 10, 11] - for i in 0..=11 { - assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); - } + for i in 0..=11 { + assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); + } - // Remove node 6. Triggers case 2.a - assert_eq!(btree.remove(&b(&[6])), Some(b(&[]))); + // Remove node 6. Triggers case 2.a + assert_eq!(btree.remove(&b(&[6])), Some(b(&[]))); - // The result should look like this: - // [5] - // / \ - // [0, 1, 2, 3, 4] [7, 8, 9, 10, 11] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(5)]); - assert_eq!(root.children_len(), 2); + // The result should look like this: + // [5] + // / \ + // [0, 1, 2, 3, 4] [7, 8, 9, 10, 11] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(5)]); + assert_eq!(root.children_len(), 2); - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![e(0), e(1), e(2), e(3), e(4)] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![e(0), e(1), e(2), e(3), e(4)] + ); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![e(7), e(8), e(9), e(10), e(11)] - ); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![e(7), e(8), e(9), e(10), e(11)] + ); - // There are three allocated nodes. - assert_eq!(btree.allocator.num_allocated_chunks(), 3); + // There are three allocated nodes. + assert_eq!(btree.allocator.num_allocated_chunks(), 3); - // Remove node 5. Triggers case 2c - assert_eq!(btree.remove(&b(&[5])), Some(b(&[]))); + // Remove node 5. Triggers case 2c + assert_eq!(btree.remove(&b(&[5])), Some(b(&[]))); - // Reload the btree to verify that we saved it correctly. - let btree = BTreeMap::, Blob<10>, _>::load(mem); + // Reload the btree to verify that we saved it correctly. + let btree = BTreeMap::, Blob<10>, _>::load(btree.into_memory()); - // The result should look like this: - // [0, 1, 2, 3, 4, 7, 8, 9, 10, 11] - let root = btree.load_node(btree.root_addr); - assert_eq!( - root.entries(btree.memory()), - vec![e(0), e(1), e(2), e(3), e(4), e(7), e(8), e(9), e(10), e(11)] - ); + // The result should look like this: + // [0, 1, 2, 3, 4, 7, 8, 9, 10, 11] + let root = btree.load_node(btree.root_addr); + assert_eq!( + root.entries(btree.memory()), + vec![e(0), e(1), e(2), e(3), e(4), e(7), e(8), e(9), e(10), e(11)] + ); - // There is only one node allocated. - assert_eq!(btree.allocator.num_allocated_chunks(), 1); + // There is only one node allocated. + assert_eq!(btree.allocator.num_allocated_chunks(), 1); + }); } #[test] fn remove_case_2b() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } - // Should now split a node. - assert_eq!(btree.insert(b(&[12]), b(&[])), None); + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } + // Should now split a node. + assert_eq!(btree.insert(b(&[12]), b(&[])), None); - // The result should look like this: - // [6] - // / \ - // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] + // The result should look like this: + // [6] + // / \ + // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - for i in 1..=12 { - assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); - } + for i in 1..=12 { + assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); + } - // Remove node 6. Triggers case 2.b - assert_eq!(btree.remove(&b(&[6])), Some(b(&[]))); + // Remove node 6. Triggers case 2.b + assert_eq!(btree.remove(&b(&[6])), Some(b(&[]))); - // The result should look like this: - // [7] - // / \ - // [1, 2, 3, 4, 5] [8, 9, 10, 11, 12] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(7)]); - assert_eq!(root.children_len(), 2); + // The result should look like this: + // [7] + // / \ + // [1, 2, 3, 4, 5] [8, 9, 10, 11, 12] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(7)]); + assert_eq!(root.children_len(), 2); - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![e(1), e(2), e(3), e(4), e(5)] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![e(1), e(2), e(3), e(4), e(5)] + ); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![e(8), e(9), e(10), e(11), e(12)] - ); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![e(8), e(9), e(10), e(11), e(12)] + ); - // Remove node 7. Triggers case 2.c - assert_eq!(btree.remove(&b(&[7])), Some(b(&[]))); - // The result should look like this: - // - // [1, 2, 3, 4, 5, 8, 9, 10, 11, 12] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Leaf); - assert_eq!( - root.entries(btree.memory()), - vec![ - e(1), - e(2), - e(3), - e(4), - e(5), - e(8), - e(9), - e(10), - e(11), - e(12) - ] - ); + // Remove node 7. Triggers case 2.c + assert_eq!(btree.remove(&b(&[7])), Some(b(&[]))); + // The result should look like this: + // + // [1, 2, 3, 4, 5, 8, 9, 10, 11, 12] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Leaf); + assert_eq!( + root.entries(btree.memory()), + vec![ + e(1), + e(2), + e(3), + e(4), + e(5), + e(8), + e(9), + e(10), + e(11), + e(12) + ] + ); + }); } #[test] fn remove_case_3a_right() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } + // Should now split a node. + assert_eq!(btree.insert(b(&[12]), b(&[])), None); - // Should now split a node. - assert_eq!(btree.insert(b(&[12]), b(&[])), None); + // The result should look like this: + // [6] + // / \ + // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - // The result should look like this: - // [6] - // / \ - // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - - // Remove node 3. Triggers case 3.a - assert_eq!(btree.remove(&b(&[3])), Some(b(&[]))); + // Remove node 3. Triggers case 3.a + assert_eq!(btree.remove(&b(&[3])), Some(b(&[]))); - // The result should look like this: - // [7] - // / \ - // [1, 2, 4, 5, 6] [8, 9, 10, 11, 12] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(7)]); - assert_eq!(root.children_len(), 2); + // The result should look like this: + // [7] + // / \ + // [1, 2, 4, 5, 6] [8, 9, 10, 11, 12] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(7)]); + assert_eq!(root.children_len(), 2); - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![e(1), e(2), e(4), e(5), e(6)] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![e(1), e(2), e(4), e(5), e(6)] + ); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![e(8), e(9), e(10), e(11), e(12)] - ); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![e(8), e(9), e(10), e(11), e(12)] + ); - // There are three allocated nodes. - assert_eq!(btree.allocator.num_allocated_chunks(), 3); + // There are three allocated nodes. + assert_eq!(btree.allocator.num_allocated_chunks(), 3); + }); } #[test] fn remove_case_3a_left() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } - // Should now split a node. - assert_eq!(btree.insert(b(&[0]), b(&[])), None); + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } + // Should now split a node. + assert_eq!(btree.insert(b(&[0]), b(&[])), None); - // The result should look like this: - // [6] - // / \ - // [0, 1, 2, 3, 4, 5] [7, 8, 9, 10, 11] + // The result should look like this: + // [6] + // / \ + // [0, 1, 2, 3, 4, 5] [7, 8, 9, 10, 11] - // Remove node 8. Triggers case 3.a left - assert_eq!(btree.remove(&b(&[8])), Some(b(&[]))); + // Remove node 8. Triggers case 3.a left + assert_eq!(btree.remove(&b(&[8])), Some(b(&[]))); - // The result should look like this: - // [5] - // / \ - // [0, 1, 2, 3, 4] [6, 7, 9, 10, 11] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(5)]); - assert_eq!(root.children_len(), 2); + // The result should look like this: + // [5] + // / \ + // [0, 1, 2, 3, 4] [6, 7, 9, 10, 11] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(5)]); + assert_eq!(root.children_len(), 2); - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![e(0), e(1), e(2), e(3), e(4)] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![e(0), e(1), e(2), e(3), e(4)] + ); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![e(6), e(7), e(9), e(10), e(11)] - ); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![e(6), e(7), e(9), e(10), e(11)] + ); - // There are three allocated nodes. - assert_eq!(btree.allocator.num_allocated_chunks(), 3); + // There are three allocated nodes. + assert_eq!(btree.allocator.num_allocated_chunks(), 3); + }); } #[test] - fn remove_case_3b_merge_into_right() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem.clone()); - - for i in 1..=11 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } - // Should now split a node. - assert_eq!(btree.insert(b(&[12]), b(&[])), None); - - // The result should look like this: - // [6] - // / \ - // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] + fn remove_case_3b_merge_into_right() { + btree_test(|mut btree| { + for i in 1..=11 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } + // Should now split a node. + assert_eq!(btree.insert(b(&[12]), b(&[])), None); - for i in 1..=12 { - assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); - } + // The result should look like this: + // [6] + // / \ + // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - // Remove node 6. Triggers case 2.b - assert_eq!(btree.remove(&b(&[6])), Some(b(&[]))); - // The result should look like this: - // [7] - // / \ - // [1, 2, 3, 4, 5] [8, 9, 10, 11, 12] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![e(7)]); - assert_eq!(root.children_len(), 2); + for i in 1..=12 { + assert_eq!(btree.get(&b(&[i])), Some(b(&[]))); + } - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![e(1), e(2), e(3), e(4), e(5)] - ); + // Remove node 6. Triggers case 2.b + assert_eq!(btree.remove(&b(&[6])), Some(b(&[]))); + // The result should look like this: + // [7] + // / \ + // [1, 2, 3, 4, 5] [8, 9, 10, 11, 12] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![e(7)]); + assert_eq!(root.children_len(), 2); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![e(8), e(9), e(10), e(11), e(12)] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![e(1), e(2), e(3), e(4), e(5)] + ); - // There are three allocated nodes. - assert_eq!(btree.allocator.num_allocated_chunks(), 3); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![e(8), e(9), e(10), e(11), e(12)] + ); - // Remove node 3. Triggers case 3.b - assert_eq!(btree.remove(&b(&[3])), Some(b(&[]))); + // There are three allocated nodes. + assert_eq!(btree.allocator.num_allocated_chunks(), 3); - // Reload the btree to verify that we saved it correctly. - let btree = BTreeMap::, Blob<10>, _>::load(mem); + // Remove node 3. Triggers case 3.b + assert_eq!(btree.remove(&b(&[3])), Some(b(&[]))); - // The result should look like this: - // - // [1, 2, 4, 5, 7, 8, 9, 10, 11, 12] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Leaf); - assert_eq!( - root.entries(btree.memory()), - vec![ - e(1), - e(2), - e(4), - e(5), - e(7), - e(8), - e(9), - e(10), - e(11), - e(12) - ] - ); + // Reload the btree to verify that we saved it correctly. + let btree = BTreeMap::, Blob<10>, _>::load(btree.into_memory()); - // There is only one allocated node remaining. - assert_eq!(btree.allocator.num_allocated_chunks(), 1); + // The result should look like this: + // + // [1, 2, 4, 5, 7, 8, 9, 10, 11, 12] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Leaf); + assert_eq!( + root.entries(btree.memory()), + vec![ + e(1), + e(2), + e(4), + e(5), + e(7), + e(8), + e(9), + e(10), + e(11), + e(12) + ] + ); + + // There is only one allocated node remaining. + assert_eq!(btree.allocator.num_allocated_chunks(), 1); + }); } #[test] @@ -1917,490 +1923,484 @@ mod test { #[test] fn reloading() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem.clone()); - - // The btree is initially empty. - assert_eq!(btree.len(), 0); - assert!(btree.is_empty()); - - // Add an entry into the btree. - assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); - assert_eq!(btree.len(), 1); - assert!(!btree.is_empty()); - - // Reload the btree. The element should still be there, and `len()` - // should still be `1`. - let btree = BTreeMap::load(mem.clone()); - assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); - assert_eq!(btree.len(), 1); - assert!(!btree.is_empty()); - - // Remove an element. Length should be zero. - let mut btree = BTreeMap::load(mem.clone()); - assert_eq!(btree.remove(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); - assert_eq!(btree.len(), 0); - assert!(btree.is_empty()); - - // Reload. Btree should still be empty. - let btree = BTreeMap::, Blob<10>, _>::load(mem); - assert_eq!(btree.get(&b(&[1, 2, 3])), None); - assert_eq!(btree.len(), 0); - assert!(btree.is_empty()); + btree_test(|mut btree| { + // The btree is initially empty. + assert_eq!(btree.len(), 0); + assert!(btree.is_empty()); + + // Add an entry into the btree. + assert_eq!(btree.insert(b(&[1, 2, 3]), b(&[4, 5, 6])), None); + assert_eq!(btree.len(), 1); + assert!(!btree.is_empty()); + + // Reload the btree. The element should still be there, and `len()` + // should still be `1`. + let btree = BTreeMap::load(btree.into_memory()); + assert_eq!(btree.get(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + assert_eq!(btree.len(), 1); + assert!(!btree.is_empty()); + + // Remove an element. Length should be zero. + let mut btree = BTreeMap::load(btree.into_memory()); + assert_eq!(btree.remove(&b(&[1, 2, 3])), Some(b(&[4, 5, 6]))); + assert_eq!(btree.len(), 0); + assert!(btree.is_empty()); + + // Reload. Btree should still be empty. + let btree = BTreeMap::, Blob<10>, _>::load(btree.into_memory()); + assert_eq!(btree.get(&b(&[1, 2, 3])), None); + assert_eq!(btree.len(), 0); + assert!(btree.is_empty()); + }); } #[test] fn len() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for i in 0..1000u32 { - assert_eq!(btree.insert(b(i.to_le_bytes().as_slice()), b(&[])), None); - } + btree_test(|mut btree| { + for i in 0..1000u32 { + assert_eq!(btree.insert(b(i.to_le_bytes().as_slice()), b(&[])), None); + } - assert_eq!(btree.len(), 1000); - assert!(!btree.is_empty()); + assert_eq!(btree.len(), 1000); + assert!(!btree.is_empty()); - for i in 0..1000u32 { - assert_eq!(btree.remove(&b(i.to_le_bytes().as_slice())), Some(b(&[]))); - } + for i in 0..1000u32 { + assert_eq!(btree.remove(&b(i.to_le_bytes().as_slice())), Some(b(&[]))); + } - assert_eq!(btree.len(), 0); - assert!(btree.is_empty()); + assert_eq!(btree.len(), 0); + assert!(btree.is_empty()); + }); } #[test] fn contains_key() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - // Insert even numbers from 0 to 1000. - for i in (0..1000u32).step_by(2) { - assert_eq!(btree.insert(b(i.to_le_bytes().as_slice()), b(&[])), None); - } + btree_test(|mut btree| { + // Insert even numbers from 0 to 1000. + for i in (0..1000u32).step_by(2) { + assert_eq!(btree.insert(b(i.to_le_bytes().as_slice()), b(&[])), None); + } - // Contains key should return true on all the even numbers and false on all the odd - // numbers. - for i in 0..1000u32 { - assert_eq!( - btree.contains_key(&b(i.to_le_bytes().as_slice())), - i % 2 == 0 - ); - } + // Contains key should return true on all the even numbers and false on all the odd + // numbers. + for i in 0..1000u32 { + assert_eq!( + btree.contains_key(&b(i.to_le_bytes().as_slice())), + i % 2 == 0 + ); + } + }); } #[test] fn range_empty() { - let mem = make_memory(); - let btree = BTreeMap::, Blob<10>, _>::new(mem); - - // Test prefixes that don't exist in the map. - assert_eq!(btree.range(b(&[0])..).collect::>(), vec![]); - assert_eq!(btree.range(b(&[1, 2, 3, 4])..).collect::>(), vec![]); + btree_test(|btree| { + // Test prefixes that don't exist in the map. + assert_eq!( + btree + .range(b(&[0])..) + .collect::, Blob<10>)>>(), + vec![] + ); + assert_eq!(btree.range(b(&[1, 2, 3, 4])..).collect::>(), vec![]); + }); } // Tests the case where the prefix is larger than all the entries in a leaf node. #[test] fn range_leaf_prefix_greater_than_all_entries() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - btree.insert(b(&[0]), b(&[])); + btree_test(|mut btree| { + btree.insert(b(&[0]), b(&[])); - // Test a prefix that's larger than the value in the leaf node. Should be empty. - assert_eq!(btree.range(b(&[1])..).collect::>(), vec![]); + // Test a prefix that's larger than the value in the leaf node. Should be empty. + assert_eq!(btree.range(b(&[1])..).collect::>(), vec![]); + }); } // Tests the case where the prefix is larger than all the entries in an internal node. #[test] fn range_internal_prefix_greater_than_all_entries() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for i in 1..=12 { - assert_eq!(btree.insert(b(&[i]), b(&[])), None); - } + btree_test(|mut btree| { + for i in 1..=12 { + assert_eq!(btree.insert(b(&[i]), b(&[])), None); + } - // The result should look like this: - // [6] - // / \ - // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] + // The result should look like this: + // [6] + // / \ + // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - // Test a prefix that's larger than the value in the internal node. - assert_eq!( - btree.range(b(&[7])..b(&[8])).collect::>(), - vec![(b(&[7]), b(&[]))] - ); + // Test a prefix that's larger than the value in the internal node. + assert_eq!( + btree.range(b(&[7])..b(&[8])).collect::>(), + vec![(b(&[7]), b(&[]))] + ); + }); } #[test] fn range_various_prefixes() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - btree.insert(b(&[0, 1]), b(&[])); - btree.insert(b(&[0, 2]), b(&[])); - btree.insert(b(&[0, 3]), b(&[])); - btree.insert(b(&[0, 4]), b(&[])); - btree.insert(b(&[1, 1]), b(&[])); - btree.insert(b(&[1, 2]), b(&[])); - btree.insert(b(&[1, 3]), b(&[])); - btree.insert(b(&[1, 4]), b(&[])); - btree.insert(b(&[2, 1]), b(&[])); - btree.insert(b(&[2, 2]), b(&[])); - btree.insert(b(&[2, 3]), b(&[])); - btree.insert(b(&[2, 4]), b(&[])); - - // The result should look like this: - // [(1, 2)] - // / \ - // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 1)] [(1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4)] + btree_test(|mut btree| { + btree.insert(b(&[0, 1]), b(&[])); + btree.insert(b(&[0, 2]), b(&[])); + btree.insert(b(&[0, 3]), b(&[])); + btree.insert(b(&[0, 4]), b(&[])); + btree.insert(b(&[1, 1]), b(&[])); + btree.insert(b(&[1, 2]), b(&[])); + btree.insert(b(&[1, 3]), b(&[])); + btree.insert(b(&[1, 4]), b(&[])); + btree.insert(b(&[2, 1]), b(&[])); + btree.insert(b(&[2, 2]), b(&[])); + btree.insert(b(&[2, 3]), b(&[])); + btree.insert(b(&[2, 4]), b(&[])); + + // The result should look like this: + // [(1, 2)] + // / \ + // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 1)] [(1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4)] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![(b(&[1, 2]), vec![])]); - assert_eq!(root.children_len(), 2); + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![(b(&[1, 2]), vec![])]); + assert_eq!(root.children_len(), 2); - // Tests a prefix that's smaller than the value in the internal node. - assert_eq!( - btree.range(b(&[0])..b(&[1])).collect::>(), - vec![ - (b(&[0, 1]), b(&[])), - (b(&[0, 2]), b(&[])), - (b(&[0, 3]), b(&[])), - (b(&[0, 4]), b(&[])), - ] - ); + // Tests a prefix that's smaller than the value in the internal node. + assert_eq!( + btree.range(b(&[0])..b(&[1])).collect::>(), + vec![ + (b(&[0, 1]), b(&[])), + (b(&[0, 2]), b(&[])), + (b(&[0, 3]), b(&[])), + (b(&[0, 4]), b(&[])), + ] + ); - // Tests a prefix that crosses several nodes. - assert_eq!( - btree.range(b(&[1])..b(&[2])).collect::>(), - vec![ - (b(&[1, 1]), b(&[])), - (b(&[1, 2]), b(&[])), - (b(&[1, 3]), b(&[])), - (b(&[1, 4]), b(&[])), - ] - ); + // Tests a prefix that crosses several nodes. + assert_eq!( + btree.range(b(&[1])..b(&[2])).collect::>(), + vec![ + (b(&[1, 1]), b(&[])), + (b(&[1, 2]), b(&[])), + (b(&[1, 3]), b(&[])), + (b(&[1, 4]), b(&[])), + ] + ); - // Tests a prefix that's larger than the value in the internal node. - assert_eq!( - btree.range(b(&[2])..b(&[3])).collect::>(), - vec![ - (b(&[2, 1]), b(&[])), - (b(&[2, 2]), b(&[])), - (b(&[2, 3]), b(&[])), - (b(&[2, 4]), b(&[])), - ] - ); + // Tests a prefix that's larger than the value in the internal node. + assert_eq!( + btree.range(b(&[2])..b(&[3])).collect::>(), + vec![ + (b(&[2, 1]), b(&[])), + (b(&[2, 2]), b(&[])), + (b(&[2, 3]), b(&[])), + (b(&[2, 4]), b(&[])), + ] + ); + }); } #[test] fn range_various_prefixes_2() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - btree.insert(b(&[0, 1]), b(&[])); - btree.insert(b(&[0, 2]), b(&[])); - btree.insert(b(&[0, 3]), b(&[])); - btree.insert(b(&[0, 4]), b(&[])); - btree.insert(b(&[1, 2]), b(&[])); - btree.insert(b(&[1, 4]), b(&[])); - btree.insert(b(&[1, 6]), b(&[])); - btree.insert(b(&[1, 8]), b(&[])); - btree.insert(b(&[1, 10]), b(&[])); - btree.insert(b(&[2, 1]), b(&[])); - btree.insert(b(&[2, 2]), b(&[])); - btree.insert(b(&[2, 3]), b(&[])); - btree.insert(b(&[2, 4]), b(&[])); - btree.insert(b(&[2, 5]), b(&[])); - btree.insert(b(&[2, 6]), b(&[])); - btree.insert(b(&[2, 7]), b(&[])); - btree.insert(b(&[2, 8]), b(&[])); - btree.insert(b(&[2, 9]), b(&[])); - - // The result should look like this: - // [(1, 4), (2, 3)] - // / | \ - // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2)] | [(2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9)] - // | - // [(1, 6), (1, 8), (1, 10), (2, 1), (2, 2)] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!( - root.entries(btree.memory()), - vec![(b(&[1, 4]), vec![]), (b(&[2, 3]), vec![])] - ); - assert_eq!(root.children_len(), 3); + btree_test(|mut btree| { + btree.insert(b(&[0, 1]), b(&[])); + btree.insert(b(&[0, 2]), b(&[])); + btree.insert(b(&[0, 3]), b(&[])); + btree.insert(b(&[0, 4]), b(&[])); + btree.insert(b(&[1, 2]), b(&[])); + btree.insert(b(&[1, 4]), b(&[])); + btree.insert(b(&[1, 6]), b(&[])); + btree.insert(b(&[1, 8]), b(&[])); + btree.insert(b(&[1, 10]), b(&[])); + btree.insert(b(&[2, 1]), b(&[])); + btree.insert(b(&[2, 2]), b(&[])); + btree.insert(b(&[2, 3]), b(&[])); + btree.insert(b(&[2, 4]), b(&[])); + btree.insert(b(&[2, 5]), b(&[])); + btree.insert(b(&[2, 6]), b(&[])); + btree.insert(b(&[2, 7]), b(&[])); + btree.insert(b(&[2, 8]), b(&[])); + btree.insert(b(&[2, 9]), b(&[])); + + // The result should look like this: + // [(1, 4), (2, 3)] + // / | \ + // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2)] | [(2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9)] + // | + // [(1, 6), (1, 8), (1, 10), (2, 1), (2, 2)] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!( + root.entries(btree.memory()), + vec![(b(&[1, 4]), vec![]), (b(&[2, 3]), vec![])] + ); + assert_eq!(root.children_len(), 3); - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![ - (b(&[0, 1]), vec![]), - (b(&[0, 2]), vec![]), - (b(&[0, 3]), vec![]), - (b(&[0, 4]), vec![]), - (b(&[1, 2]), vec![]), - ] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![ + (b(&[0, 1]), vec![]), + (b(&[0, 2]), vec![]), + (b(&[0, 3]), vec![]), + (b(&[0, 4]), vec![]), + (b(&[1, 2]), vec![]), + ] + ); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![ - (b(&[1, 6]), vec![]), - (b(&[1, 8]), vec![]), - (b(&[1, 10]), vec![]), - (b(&[2, 1]), vec![]), - (b(&[2, 2]), vec![]), - ] - ); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![ + (b(&[1, 6]), vec![]), + (b(&[1, 8]), vec![]), + (b(&[1, 10]), vec![]), + (b(&[2, 1]), vec![]), + (b(&[2, 2]), vec![]), + ] + ); - let child_2 = btree.load_node(root.child(2)); - assert_eq!( - child_2.entries(btree.memory()), - vec![ - (b(&[2, 4]), vec![]), - (b(&[2, 5]), vec![]), - (b(&[2, 6]), vec![]), - (b(&[2, 7]), vec![]), - (b(&[2, 8]), vec![]), - (b(&[2, 9]), vec![]), - ] - ); + let child_2 = btree.load_node(root.child(2)); + assert_eq!( + child_2.entries(btree.memory()), + vec![ + (b(&[2, 4]), vec![]), + (b(&[2, 5]), vec![]), + (b(&[2, 6]), vec![]), + (b(&[2, 7]), vec![]), + (b(&[2, 8]), vec![]), + (b(&[2, 9]), vec![]), + ] + ); - // Tests a prefix that doesn't exist, but is in the middle of the root node. - assert_eq!( - btree.range(b(&[1, 5])..b(&[1, 6])).collect::>(), - vec![] - ); + // Tests a prefix that doesn't exist, but is in the middle of the root node. + assert_eq!( + btree.range(b(&[1, 5])..b(&[1, 6])).collect::>(), + vec![] + ); - // Tests a prefix that crosses several nodes. - assert_eq!( - btree.range(b(&[1])..b(&[2])).collect::>(), - vec![ - (b(&[1, 2]), b(&[])), - (b(&[1, 4]), b(&[])), - (b(&[1, 6]), b(&[])), - (b(&[1, 8]), b(&[])), - (b(&[1, 10]), b(&[])), - ] - ); + // Tests a prefix that crosses several nodes. + assert_eq!( + btree.range(b(&[1])..b(&[2])).collect::>(), + vec![ + (b(&[1, 2]), b(&[])), + (b(&[1, 4]), b(&[])), + (b(&[1, 6]), b(&[])), + (b(&[1, 8]), b(&[])), + (b(&[1, 10]), b(&[])), + ] + ); - // Tests a prefix that starts from a leaf node, then iterates through the root and right - // sibling. - assert_eq!( - btree.range(b(&[2])..).collect::>(), - vec![ - (b(&[2, 1]), b(&[])), - (b(&[2, 2]), b(&[])), - (b(&[2, 3]), b(&[])), - (b(&[2, 4]), b(&[])), - (b(&[2, 5]), b(&[])), - (b(&[2, 6]), b(&[])), - (b(&[2, 7]), b(&[])), - (b(&[2, 8]), b(&[])), - (b(&[2, 9]), b(&[])), - ] - ); + // Tests a prefix that starts from a leaf node, then iterates through the root and right + // sibling. + assert_eq!( + btree.range(b(&[2])..).collect::>(), + vec![ + (b(&[2, 1]), b(&[])), + (b(&[2, 2]), b(&[])), + (b(&[2, 3]), b(&[])), + (b(&[2, 4]), b(&[])), + (b(&[2, 5]), b(&[])), + (b(&[2, 6]), b(&[])), + (b(&[2, 7]), b(&[])), + (b(&[2, 8]), b(&[])), + (b(&[2, 9]), b(&[])), + ] + ); + }); } #[test] fn range_large() { - let mem = make_memory(); - let mut btree = BTreeMap::, Blob<10>, _>::new(mem); + btree_test(|mut btree| { + // Insert 1000 elements with prefix 0 and another 1000 elements with prefix 1. + for prefix in 0..=1 { + for i in 0..1000u32 { + assert_eq!( + btree.insert( + // The key is the prefix followed by the integer's encoding. + // The encoding is big-endian so that the byte representation of the + // integers are sorted. + b([vec![prefix], i.to_be_bytes().to_vec()] + .into_iter() + .flatten() + .collect::>() + .as_slice()), + b(&[]) + ), + None + ); + } + } - // Insert 1000 elements with prefix 0 and another 1000 elements with prefix 1. - for prefix in 0..=1 { - for i in 0..1000u32 { - assert_eq!( - btree.insert( - // The key is the prefix followed by the integer's encoding. - // The encoding is big-endian so that the byte representation of the - // integers are sorted. - b([vec![prefix], i.to_be_bytes().to_vec()] + // Getting the range with a prefix should return all 1000 elements with that prefix. + for prefix in 0..=1 { + let mut i: u32 = 0; + for (key, _) in btree.range(b(&[prefix])..b(&[prefix + 1])) { + assert_eq!( + key, + b(&[vec![prefix], i.to_be_bytes().to_vec()] .into_iter() .flatten() - .collect::>() - .as_slice()), - b(&[]) - ), - None - ); - } - } - - // Getting the range with a prefix should return all 1000 elements with that prefix. - for prefix in 0..=1 { - let mut i: u32 = 0; - for (key, _) in btree.range(b(&[prefix])..b(&[prefix + 1])) { - assert_eq!( - key, - b(&[vec![prefix], i.to_be_bytes().to_vec()] - .into_iter() - .flatten() - .collect::>()) - ); - i += 1; + .collect::>()) + ); + i += 1; + } + assert_eq!(i, 1000); } - assert_eq!(i, 1000); - } + }); } #[test] fn range_various_prefixes_with_offset() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - btree.insert(b(&[0, 1]), b(&[])); - btree.insert(b(&[0, 2]), b(&[])); - btree.insert(b(&[0, 3]), b(&[])); - btree.insert(b(&[0, 4]), b(&[])); - btree.insert(b(&[1, 1]), b(&[])); - btree.insert(b(&[1, 2]), b(&[])); - btree.insert(b(&[1, 3]), b(&[])); - btree.insert(b(&[1, 4]), b(&[])); - btree.insert(b(&[2, 1]), b(&[])); - btree.insert(b(&[2, 2]), b(&[])); - btree.insert(b(&[2, 3]), b(&[])); - btree.insert(b(&[2, 4]), b(&[])); + btree_test(|mut btree| { + btree.insert(b(&[0, 1]), b(&[])); + btree.insert(b(&[0, 2]), b(&[])); + btree.insert(b(&[0, 3]), b(&[])); + btree.insert(b(&[0, 4]), b(&[])); + btree.insert(b(&[1, 1]), b(&[])); + btree.insert(b(&[1, 2]), b(&[])); + btree.insert(b(&[1, 3]), b(&[])); + btree.insert(b(&[1, 4]), b(&[])); + btree.insert(b(&[2, 1]), b(&[])); + btree.insert(b(&[2, 2]), b(&[])); + btree.insert(b(&[2, 3]), b(&[])); + btree.insert(b(&[2, 4]), b(&[])); + + // The result should look like this: + // [(1, 2)] + // / \ + // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 1)] [(1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4)] - // The result should look like this: - // [(1, 2)] - // / \ - // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 1)] [(1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4)] - - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!(root.entries(btree.memory()), vec![(b(&[1, 2]), vec![])]); - assert_eq!(root.children_len(), 2); + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!(root.entries(btree.memory()), vec![(b(&[1, 2]), vec![])]); + assert_eq!(root.children_len(), 2); - assert_eq!( - btree.range(b(&[0])..b(&[1])).collect::>(), - vec![ - (b(&[0, 1]), b(&[])), - (b(&[0, 2]), b(&[])), - (b(&[0, 3]), b(&[])), - (b(&[0, 4]), b(&[])), - ] - ); + assert_eq!( + btree.range(b(&[0])..b(&[1])).collect::>(), + vec![ + (b(&[0, 1]), b(&[])), + (b(&[0, 2]), b(&[])), + (b(&[0, 3]), b(&[])), + (b(&[0, 4]), b(&[])), + ] + ); - // Tests a offset that has a value somewhere in the range of values of an internal node. - assert_eq!( - btree.range(b(&[1, 3])..b(&[2])).collect::>(), - vec![(b(&[1, 3]), b(&[])), (b(&[1, 4]), b(&[])),] - ); + // Tests a offset that has a value somewhere in the range of values of an internal node. + assert_eq!( + btree.range(b(&[1, 3])..b(&[2])).collect::>(), + vec![(b(&[1, 3]), b(&[])), (b(&[1, 4]), b(&[])),] + ); - // Tests a offset that's larger than the value in the internal node. - assert_eq!(btree.range(b(&[2, 5])..).collect::>(), vec![]); + // Tests a offset that's larger than the value in the internal node. + assert_eq!(btree.range(b(&[2, 5])..).collect::>(), vec![]); + }); } #[test] fn range_various_prefixes_with_offset_2() { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - btree.insert(b(&[0, 1]), b(&[])); - btree.insert(b(&[0, 2]), b(&[])); - btree.insert(b(&[0, 3]), b(&[])); - btree.insert(b(&[0, 4]), b(&[])); - btree.insert(b(&[1, 2]), b(&[])); - btree.insert(b(&[1, 4]), b(&[])); - btree.insert(b(&[1, 6]), b(&[])); - btree.insert(b(&[1, 8]), b(&[])); - btree.insert(b(&[1, 10]), b(&[])); - btree.insert(b(&[2, 1]), b(&[])); - btree.insert(b(&[2, 2]), b(&[])); - btree.insert(b(&[2, 3]), b(&[])); - btree.insert(b(&[2, 4]), b(&[])); - btree.insert(b(&[2, 5]), b(&[])); - btree.insert(b(&[2, 6]), b(&[])); - btree.insert(b(&[2, 7]), b(&[])); - btree.insert(b(&[2, 8]), b(&[])); - btree.insert(b(&[2, 9]), b(&[])); - - // The result should look like this: - // [(1, 4), (2, 3)] - // / | \ - // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2)] | [(2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9)] - // | - // [(1, 6), (1, 8), (1, 10), (2, 1), (2, 2)] - let root = btree.load_node(btree.root_addr); - assert_eq!(root.node_type(), NodeType::Internal); - assert_eq!( - root.entries(btree.memory()), - vec![(b(&[1, 4]), vec![]), (b(&[2, 3]), vec![])] - ); - assert_eq!(root.children_len(), 3); + btree_test(|mut btree| { + btree.insert(b(&[0, 1]), b(&[])); + btree.insert(b(&[0, 2]), b(&[])); + btree.insert(b(&[0, 3]), b(&[])); + btree.insert(b(&[0, 4]), b(&[])); + btree.insert(b(&[1, 2]), b(&[])); + btree.insert(b(&[1, 4]), b(&[])); + btree.insert(b(&[1, 6]), b(&[])); + btree.insert(b(&[1, 8]), b(&[])); + btree.insert(b(&[1, 10]), b(&[])); + btree.insert(b(&[2, 1]), b(&[])); + btree.insert(b(&[2, 2]), b(&[])); + btree.insert(b(&[2, 3]), b(&[])); + btree.insert(b(&[2, 4]), b(&[])); + btree.insert(b(&[2, 5]), b(&[])); + btree.insert(b(&[2, 6]), b(&[])); + btree.insert(b(&[2, 7]), b(&[])); + btree.insert(b(&[2, 8]), b(&[])); + btree.insert(b(&[2, 9]), b(&[])); + + // The result should look like this: + // [(1, 4), (2, 3)] + // / | \ + // [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2)] | [(2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9)] + // | + // [(1, 6), (1, 8), (1, 10), (2, 1), (2, 2)] + let root = btree.load_node(btree.root_addr); + assert_eq!(root.node_type(), NodeType::Internal); + assert_eq!( + root.entries(btree.memory()), + vec![(b(&[1, 4]), vec![]), (b(&[2, 3]), vec![])] + ); + assert_eq!(root.children_len(), 3); - let child_0 = btree.load_node(root.child(0)); - assert_eq!(child_0.node_type(), NodeType::Leaf); - assert_eq!( - child_0.entries(btree.memory()), - vec![ - (b(&[0, 1]), vec![]), - (b(&[0, 2]), vec![]), - (b(&[0, 3]), vec![]), - (b(&[0, 4]), vec![]), - (b(&[1, 2]), vec![]), - ] - ); + let child_0 = btree.load_node(root.child(0)); + assert_eq!(child_0.node_type(), NodeType::Leaf); + assert_eq!( + child_0.entries(btree.memory()), + vec![ + (b(&[0, 1]), vec![]), + (b(&[0, 2]), vec![]), + (b(&[0, 3]), vec![]), + (b(&[0, 4]), vec![]), + (b(&[1, 2]), vec![]), + ] + ); - let child_1 = btree.load_node(root.child(1)); - assert_eq!(child_1.node_type(), NodeType::Leaf); - assert_eq!( - child_1.entries(btree.memory()), - vec![ - (b(&[1, 6]), vec![]), - (b(&[1, 8]), vec![]), - (b(&[1, 10]), vec![]), - (b(&[2, 1]), vec![]), - (b(&[2, 2]), vec![]), - ] - ); + let child_1 = btree.load_node(root.child(1)); + assert_eq!(child_1.node_type(), NodeType::Leaf); + assert_eq!( + child_1.entries(btree.memory()), + vec![ + (b(&[1, 6]), vec![]), + (b(&[1, 8]), vec![]), + (b(&[1, 10]), vec![]), + (b(&[2, 1]), vec![]), + (b(&[2, 2]), vec![]), + ] + ); - let child_2 = btree.load_node(root.child(2)); - assert_eq!( - child_2.entries(btree.memory()), - vec![ - (b(&[2, 4]), vec![]), - (b(&[2, 5]), vec![]), - (b(&[2, 6]), vec![]), - (b(&[2, 7]), vec![]), - (b(&[2, 8]), vec![]), - (b(&[2, 9]), vec![]), - ] - ); + let child_2 = btree.load_node(root.child(2)); + assert_eq!( + child_2.entries(btree.memory()), + vec![ + (b(&[2, 4]), vec![]), + (b(&[2, 5]), vec![]), + (b(&[2, 6]), vec![]), + (b(&[2, 7]), vec![]), + (b(&[2, 8]), vec![]), + (b(&[2, 9]), vec![]), + ] + ); - // Tests a offset that crosses several nodes. - assert_eq!( - btree.range(b(&[1, 4])..b(&[2])).collect::>(), - vec![ - (b(&[1, 4]), b(&[])), - (b(&[1, 6]), b(&[])), - (b(&[1, 8]), b(&[])), - (b(&[1, 10]), b(&[])), - ] - ); + // Tests a offset that crosses several nodes. + assert_eq!( + btree.range(b(&[1, 4])..b(&[2])).collect::>(), + vec![ + (b(&[1, 4]), b(&[])), + (b(&[1, 6]), b(&[])), + (b(&[1, 8]), b(&[])), + (b(&[1, 10]), b(&[])), + ] + ); - // Tests a offset that starts from a leaf node, then iterates through the root and right - // sibling. - assert_eq!( - btree.range(b(&[2, 2])..b(&[3])).collect::>(), - vec![ - (b(&[2, 2]), b(&[])), - (b(&[2, 3]), b(&[])), - (b(&[2, 4]), b(&[])), - (b(&[2, 5]), b(&[])), - (b(&[2, 6]), b(&[])), - (b(&[2, 7]), b(&[])), - (b(&[2, 8]), b(&[])), - (b(&[2, 9]), b(&[])), - ] - ); + // Tests a offset that starts from a leaf node, then iterates through the root and right + // sibling. + assert_eq!( + btree.range(b(&[2, 2])..b(&[3])).collect::>(), + vec![ + (b(&[2, 2]), b(&[])), + (b(&[2, 3]), b(&[])), + (b(&[2, 4]), b(&[])), + (b(&[2, 5]), b(&[])), + (b(&[2, 6]), b(&[])), + (b(&[2, 7]), b(&[])), + (b(&[2, 8]), b(&[])), + (b(&[2, 9]), b(&[])), + ] + ); + }); } #[test] @@ -2441,89 +2441,87 @@ mod test { #[test] fn bruteforce_range_search() { - use super::BTreeMap as StableBTreeMap; - use std::collections::BTreeMap; - - const NKEYS: u64 = 60; - - let mut std_map = BTreeMap::new(); - let mut stable_map = StableBTreeMap::new(make_memory()); - - for k in 0..NKEYS { - std_map.insert(k, k); - stable_map.insert(k, k); - } - - assert_eq!( - std_map.range(..).map(|(k, v)| (*k, *v)).collect::>(), - stable_map.range(..).collect::>() - ); + btree_test(|mut stable_map| { + use std::collections::BTreeMap; + const NKEYS: u64 = 60; + let mut std_map = BTreeMap::new(); + + for k in 0..NKEYS { + std_map.insert(k, k); + stable_map.insert(k, k); + } - for l in 0..=NKEYS { assert_eq!( - std_map - .range(l..) - .map(|(k, v)| (*k, *v)) - .collect::>(), - stable_map.range(l..).collect::>() + std_map.range(..).map(|(k, v)| (*k, *v)).collect::>(), + stable_map.range(..).collect::>() ); - assert_eq!( - std_map - .range(..l) - .map(|(k, v)| (*k, *v)) - .collect::>(), - stable_map.range(..l).collect::>() - ); + for l in 0..=NKEYS { + assert_eq!( + std_map + .range(l..) + .map(|(k, v)| (*k, *v)) + .collect::>(), + stable_map.range(l..).collect::>() + ); - assert_eq!( - std_map - .range(..=l) - .map(|(k, v)| (*k, *v)) - .collect::>(), - stable_map.range(..=l).collect::>() - ); + assert_eq!( + std_map + .range(..l) + .map(|(k, v)| (*k, *v)) + .collect::>(), + stable_map.range(..l).collect::>() + ); - for r in l + 1..=NKEYS { - for lbound in [Bound::Included(l), Bound::Excluded(l)] { - for rbound in [Bound::Included(r), Bound::Excluded(r)] { - let range = (lbound, rbound); - assert_eq!( - std_map - .range(range) - .map(|(k, v)| (*k, *v)) - .collect::>(), - stable_map.range(range).collect::>(), - "range: {range:?}" - ); + assert_eq!( + std_map + .range(..=l) + .map(|(k, v)| (*k, *v)) + .collect::>(), + stable_map.range(..=l).collect::>() + ); + + for r in l + 1..=NKEYS { + for lbound in [Bound::Included(l), Bound::Excluded(l)] { + for rbound in [Bound::Included(r), Bound::Excluded(r)] { + let range = (lbound, rbound); + assert_eq!( + std_map + .range(range) + .map(|(k, v)| (*k, *v)) + .collect::>(), + stable_map.range(range).collect::>(), + "range: {range:?}" + ); + } } } } - } + }); } #[test] fn test_iter_upper_bound() { - let mut stable_map = super::BTreeMap::new(make_memory()); - for k in 0..100u64 { - stable_map.insert(k, ()); - for i in 0..=k { + btree_test(|mut btree| { + for k in 0..100u64 { + btree.insert(k, ()); + for i in 0..=k { + assert_eq!( + Some((i, ())), + btree.iter_upper_bound(&(i + 1)).next(), + "failed to get an upper bound for key {}", + i + 1 + ); + } assert_eq!( - Some((i, ())), - stable_map.iter_upper_bound(&(i + 1)).next(), - "failed to get an upper bound for key {}", - i + 1 + None, + btree.iter_upper_bound(&0).next(), + "key 0 must not have an upper bound" ); } - assert_eq!( - None, - stable_map.iter_upper_bound(&0).next(), - "key 0 must not have an upper bound" - ); - } + }); } - /* #[test] #[should_panic(expected = "Key is too large. Expected <= 0 bytes, found 4 bytes")] fn panics_if_key_is_too_large() { @@ -2574,7 +2572,7 @@ mod test { let mut btree: BTreeMap<(), V, _> = BTreeMap::init(make_memory()); btree.insert((), V); - }*/ + } // To generate the memory dump file for the current version: // cargo test create_btreemap_dump_file -- --include-ignored diff --git a/src/btreemap/node/v1.rs b/src/btreemap/node/v1.rs index c46317a9..e0be1d1b 100644 --- a/src/btreemap/node/v1.rs +++ b/src/btreemap/node/v1.rs @@ -40,11 +40,7 @@ use super::*; impl Node { /// Creates a new v1 node at the given address. - pub fn new_v1( - address: Address, - node_type: NodeType, - page_size: DerivedPageSize, - ) -> Node { + pub fn new_v1(address: Address, node_type: NodeType, page_size: DerivedPageSize) -> Node { Node { address, node_type, From 12e300b4a1542698e376f28b8ab09aaae6bdbf6a Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Tue, 22 Aug 2023 13:09:05 +0200 Subject: [PATCH 3/7] docs --- src/btreemap.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index c881dcbd..c208b393 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -147,7 +147,7 @@ where btree } - /// Create a V2 of the BTree. + /// Create a v2 instance of the BTree. /// This is currently exposed only for beta-testing purposes and will be removed in favor of /// using BTreeMap::new directly once V2 is tested well enough. pub fn new_v2(memory: M) -> Self { @@ -227,7 +227,8 @@ where } } LAYOUT_VERSION_2 => { - // FIXME: deal with derived page size. + // TODO: Handle derived page sizes. + // Deserialize the fields BTreeHeader { version: Version::V2(PageSize::Value(u32::from_le_bytes( From 5af65b2a5780724089361b6e91ad05f8cdb4b716 Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Tue, 22 Aug 2023 13:18:10 +0200 Subject: [PATCH 4/7] . --- src/btreemap.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/btreemap.rs b/src/btreemap.rs index c208b393..716e4e48 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -113,6 +113,28 @@ where } } + /// Initializes a v2 `BTreeMap`. + /// + /// This is currently exposed only for beta-testing purposes and will be removed in favor of + /// using BTreeMap::init directly once V2 is tested well enough. + pub fn init_v2(memory: M) -> Self { + if memory.size() == 0 { + // Memory is empty. Create a new map. + return BTreeMap::new(memory); + } + + // Check if the magic in the memory corresponds to a BTreeMap. + let mut dst = vec![0; 3]; + memory.read(0, &mut dst); + if dst != MAGIC { + // No BTreeMap found. Create a new instance. + BTreeMap::new_v2(memory) + } else { + // The memory already contains a BTreeMap. Load it. + BTreeMap::load(memory) + } + } + /// Creates a new instance a `BTreeMap`. /// /// The given `memory` is assumed to be exclusively reserved for this data From 8620b4f48f2e1b61a60d497ebc24acc04da95f6a Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Tue, 22 Aug 2023 13:28:39 +0200 Subject: [PATCH 5/7] . --- src/btreemap.rs | 2 +- src/btreemap/proptests.rs | 71 ++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 716e4e48..eeee7aa3 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1221,7 +1221,7 @@ mod test { } // A test runner that runs the test using both V1 and V2 btrees. - fn btree_test(f: F) + pub fn btree_test(f: F) where K: Storable + Ord + Clone, V: Storable, diff --git a/src/btreemap/proptests.rs b/src/btreemap/proptests.rs index 2235a6f2..c8447620 100644 --- a/src/btreemap/proptests.rs +++ b/src/btreemap/proptests.rs @@ -1,13 +1,10 @@ -use crate::{btreemap::test::b, storable::Blob, BTreeMap}; +use crate::{ + btreemap::test::{b, btree_test}, + storable::Blob, +}; use proptest::collection::btree_set as pset; use proptest::collection::vec as pvec; use proptest::prelude::*; -use std::cell::RefCell; -use std::rc::Rc; - -fn make_memory() -> Rc>> { - Rc::new(RefCell::new(Vec::new())) -} fn arb_blob() -> impl Strategy> { pvec(0..u8::MAX, 0..10).prop_map(|v| Blob::<10>::try_from(v.as_slice()).unwrap()) @@ -17,47 +14,51 @@ proptest! { #![proptest_config(ProptestConfig::with_cases(10))] #[test] fn insert(keys in pset(arb_blob(), 1000..10_000)) { - let mem = make_memory(); - let mut btree = BTreeMap::new(mem); - - for key in keys.iter() { - assert_eq!(btree.insert(*key, *key), None); - } - - for key in keys.into_iter() { - // Assert we retrieved the old value correctly. - assert_eq!(btree.insert(key, b(&[])), Some(key)); - // Assert we retrieved the new value correctly. - assert_eq!(btree.get(&key), Some(b(&[]))); - } + btree_test(|mut btree| { + let keys = keys.clone(); + for key in keys.iter() { + assert_eq!(btree.insert(*key, *key), None); + } + + for key in keys.into_iter() { + // Assert we retrieved the old value correctly. + assert_eq!(btree.insert(key, b(&[])), Some(key)); + // Assert we retrieved the new value correctly. + assert_eq!(btree.get(&key), Some(b(&[]))); + } + }); } #[test] fn map_min_max(keys in pvec(any::(), 10..100)) { - let mut map = BTreeMap::::new(make_memory()); + btree_test(|mut map| { + prop_assert_eq!(map.first_key_value(), None); + prop_assert_eq!(map.last_key_value(), None); - prop_assert_eq!(map.first_key_value(), None); - prop_assert_eq!(map.last_key_value(), None); + for (n, key) in keys.iter().enumerate() { + map.insert(*key, *key); - for (n, key) in keys.iter().enumerate() { - map.insert(*key, *key); + let min = keys[0..=n].iter().min().unwrap(); + let max = keys[0..=n].iter().max().unwrap(); - let min = keys[0..=n].iter().min().unwrap(); - let max = keys[0..=n].iter().max().unwrap(); + prop_assert_eq!(map.first_key_value(), Some((*min, *min))); + prop_assert_eq!(map.last_key_value(), Some((*max, *max))) + } - prop_assert_eq!(map.first_key_value(), Some((*min, *min))); - prop_assert_eq!(map.last_key_value(), Some((*max, *max))) - } + Ok(()) + }); } #[test] fn map_upper_bound_iter(keys in pvec(0u64..u64::MAX -1 , 10..100)) { - let mut map = BTreeMap::::new(make_memory()); + btree_test(|mut map| { + for k in keys.iter() { + map.insert(*k, ()); - for k in keys.iter() { - map.insert(*k, ()); + prop_assert_eq!(Some((*k, ())), map.iter_upper_bound(&(k + 1)).next()); + } - prop_assert_eq!(Some((*k, ())), map.iter_upper_bound(&(k + 1)).next()); - } + Ok(()) + }); } } From 9fd653246291fafdae47787617968541e43a3823 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 28 Aug 2023 09:45:01 +0200 Subject: [PATCH 6/7] fix: init_v2 BTreeMap::new (#117) Hi @ielashi, I'm not sure but, is it possible that the init of a new `BTreeMap::new` in `init_v2` is incorrect in your PR and should be replaced with `BTreeMap::new_v2`? At least, while testing your PR, I was facing following error which seems to be resolved by tweaking those. > 2023-08-26 14:42:36.677876 UTC: [Canister by6od-j4aaa-aaaaa-qaadq-cai] Panicked at 'Cannot get max size of unbounded type.', /Users/daviddalbusco/.cargo/git/checkouts/stable-structures-10185dfe005f3b12/8620b4f/src/storable.rs:450:9 --- src/btreemap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index eeee7aa3..b4165bb9 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -120,7 +120,7 @@ where pub fn init_v2(memory: M) -> Self { if memory.size() == 0 { // Memory is empty. Create a new map. - return BTreeMap::new(memory); + return BTreeMap::new_v2(memory); } // Check if the magic in the memory corresponds to a BTreeMap. From f781a92267c0386c064b27d7e1b229083f22206a Mon Sep 17 00:00:00 2001 From: Islam El-Ashi Date: Tue, 5 Sep 2023 10:29:45 +0200 Subject: [PATCH 7/7] . --- src/btreemap.rs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index eeee7aa3..0d21ce20 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -1307,10 +1307,10 @@ mod test { let median_index = right_child.entries_len() / 2; assert_eq!(right_child.key(median_index), &b(&[12])); - // Overwrite the median key. + // Overwrite the value of the median key. assert_eq!(btree.insert(b(&[12]), b(&[1, 2, 3])), Some(b(&[]))); - // The key is overwritten successfully. + // The value is overwritten successfully. assert_eq!(btree.get(&b(&[12])), Some(b(&[1, 2, 3]))); // The child has not been split and is still full. @@ -1384,10 +1384,10 @@ mod test { fn insert_same_key_multiple() { btree_test(|mut btree| { assert_eq!(btree.insert(b(&[1]), b(&[2])), None); - for i in 2..10 { assert_eq!(btree.insert(b(&[1]), b(&[i + 1])), Some(b(&[i]))); } + assert_eq!(btree.get(&b(&[1])), Some(b(&[10]))); }); } @@ -2053,7 +2053,7 @@ mod test { // / \ // [1, 2, 3, 4, 5] [7, 8, 9, 10, 11, 12] - // Test a prefix that's larger than the value in the internal node. + // Test a prefix that's larger than the key in the internal node. assert_eq!( btree.range(b(&[7])..b(&[8])).collect::>(), vec![(b(&[7]), b(&[]))] @@ -2087,7 +2087,7 @@ mod test { assert_eq!(root.entries(btree.memory()), vec![(b(&[1, 2]), vec![])]); assert_eq!(root.children_len(), 2); - // Tests a prefix that's smaller than the value in the internal node. + // Tests a prefix that's smaller than the key in the internal node. assert_eq!( btree.range(b(&[0])..b(&[1])).collect::>(), vec![ @@ -2109,7 +2109,7 @@ mod test { ] ); - // Tests a prefix that's larger than the value in the internal node. + // Tests a prefix that's larger than the key in the internal node. assert_eq!( btree.range(b(&[2])..b(&[3])).collect::>(), vec![ @@ -2203,6 +2203,22 @@ mod test { vec![] ); + // Tests a prefix beginning in the middle of the tree and crossing several nodes. + assert_eq!( + btree.range(b(&[1, 5])..=b(&[2, 6])).collect::>(), + vec![ + (b(&[1, 6]), b(&[])), + (b(&[1, 8]), b(&[])), + (b(&[1, 10]), b(&[])), + (b(&[2, 1]), b(&[])), + (b(&[2, 2]), b(&[])), + (b(&[2, 3]), b(&[])), + (b(&[2, 4]), b(&[])), + (b(&[2, 5]), b(&[])), + (b(&[2, 6]), b(&[])), + ] + ); + // Tests a prefix that crosses several nodes. assert_eq!( btree.range(b(&[1])..b(&[2])).collect::>(), @@ -2311,13 +2327,13 @@ mod test { ] ); - // Tests a offset that has a value somewhere in the range of values of an internal node. + // Tests an offset that has a keys somewhere in the range of keys of an internal node. assert_eq!( btree.range(b(&[1, 3])..b(&[2])).collect::>(), vec![(b(&[1, 3]), b(&[])), (b(&[1, 4]), b(&[])),] ); - // Tests a offset that's larger than the value in the internal node. + // Tests an offset that's larger than the key in the internal node. assert_eq!(btree.range(b(&[2, 5])..).collect::>(), vec![]); }); }