use core::{
cmp::Ordering,
fmt,
iter::FusedIterator,
ops::{Bound, ControlFlow},
};
use crate::{
allocator::{Allocator, Global},
map::{NonEmptyTree, DEFAULT_PREFIX_LEN},
raw::{
match_concrete_node_ptr, maximum_unchecked, minimum_unchecked,
AttemptOptimisticPrefixMatch, ConcreteNodePtr, InnerNode, LeafNode, NodePtr, OpaqueNodePtr,
PessimisticMismatch, PrefixMatchBehavior, RawIterator,
},
AsBytes, TreeMap,
};
pub(crate) struct InnerNodeSearchResult<K, V, const PREFIX_LEN: usize> {
pub node_ptr: OpaqueNodePtr<K, V, PREFIX_LEN>,
pub current_depth: usize,
pub node_prefix_comparison_to_search_key_segment: Ordering,
pub reason: InnerNodeSearchResultReason,
}
impl<K, V, const PREFIX_LEN: usize> fmt::Debug for InnerNodeSearchResult<K, V, PREFIX_LEN> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InnerNodeSearchResult")
.field("node_ptr", &self.node_ptr)
.field("current_depth", &self.current_depth)
.field(
"node_prefix_comparison_to_search_key_segment",
&self.node_prefix_comparison_to_search_key_segment,
)
.field("reason", &self.reason)
.finish()
}
}
#[derive(Debug)]
pub(crate) enum InnerNodeSearchResultReason {
PrefixMismatch,
InsufficientBytes,
MissingChild,
}
pub(crate) enum TerminatingNodeSearchResult<K, V, const PREFIX_LEN: usize> {
Leaf {
leaf_ptr: NodePtr<PREFIX_LEN, LeafNode<K, V, PREFIX_LEN>>,
leaf_key_comparison_to_search_key: Ordering,
},
InnerNode(InnerNodeSearchResult<K, V, PREFIX_LEN>),
}
impl<K, V, const PREFIX_LEN: usize> fmt::Debug for TerminatingNodeSearchResult<K, V, PREFIX_LEN> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Leaf {
leaf_ptr,
leaf_key_comparison_to_search_key,
} => f
.debug_struct("Leaf")
.field("leaf_ptr", leaf_ptr)
.field(
"leaf_key_comparison_to_search_key",
leaf_key_comparison_to_search_key,
)
.finish(),
Self::InnerNode(arg0) => f.debug_tuple("InnerNode").field(arg0).finish(),
}
}
}
pub(crate) unsafe fn find_terminating_node<K: AsBytes, V, const PREFIX_LEN: usize>(
root: OpaqueNodePtr<K, V, PREFIX_LEN>,
key_bytes: &[u8],
) -> TerminatingNodeSearchResult<K, V, PREFIX_LEN> {
unsafe fn check_prefix_lookup_child<K, V, N, const PREFIX_LEN: usize>(
inner_ptr: NodePtr<PREFIX_LEN, N>,
key_bytes: &[u8],
current_depth: &mut usize,
prefix_match_behavior: &mut PrefixMatchBehavior,
) -> ControlFlow<InnerNodeSearchResult<K, V, PREFIX_LEN>, OpaqueNodePtr<K, V, PREFIX_LEN>>
where
N: InnerNode<PREFIX_LEN, Key = K, Value = V>,
K: AsBytes,
{
let inner_node = unsafe { inner_ptr.as_ref() };
let match_prefix =
prefix_match_behavior.match_prefix(inner_node, &key_bytes[*current_depth..]);
match match_prefix {
Err(PessimisticMismatch { matched_bytes, .. }) => {
let (full_prefix, _) = inner_node.read_full_prefix(*current_depth);
let upper_bound = (*current_depth + full_prefix.len()).min(key_bytes.len());
let key_segment = &key_bytes[(*current_depth)..upper_bound];
let node_prefix_comparison_to_search_key_segment = full_prefix.cmp(key_segment);
debug_assert_ne!(
node_prefix_comparison_to_search_key_segment,
Ordering::Equal,
"if there was a mismatch, the prefix must not be equal"
);
let reason = if matched_bytes == key_bytes[*current_depth..].len() {
InnerNodeSearchResultReason::InsufficientBytes
} else {
InnerNodeSearchResultReason::PrefixMismatch
};
ControlFlow::Break(InnerNodeSearchResult {
node_ptr: inner_ptr.to_opaque(),
current_depth: *current_depth,
reason,
node_prefix_comparison_to_search_key_segment,
})
},
Ok(AttemptOptimisticPrefixMatch { matched_bytes, .. }) => {
*current_depth += matched_bytes;
let next_key_fragment = if *current_depth < key_bytes.len() {
key_bytes[*current_depth]
} else {
return ControlFlow::Break(InnerNodeSearchResult {
node_ptr: inner_ptr.to_opaque(),
current_depth: *current_depth,
reason: InnerNodeSearchResultReason::InsufficientBytes,
node_prefix_comparison_to_search_key_segment: Ordering::Equal,
});
};
let child_lookup = inner_node.lookup_child(next_key_fragment);
match child_lookup {
Some(child_ptr) => {
*current_depth += 1;
ControlFlow::Continue(child_ptr)
},
None => ControlFlow::Break(InnerNodeSearchResult {
node_ptr: inner_ptr.to_opaque(),
current_depth: *current_depth,
reason: InnerNodeSearchResultReason::MissingChild,
node_prefix_comparison_to_search_key_segment: Ordering::Equal,
}),
}
},
}
}
let mut current_node = root;
let mut current_depth = 0;
let mut prefix_match_behavior = PrefixMatchBehavior::default();
loop {
let next_node = match_concrete_node_ptr!(match (current_node.to_node_ptr()) {
InnerNode(inner_ptr) => unsafe {
check_prefix_lookup_child(
inner_ptr,
key_bytes,
&mut current_depth,
&mut prefix_match_behavior,
)
},
LeafNode(leaf_ptr) => {
let leaf_node = unsafe { leaf_ptr.as_ref() };
let leaf_key_comparison_to_search_key =
prefix_match_behavior.compare_leaf_key(leaf_node, key_bytes, current_depth);
return TerminatingNodeSearchResult::Leaf {
leaf_ptr,
leaf_key_comparison_to_search_key,
};
},
});
match next_node {
ControlFlow::Continue(next_node) => {
current_node = next_node;
},
ControlFlow::Break(reason) => {
return TerminatingNodeSearchResult::InnerNode(reason);
},
}
}
}
#[track_caller]
pub(crate) fn validate_range_bounds(start_bound: &Bound<&[u8]>, end_bound: &Bound<&[u8]>) {
match (*start_bound, *end_bound) {
(Bound::Excluded(s), Bound::Excluded(e)) if s == e => {
panic!("range start and end are equal and excluded: ({s:?})")
},
(Bound::Included(s) | Bound::Excluded(s), Bound::Included(e) | Bound::Excluded(e))
if s > e =>
{
panic!("range start ({s:?}) is greater than range end ({e:?})")
},
_ => {},
}
}
type KeyByteAndChild<K, V, const PREFIX_LEN: usize> = Option<(u8, OpaqueNodePtr<K, V, PREFIX_LEN>)>;
pub(crate) unsafe fn find_leaf_pointer_for_bound<K: AsBytes, V, const PREFIX_LEN: usize>(
tree: &NonEmptyTree<K, V, PREFIX_LEN>,
bound: Bound<&[u8]>,
is_start: bool,
) -> Option<NodePtr<PREFIX_LEN, LeafNode<K, V, PREFIX_LEN>>> {
Some(match bound {
Bound::Included(key_bytes) | Bound::Excluded(key_bytes) => {
let terminating_node = unsafe { find_terminating_node(tree.root, key_bytes) };
match terminating_node {
TerminatingNodeSearchResult::Leaf {
leaf_ptr,
leaf_key_comparison_to_search_key,
} => match leaf_key_comparison_to_search_key {
Ordering::Less => {
if is_start {
let leaf = unsafe { leaf_ptr.as_ref() };
leaf.next?
} else {
leaf_ptr
}
},
Ordering::Equal => {
if matches!(bound, Bound::Excluded(_)) {
let leaf = unsafe { leaf_ptr.as_ref() };
if is_start { leaf.next } else { leaf.previous }?
} else {
leaf_ptr
}
},
Ordering::Greater => {
if is_start {
leaf_ptr
} else {
let leaf = unsafe { leaf_ptr.as_ref() };
leaf.previous?
}
},
},
TerminatingNodeSearchResult::InnerNode(InnerNodeSearchResult {
node_ptr,
current_depth,
reason,
node_prefix_comparison_to_search_key_segment,
}) => match reason {
InnerNodeSearchResultReason::PrefixMismatch
| InnerNodeSearchResultReason::InsufficientBytes => {
match (is_start, node_prefix_comparison_to_search_key_segment) {
(true, Ordering::Equal | Ordering::Greater) => unsafe {
minimum_unchecked(node_ptr)
},
(true, Ordering::Less) => unsafe {
let max_leaf_ptr = maximum_unchecked(node_ptr);
let max_leaf = max_leaf_ptr.as_ref();
max_leaf.next?
},
(false, Ordering::Equal | Ordering::Less) => unsafe {
maximum_unchecked(node_ptr)
},
(false, Ordering::Greater) => unsafe {
let min_leaf_ptr = minimum_unchecked(node_ptr);
let min_leaf = min_leaf_ptr.as_ref();
min_leaf.previous?
},
}
},
InnerNodeSearchResultReason::MissingChild => {
fn access_closest_child<
const PREFIX_LEN: usize,
N: InnerNode<PREFIX_LEN>,
>(
inner_ptr: NodePtr<PREFIX_LEN, N>,
child_bounds: (Bound<u8>, Bound<u8>),
is_start: bool,
) -> KeyByteAndChild<N::Key, N::Value, PREFIX_LEN> {
let inner = unsafe { inner_ptr.as_ref() };
let mut child_it = inner.range(child_bounds);
if is_start {
child_it.next()
} else {
child_it.next_back()
}
}
let child_bounds = if is_start {
(bound.map(|_| key_bytes[current_depth]), Bound::Unbounded)
} else {
(Bound::Unbounded, bound.map(|_| key_bytes[current_depth]))
};
let possible_next_child =
match_concrete_node_ptr!(match (node_ptr.to_node_ptr()) {
InnerNode(inner_ptr) => {
access_closest_child(inner_ptr, child_bounds, is_start)
},
LeafNode(_leaf) => unreachable!(
"This branch is unreachable because the \
TerminatingNodeSearchResult had the value InnerNode"
),
});
let (_, next_child) = possible_next_child?;
if is_start {
unsafe { minimum_unchecked(next_child) }
} else {
unsafe { maximum_unchecked(next_child) }
}
},
},
}
},
Bound::Unbounded => {
if is_start {
tree.min_leaf
} else {
tree.max_leaf
}
},
})
}
macro_rules! implement_range_iter {
(
$(#[$outer:meta])*
struct $name:ident {
tree: $tree_ty:ty,
item: $item_ty:ty,
$leaf_accessor_func:ident
}
) => {
$(#[$outer])*
pub struct $name<'a, K, V, const PREFIX_LEN: usize = DEFAULT_PREFIX_LEN, A: Allocator = Global> {
inner: RawIterator<K, V, PREFIX_LEN>,
_tree: $tree_ty,
}
impl<'a, K: AsBytes, V, const PREFIX_LEN: usize, A: Allocator> $name<'a, K, V, PREFIX_LEN, A> {
pub(crate) fn new(
tree: $tree_ty,
start_bound: Bound<&[u8]>,
end_bound: Bound<&[u8]>,
) -> Self {
let Some(tree_state) = &tree.state else {
return Self {
_tree: tree,
inner: RawIterator::empty(),
};
};
validate_range_bounds(&start_bound, &end_bound);
let possible_start =
unsafe { find_leaf_pointer_for_bound(tree_state, start_bound, true) };
let Some(start) = possible_start else {
return Self {
_tree: tree,
inner: RawIterator::empty(),
};
};
let possible_end =
unsafe { find_leaf_pointer_for_bound(tree_state, end_bound, false) };
let Some(end) = possible_end else {
return Self {
_tree: tree,
inner: RawIterator::empty(),
};
};
unsafe {
let start_leaf_bytes = start.as_key_ref().as_bytes();
let end_leaf_bytes = end.as_key_ref().as_bytes();
if start_leaf_bytes > end_leaf_bytes {
return Self {
_tree: tree,
inner: RawIterator::empty(),
};
}
}
Self {
_tree: tree,
inner: unsafe { RawIterator::new(start, end) },
}
}
}
impl<'a, K, V, const PREFIX_LEN: usize, A: Allocator> Iterator for $name<'a, K, V, PREFIX_LEN, A> {
type Item = $item_ty;
fn next(&mut self) -> Option<Self::Item> {
let leaf_ptr = unsafe { self.inner.next()? };
Some(unsafe { leaf_ptr.$leaf_accessor_func() })
}
}
impl<'a, K, V, const PREFIX_LEN: usize, A: Allocator> DoubleEndedIterator
for $name<'a, K, V, PREFIX_LEN, A>
{
fn next_back(&mut self) -> Option<Self::Item> {
let leaf_ptr = unsafe { self.inner.next_back()? };
Some(unsafe { leaf_ptr.$leaf_accessor_func() })
}
}
impl<'a, K, V, const PREFIX_LEN: usize, A: Allocator> FusedIterator for $name<'a, K, V, PREFIX_LEN, A> {}
};
}
implement_range_iter!(
struct Range {
tree: &'a TreeMap<K, V, PREFIX_LEN, A>,
item: (&'a K, &'a V),
as_key_value_ref
}
);
unsafe impl<K, V, A, const PREFIX_LEN: usize> Send for Range<'_, K, V, PREFIX_LEN, A>
where
K: Sync,
V: Sync,
A: Sync + Allocator,
{
}
unsafe impl<K, V, A, const PREFIX_LEN: usize> Sync for Range<'_, K, V, PREFIX_LEN, A>
where
K: Sync,
V: Sync,
A: Sync + Allocator,
{
}
implement_range_iter!(
struct RangeMut {
tree: &'a mut TreeMap<K, V, PREFIX_LEN, A>,
item: (&'a K, &'a mut V),
as_key_ref_value_mut
}
);
unsafe impl<K, V, A, const PREFIX_LEN: usize> Send for RangeMut<'_, K, V, PREFIX_LEN, A>
where
K: Send,
V: Send,
A: Send + Allocator,
{
}
unsafe impl<K, V, A, const PREFIX_LEN: usize> Sync for RangeMut<'_, K, V, PREFIX_LEN, A>
where
K: Sync,
V: Sync,
A: Sync + Allocator,
{
}
#[cfg(test)]
mod tests {
use alloc::{boxed::Box, vec::Vec};
use super::*;
use crate::testing::{generate_key_with_prefix, swap, PrefixExpansion};
#[test]
fn iterators_are_send_sync() {
fn is_send<T: Send>() {}
fn is_sync<T: Sync>() {}
fn range_is_send<'a, K: Sync + 'a, V: Sync + 'a, A: Sync + Allocator + 'a>() {
is_send::<Range<'a, K, V, DEFAULT_PREFIX_LEN, A>>();
}
fn range_is_sync<'a, K: Sync + 'a, V: Sync + 'a, A: Sync + Allocator + 'a>() {
is_sync::<Range<'a, K, V, DEFAULT_PREFIX_LEN, A>>();
}
range_is_send::<[u8; 3], usize, Global>();
range_is_sync::<[u8; 3], usize, Global>();
fn range_mut_is_send<'a, K: Send + 'a, V: Send + 'a, A: Send + Allocator + 'a>() {
is_send::<RangeMut<'a, K, V, DEFAULT_PREFIX_LEN, A>>();
}
fn range_mut_is_sync<'a, K: Sync + 'a, V: Sync + 'a, A: Sync + Allocator + 'a>() {
is_sync::<RangeMut<'a, K, V, DEFAULT_PREFIX_LEN, A>>();
}
range_mut_is_send::<[u8; 3], usize, Global>();
range_mut_is_sync::<[u8; 3], usize, Global>();
}
fn fixture_tree() -> TreeMap<[u8; 3], usize> {
[
[0, 0, 0],
[0, 0, u8::MAX],
[0, u8::MAX, 0],
[0, u8::MAX, u8::MAX],
[127, 127, 0],
[127, 127, 127],
[127, 127, u8::MAX],
[u8::MAX, 0, 0],
[u8::MAX, 0, u8::MAX],
[u8::MAX, u8::MAX, 0],
[u8::MAX, u8::MAX, u8::MAX],
]
.into_iter()
.enumerate()
.map(swap)
.collect()
}
#[test]
fn range_iteration_normal_tree() {
let tree = fixture_tree();
let mut it = Range::new(
&tree,
Bound::Included([u8::MAX, u8::MAX, u8::MAX].as_slice()),
Bound::Unbounded,
)
.map(|(k, _)| k);
assert_eq!(it.next().unwrap(), &[u8::MAX, u8::MAX, u8::MAX]);
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Excluded([u8::MAX, u8::MAX, u8::MAX].as_slice()),
Bound::Unbounded,
)
.map(|(k, _)| k);
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Unbounded,
Bound::Included([0, 0, 0].as_slice()),
)
.map(|(k, _)| k);
assert_eq!(it.next().unwrap(), &[0, 0, 0]);
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Unbounded,
Bound::Excluded([0, 0, 0].as_slice()),
)
.map(|(k, _)| k);
assert_eq!(it.next(), None);
}
#[test]
fn range_iteration_on_key_prefix() {
let tree = fixture_tree();
let mut it = Range::new(
&tree,
Bound::Included([0].as_slice()),
Bound::Excluded([1].as_slice()),
)
.map(|(k, _)| k);
assert_eq!(it.next().unwrap(), &[0, 0, 0]);
assert_eq!(it.next().unwrap(), &[0, 0, u8::MAX]);
assert_eq!(it.next().unwrap(), &[0, u8::MAX, 0]);
assert_eq!(it.next().unwrap(), &[0, u8::MAX, u8::MAX]);
assert_eq!(it.next(), None);
}
#[test]
fn range_prefix_mismatch_or_insufficient_bytes_lower_bounds() {
let tree = fixture_tree();
let mut it = Range::new(
&tree,
Bound::Included([127, 126].as_slice()),
Bound::Excluded([128].as_slice()),
)
.map(|(k, _)| k);
assert!([127, 126].as_slice() < [127, 127, 0].as_slice());
assert_eq!(it.next().unwrap(), &[127, 127, 0]);
assert_eq!(it.next().unwrap(), &[127, 127, 127]);
assert_eq!(it.next().unwrap(), &[127, 127, u8::MAX]);
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Included([127, 128].as_slice()),
Bound::Excluded([128].as_slice()),
)
.map(|(k, _)| k);
assert!([127, 128].as_slice() > [127, 127, u8::MAX].as_slice());
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Included([127].as_slice()),
Bound::Excluded([128].as_slice()),
)
.map(|(k, _)| k);
assert!([127].as_slice() < [127, 127, 0].as_slice());
assert_eq!(it.next().unwrap(), &[127, 127, 0]);
assert_eq!(it.next().unwrap(), &[127, 127, 127]);
assert_eq!(it.next().unwrap(), &[127, 127, u8::MAX]);
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Included([127, 127].as_slice()),
Bound::Excluded([128].as_slice()),
)
.map(|(k, _)| k);
assert!([127, 127].as_slice() < [127, 127, 0].as_slice());
assert_eq!(it.next().unwrap(), &[127, 127, 0]);
assert_eq!(it.next().unwrap(), &[127, 127, 127]);
assert_eq!(it.next().unwrap(), &[127, 127, u8::MAX]);
assert_eq!(it.next(), None);
}
#[test]
fn range_prefix_mismatch_or_insufficient_bytes_upper_bounds() {
let tree = fixture_tree();
let mut it = Range::new(
&tree,
Bound::Excluded([126].as_slice()),
Bound::Excluded([128].as_slice()),
)
.map(|(k, _)| k);
assert_eq!(it.next().unwrap(), &[127, 127, 0]);
assert_eq!(it.next().unwrap(), &[127, 127, 127]);
assert_eq!(it.next().unwrap(), &[127, 127, u8::MAX]);
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Excluded([126].as_slice()),
Bound::Included([127, 127, u8::MAX].as_slice()),
)
.map(|(k, _)| k);
assert_eq!(it.next().unwrap(), &[127, 127, 0]);
assert_eq!(it.next().unwrap(), &[127, 127, 127]);
assert_eq!(it.next().unwrap(), &[127, 127, u8::MAX]);
assert_eq!(it.next(), None);
let mut it = Range::new(
&tree,
Bound::Excluded([126].as_slice()),
Bound::Included([127, 127, 0].as_slice()),
)
.map(|(k, _)| k);
assert_eq!(it.next().unwrap(), &[127, 127, 0]);
assert_eq!(it.next(), None);
}
#[test]
fn empty_tree_empty_iterator() {
let tree = TreeMap::<[u8; 3], usize>::new();
let it = Range::new(
&tree,
Bound::Excluded([1, 1, 1].as_slice()),
Bound::Excluded([1, 1, 1].as_slice()),
);
assert_eq!(it.count(), 0);
}
#[test]
#[should_panic(expected = "range start and end are equal and excluded: ([1, 1, 1])")]
fn equal_excluded_bounds_should_panic() {
let tree = fixture_tree();
let _it = Range::new(
&tree,
Bound::Excluded([1, 1, 1].as_slice()),
Bound::Excluded([1, 1, 1].as_slice()),
);
}
#[test]
#[should_panic(expected = "range start ([1, 1, 1]) is greater than range end ([0, 0, 0])")]
fn excluded_excluded_start_greater_than_end_should_panic() {
let tree = fixture_tree();
let _it = Range::new(
&tree,
Bound::Excluded([1, 1, 1].as_slice()),
Bound::Excluded([0, 0, 0].as_slice()),
);
}
#[test]
#[should_panic(expected = "range start ([1, 1, 1]) is greater than range end ([0, 0, 0])")]
fn included_included_start_greater_than_end_should_panic() {
let tree = fixture_tree();
let _it = Range::new(
&tree,
Bound::Included([1, 1, 1].as_slice()),
Bound::Included([0, 0, 0].as_slice()),
);
}
#[test]
fn excluded_excluded_empty_range_should_be_empty() {
let tree = fixture_tree();
let it = Range::new(
&tree,
Bound::Excluded([127, 127, 127].as_slice()),
Bound::Excluded([127, 127, 128].as_slice()),
);
assert_eq!(it.count(), 0);
let it = Range::new(
&tree,
Bound::Excluded([127, 127, 126].as_slice()),
Bound::Excluded([127, 127, 127].as_slice()),
);
assert_eq!(it.count(), 0);
}
#[test]
fn included_included_range_on_middle() {
let tree = fixture_tree();
let mut it = Range::new(
&tree,
Bound::Included([126, 0, 0].as_slice()),
Bound::Included([128, 0, 0].as_slice()),
)
.map(|(k, _)| k);
assert_eq!(it.next().unwrap(), &[127, 127, 0]);
assert_eq!(it.next().unwrap(), &[127, 127, 127]);
assert_eq!(it.next().unwrap(), &[127, 127, u8::MAX]);
assert_eq!(it.next(), None);
}
#[test]
fn range_mut_mutate_some_keys() {
let mut tree = fixture_tree();
tree.values_mut().for_each(|value| *value = 0);
tree.range_mut([126, 0, 0]..=[128u8, 0, 0])
.for_each(|(_, value)| *value = 1024);
assert_eq!(
tree.into_iter().collect::<Vec<_>>(),
[
([0, 0, 0], 0),
([0, 0, 255], 0),
([0, 255, 0], 0),
([0, 255, 255], 0),
([127, 127, 0], 1024),
([127, 127, 127], 1024),
([127, 127, 255], 1024),
([255, 0, 0], 0),
([255, 0, 255], 0),
([255, 255, 0], 0),
([255, 255, 255], 0)
]
);
}
#[test]
fn lookup_on_tree_with_implicit_prefix_bytes() {
let mut tree = TreeMap::<_, _, 0>::with_prefix_len();
for (key, value) in generate_key_with_prefix(
[3, 3, 3],
[PrefixExpansion {
base_index: 0,
expanded_length: 4,
}],
)
.enumerate()
.map(swap)
{
let _ = tree.try_insert(key, value).unwrap();
}
assert_eq!(
tree.range(Box::from([0u8, 0, 0, 0, 0, 0])..Box::from([0u8, 0, 0, 0, 0, 4]))
.collect::<Vec<_>>(),
&[
(&[0, 0, 0, 0, 0, 0].into(), &0),
(&[0, 0, 0, 0, 0, 1].into(), &1),
(&[0, 0, 0, 0, 0, 2].into(), &2),
(&[0, 0, 0, 0, 0, 3].into(), &3)
]
);
assert_eq!(
tree.range(Box::from([0u8, 0])..Box::from([0u8, 0, 0, 0, 0, 4]))
.collect::<Vec<_>>(),
&[
(&[0, 0, 0, 0, 0, 0].into(), &0),
(&[0, 0, 0, 0, 0, 1].into(), &1),
(&[0, 0, 0, 0, 0, 2].into(), &2),
(&[0, 0, 0, 0, 0, 3].into(), &3)
]
);
}
}