From 9d7c8176fb273dc86b25da9846ed5c4f98710da0 Mon Sep 17 00:00:00 2001 From: Rob Johnson Date: Wed, 13 May 2026 19:02:20 -0700 Subject: [PATCH 1/2] implement range bounds in iterators Signed-off-by: Rob Johnson --- .gitignore | 148 ++++++ ...interdb_custom_ipv4_addr_sortcmp_example.c | 2 +- examples/splinterdb_intro_example.c | 2 +- examples/splinterdb_iterators_example.c | 2 +- include/splinterdb/splinterdb.h | 32 +- src/btree.c | 126 +++-- src/btree.h | 6 +- src/core.c | 162 +++++-- src/core.h | 9 +- src/iterator.h | 31 +- src/splinterdb.c | 68 ++- src/trunk.c | 4 +- tests/functional/btree_test.c | 28 +- tests/functional/test_functionality.c | 8 +- tests/unit/btree_stress_test.c | 14 +- tests/unit/splinterdb_quick_test.c | 438 +++++++++++++++++- tests/unit/splinterdb_stress_test.c | 4 +- 17 files changed, 946 insertions(+), 138 deletions(-) diff --git a/.gitignore b/.gitignore index 395e1b91..10209aa4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,151 @@ build compile_commands.json *~ .cache/ +.cursor-ai/.gitignore +.cursor-ai/config.json +.cursor-ai/sync-vault.ps1 +.cursor-ai/sync-vault.sh +.vscode/settings.json +raw-data/matrixkv_ycsb.build +raw-data/matrixkv.build +raw-data/matrixkv.conf +raw-data/non-txn_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/non-txn_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/non-txn_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/non-txn_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/non-txn_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/non-txn_ycsb.build +raw-data/non-txn.build +raw-data/persistron_threads_2_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_2_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_2_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_2_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_2_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_4_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_4_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_4_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_4_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_4_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_6_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_6_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_6_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_6_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_6_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_8_dram_cache_size_2048_pmem_cache_size_24576_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_8_dram_cache_size_2048_pmem_cache_size_24576_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_8_dram_cache_size_2048_pmem_cache_size_24576_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_8_dram_cache_size_2048_pmem_cache_size_24576_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_8_dram_cache_size_2048_pmem_cache_size_24576_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_8_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_8_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_8_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_8_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_8_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_8_dram_cache_size_6144_pmem_cache_size_8192_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_8_dram_cache_size_6144_pmem_cache_size_8192_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_8_dram_cache_size_6144_pmem_cache_size_8192_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_8_dram_cache_size_6144_pmem_cache_size_8192_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_8_dram_cache_size_6144_pmem_cache_size_8192_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_10_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_10_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_10_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_10_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_10_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_12_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_12_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_12_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_12_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_12_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_14_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_14_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_14_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_14_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_14_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_threads_16_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/persistron_threads_16_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/persistron_threads_16_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/persistron_threads_16_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/persistron_threads_16_dram_cache_size_4096_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/persistron_ycsb.build +raw-data/persistron.build +raw-data/pmem-cow_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/pmem-cow_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/pmem-cow_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/pmem-cow_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/pmem-cow_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/pmem-cow_ycsb.build +raw-data/pmem-cow.build +raw-data/pmem-only_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_1.out +raw-data/pmem-only_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_2.out +raw-data/pmem-only_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_3.out +raw-data/pmem-only_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_4.out +raw-data/pmem-only_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_2_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_2_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_2_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_2_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_2_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_4_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_4_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_4_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_4_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_4_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_6_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_6_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_6_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_6_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_6_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_8_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_8_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_8_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_8_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_8_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_10_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_10_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_10_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_10_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_10_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_12_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_12_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_12_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_12_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_12_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_14_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_14_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_14_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_14_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_14_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_threads_16_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_1.out +raw-data/splinterdb_threads_16_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_2.out +raw-data/splinterdb_threads_16_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_3.out +raw-data/splinterdb_threads_16_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_4.out +raw-data/splinterdb_threads_16_dram_cache_size_8192_pmem_cache_size_0_log_flush_interval_0_iter_5.out +raw-data/splinterdb_ycsb.build +raw-data/splinterdb-withlog_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_10000_iter_1.out +raw-data/splinterdb-withlog_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_10000_iter_2.out +raw-data/splinterdb-withlog_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_10000_iter_3.out +raw-data/splinterdb-withlog_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_10000_iter_4.out +raw-data/splinterdb-withlog_threads_8_dram_cache_size_0_pmem_cache_size_16384_log_flush_interval_10000_iter_5.out +raw-data/splinterdb-withlog_ycsb.build +raw-data/splinterdb-withlog.build +raw-data/splinterdb.build +.codex +.gitignore +deadlock-notes.txt +deadlock-notes2.txt +deadlock-notes3.txt +fio.db +non-txn.build +notes.txt +perf.data +perf.data.old +persistron_ycsb.build +persistron.build +pmem-cow_ycsb.build +pmem-cow.build +splinterdb_ycsb.build +splinterdb.build +TAGS +trunk.log +src/trunk.c.orig +src/trunk.h.orig diff --git a/examples/splinterdb_custom_ipv4_addr_sortcmp_example.c b/examples/splinterdb_custom_ipv4_addr_sortcmp_example.c index 933fb35c..3898321b 100644 --- a/examples/splinterdb_custom_ipv4_addr_sortcmp_example.c +++ b/examples/splinterdb_custom_ipv4_addr_sortcmp_example.c @@ -346,7 +346,7 @@ do_iterate_from(splinterdb *spl_handle, const char *from_key) slice start_key = (from_key ? slice_create(strlen(from_key), from_key) : NULL_SLICE); int rc = splinterdb_iterator_init( - spl_handle, &it, start_key, greater_than_or_equal); + spl_handle, &it, greater_than_or_equal, start_key); int i = 0; diff --git a/examples/splinterdb_intro_example.c b/examples/splinterdb_intro_example.c index 0fe611cc..ca0247af 100644 --- a/examples/splinterdb_intro_example.c +++ b/examples/splinterdb_intro_example.c @@ -113,7 +113,7 @@ main() splinterdb_iterator *it = NULL; rc = splinterdb_iterator_init( - spl_handle, &it, NULL_SLICE, greater_than_or_equal); + spl_handle, &it, greater_than_or_equal, NULL_SLICE); int i = 0; for (; splinterdb_iterator_valid(it); splinterdb_iterator_next(it)) { diff --git a/examples/splinterdb_iterators_example.c b/examples/splinterdb_iterators_example.c index 630d2e24..1d37977b 100644 --- a/examples/splinterdb_iterators_example.c +++ b/examples/splinterdb_iterators_example.c @@ -146,7 +146,7 @@ do_iterate_from(splinterdb *spl_handle, const char *from_key) slice start_key = (from_key ? slice_create(strlen(from_key), from_key) : NULL_SLICE); int rc = splinterdb_iterator_init( - spl_handle, &it, start_key, greater_than_or_equal); + spl_handle, &it, greater_than_or_equal, start_key); int i = 0; diff --git a/include/splinterdb/splinterdb.h b/include/splinterdb/splinterdb.h index 2fd1f18c..6612003c 100644 --- a/include/splinterdb/splinterdb.h +++ b/include/splinterdb/splinterdb.h @@ -324,8 +324,9 @@ Known issue: a live iterator may block inserts and deletes from the same thread. Sample application code: splinterdb_iterator* it; - int rc = splinterdb_iterator_init(kvs, &it, NULL_SLICE, -greater_than_or_equal); if (rc != 0) { ... handle error ... } + int rc = splinterdb_iterator_init(kvs, &it, greater_than_or_equal, + NULL_SLICE); + if (rc != 0) { ... handle error ... } slice key, value; @@ -363,8 +364,31 @@ typedef enum comparison { int splinterdb_iterator_init(splinterdb *kvs, // IN splinterdb_iterator **iter, // OUT - slice start_key, // IN - comparison start_type // IN + comparison start_type, // IN + slice start_key // IN +); + +// Initialize a new iterator with lower and upper bounds. +// +// If min_key is NULL_SLICE, the iterator has no lower bound. +// min_key_comparison must be greater_than or greater_than_or_equal. +// +// If max_key is NULL_SLICE, the iterator has no upper bound. +// max_key_comparison must be less_than or less_than_or_equal. +// +// If start_key is NULL_SLICE, the iterator will start at: +// - greater_than_or_equal / greater_than: the first key in bounds +// - less_than_or_equal / less_than: the last key in bounds +int +splinterdb_iterator_init_with_bounds( + splinterdb *kvs, // IN + splinterdb_iterator **iter, // OUT + comparison min_key_comparison, // IN + slice min_key, // IN + comparison max_key_comparison, // IN + slice max_key, // IN + comparison start_type, // IN + slice start_key // IN ); // Deinitialize an iterator diff --git a/src/btree.c b/src/btree.c index 2031de43..ef14e49e 100644 --- a/src/btree.c +++ b/src/btree.c @@ -2374,9 +2374,9 @@ btree_lookup_async(btree_lookup_async_state *state) * btree_iterator_can_prev -- * btree_iterator_can_next -- * - * This iterator implementation supports an upper bound key ub. Given - * an upper bound, the iterator will return only keys strictly less - * than ub. + * This iterator implementation supports lower and upper bound keys. The lower + * bound may be exclusive or inclusive, and the upper bound may be exclusive or + * inclusive. * * In order to avoid comparing every key with ub, it precomputes, * during initialization, the end leaf and end_idx of ub within that @@ -2473,7 +2473,7 @@ find_key_in_node(btree_iterator *itor, *found = FALSE; return 0; // this iterator is invalid, so return 0 for all lookups } else { - tmp = btree_find_pivot(itor->cfg, hdr, itor->min_key, found); + tmp = btree_find_pivot(itor->cfg, hdr, target, found); } switch (position_rule) { @@ -2515,8 +2515,12 @@ btree_iterator_find_end(btree_iterator *itor) if (key_is_positive_infinity(itor->max_key)) { itor->end_idx = btree_num_entries(end.hdr); } else { - itor->end_idx = find_key_in_node( - itor, end.hdr, itor->max_key, greater_than_or_equal, NULL); + itor->end_idx = + find_key_in_node(itor, + end.hdr, + itor->max_key, + comparison_invert(itor->max_key_comparison), + NULL); } btree_node_unget(itor->cc, itor->cfg, &end); @@ -2624,14 +2628,8 @@ btree_iterator_prev_leaf(btree_iterator *itor) if (btree_key_compare(cfg, itor->min_key, first_key) < 0) { itor->curr_min_idx = -1; } else { - bool32 found; - itor->curr_min_idx = - itor->height - ? btree_find_pivot(cfg, itor->curr.hdr, itor->min_key, &found) - : btree_find_tuple(cfg, itor->curr.hdr, itor->min_key, &found); - if (!found) { - itor->curr_min_idx++; - } + itor->curr_min_idx = find_key_in_node( + itor, itor->curr.hdr, itor->min_key, itor->min_key_comparison, NULL); } if (itor->curr.hdr->prev_addr == 0 && itor->curr_min_idx == -1) { itor->curr_min_idx = 0; @@ -2700,7 +2698,35 @@ btree_iterator_prev(iterator *base_itor) return STATUS_OK; } -// This function voilates our locking rules. See comment at top of file. +static inline void +btree_iterator_bound_idx(btree_iterator *itor, comparison position_rule) +{ + bool32 forward = comparison_is_forward(position_rule); + + if (itor->idx < itor->curr_min_idx) { + itor->idx = forward ? itor->curr_min_idx : itor->curr_min_idx - 1; + } + + if (itor->curr.addr == itor->end_addr && itor->idx >= itor->end_idx) { + itor->idx = forward ? itor->end_idx : itor->end_idx - 1; + } +} + +static inline void +btree_iterator_move_leaf_if_needed(btree_iterator *itor) +{ + if (itor->curr.addr != itor->end_addr + && itor->idx == btree_num_entries(itor->curr.hdr)) + { + btree_iterator_next_leaf(itor); + itor->curr_min_idx = 0; // we came from an irrelevant leaf + } + if (itor->curr_min_idx == -1 && itor->idx == -1) { + btree_iterator_prev_leaf(itor); + } +} + +// This function violates our locking rules. See comment at top of file. static inline void find_btree_node_and_get_idx_bounds(btree_iterator *itor, key target, @@ -2755,9 +2781,9 @@ find_btree_node_and_get_idx_bounds(btree_iterator *itor, // find the index of the minimum key bool32 found; int64 tmp = find_key_in_node( - itor, itor->curr.hdr, itor->min_key, greater_than_or_equal, &found); + itor, itor->curr.hdr, itor->min_key, itor->min_key_comparison, &found); // If min key doesn't exist in current node, but is: - // 1) in range: Min idx = smallest key > min_key + // 1) in range: Min idx = first key satisfying min_key_comparison // 2) out of range: Min idx = -1 itor->curr_min_idx = !found && tmp == 0 ? tmp - 1 : tmp; // if min_key is not within the current node but there is no previous node @@ -2769,17 +2795,10 @@ find_btree_node_and_get_idx_bounds(btree_iterator *itor, // find the index of the actual target itor->idx = find_key_in_node(itor, itor->curr.hdr, target, position_rule, &found); + btree_iterator_bound_idx(itor, position_rule); // check if we already need to move to the prev/next leaf - if (itor->curr.addr != itor->end_addr - && itor->idx == btree_num_entries(itor->curr.hdr)) - { - btree_iterator_next_leaf(itor); - itor->curr_min_idx = 0; // we came from an irrelevant leaf - } - if (itor->curr_min_idx == -1 && itor->idx == -1) { - btree_iterator_prev_leaf(itor); - } + btree_iterator_move_leaf_if_needed(itor); } /* @@ -2788,7 +2807,7 @@ find_btree_node_and_get_idx_bounds(btree_iterator *itor, * key. */ platform_status -btree_iterator_seek(iterator *base_itor, key seek_key, comparison seek_type) +btree_iterator_seek(iterator *base_itor, comparison seek_type, key seek_key) { debug_assert(base_itor != NULL); btree_iterator *itor = (btree_iterator *)base_itor; @@ -2800,13 +2819,14 @@ btree_iterator_seek(iterator *base_itor, key seek_key, comparison seek_type) } // check if seek_key is within our current node - key first_key = itor->height - ? btree_get_pivot(itor->cfg, itor->curr.hdr, 0) - : btree_get_tuple_key(itor->cfg, itor->curr.hdr, 0); - key last_key = + key first_key = itor->height + ? btree_get_pivot(itor->cfg, itor->curr.hdr, 0) + : btree_get_tuple_key(itor->cfg, itor->curr.hdr, 0); + uint64 num_entries = btree_num_entries(itor->curr.hdr); + key last_key = itor->height - ? btree_get_pivot(itor->cfg, itor->curr.hdr, itor->end_idx - 1) - : btree_get_tuple_key(itor->cfg, itor->curr.hdr, itor->end_idx - 1); + ? btree_get_pivot(itor->cfg, itor->curr.hdr, num_entries - 1) + : btree_get_tuple_key(itor->cfg, itor->curr.hdr, num_entries - 1); if (btree_key_compare(itor->cfg, seek_key, first_key) >= 0 && btree_key_compare(itor->cfg, seek_key, last_key) <= 0) @@ -2815,7 +2835,9 @@ btree_iterator_seek(iterator *base_itor, key seek_key, comparison seek_type) bool32 found; itor->idx = find_key_in_node(itor, itor->curr.hdr, seek_key, seek_type, &found); - platform_assert(0 <= itor->idx); + btree_iterator_bound_idx(itor, seek_type); + btree_iterator_move_leaf_if_needed(itor); + platform_assert(-1 <= itor->idx); } else { // seek key is not within our current leaf. So find the correct leaf find_btree_node_and_get_idx_bounds(itor, seek_key, seek_type); @@ -2869,10 +2891,12 @@ btree_iterator_init(cache *cc, btree_iterator *itor, uint64 root_addr, page_type page_type, + comparison min_key_comparison, key min_key, + comparison max_key_comparison, key max_key, - key start_key, comparison start_type, + key start_key, bool32 do_prefetch, uint32 height) { @@ -2882,9 +2906,15 @@ btree_iterator_init(cache *cc, debug_assert(!key_is_null(min_key) && !key_is_null(max_key) && !key_is_null(start_key)); + debug_assert(min_key_comparison == greater_than + || min_key_comparison == greater_than_or_equal); + debug_assert(max_key_comparison == less_than + || max_key_comparison == less_than_or_equal); if (btree_key_compare(cfg, min_key, max_key) > 0) { - max_key = min_key; + max_key = min_key; + min_key_comparison = greater_than_or_equal; + max_key_comparison = less_than; } if (btree_key_compare(cfg, start_key, min_key) < 0) { start_key = min_key; @@ -2894,15 +2924,17 @@ btree_iterator_init(cache *cc, } ZERO_CONTENTS(itor); - itor->cc = cc; - itor->cfg = cfg; - itor->root_addr = root_addr; - itor->do_prefetch = do_prefetch; - itor->height = height; - itor->min_key = min_key; - itor->max_key = max_key; - itor->page_type = page_type; - itor->super.ops = &btree_iterator_ops; + itor->cc = cc; + itor->cfg = cfg; + itor->root_addr = root_addr; + itor->do_prefetch = do_prefetch; + itor->height = height; + itor->min_key_comparison = min_key_comparison; + itor->min_key = min_key; + itor->max_key_comparison = max_key_comparison; + itor->max_key = max_key; + itor->page_type = page_type; + itor->super.ops = &btree_iterator_ops; find_btree_node_and_get_idx_bounds(itor, start_key, start_type); @@ -3286,11 +3318,13 @@ btree_count_in_range_by_iterator(cache *cc, &btree_itor, root_addr, PAGE_TYPE_BRANCH, + greater_than_or_equal, min_key, + less_than, max_key, + greater_than_or_equal, min_key, TRUE, - TRUE, 0); memset(stats, 0, sizeof(*stats)); diff --git a/src/btree.h b/src/btree.h index 35d8ce93..cb03c927 100644 --- a/src/btree.h +++ b/src/btree.h @@ -138,7 +138,9 @@ typedef struct btree_iterator { bool32 do_prefetch; uint32 height; page_type page_type; + comparison min_key_comparison; key min_key; + comparison max_key_comparison; key max_key; uint64 root_addr; @@ -275,10 +277,12 @@ btree_iterator_init(cache *cc, btree_iterator *itor, uint64 root_addr, page_type page_type, + comparison min_key_comparison, key min_key, + comparison max_key_comparison, key max_key, - key start_key, comparison start_type, + key start_key, bool32 do_prefetch, uint32 height); diff --git a/src/core.c b/src/core.c index 4f6edd72..eee2f1f1 100644 --- a/src/core.c +++ b/src/core.c @@ -315,10 +315,12 @@ static void core_memtable_iterator_init(core_handle *spl, btree_iterator *itor, uint64 root_addr, + comparison min_key_comparison, key min_key, + comparison max_key_comparison, key max_key, + comparison start_key_comparison, key start_key, - comparison start_type, bool32 is_live, bool32 inc_ref) { @@ -330,10 +332,12 @@ core_memtable_iterator_init(core_handle *spl, itor, root_addr, PAGE_TYPE_MEMTABLE, + min_key_comparison, min_key, + max_key_comparison, max_key, + start_key_comparison, start_key, - start_type, FALSE, 0); } @@ -437,10 +441,12 @@ core_memtable_compact(core_handle *spl, uint64 generation, const threadid tid) core_memtable_iterator_init(spl, &btree_itor, memtable_root_addr, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - NEGATIVE_INFINITY_KEY, greater_than_or_equal, + NEGATIVE_INFINITY_KEY, FALSE, FALSE); const routing_config *rfcfg = spl->cfg.trunk_node_cfg->filter_cfg; @@ -795,10 +801,12 @@ static void core_branch_iterator_init(core_handle *spl, btree_iterator *itor, uint64 branch_addr, + comparison min_key_comparison, key min_key, + comparison max_key_comparison, key max_key, + comparison start_key_comparison, key start_key, - comparison start_type, bool32 do_prefetch, bool32 should_inc_ref) { @@ -812,10 +820,12 @@ core_branch_iterator_init(core_handle *spl, itor, branch_addr, PAGE_TYPE_BRANCH, + min_key_comparison, min_key, + max_key_comparison, max_key, + start_key_comparison, start_key, - start_type, do_prefetch, 0); } @@ -865,13 +875,28 @@ const static iterator_ops core_range_iterator_ops = { .prev = core_range_iterator_prev, }; +static inline bool32 +core_range_iterator_has_next_leaf(core_range_iterator *range_itor) +{ + key local_max_key = key_buffer_key(&range_itor->local_max_key); + key max_key = key_buffer_key(&range_itor->max_key); + int cmp = core_key_compare(range_itor->spl, local_max_key, max_key); + bool32 max_is_finite = !key_is_positive_infinity(max_key); + + return cmp < 0 + || (cmp == 0 && max_is_finite && !range_itor->local_max_key_truncated + && range_itor->max_key_comparison == less_than_or_equal); +} + platform_status core_range_iterator_init(core_handle *spl, core_range_iterator *range_itor, + comparison min_key_comparison, key min_key, + comparison max_key_comparison, key max_key, + comparison start_key_comparison, key start_key, - comparison start_type, uint64 num_tuples) { platform_status rc; @@ -879,27 +904,52 @@ core_range_iterator_init(core_handle *spl, debug_assert(!key_is_null(min_key)); debug_assert(!key_is_null(max_key)); debug_assert(!key_is_null(start_key)); - - range_itor->spl = spl; - range_itor->super.ops = &core_range_iterator_ops; - range_itor->num_branches = 0; - range_itor->num_tuples = num_tuples; - range_itor->merge_itor = NULL; - range_itor->can_prev = TRUE; - range_itor->can_next = TRUE; + debug_assert(min_key_comparison == greater_than + || min_key_comparison == greater_than_or_equal); + debug_assert(max_key_comparison == less_than + || max_key_comparison == less_than_or_equal); + + range_itor->spl = spl; + range_itor->super.ops = &core_range_iterator_ops; + range_itor->num_branches = 0; + range_itor->num_tuples = num_tuples; + range_itor->merge_itor = NULL; + range_itor->can_prev = TRUE; + range_itor->can_next = TRUE; + range_itor->min_key_comparison = min_key_comparison; + range_itor->max_key_comparison = max_key_comparison; key_buffer_init(&range_itor->min_key, PROCESS_PRIVATE_HEAP_ID); key_buffer_init(&range_itor->max_key, PROCESS_PRIVATE_HEAP_ID); key_buffer_init(&range_itor->local_min_key, PROCESS_PRIVATE_HEAP_ID); key_buffer_init(&range_itor->local_max_key, PROCESS_PRIVATE_HEAP_ID); - if (core_key_compare(spl, min_key, start_key) > 0) { - // in bounds, start at min - start_key = min_key; + bool32 forward_start = comparison_is_forward(start_key_comparison); + int min_start_cmp = core_key_compare(spl, min_key, start_key); + if (min_start_cmp > 0) { + start_key = min_key; + start_key_comparison = forward_start + ? min_key_comparison + : comparison_invert(min_key_comparison); + } else if (min_start_cmp == 0) { + if (forward_start && min_key_comparison == greater_than) { + start_key_comparison = greater_than; + } else if (!forward_start && min_key_comparison == greater_than) { + start_key_comparison = less_than_or_equal; + } } - if (core_key_compare(spl, max_key, start_key) <= 0) { - // out of bounds, start at max - start_key = max_key; + int max_start_cmp = core_key_compare(spl, max_key, start_key); + if (max_start_cmp < 0) { + start_key = max_key; + start_key_comparison = forward_start + ? comparison_invert(max_key_comparison) + : max_key_comparison; + } else if (max_start_cmp == 0) { + if (!forward_start && max_key_comparison == less_than) { + start_key_comparison = less_than; + } else if (forward_start && max_key_comparison == less_than) { + start_key_comparison = greater_than_or_equal; + } } // copy over global min and max @@ -966,7 +1016,7 @@ core_range_iterator_init(core_handle *spl, rc = trunk_collect_branches(&spl->trunk_context, &root_handle, start_key, - start_type, + start_key_comparison, CORE_RANGE_ITOR_MAX_BRANCHES, &range_itor->num_branches, range_itor->branch, @@ -982,12 +1032,17 @@ core_range_iterator_init(core_handle *spl, range_itor->compacted[i] = TRUE; } + range_itor->local_min_key_comparison = greater_than_or_equal; + range_itor->local_max_key_comparison = less_than; + range_itor->local_max_key_truncated = FALSE; + // have a leaf, use to establish local bounds if (core_key_compare( spl, key_buffer_key(&range_itor->local_min_key), min_key) <= 0) { rc = key_buffer_copy_key(&range_itor->local_min_key, min_key); + range_itor->local_min_key_comparison = min_key_comparison; if (!SUCCESS(rc)) { core_range_iterator_deinit(range_itor); return rc; @@ -995,9 +1050,11 @@ core_range_iterator_init(core_handle *spl, } if (core_key_compare( spl, key_buffer_key(&range_itor->local_max_key), max_key) - >= 0) + > 0) { rc = key_buffer_copy_key(&range_itor->local_max_key, max_key); + range_itor->local_max_key_comparison = max_key_comparison; + range_itor->local_max_key_truncated = TRUE; if (!SUCCESS(rc)) { core_range_iterator_deinit(range_itor); return rc; @@ -1016,10 +1073,12 @@ core_range_iterator_init(core_handle *spl, core_branch_iterator_init(spl, btree_itor, branch_addr, + range_itor->local_min_key_comparison, key_buffer_key(&range_itor->local_min_key), + range_itor->local_max_key_comparison, key_buffer_key(&range_itor->local_max_key), + start_key_comparison, start_key, - start_type, do_prefetch, FALSE); } else { @@ -1027,10 +1086,12 @@ core_range_iterator_init(core_handle *spl, core_memtable_iterator_init(spl, btree_itor, branch_addr, + range_itor->local_min_key_comparison, key_buffer_key(&range_itor->local_min_key), + range_itor->local_max_key_comparison, key_buffer_key(&range_itor->local_max_key), + start_key_comparison, start_key, - start_type, is_live, FALSE); } @@ -1042,7 +1103,7 @@ core_range_iterator_init(core_handle *spl, range_itor->num_branches, range_itor->itor, MERGE_FULL, - greater_than <= start_type, + greater_than <= start_key_comparison, &range_itor->merge_itor); if (!SUCCESS(rc)) { core_range_iterator_deinit(range_itor); @@ -1055,12 +1116,13 @@ core_range_iterator_init(core_handle *spl, * if the merge itor is already exhausted, and there are more keys in the * db/range, move to prev/next leaf */ - if (!in_range && start_type >= greater_than) { - key local_max = key_buffer_key(&range_itor->local_max_key); - if (core_key_compare(spl, local_max, max_key) < 0) { + if (!in_range && start_key_comparison >= greater_than) { + if (core_range_iterator_has_next_leaf(range_itor)) { + key local_max = key_buffer_key(&range_itor->local_max_key); key_buffer local_max_buffer; rc = key_buffer_init_from_key( &local_max_buffer, PROCESS_PRIVATE_HEAP_ID, local_max); + uint64 num_tuples = range_itor->num_tuples; core_range_iterator_deinit(range_itor); if (!SUCCESS(rc)) { return rc; @@ -1068,11 +1130,13 @@ core_range_iterator_init(core_handle *spl, local_max = key_buffer_key(&local_max_buffer); rc = core_range_iterator_init(spl, range_itor, + min_key_comparison, min_key, + max_key_comparison, max_key, + greater_than_or_equal, local_max, - start_type, - range_itor->num_tuples); + num_tuples); key_buffer_deinit(&local_max_buffer); if (!SUCCESS(rc)) { return rc; @@ -1084,12 +1148,13 @@ core_range_iterator_init(core_handle *spl, iterator_can_prev(&range_itor->merge_itor->super); } } - if (!in_range && start_type <= less_than_or_equal) { + if (!in_range && start_key_comparison <= less_than_or_equal) { key local_min = key_buffer_key(&range_itor->local_min_key); if (core_key_compare(spl, local_min, min_key) > 0) { key_buffer local_min_buffer; rc = key_buffer_init_from_key( &local_min_buffer, PROCESS_PRIVATE_HEAP_ID, local_min); + uint64 num_tuples = range_itor->num_tuples; core_range_iterator_deinit(range_itor); if (!SUCCESS(rc)) { return rc; @@ -1097,11 +1162,13 @@ core_range_iterator_init(core_handle *spl, local_min = key_buffer_key(&local_min_buffer); rc = core_range_iterator_init(spl, range_itor, + min_key_comparison, min_key, + max_key_comparison, max_key, + less_than, local_min, - start_type, - range_itor->num_tuples); + num_tuples); key_buffer_deinit(&local_min_buffer); if (!SUCCESS(rc)) { return rc; @@ -1162,15 +1229,20 @@ core_range_iterator_next(iterator *itor) } // if there is more data to get, rebuild the iterator for next leaf - if (core_key_compare(range_itor->spl, local_max_key, max_key) < 0) { - uint64 temp_tuples = range_itor->num_tuples; + if (core_range_iterator_has_next_leaf(range_itor)) { + core_handle *spl = range_itor->spl; + uint64 temp_tuples = range_itor->num_tuples; + comparison min_key_comparison = range_itor->min_key_comparison; + comparison max_key_comparison = range_itor->max_key_comparison; core_range_iterator_deinit(range_itor); - rc = core_range_iterator_init(range_itor->spl, + rc = core_range_iterator_init(spl, range_itor, + min_key_comparison, min_key, + max_key_comparison, max_key, - local_max_key, greater_than_or_equal, + local_max_key, temp_tuples); if (!SUCCESS(rc)) { return rc; @@ -1222,14 +1294,20 @@ core_range_iterator_prev(iterator *itor) // if there is more data to get, rebuild the iterator for prev leaf if (core_key_compare(range_itor->spl, local_min_key, min_key) > 0) { + core_handle *spl = range_itor->spl; + uint64 temp_tuples = range_itor->num_tuples; + comparison min_key_comparison = range_itor->min_key_comparison; + comparison max_key_comparison = range_itor->max_key_comparison; core_range_iterator_deinit(range_itor); - rc = core_range_iterator_init(range_itor->spl, + rc = core_range_iterator_init(spl, range_itor, + min_key_comparison, min_key, + max_key_comparison, max_key, - local_min_key, less_than, - range_itor->num_tuples); + local_min_key, + temp_tuples); if (!SUCCESS(rc)) { return rc; } @@ -1477,10 +1555,12 @@ core_apply_to_range(core_handle *spl, TYPED_MALLOC(PROCESS_PRIVATE_HEAP_ID, range_itor); platform_status rc = core_range_iterator_init(spl, range_itor, + greater_than_or_equal, start_key, + less_than, POSITIVE_INFINITY_KEY, - start_key, greater_than_or_equal, + start_key, num_tuples); if (!SUCCESS(rc)) { goto destroy_range_itor; diff --git a/src/core.h b/src/core.h index 9e74f4ed..ee46fcfd 100644 --- a/src/core.h +++ b/src/core.h @@ -131,6 +131,11 @@ typedef struct core_range_iterator { key_buffer max_key; key_buffer local_min_key; key_buffer local_max_key; + comparison min_key_comparison; + comparison max_key_comparison; + comparison local_min_key_comparison; + comparison local_max_key_comparison; + bool32 local_max_key_truncated; btree_iterator btree_itor[CORE_RANGE_ITOR_MAX_BRANCHES]; trunk_branch_info branch[CORE_RANGE_ITOR_MAX_BRANCHES]; @@ -179,10 +184,12 @@ core_lookup_async(core_lookup_async_state *state); platform_status core_range_iterator_init(core_handle *spl, core_range_iterator *range_itor, + comparison min_key_comparison, key min_key, + comparison max_key_comparison, key max_key, + comparison start_key_comparison, key start_key, - comparison start_type, uint64 num_tuples); void core_range_iterator_deinit(core_range_iterator *range_itor); diff --git a/src/iterator.h b/src/iterator.h index 60903787..f42035aa 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -13,8 +13,8 @@ typedef void (*iterator_curr_fn)(iterator *itor, key *curr_key, message *msg); typedef bool32 (*iterator_bound_fn)(iterator *itor); typedef platform_status (*iterator_step_fn)(iterator *itor); typedef platform_status (*iterator_seek_fn)(iterator *itor, - key seek_key, - comparison from_above); + comparison seek_type, + key seek_key); typedef void (*iterator_print_fn)(iterator *itor); typedef struct iterator_ops { @@ -35,6 +35,29 @@ struct iterator { typedef VECTOR(iterator *) iterator_vector; +static inline bool32 +comparison_is_forward(comparison c) +{ + return c == greater_than || c == greater_than_or_equal; +} + +static inline comparison +comparison_invert(comparison c) +{ + switch (c) { + case less_than: + return greater_than_or_equal; + case less_than_or_equal: + return greater_than; + case greater_than_or_equal: + return less_than; + case greater_than: + return less_than_or_equal; + default: + platform_assert(FALSE, "Invalid comparison %d", c); + } +} + // It is safe to call curr whenever iterator_in_range() returns true // otherwise the behavior of iterator_curr is undefined static inline void @@ -74,9 +97,9 @@ iterator_prev(iterator *itor) } static inline platform_status -iterator_seek(iterator *itor, key seek_key, comparison seek_type) +iterator_seek(iterator *itor, comparison seek_type, key seek_key) { - return itor->ops->seek(itor, seek_key, seek_type); + return itor->ops->seek(itor, seek_type, seek_key); } static inline void diff --git a/src/splinterdb.c b/src/splinterdb.c index 0164ac54..6a1c55ff 100644 --- a/src/splinterdb.c +++ b/src/splinterdb.c @@ -623,13 +623,52 @@ struct splinterdb_iterator { const splinterdb *parent; }; +static inline bool32 +splinterdb_iterator_min_comparison_is_valid(comparison cmp) +{ + return cmp == greater_than || cmp == greater_than_or_equal; +} + +static inline bool32 +splinterdb_iterator_max_comparison_is_valid(comparison cmp) +{ + return cmp == less_than || cmp == less_than_or_equal; +} + int -splinterdb_iterator_init(splinterdb *kvs, // IN - splinterdb_iterator **iter, // OUT - slice user_start_key, // IN - comparison start_type // IN +splinterdb_iterator_init(splinterdb *kvs, // IN + splinterdb_iterator **iter, // OUT + comparison start_key_comparison, // IN + slice user_start_key // IN ) { + return splinterdb_iterator_init_with_bounds(kvs, + iter, + greater_than_or_equal, + NULL_SLICE, + less_than, + NULL_SLICE, + start_key_comparison, + user_start_key); +} + +int +splinterdb_iterator_init_with_bounds(splinterdb *kvs, // IN + splinterdb_iterator **iter, // OUT + comparison min_key_comparison, // IN + slice user_min_key, // IN + comparison max_key_comparison, // IN + slice user_max_key, // IN + comparison start_key_comparison, // IN + slice user_start_key // IN +) +{ + if (!splinterdb_iterator_min_comparison_is_valid(min_key_comparison) + || !splinterdb_iterator_max_comparison_is_valid(max_key_comparison)) + { + return platform_status_to_int(STATUS_BAD_PARAM); + } + splinterdb_iterator *it = TYPED_MALLOC(kvs->spl.heap_id, it); if (it == NULL) { platform_error_log("TYPED_MALLOC error\n"); @@ -638,10 +677,19 @@ splinterdb_iterator_init(splinterdb *kvs, // IN it->last_rc = STATUS_OK; core_range_iterator *range_itor = &(it->sri); + key min_key; + key max_key; key start_key; + min_key = slice_is_null(user_min_key) + ? NEGATIVE_INFINITY_KEY + : key_create_from_slice(TRUE, user_min_key); + max_key = slice_is_null(user_max_key) + ? POSITIVE_INFINITY_KEY + : key_create_from_slice(TRUE, user_max_key); + if (slice_is_null(user_start_key)) { - if (start_type <= less_than_or_equal) { + if (start_key_comparison <= less_than_or_equal) { start_key = POSITIVE_INFINITY_KEY; } else { start_key = NEGATIVE_INFINITY_KEY; @@ -652,13 +700,15 @@ splinterdb_iterator_init(splinterdb *kvs, // IN platform_status rc = core_range_iterator_init(&kvs->spl, range_itor, - NEGATIVE_INFINITY_KEY, - POSITIVE_INFINITY_KEY, + min_key_comparison, + min_key, + max_key_comparison, + max_key, + start_key_comparison, start_key, - start_type, UINT64_MAX); if (!SUCCESS(rc)) { - platform_free(kvs->spl.heap_id, *iter); + platform_free(kvs->spl.heap_id, it); return platform_status_to_int(rc); } it->parent = kvs; diff --git a/src/trunk.c b/src/trunk.c index 3837d89d..18acaca7 100644 --- a/src/trunk.c +++ b/src/trunk.c @@ -2244,10 +2244,12 @@ trunk_branch_merger_add_branch(trunk_branch_merger *merger, iter, addr, type, + greater_than_or_equal, merger->min_key, + less_than, merger->max_key, - merger->min_key, greater_than_or_equal, + merger->min_key, TRUE, merger->height); platform_status rc = vector_append(&merger->itors, (iterator *)iter); diff --git a/tests/functional/btree_test.c b/tests/functional/btree_test.c index ecbe5634..6a9458a1 100644 --- a/tests/functional/btree_test.c +++ b/tests/functional/btree_test.c @@ -645,10 +645,12 @@ test_btree_basic(cache *cc, &itor, root_addr, PAGE_TYPE_MEMTABLE, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - NEGATIVE_INFINITY_KEY, greater_than_or_equal, + NEGATIVE_INFINITY_KEY, FALSE, 0); platform_default_log("btree iterator init time %luns\n", @@ -825,10 +827,12 @@ test_btree_create_packed_trees(cache *cc, &itor, memtable_root_addr(mt), PAGE_TYPE_MEMTABLE, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - NEGATIVE_INFINITY_KEY, greater_than_or_equal, + NEGATIVE_INFINITY_KEY, FALSE, 0); @@ -878,10 +882,12 @@ test_count_tuples_in_range(cache *cc, &itor, root_addr[i], type, + greater_than_or_equal, low_key, + less_than, high_key, - low_key, greater_than_or_equal, + low_key, TRUE, 0); key last_key = NULL_KEY; @@ -971,10 +977,12 @@ test_btree_print_all_keys(cache *cc, &itor, root_addr[i], type, + greater_than_or_equal, low_key, + less_than, high_key, - low_key, greater_than_or_equal, + low_key, TRUE, 0); while (iterator_can_curr(&itor.super)) { @@ -1048,10 +1056,12 @@ test_btree_merge_basic(cache *cc, &btree_itor_arr[tree_no], root_addr[tree_no], PAGE_TYPE_BRANCH, + greater_than_or_equal, lo, + less_than, hi, - lo, greater_than_or_equal, + lo, TRUE, 0); itor_arr[tree_no] = &btree_itor_arr[tree_no].super; @@ -1279,10 +1289,12 @@ test_btree_rough_iterator(cache *cc, &rough_btree_itor[tree_no], root_addr[tree_no], PAGE_TYPE_BRANCH, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - NEGATIVE_INFINITY_KEY, greater_than_or_equal, + NEGATIVE_INFINITY_KEY, TRUE, 1); if (iterator_can_curr(&rough_btree_itor[tree_no].super)) { @@ -1440,10 +1452,12 @@ test_btree_merge_perf(cache *cc, &btree_itor_arr[tree_no], root_addr[global_tree_no], PAGE_TYPE_BRANCH, + greater_than_or_equal, min_key, + less_than, max_key, - min_key, greater_than_or_equal, + min_key, TRUE, 0); itor_arr[tree_no] = &btree_itor_arr[tree_no].super; diff --git a/tests/functional/test_functionality.c b/tests/functional/test_functionality.c index 2a823378..85f32103 100644 --- a/tests/functional/test_functionality.c +++ b/tests/functional/test_functionality.c @@ -35,10 +35,12 @@ search_for_key_via_iterator(core_handle *spl, key target) core_range_iterator_init(spl, &iter, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - NEGATIVE_INFINITY_KEY, greater_than_or_equal, + NEGATIVE_INFINITY_KEY, UINT64_MAX); uint64 count = 0; while (iterator_can_curr((iterator *)&iter)) { @@ -242,10 +244,12 @@ verify_range_against_shadow(core_handle *spl, platform_assert(range_itor != NULL); status = core_range_iterator_init(spl, range_itor, + greater_than_or_equal, start_key, + less_than, end_key, - start_key, greater_than_or_equal, + start_key, end_index - start_index); if (!SUCCESS(status)) { platform_error_log("failed to create range itor: %s\n", diff --git a/tests/unit/btree_stress_test.c b/tests/unit/btree_stress_test.c index 6b439c89..04faa897 100644 --- a/tests/unit/btree_stress_test.c +++ b/tests/unit/btree_stress_test.c @@ -696,10 +696,12 @@ iterator_tests(cache *cc, &dbiter, root_addr, type, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - start_key, greater_than_or_equal, + start_key, FALSE, 0); @@ -745,10 +747,12 @@ iterator_seek_tests(cache *cc, &dbiter, root_addr, PAGE_TYPE_MEMTABLE, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - start_key, greater_than_or_equal, + start_key, FALSE, 0); iterator *iter = (iterator *)&dbiter; @@ -757,7 +761,7 @@ iterator_seek_tests(cache *cc, uint64 found_down = iterator_test(hid, cfg, nkvs, iter, FALSE); // seek back to start_key - iterator_seek(iter, start_key, TRUE); + iterator_seek(iter, greater_than_or_equal, start_key); // skip start_key iterator_next(iter); @@ -789,10 +793,12 @@ pack_tests(cache *cc, &dbiter, root_addr, PAGE_TYPE_MEMTABLE, + greater_than_or_equal, NEGATIVE_INFINITY_KEY, + less_than, POSITIVE_INFINITY_KEY, - NEGATIVE_INFINITY_KEY, greater_than_or_equal, + NEGATIVE_INFINITY_KEY, FALSE, 0); diff --git a/tests/unit/splinterdb_quick_test.c b/tests/unit/splinterdb_quick_test.c index 0a4d7919..3db51308 100644 --- a/tests/unit/splinterdb_quick_test.c +++ b/tests/unit/splinterdb_quick_test.c @@ -75,6 +75,37 @@ test_two_step_iterator(splinterdb *kvsb, int start_i, int hop_i); +static int +check_bounded_iterator_sequence(splinterdb *kvsb, + comparison min_key_comparison, + slice min_key, + comparison max_key_comparison, + slice max_key, + comparison start_type, + const int *expected, + uint64 expected_count); + +static int +check_bounded_iterator_current_from_start(splinterdb *kvsb, + comparison min_key_comparison, + slice min_key, + comparison max_key_comparison, + slice max_key, + comparison start_key_comparison, + slice start_key, + int expected_i); + +static int +check_bounded_iterator_boundary_from_start(splinterdb *kvsb, + comparison min_key_comparison, + slice min_key, + comparison max_key_comparison, + slice max_key, + comparison start_key_comparison, + slice start_key, + bool32 step_next, + int expected_i); + static int custom_key_comparator(const data_config *cfg, user_key key1, user_key key2); @@ -467,7 +498,7 @@ CTEST2(splinterdb_quick, test_basic_iterator) splinterdb_iterator *it = NULL; rc = splinterdb_iterator_init( - data->kvsb, &it, NULL_SLICE, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, NULL_SLICE); ASSERT_EQUAL(0, rc); for (; splinterdb_iterator_valid(it); splinterdb_iterator_next(it)) { @@ -490,7 +521,7 @@ CTEST2(splinterdb_quick, test_empty_iterator) { splinterdb_iterator *it = NULL; int rc = splinterdb_iterator_init( - data->kvsb, &it, NULL_SLICE, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, NULL_SLICE); ASSERT_EQUAL(0, rc); ASSERT_FALSE(splinterdb_iterator_valid(it)); @@ -502,6 +533,271 @@ CTEST2(splinterdb_quick, test_empty_iterator) splinterdb_iterator_deinit(it); } +CTEST2(splinterdb_quick, test_bounded_iterator) +{ + const int num_inserts = 10; + int rc = insert_some_keys(num_inserts, data->kvsb); + ASSERT_EQUAL(0, rc); + + char min_key[TEST_INSERT_KEY_LENGTH] = {0}; + char max_key[TEST_INSERT_KEY_LENGTH] = {0}; + + snprintf(min_key, sizeof(min_key), key_fmt, 3); + snprintf(max_key, sizeof(max_key), key_fmt, 6); + + slice min = slice_create(sizeof(min_key), min_key); + slice max = slice_create(sizeof(max_key), max_key); + + const int forward_exclusive_max[] = {3, 4, 5}; + rc = check_bounded_iterator_sequence(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + greater_than_or_equal, + forward_exclusive_max, + ARRAY_SIZE(forward_exclusive_max)); + ASSERT_EQUAL(0, rc); + + const int forward_exclusive_min[] = {4, 5, 6}; + rc = check_bounded_iterator_sequence(data->kvsb, + greater_than, + min, + less_than_or_equal, + max, + greater_than_or_equal, + forward_exclusive_min, + ARRAY_SIZE(forward_exclusive_min)); + ASSERT_EQUAL(0, rc); + + const int reverse_exclusive_max[] = {5, 4, 3}; + rc = check_bounded_iterator_sequence(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + less_than_or_equal, + reverse_exclusive_max, + ARRAY_SIZE(reverse_exclusive_max)); + ASSERT_EQUAL(0, rc); + + snprintf(min_key, sizeof(min_key), key_fmt, 5); + snprintf(max_key, sizeof(max_key), key_fmt, 5); + min = slice_create(sizeof(min_key), min_key); + max = slice_create(sizeof(max_key), max_key); + + const int singleton[] = {5}; + rc = check_bounded_iterator_sequence(data->kvsb, + greater_than_or_equal, + min, + less_than_or_equal, + max, + greater_than_or_equal, + singleton, + ARRAY_SIZE(singleton)); + ASSERT_EQUAL(0, rc); + + rc = splinterdb_iterator_init_with_bounds(data->kvsb, + NULL, + less_than, + min, + less_than_or_equal, + max, + greater_than_or_equal, + NULL_SLICE); + ASSERT_EQUAL(EINVAL, rc); +} + +CTEST2(splinterdb_quick, test_bounded_iterator_start_normalization) +{ + const int num_inserts = 10; + int rc = insert_some_keys(num_inserts, data->kvsb); + ASSERT_EQUAL(0, rc); + + char min_key[TEST_INSERT_KEY_LENGTH] = {0}; + char max_key[TEST_INSERT_KEY_LENGTH] = {0}; + char below_min_key[TEST_INSERT_KEY_LENGTH] = {0}; + char above_max_key[TEST_INSERT_KEY_LENGTH] = {0}; + + snprintf(min_key, sizeof(min_key), key_fmt, 3); + snprintf(max_key, sizeof(max_key), key_fmt, 6); + snprintf(below_min_key, sizeof(below_min_key), key_fmt, 1); + snprintf(above_max_key, sizeof(above_max_key), key_fmt, 8); + + slice min = slice_create(sizeof(min_key), min_key); + slice max = slice_create(sizeof(max_key), max_key); + slice below_min = slice_create(sizeof(below_min_key), below_min_key); + slice above_max = slice_create(sizeof(above_max_key), above_max_key); + + // start_key < min_key: forward starts at the lower bound. + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + greater_than_or_equal, + below_min, + 3); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than, + min, + less_than_or_equal, + max, + greater_than_or_equal, + below_min, + 4); + ASSERT_EQUAL(0, rc); + + // start_key < min_key: backward starts just before the lower bound. + rc = check_bounded_iterator_boundary_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + less_than_or_equal, + below_min, + TRUE, + 3); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_boundary_from_start(data->kvsb, + greater_than, + min, + less_than_or_equal, + max, + less_than_or_equal, + below_min, + TRUE, + 4); + ASSERT_EQUAL(0, rc); + + // start_key == min_key: an exclusive lower bound moves forward starts above + // min_key and backward starts to the invalid low-side boundary. + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than, + min, + less_than_or_equal, + max, + greater_than_or_equal, + min, + 4); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_boundary_from_start(data->kvsb, + greater_than, + min, + less_than_or_equal, + max, + less_than_or_equal, + min, + TRUE, + 4); + ASSERT_EQUAL(0, rc); + + // start_key == min_key: an inclusive lower bound remains on min_key. + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + greater_than_or_equal, + min, + 3); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + less_than_or_equal, + min, + 3); + ASSERT_EQUAL(0, rc); + + // start_key > max_key: backward starts at the upper bound. + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + less_than_or_equal, + above_max, + 5); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than_or_equal, + max, + less_than_or_equal, + above_max, + 6); + ASSERT_EQUAL(0, rc); + + // start_key > max_key: forward starts just after the upper bound. + rc = check_bounded_iterator_boundary_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + greater_than_or_equal, + above_max, + FALSE, + 5); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_boundary_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than_or_equal, + max, + greater_than_or_equal, + above_max, + FALSE, + 6); + ASSERT_EQUAL(0, rc); + + // start_key == max_key: an exclusive upper bound moves backward starts below + // max_key and forward starts to the invalid high-side boundary. + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + less_than_or_equal, + max, + 5); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_boundary_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than, + max, + greater_than_or_equal, + max, + FALSE, + 5); + ASSERT_EQUAL(0, rc); + + // start_key == max_key: an inclusive upper bound remains on max_key. + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than_or_equal, + max, + greater_than_or_equal, + max, + 6); + ASSERT_EQUAL(0, rc); + rc = check_bounded_iterator_current_from_start(data->kvsb, + greater_than_or_equal, + min, + less_than_or_equal, + max, + less_than_or_equal, + max, + 6); + ASSERT_EQUAL(0, rc); +} + /* * Test case to exercise and verify that splinterdb iterator interfaces with a * non-NULL start key correctly sets up the start scan at the requested @@ -522,7 +818,7 @@ CTEST2(splinterdb_quick, test_splinterdb_iterator_with_startkey) snprintf(key, sizeof(key), key_fmt, ictr); slice start_key = slice_create(strlen(key), key); rc = splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); bool32 is_valid = splinterdb_iterator_valid(it); @@ -557,7 +853,7 @@ CTEST2(splinterdb_quick, test_splinterdb_iterator_with_non_existent_startkey) slice start_key = slice_create(strlen(keystring), keystring); rc = splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, start_key); // Iterator should be invalid, as lookup key is non-existent. bool32 is_valid = splinterdb_iterator_valid(it); @@ -571,7 +867,7 @@ CTEST2(splinterdb_quick, test_splinterdb_iterator_with_non_existent_startkey) keystring = "UnknownKey"; start_key = slice_create(strlen(keystring), keystring); rc = splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); int ictr = 0; @@ -625,7 +921,7 @@ CTEST2(splinterdb_quick, splinterdb_iterator *it = NULL; slice start_key = slice_create(strlen(key), key); rc = splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); bool32 is_valid = splinterdb_iterator_valid(it); @@ -645,7 +941,7 @@ CTEST2(splinterdb_quick, snprintf(key, sizeof(key), key_fmt, kctr); start_key = slice_create(strlen(key), key); rc = splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); is_valid = splinterdb_iterator_valid(it); @@ -664,7 +960,7 @@ CTEST2(splinterdb_quick, snprintf(key, sizeof(key), key_fmt, kctr); start_key = slice_create(strlen(key), key); rc = splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); is_valid = splinterdb_iterator_valid(it); @@ -683,7 +979,7 @@ CTEST2(splinterdb_quick, snprintf(key, sizeof(key), key_fmt, kctr); start_key = slice_create(strlen(key), key); rc = splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); is_valid = splinterdb_iterator_valid(it); @@ -1152,7 +1448,7 @@ CTEST2(splinterdb_quick, test_iterator_custom_comparator) splinterdb_iterator *it = NULL; rc = splinterdb_iterator_init( - data->kvsb, &it, NULL_SLICE, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, NULL_SLICE); ASSERT_EQUAL(0, rc); int i = 0; @@ -1195,7 +1491,7 @@ CTEST2(splinterdb_quick, test_iterator_init_bug) // Iterator init should find nothing when no keys were inserted, yet. splinterdb_iterator *it = NULL; rc = splinterdb_iterator_init( - data->kvsb, &it, NULL_SLICE, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, NULL_SLICE); ASSERT_EQUAL(0, rc); bool32 iter_valid = splinterdb_iterator_valid(it); @@ -1210,7 +1506,7 @@ CTEST2(splinterdb_quick, test_iterator_init_bug) it = NULL; rc = splinterdb_iterator_init( - data->kvsb, &it, NULL_SLICE, greater_than_or_equal); + data->kvsb, &it, greater_than_or_equal, NULL_SLICE); ASSERT_EQUAL(0, rc); iter_valid = splinterdb_iterator_valid(it); @@ -1421,6 +1717,122 @@ check_current_tuple(splinterdb_iterator *it, const int expected_i) return rc; } +static int +check_bounded_iterator_sequence(splinterdb *kvsb, + comparison min_key_comparison, + slice min_key, + comparison max_key_comparison, + slice max_key, + comparison start_type, + const int *expected, + uint64 expected_count) +{ + splinterdb_iterator *it = NULL; + int rc = splinterdb_iterator_init_with_bounds(kvsb, + &it, + min_key_comparison, + min_key, + max_key_comparison, + max_key, + start_type, + NULL_SLICE); + ASSERT_EQUAL(0, rc); + + for (uint64 i = 0; i < expected_count; i++) { + ASSERT_TRUE(splinterdb_iterator_valid(it)); + rc = check_current_tuple(it, expected[i]); + ASSERT_EQUAL(0, rc); + if (start_type <= less_than_or_equal) { + splinterdb_iterator_prev(it); + } else { + splinterdb_iterator_next(it); + } + } + + ASSERT_FALSE(splinterdb_iterator_valid(it)); + rc = splinterdb_iterator_status(it); + ASSERT_EQUAL(0, rc); + splinterdb_iterator_deinit(it); + return rc; +} + +static int +check_bounded_iterator_current_from_start(splinterdb *kvsb, + comparison min_key_comparison, + slice min_key, + comparison max_key_comparison, + slice max_key, + comparison start_key_comparison, + slice start_key, + int expected_i) +{ + splinterdb_iterator *it = NULL; + int rc = splinterdb_iterator_init_with_bounds(kvsb, + &it, + min_key_comparison, + min_key, + max_key_comparison, + max_key, + start_key_comparison, + start_key); + ASSERT_EQUAL(0, rc); + + ASSERT_TRUE(splinterdb_iterator_valid(it)); + rc = check_current_tuple(it, expected_i); + ASSERT_EQUAL(0, rc); + + rc = splinterdb_iterator_status(it); + ASSERT_EQUAL(0, rc); + splinterdb_iterator_deinit(it); + return rc; +} + +static int +check_bounded_iterator_boundary_from_start(splinterdb *kvsb, + comparison min_key_comparison, + slice min_key, + comparison max_key_comparison, + slice max_key, + comparison start_key_comparison, + slice start_key, + bool32 step_next, + int expected_i) +{ + splinterdb_iterator *it = NULL; + int rc = splinterdb_iterator_init_with_bounds(kvsb, + &it, + min_key_comparison, + min_key, + max_key_comparison, + max_key, + start_key_comparison, + start_key); + ASSERT_EQUAL(0, rc); + + ASSERT_FALSE(splinterdb_iterator_valid(it)); + rc = splinterdb_iterator_status(it); + ASSERT_EQUAL(0, rc); + + if (step_next) { + ASSERT_TRUE(splinterdb_iterator_can_next(it)); + ASSERT_FALSE(splinterdb_iterator_can_prev(it)); + splinterdb_iterator_next(it); + } else { + ASSERT_FALSE(splinterdb_iterator_can_next(it)); + ASSERT_TRUE(splinterdb_iterator_can_prev(it)); + splinterdb_iterator_prev(it); + } + + ASSERT_TRUE(splinterdb_iterator_valid(it)); + rc = check_current_tuple(it, expected_i); + ASSERT_EQUAL(0, rc); + + rc = splinterdb_iterator_status(it); + ASSERT_EQUAL(0, rc); + splinterdb_iterator_deinit(it); + return rc; +} + // Test moving iterator 2 steps up, 1 step back and then all the way back down static int test_two_step_iterator(splinterdb *kvsb, @@ -1432,7 +1844,7 @@ test_two_step_iterator(splinterdb *kvsb, { int rc; splinterdb_iterator *it = NULL; - rc = splinterdb_iterator_init(kvsb, &it, start_key, greater_than_or_equal); + rc = splinterdb_iterator_init(kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); for (int i = start_i; i < num_keys; i++) { diff --git a/tests/unit/splinterdb_stress_test.c b/tests/unit/splinterdb_stress_test.c index 41b01223..341a3fa3 100644 --- a/tests/unit/splinterdb_stress_test.c +++ b/tests/unit/splinterdb_stress_test.c @@ -166,7 +166,7 @@ CTEST2(splinterdb_stress, test_iterator_over_many_kvs) slice start_key = slice_create(sizeof(key_str), key_str); ASSERT_EQUAL(0, splinterdb_iterator_init( - data->kvsb, &it, start_key, greater_than_or_equal)); + data->kvsb, &it, greater_than_or_equal, start_key)); // assert that the iterator is in the state we expect ASSERT_FALSE(splinterdb_iterator_valid(it)); @@ -321,7 +321,7 @@ naive_range_delete(splinterdb *kvsb, slice start_key, uint32 count) splinterdb_iterator *it; int rc = - splinterdb_iterator_init(kvsb, &it, start_key, greater_than_or_equal); + splinterdb_iterator_init(kvsb, &it, greater_than_or_equal, start_key); ASSERT_EQUAL(0, rc); slice key, value; From 0bc60b2e26955a8090e8e0c9afb0dc7ee40f7c33 Mon Sep 17 00:00:00 2001 From: Rob Johnson Date: Wed, 13 May 2026 19:13:00 -0700 Subject: [PATCH 2/2] formatting Signed-off-by: Rob Johnson --- include/splinterdb/splinterdb.h | 21 ++++++++++----------- tests/unit/splinterdb_quick_test.c | 12 ++++++------ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/splinterdb/splinterdb.h b/include/splinterdb/splinterdb.h index 6612003c..471d0b4e 100644 --- a/include/splinterdb/splinterdb.h +++ b/include/splinterdb/splinterdb.h @@ -362,8 +362,8 @@ typedef enum comparison { // - greater_than_or_equal: the minimum key // - less_than_or_equal: the maximum key int -splinterdb_iterator_init(splinterdb *kvs, // IN - splinterdb_iterator **iter, // OUT +splinterdb_iterator_init(splinterdb *kvs, // IN + splinterdb_iterator **iter, // OUT comparison start_type, // IN slice start_key // IN ); @@ -380,15 +380,14 @@ splinterdb_iterator_init(splinterdb *kvs, // IN // - greater_than_or_equal / greater_than: the first key in bounds // - less_than_or_equal / less_than: the last key in bounds int -splinterdb_iterator_init_with_bounds( - splinterdb *kvs, // IN - splinterdb_iterator **iter, // OUT - comparison min_key_comparison, // IN - slice min_key, // IN - comparison max_key_comparison, // IN - slice max_key, // IN - comparison start_type, // IN - slice start_key // IN +splinterdb_iterator_init_with_bounds(splinterdb *kvs, // IN + splinterdb_iterator **iter, // OUT + comparison min_key_comparison, // IN + slice min_key, // IN + comparison max_key_comparison, // IN + slice max_key, // IN + comparison start_type, // IN + slice start_key // IN ); // Deinitialize an iterator diff --git a/tests/unit/splinterdb_quick_test.c b/tests/unit/splinterdb_quick_test.c index 3db51308..9179d15a 100644 --- a/tests/unit/splinterdb_quick_test.c +++ b/tests/unit/splinterdb_quick_test.c @@ -587,7 +587,7 @@ CTEST2(splinterdb_quick, test_bounded_iterator) max = slice_create(sizeof(max_key), max_key); const int singleton[] = {5}; - rc = check_bounded_iterator_sequence(data->kvsb, + rc = check_bounded_iterator_sequence(data->kvsb, greater_than_or_equal, min, less_than_or_equal, @@ -614,8 +614,8 @@ CTEST2(splinterdb_quick, test_bounded_iterator_start_normalization) int rc = insert_some_keys(num_inserts, data->kvsb); ASSERT_EQUAL(0, rc); - char min_key[TEST_INSERT_KEY_LENGTH] = {0}; - char max_key[TEST_INSERT_KEY_LENGTH] = {0}; + char min_key[TEST_INSERT_KEY_LENGTH] = {0}; + char max_key[TEST_INSERT_KEY_LENGTH] = {0}; char below_min_key[TEST_INSERT_KEY_LENGTH] = {0}; char above_max_key[TEST_INSERT_KEY_LENGTH] = {0}; @@ -1728,7 +1728,7 @@ check_bounded_iterator_sequence(splinterdb *kvsb, uint64 expected_count) { splinterdb_iterator *it = NULL; - int rc = splinterdb_iterator_init_with_bounds(kvsb, + int rc = splinterdb_iterator_init_with_bounds(kvsb, &it, min_key_comparison, min_key, @@ -1767,7 +1767,7 @@ check_bounded_iterator_current_from_start(splinterdb *kvsb, int expected_i) { splinterdb_iterator *it = NULL; - int rc = splinterdb_iterator_init_with_bounds(kvsb, + int rc = splinterdb_iterator_init_with_bounds(kvsb, &it, min_key_comparison, min_key, @@ -1799,7 +1799,7 @@ check_bounded_iterator_boundary_from_start(splinterdb *kvsb, int expected_i) { splinterdb_iterator *it = NULL; - int rc = splinterdb_iterator_init_with_bounds(kvsb, + int rc = splinterdb_iterator_init_with_bounds(kvsb, &it, min_key_comparison, min_key,