use std::cmp::Ordering;
use std::sync::Arc;
use super::cid::Cid;
use super::error::Diff;
use super::error::Error;
use super::node::Node;
use super::store::Store;
use super::tree::Tree;
pub struct CursorIterator<'a, S: Store> {
cursor: Cursor,
store: &'a S,
start_key: Option<Vec<u8>>,
end_key: Option<Vec<u8>>,
started: bool,
}
impl<'a, S: Store> CursorIterator<'a, S> {
pub fn new(cursor: Cursor, store: &'a S, end_key: Option<Vec<u8>>) -> Self {
Self {
cursor,
store,
start_key: None,
end_key,
started: false,
}
}
pub fn with_bounds(
cursor: Cursor,
store: &'a S,
start_key: Option<Vec<u8>>,
end_key: Option<Vec<u8>>,
) -> Self {
Self {
cursor,
store,
start_key,
end_key,
started: false,
}
}
fn is_at_or_after_start(&self) -> bool {
match (&self.start_key, self.cursor.get_key()) {
(Some(start), Some(current)) => current >= start.as_slice(),
(None, Some(_)) => true,
_ => false,
}
}
fn is_before_end(&self) -> bool {
match (&self.end_key, self.cursor.get_key()) {
(Some(end), Some(current)) => current < end.as_slice(),
(None, Some(_)) => true,
_ => false,
}
}
}
impl<'a, S: Store> Iterator for CursorIterator<'a, S> {
type Item = (Vec<u8>, Vec<u8>);
fn next(&mut self) -> Option<Self::Item> {
if !self.cursor.is_valid() {
return None;
}
if self.started {
if !self
.cursor
.advance_before(self.store, self.end_key.as_deref())
{
return None;
}
} else {
self.started = true;
while self.cursor.is_valid() && !self.is_at_or_after_start() {
if !self
.cursor
.advance_before(self.store, self.end_key.as_deref())
{
return None;
}
}
}
if !self.cursor.is_valid() {
return None;
}
if !self.is_before_end() {
return None;
}
let key = self.cursor.get_key()?.to_vec();
let value = self.cursor.get_value()?.to_vec();
Some((key, value))
}
}
pub struct DiffCursor<'a, S: Store> {
cursor_base: Cursor,
cursor_other: Cursor,
store: &'a S,
done: bool,
}
impl<'a, S: Store> DiffCursor<'a, S> {
pub fn new(store: &'a S, base: &Tree, other: &Tree) -> Result<Self, Error> {
if base.root == other.root {
let invalid = Cursor::invalid();
return Ok(DiffCursor {
cursor_base: invalid.clone(),
cursor_other: invalid,
store,
done: true,
});
}
let cursor_base = Cursor::at_item(store, base, &[])?;
let cursor_other = Cursor::at_item(store, other, &[])?;
Ok(DiffCursor {
cursor_base,
cursor_other,
store,
done: false,
})
}
}
impl<'a, S: Store> Iterator for DiffCursor<'a, S> {
type Item = Diff;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
loop {
let base_valid = self.cursor_base.is_valid();
let other_valid = self.cursor_other.is_valid();
match (base_valid, other_valid) {
(false, false) => {
self.done = true;
return None;
}
(false, true) => {
let key = self.cursor_other.get_key()?.to_vec();
let val = self.cursor_other.get_value()?.to_vec();
self.cursor_other.advance(self.store);
return Some(Diff::Added { key, val });
}
(true, false) => {
let key = self.cursor_base.get_key()?.to_vec();
let val = self.cursor_base.get_value()?.to_vec();
self.cursor_base.advance(self.store);
return Some(Diff::Removed { key, val });
}
(true, true) => {
let base_key = self.cursor_base.get_key()?.to_vec();
let other_key = self.cursor_other.get_key()?.to_vec();
match base_key.cmp(&other_key) {
Ordering::Less => {
let val = self.cursor_base.get_value()?.to_vec();
self.cursor_base.advance(self.store);
return Some(Diff::Removed { key: base_key, val });
}
Ordering::Greater => {
let val = self.cursor_other.get_value()?.to_vec();
self.cursor_other.advance(self.store);
return Some(Diff::Added {
key: other_key,
val,
});
}
Ordering::Equal => {
let base_val = self.cursor_base.get_value()?.to_vec();
let other_val = self.cursor_other.get_value()?.to_vec();
self.cursor_base.advance(self.store);
self.cursor_other.advance(self.store);
if base_val != other_val {
return Some(Diff::Changed {
key: base_key,
old: base_val,
new: other_val,
});
}
continue;
}
}
}
}
}
}
}
#[derive(Clone)]
pub struct Cursor {
pub index: usize,
pub node: Arc<Node>,
pub parent: Option<Box<Cursor>>,
}
impl Cursor {
pub(crate) fn invalid() -> Self {
Self {
index: 0,
node: Arc::new(Node::new_leaf()),
parent: None,
}
}
pub fn at_item<S: Store>(store: &S, tree: &Tree, key: &[u8]) -> Result<Self, Error> {
let Some(root_cid) = &tree.root else {
return Ok(Self::invalid());
};
let root_node = Self::load_node(store, root_cid)?;
Self::navigate_to_key(store, root_node, key, None)
}
fn navigate_to_key<S: Store>(
store: &S,
node: Node,
key: &[u8],
parent: Option<Box<Cursor>>,
) -> Result<Self, Error> {
let index = Self::key_index(&node.keys, key);
if node.leaf {
Ok(Self {
index,
node: Arc::new(node),
parent,
})
} else {
let child_cid = child_cid_at(&node, index)?;
let current_cursor = Self {
index,
node: Arc::new(node),
parent,
};
let child_node = Self::load_node(store, &child_cid)?;
Self::navigate_to_key(store, child_node, key, Some(Box::new(current_cursor)))
}
}
fn load_node<S: Store>(store: &S, cid: &Cid) -> Result<Node, Error> {
let bytes = store
.get(cid.as_bytes())
.map_err(|e| Error::Store(Box::new(e)))?
.ok_or_else(|| Error::NotFound(cid.clone()))?;
Node::from_bytes(&bytes)
}
pub fn get_key(&self) -> Option<&[u8]> {
if self.is_valid() {
Some(&self.node.keys[self.index])
} else {
None
}
}
pub fn get_value(&self) -> Option<&[u8]> {
if self.is_valid() && self.node.leaf {
self.node.vals.get(self.index).map(|value| value.as_slice())
} else {
None
}
}
pub fn is_valid(&self) -> bool {
!self.node.is_empty() && self.index < self.node.len()
}
pub fn is_at_end(&self) -> bool {
self.node.is_empty() || self.index >= self.node.len() - 1
}
pub fn advance<S: Store>(&mut self, store: &S) -> bool {
self.advance_before(store, None)
}
fn advance_before<S: Store>(&mut self, store: &S, end: Option<&[u8]>) -> bool {
if !self.is_valid() {
return false;
}
if self.index + 1 < self.node.len() {
if let Some(end) = end {
if self.node.keys[self.index + 1].as_slice() >= end {
self.index = self.node.len();
return false;
}
}
self.index += 1;
return true;
}
self.advance_via_parent_before(store, end)
}
fn advance_via_parent_before<S: Store>(&mut self, store: &S, end: Option<&[u8]>) -> bool {
let Some(mut parent) = self.parent.take() else {
self.index = self.node.len(); return false;
};
if parent.index + 1 < parent.node.len() {
let next_index = parent.index + 1;
if child_starts_at_or_after_end(end, &parent.node, next_index) {
self.index = self.node.len();
return false;
}
parent.index = next_index;
match self.descend_to_leftmost_leaf(store, &parent) {
Ok(new_cursor) => {
*self = new_cursor;
true
}
Err(_) => {
self.index = self.node.len();
false
}
}
} else {
let mut temp_cursor = *parent;
if temp_cursor.advance_via_parent_before(store, end) {
*self = temp_cursor;
true
} else {
self.index = self.node.len();
false
}
}
}
fn descend_to_leftmost_leaf<S: Store>(
&self,
store: &S,
parent: &Cursor,
) -> Result<Cursor, Error> {
let child_cid = child_cid_at(&parent.node, parent.index)?;
let child_node = Self::load_node(store, &child_cid)?;
if child_node.leaf {
Ok(Cursor {
index: 0,
node: Arc::new(child_node),
parent: Some(Box::new(parent.clone())),
})
} else {
let child_cursor = Cursor {
index: 0,
node: Arc::new(child_node),
parent: Some(Box::new(parent.clone())),
};
child_cursor.descend_to_leftmost_leaf_from_self(store)
}
}
fn descend_to_leftmost_leaf_from_self<S: Store>(self, store: &S) -> Result<Cursor, Error> {
if self.node.leaf {
return Ok(self);
}
let child_cid = child_cid_at(&self.node, self.index)?;
let child_node = Self::load_node(store, &child_cid)?;
let child_cursor = Cursor {
index: 0,
node: Arc::new(child_node),
parent: Some(Box::new(self)),
};
child_cursor.descend_to_leftmost_leaf_from_self(store)
}
fn key_index(keys: &[Vec<u8>], key: &[u8]) -> usize {
if keys.is_empty() {
return 0;
}
match keys.binary_search_by(|k| k.as_slice().cmp(key)) {
Ok(i) => i,
Err(i) => {
if i == 0 {
0
} else {
i - 1
}
}
}
}
}
fn child_cid_at(node: &Node, index: usize) -> Result<Cid, Error> {
let child_cid_bytes = node.vals.get(index).ok_or(Error::InvalidNode)?;
Ok(Cid(child_cid_bytes
.as_slice()
.try_into()
.map_err(|_| Error::InvalidNode)?))
}
fn child_starts_at_or_after_end(end: Option<&[u8]>, node: &Node, child_index: usize) -> bool {
let Some(end) = end else {
return false;
};
match node.keys.get(child_index) {
Some(first_key) => first_key.as_slice() >= end,
None => true,
}
}
#[cfg(test)]
mod tests {
use super::super::config::Config;
use super::super::store::{BatchOp, MemStore};
use super::super::Prolly;
use super::*;
use std::collections::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
#[derive(Debug)]
struct CountingStoreError;
impl std::fmt::Display for CountingStoreError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("counting store error")
}
}
impl std::error::Error for CountingStoreError {}
#[derive(Default)]
struct CountingStore {
data: Mutex<BTreeMap<Vec<u8>, Vec<u8>>>,
get_calls: AtomicUsize,
}
impl Store for CountingStore {
type Error = CountingStoreError;
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
self.get_calls.fetch_add(1, Ordering::Relaxed);
Ok(self.data.lock().unwrap().get(key).cloned())
}
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Self::Error> {
self.data
.lock()
.unwrap()
.insert(key.to_vec(), value.to_vec());
Ok(())
}
fn delete(&self, key: &[u8]) -> Result<(), Self::Error> {
self.data.lock().unwrap().remove(key);
Ok(())
}
fn batch(&self, ops: &[BatchOp]) -> Result<(), Self::Error> {
let mut data = self.data.lock().unwrap();
for op in ops {
match op {
BatchOp::Upsert { key, value } => {
data.insert(key.to_vec(), value.to_vec());
}
BatchOp::Delete { key } => {
data.remove(*key);
}
}
}
Ok(())
}
}
#[test]
fn test_key_index_empty_keys() {
let keys: Vec<Vec<u8>> = vec![];
assert_eq!(Cursor::key_index(&keys, b"any"), 0);
}
#[test]
fn test_key_index_exact_match() {
let keys = vec![b"a".to_vec(), b"c".to_vec(), b"e".to_vec()];
assert_eq!(Cursor::key_index(&keys, b"a"), 0);
assert_eq!(Cursor::key_index(&keys, b"c"), 1);
assert_eq!(Cursor::key_index(&keys, b"e"), 2);
}
#[test]
fn test_key_index_between_keys() {
let keys = vec![b"a".to_vec(), b"c".to_vec(), b"e".to_vec()];
assert_eq!(Cursor::key_index(&keys, b"b"), 0);
assert_eq!(Cursor::key_index(&keys, b"d"), 1);
}
#[test]
fn test_key_index_smaller_than_all() {
let keys = vec![b"b".to_vec(), b"c".to_vec(), b"d".to_vec()];
assert_eq!(Cursor::key_index(&keys, b"a"), 0);
}
#[test]
fn test_key_index_larger_than_all() {
let keys = vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()];
assert_eq!(Cursor::key_index(&keys, b"z"), 2);
}
#[test]
fn test_key_index_single_key() {
let keys = vec![b"m".to_vec()];
assert_eq!(Cursor::key_index(&keys, b"a"), 0); assert_eq!(Cursor::key_index(&keys, b"m"), 0); assert_eq!(Cursor::key_index(&keys, b"z"), 0); }
#[test]
fn test_at_item_empty_tree() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let tree = prolly.create();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"any").unwrap();
assert!(!cursor.is_valid());
assert_eq!(cursor.get_key(), None);
assert_eq!(cursor.get_value(), None);
}
#[test]
fn diff_cursor_skips_identical_roots_without_reads() {
let store = std::sync::Arc::new(CountingStore::default());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
let gets_before = store.get_calls.load(Ordering::Relaxed);
let mut diff = DiffCursor::new(store.as_ref(), &tree, &tree).unwrap();
assert_eq!(diff.next(), None);
assert_eq!(
store.get_calls.load(Ordering::Relaxed),
gets_before,
"identical-root cursor diffs should avoid root and leaf reads"
);
}
#[test]
fn get_value_returns_none_for_leaf_with_missing_value() {
let mut leaf = Node::new_leaf();
leaf.keys.push(b"a".to_vec());
let cursor = Cursor {
index: 0,
node: Arc::new(leaf),
parent: None,
};
assert!(cursor.is_valid());
assert_eq!(cursor.get_key(), Some(b"a".as_slice()));
assert_eq!(cursor.get_value(), None);
}
#[test]
fn internal_descent_rejects_missing_child_cid() {
let store = std::sync::Arc::new(MemStore::new());
let mut root = Node::new_internal(1);
root.keys.push(b"a".to_vec());
let cursor = Cursor {
index: 0,
node: Arc::new(root),
parent: None,
};
let err = match cursor.descend_to_leftmost_leaf_from_self(store.as_ref()) {
Ok(_) => panic!("malformed internal node should be rejected"),
Err(err) => err,
};
assert!(matches!(err, Error::InvalidNode));
}
#[test]
fn test_at_item_exact_key() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"b").unwrap();
assert!(cursor.is_valid());
assert_eq!(cursor.get_key(), Some(b"b".as_slice()));
assert_eq!(cursor.get_value(), Some(b"2".as_slice()));
}
#[test]
fn test_at_item_nonexistent_key() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
tree = prolly.put(&tree, b"e".to_vec(), b"5".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"b").unwrap();
assert!(cursor.is_valid());
assert_eq!(cursor.get_key(), Some(b"a".as_slice()));
let cursor = Cursor::at_item(store.as_ref(), &tree, b"d").unwrap();
assert!(cursor.is_valid());
assert_eq!(cursor.get_key(), Some(b"c".as_slice()));
}
#[test]
fn test_at_item_key_smaller_than_all() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"a").unwrap();
assert!(cursor.is_valid());
assert_eq!(cursor.get_key(), Some(b"b".as_slice()));
}
#[test]
fn test_at_item_key_larger_than_all() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"z").unwrap();
assert!(cursor.is_valid());
assert_eq!(cursor.get_key(), Some(b"b".as_slice()));
}
#[test]
fn test_cursor_is_at_end() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"a").unwrap();
assert!(!cursor.is_at_end());
let cursor = Cursor::at_item(store.as_ref(), &tree, b"b").unwrap();
assert!(cursor.is_at_end());
}
#[test]
fn test_cursor_clone_independence() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
let cursor1 = Cursor::at_item(store.as_ref(), &tree, b"a").unwrap();
let cursor2 = cursor1.clone();
assert_eq!(cursor1.get_key(), cursor2.get_key());
assert_eq!(cursor1.get_value(), cursor2.get_value());
}
#[test]
fn test_advance_within_node() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"a").unwrap();
assert_eq!(cursor.get_key(), Some(b"a".as_slice()));
assert!(cursor.advance(store.as_ref()));
assert_eq!(cursor.get_key(), Some(b"b".as_slice()));
assert_eq!(cursor.get_value(), Some(b"2".as_slice()));
assert!(cursor.advance(store.as_ref()));
assert_eq!(cursor.get_key(), Some(b"c".as_slice()));
assert_eq!(cursor.get_value(), Some(b"3".as_slice()));
}
#[test]
fn test_advance_at_end_of_tree() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"b").unwrap();
assert_eq!(cursor.get_key(), Some(b"b".as_slice()));
assert!(!cursor.advance(store.as_ref()));
assert!(!cursor.is_valid());
}
#[test]
fn test_advance_full_iteration() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
let entries = vec![
(b"a".to_vec(), b"1".to_vec()),
(b"b".to_vec(), b"2".to_vec()),
(b"c".to_vec(), b"3".to_vec()),
(b"d".to_vec(), b"4".to_vec()),
];
for (k, v) in &entries {
tree = prolly.put(&tree, k.clone(), v.clone()).unwrap();
}
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"a").unwrap();
let mut collected: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
while cursor.is_valid() {
let key = cursor.get_key().unwrap().to_vec();
let val = cursor.get_value().unwrap().to_vec();
collected.push((key, val));
if !cursor.advance(store.as_ref()) {
break;
}
}
assert_eq!(collected, entries);
}
#[test]
fn test_advance_single_entry() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly
.put(&tree, b"only".to_vec(), b"one".to_vec())
.unwrap();
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"only").unwrap();
assert!(cursor.is_valid());
assert_eq!(cursor.get_key(), Some(b"only".as_slice()));
assert!(!cursor.advance(store.as_ref()));
assert!(!cursor.is_valid());
}
#[test]
fn test_advance_invalid_cursor() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let tree = prolly.create();
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"any").unwrap();
assert!(!cursor.is_valid());
assert!(!cursor.advance(store.as_ref()));
}
#[test]
fn test_cursor_iterator_full_iteration() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
let entries = vec![
(b"a".to_vec(), b"1".to_vec()),
(b"b".to_vec(), b"2".to_vec()),
(b"c".to_vec(), b"3".to_vec()),
(b"d".to_vec(), b"4".to_vec()),
];
for (k, v) in &entries {
tree = prolly.put(&tree, k.clone(), v.clone()).unwrap();
}
let cursor = Cursor::at_item(store.as_ref(), &tree, b"").unwrap();
let iter = CursorIterator::new(cursor, store.as_ref(), None);
let collected: Vec<(Vec<u8>, Vec<u8>)> = iter.collect();
assert_eq!(collected, entries);
}
#[test]
fn test_cursor_iterator_with_end_bound() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
tree = prolly.put(&tree, b"d".to_vec(), b"4".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"a").unwrap();
let iter = CursorIterator::new(cursor, store.as_ref(), Some(b"c".to_vec()));
let collected: Vec<(Vec<u8>, Vec<u8>)> = iter.collect();
let expected = vec![
(b"a".to_vec(), b"1".to_vec()),
(b"b".to_vec(), b"2".to_vec()),
];
assert_eq!(collected, expected);
}
#[test]
fn test_cursor_iterator_empty_tree() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let tree = prolly.create();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"any").unwrap();
let iter = CursorIterator::new(cursor, store.as_ref(), None);
let collected: Vec<(Vec<u8>, Vec<u8>)> = iter.collect();
assert!(collected.is_empty());
}
#[test]
fn test_cursor_iterator_single_entry() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly
.put(&tree, b"only".to_vec(), b"one".to_vec())
.unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"").unwrap();
let iter = CursorIterator::new(cursor, store.as_ref(), None);
let collected: Vec<(Vec<u8>, Vec<u8>)> = iter.collect();
assert_eq!(collected, vec![(b"only".to_vec(), b"one".to_vec())]);
}
#[test]
fn test_cursor_iterator_end_bound_at_start() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"").unwrap();
let iter = CursorIterator::new(cursor, store.as_ref(), Some(b"a".to_vec()));
let collected: Vec<(Vec<u8>, Vec<u8>)> = iter.collect();
assert!(collected.is_empty());
}
#[test]
fn test_cursor_iterator_start_from_middle() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
tree = prolly.put(&tree, b"d".to_vec(), b"4".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"b").unwrap();
let iter = CursorIterator::new(cursor, store.as_ref(), None);
let collected: Vec<(Vec<u8>, Vec<u8>)> = iter.collect();
let expected = vec![
(b"b".to_vec(), b"2".to_vec()),
(b"c".to_vec(), b"3".to_vec()),
(b"d".to_vec(), b"4".to_vec()),
];
assert_eq!(collected, expected);
}
#[test]
fn test_cursor_iterator_range_middle() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
tree = prolly.put(&tree, b"b".to_vec(), b"2".to_vec()).unwrap();
tree = prolly.put(&tree, b"c".to_vec(), b"3".to_vec()).unwrap();
tree = prolly.put(&tree, b"d".to_vec(), b"4".to_vec()).unwrap();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"b").unwrap();
let iter = CursorIterator::new(cursor, store.as_ref(), Some(b"d".to_vec()));
let collected: Vec<(Vec<u8>, Vec<u8>)> = iter.collect();
let expected = vec![
(b"b".to_vec(), b"2".to_vec()),
(b"c".to_vec(), b"3".to_vec()),
];
assert_eq!(collected, expected);
}
#[test]
fn test_empty_tree_cursor_is_invalid() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let tree = prolly.create();
let cursor = Cursor::at_item(store.as_ref(), &tree, b"any_key").unwrap();
assert!(!cursor.is_valid(), "Cursor on empty tree should be invalid");
assert_eq!(
cursor.get_key(),
None,
"get_key should return None for empty tree cursor"
);
assert_eq!(
cursor.get_value(),
None,
"get_value should return None for empty tree cursor"
);
assert!(
cursor.is_at_end(),
"is_at_end should return true for empty tree cursor"
);
}
#[test]
fn test_single_entry_tree_navigation() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly
.put(&tree, b"only_key".to_vec(), b"only_value".to_vec())
.unwrap();
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"only_key").unwrap();
assert!(
cursor.is_valid(),
"Cursor should be valid for single-entry tree"
);
assert_eq!(
cursor.get_key(),
Some(b"only_key".as_slice()),
"Cursor should be at the only key"
);
assert_eq!(
cursor.get_value(),
Some(b"only_value".as_slice()),
"Cursor should return the only value"
);
assert!(
cursor.is_at_end(),
"Cursor should be at end for single-entry tree"
);
assert!(
!cursor.advance(store.as_ref()),
"advance should return false for single-entry tree"
);
assert!(
!cursor.is_valid(),
"Cursor should be invalid after advancing past end"
);
assert_eq!(
cursor.get_key(),
None,
"get_key should return None after advancing past end"
);
assert_eq!(
cursor.get_value(),
None,
"get_value should return None after advancing past end"
);
}
#[test]
fn test_cursor_at_internal_node_get_value_returns_none() {
use super::super::node::Node;
let internal_node = Node::builder()
.keys(vec![b"key1".to_vec(), b"key2".to_vec()])
.vals(vec![vec![0u8; 32], vec![1u8; 32]]) .leaf(false) .level(1)
.build();
let cursor = Cursor {
index: 0,
node: Arc::new(internal_node),
parent: None,
};
assert!(cursor.is_valid(), "Cursor at internal node should be valid");
assert_eq!(
cursor.get_key(),
Some(b"key1".as_slice()),
"get_key should return key for internal node"
);
assert_eq!(
cursor.get_value(),
None,
"get_value should return None for internal node"
);
let cursor_at_second = Cursor {
index: 1,
node: cursor.node.clone(),
parent: None,
};
assert!(
cursor_at_second.is_valid(),
"Cursor at second index should be valid"
);
assert_eq!(
cursor_at_second.get_key(),
Some(b"key2".as_slice()),
"get_key should return second key"
);
assert_eq!(
cursor_at_second.get_value(),
None,
"get_value should still return None for internal node"
);
}
#[test]
fn test_invalid_cursor_accessors_return_none() {
use super::super::node::Node;
let empty_node = Node::new_leaf();
let cursor_empty = Cursor {
index: 0,
node: Arc::new(empty_node),
parent: None,
};
assert!(
!cursor_empty.is_valid(),
"Cursor with empty node should be invalid"
);
assert_eq!(
cursor_empty.get_key(),
None,
"get_key should return None for cursor with empty node"
);
assert_eq!(
cursor_empty.get_value(),
None,
"get_value should return None for cursor with empty node"
);
let node_with_data = Node::builder()
.keys(vec![b"a".to_vec(), b"b".to_vec()])
.vals(vec![b"1".to_vec(), b"2".to_vec()])
.leaf(true)
.build();
let cursor_out_of_bounds = Cursor {
index: 5, node: Arc::new(node_with_data.clone()),
parent: None,
};
assert!(
!cursor_out_of_bounds.is_valid(),
"Cursor with out-of-bounds index should be invalid"
);
assert_eq!(
cursor_out_of_bounds.get_key(),
None,
"get_key should return None for out-of-bounds cursor"
);
assert_eq!(
cursor_out_of_bounds.get_value(),
None,
"get_value should return None for out-of-bounds cursor"
);
let cursor_at_boundary = Cursor {
index: 2, node: Arc::new(node_with_data),
parent: None,
};
assert!(
!cursor_at_boundary.is_valid(),
"Cursor at boundary should be invalid"
);
assert_eq!(
cursor_at_boundary.get_key(),
None,
"get_key should return None for cursor at boundary"
);
assert_eq!(
cursor_at_boundary.get_value(),
None,
"get_value should return None for cursor at boundary"
);
}
#[test]
fn test_advance_on_invalid_cursor_returns_false() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let tree = prolly.create();
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"any").unwrap();
assert!(
!cursor.is_valid(),
"Cursor should be invalid for empty tree"
);
assert!(
!cursor.advance(store.as_ref()),
"advance on invalid cursor should return false"
);
assert!(
!cursor.is_valid(),
"Cursor should remain invalid after advance"
);
}
#[test]
fn test_multiple_advances_past_end() {
let store = std::sync::Arc::new(MemStore::new());
let prolly = Prolly::new(store.clone(), Config::default());
let mut tree = prolly.create();
tree = prolly.put(&tree, b"a".to_vec(), b"1".to_vec()).unwrap();
let mut cursor = Cursor::at_item(store.as_ref(), &tree, b"a").unwrap();
assert!(cursor.is_valid());
assert!(
!cursor.advance(store.as_ref()),
"First advance past end should return false"
);
assert!(
!cursor.is_valid(),
"Cursor should be invalid after first advance past end"
);
assert!(
!cursor.advance(store.as_ref()),
"Second advance should also return false"
);
assert!(!cursor.is_valid(), "Cursor should remain invalid");
assert!(
!cursor.advance(store.as_ref()),
"Third advance should also return false"
);
assert!(!cursor.is_valid(), "Cursor should still be invalid");
assert_eq!(cursor.get_key(), None);
assert_eq!(cursor.get_value(), None);
}
}