use super::node::{BTreeNode, LeafNode};
use crate::error::Result;
use crate::storage::{BufferPool, PageId};
use oxicode::Decode;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::sync::Arc;
pub struct BTreeIterator<K, V>
where
K: Ord + Clone + Serialize + for<'de> Deserialize<'de> + Debug,
V: Clone + Serialize + for<'de> Deserialize<'de> + Debug,
{
buffer_pool: Arc<BufferPool>,
current_leaf: Option<PageId>,
current_entries: Vec<(K, V)>,
current_index: usize,
end_key: Option<K>,
}
impl<K, V> BTreeIterator<K, V>
where
K: Ord + Clone + Serialize + for<'de> Deserialize<'de> + Debug,
V: Clone + Serialize + for<'de> Deserialize<'de> + Debug,
{
pub fn empty(buffer_pool: Arc<BufferPool>) -> Self {
BTreeIterator {
buffer_pool,
current_leaf: None,
current_entries: Vec::new(),
current_index: 0,
end_key: None,
}
}
pub fn new(
buffer_pool: Arc<BufferPool>,
leaf_id: PageId,
start_key: Option<K>,
end_key: Option<K>,
) -> Result<Self> {
let mut iterator = BTreeIterator {
buffer_pool,
current_leaf: Some(leaf_id),
current_entries: Vec::new(),
current_index: 0,
end_key,
};
iterator.load_leaf(leaf_id, start_key.as_ref())?;
Ok(iterator)
}
fn load_leaf(&mut self, leaf_id: PageId, start_key: Option<&K>) -> Result<()> {
let guard = self.buffer_pool.fetch_page(leaf_id)?;
let page = guard.page();
let page_ref = page
.as_ref()
.ok_or(crate::error::TdbError::PageNotFound(leaf_id))?;
let node = BTreeNode::<K, V>::deserialize_from_page(page_ref)?;
if let BTreeNode::Leaf(leaf) = node {
self.current_entries = leaf.entries.clone();
if let Some(start) = start_key {
match self.current_entries.binary_search_by(|(k, _)| k.cmp(start)) {
Ok(idx) => self.current_index = idx,
Err(idx) => self.current_index = idx,
}
} else {
self.current_index = 0;
}
self.current_leaf = leaf.next;
} else {
return Err(crate::error::TdbError::Other(
"Expected leaf node in iterator".to_string(),
));
}
Ok(())
}
fn advance(&mut self) -> Result<bool> {
self.current_index += 1;
if self.current_index >= self.current_entries.len() {
if let Some(next_leaf) = self.current_leaf {
self.load_leaf(next_leaf, None)?;
return Ok(!self.current_entries.is_empty());
}
return Ok(false);
}
Ok(true)
}
}
impl<K, V> Iterator for BTreeIterator<K, V>
where
K: Ord + Clone + Serialize + for<'de> Deserialize<'de> + Debug,
V: Clone + Serialize + for<'de> Deserialize<'de> + Debug,
{
type Item = Result<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_index >= self.current_entries.len() {
return None;
}
let entry = &self.current_entries[self.current_index];
if let Some(ref end) = self.end_key {
if &entry.0 >= end {
return None;
}
}
let result = (entry.0.clone(), entry.1.clone());
match self.advance() {
Ok(has_more) => {
if !has_more && self.current_index >= self.current_entries.len() {
}
}
Err(e) => return Some(Err(e)),
}
Some(Ok(result))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::{Allocator, FileManager, PageType, PAGE_SIZE};
use std::path::PathBuf;
use tempfile::TempDir;
#[test]
fn test_iterator_single_leaf() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("test.db");
let file_manager = Arc::new(FileManager::open(&db_path, true)?);
let buffer_pool = Arc::new(BufferPool::new(10, file_manager));
let leaf = LeafNode {
entries: vec![
(1, "one".to_string()),
(2, "two".to_string()),
(3, "three".to_string()),
(4, "four".to_string()),
(5, "five".to_string()),
],
next: None,
};
let guard = buffer_pool.new_page(PageType::BTreeLeaf)?;
let leaf_id = guard.page_id();
{
let mut page = guard.page_mut();
let page = page.as_mut().unwrap();
let node = BTreeNode::Leaf(leaf);
node.serialize_to_page(page)?;
}
drop(guard);
let iter = BTreeIterator::new(buffer_pool.clone(), leaf_id, None, None)?;
let results: Result<Vec<_>> = iter.collect();
let results = results?;
assert_eq!(results.len(), 5);
assert_eq!(results[0], (1, "one".to_string()));
assert_eq!(results[4], (5, "five".to_string()));
Ok(())
}
#[test]
fn test_iterator_with_range() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("test.db");
let file_manager = Arc::new(FileManager::open(&db_path, true)?);
let buffer_pool = Arc::new(BufferPool::new(10, file_manager));
let leaf = LeafNode {
entries: vec![
(1, "one".to_string()),
(2, "two".to_string()),
(3, "three".to_string()),
(4, "four".to_string()),
(5, "five".to_string()),
],
next: None,
};
let guard = buffer_pool.new_page(PageType::BTreeLeaf)?;
let leaf_id = guard.page_id();
{
let mut page = guard.page_mut();
let page = page.as_mut().unwrap();
let node = BTreeNode::Leaf(leaf);
node.serialize_to_page(page)?;
}
drop(guard);
let iter = BTreeIterator::new(buffer_pool.clone(), leaf_id, Some(2), Some(4))?;
let results: Result<Vec<_>> = iter.collect();
let results = results?;
assert_eq!(results.len(), 2);
assert_eq!(results[0], (2, "two".to_string()));
assert_eq!(results[1], (3, "three".to_string()));
Ok(())
}
}