pub mod cursor;
pub mod key;
pub mod node;
pub mod ops;
use std::path::Path;
use uuid::Uuid;
use crate::error::Result;
use crate::page::manager::PageManager;
use crate::page::{PAGE_SIZE, PageHeader, PageType};
use self::key::Key;
use self::node::LeafNode;
#[derive(Debug, Clone, Copy)]
pub struct BTreeMeta<K: Key> {
pub root_page_id: u32,
pub height: u32,
pub num_entries: u64,
pub config: K::Config,
}
pub struct BTree<K: Key> {
meta_page_id: u32,
pub(crate) pm: PageManager,
pub(crate) meta: BTreeMeta<K>,
}
impl<K: Key> BTree<K> {
pub(crate) const META_PAGE_ID: u32 = 1;
pub fn create_with(path: impl AsRef<Path>, cfg: K::Config) -> Result<Self> {
let mut pm = PageManager::new(path)?;
let meta_page_id = pm.allocate_page()?;
let root_page_id = pm.allocate_page()?;
let root: LeafNode<K> = LeafNode::new(root_page_id, cfg);
pm.write_page(root_page_id, &root.to_bytes())?;
let meta = BTreeMeta {
root_page_id,
height: 1,
num_entries: 0,
config: cfg,
};
let mut meta_buf = [0u8; PAGE_SIZE];
let header = PageHeader::new(meta_page_id, PageType::BTreeInternal);
header.write_to(&mut meta_buf);
Self::write_meta_to_buf(&mut meta_buf, &meta);
pm.write_page(meta_page_id, &meta_buf)?;
pm.sync()?;
Ok(Self {
meta_page_id,
pm,
meta,
})
}
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
let mut pm = PageManager::new(path)?;
let meta_buf = pm.read_page(Self::META_PAGE_ID)?;
let meta = Self::read_meta_from_buf(&meta_buf);
Ok(Self {
meta_page_id: Self::META_PAGE_ID,
pm,
meta,
})
}
pub(crate) fn flush_meta(&mut self) -> Result<()> {
let mut buf = [0u8; PAGE_SIZE];
let header = PageHeader::new(self.meta_page_id, PageType::BTreeInternal);
header.write_to(&mut buf);
Self::write_meta_to_buf(&mut buf, &self.meta);
self.pm.write_page(self.meta_page_id, &buf)
}
pub fn sync(&self) -> Result<()> {
self.pm.sync()
}
pub fn len(&self) -> u64 {
self.meta.num_entries
}
pub fn is_empty(&self) -> bool {
self.meta.num_entries == 0
}
pub fn height(&self) -> u32 {
self.meta.height
}
pub fn config(&self) -> K::Config {
self.meta.config
}
fn write_meta_to_buf(buf: &mut [u8; PAGE_SIZE], meta: &BTreeMeta<K>) {
buf[32..36].copy_from_slice(&meta.root_page_id.to_le_bytes());
buf[36..40].copy_from_slice(&meta.height.to_le_bytes());
buf[40..48].copy_from_slice(&meta.num_entries.to_le_bytes());
K::write_tree_config(meta.config, buf);
}
fn read_meta_from_buf(buf: &[u8; PAGE_SIZE]) -> BTreeMeta<K> {
BTreeMeta {
root_page_id: u32::from_le_bytes([buf[32], buf[33], buf[34], buf[35]]),
height: u32::from_le_bytes([buf[36], buf[37], buf[38], buf[39]]),
num_entries: u64::from_le_bytes([
buf[40], buf[41], buf[42], buf[43], buf[44], buf[45], buf[46], buf[47],
]),
config: K::read_tree_config(buf),
}
}
}
impl BTree<Uuid> {
pub fn create(path: impl AsRef<Path>) -> Result<Self> {
Self::create_with(path, ())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_uuid_btree_create_and_open() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("index.db");
{
let btree = BTree::<Uuid>::create(&path).unwrap();
assert_eq!(btree.len(), 0);
assert!(btree.is_empty());
assert_eq!(btree.height(), 1);
assert_eq!(btree.meta.root_page_id, 2);
}
{
let btree = BTree::<Uuid>::open(&path).unwrap();
assert_eq!(btree.len(), 0);
assert_eq!(btree.height(), 1);
assert_eq!(btree.meta.root_page_id, 2);
}
}
#[test]
fn test_uuid_btree_meta_persistence() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("index.db");
{
let mut btree = BTree::<Uuid>::create(&path).unwrap();
btree.meta.num_entries = 42;
btree.meta.height = 3;
btree.meta.root_page_id = 7;
btree.flush_meta().unwrap();
btree.sync().unwrap();
}
{
let btree = BTree::<Uuid>::open(&path).unwrap();
assert_eq!(btree.len(), 42);
assert_eq!(btree.height(), 3);
assert_eq!(btree.meta.root_page_id, 7);
}
}
#[test]
fn test_uuid_btree_initial_root_is_empty_leaf() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("index.db");
let mut btree = BTree::<Uuid>::create(&path).unwrap();
let buf = btree.pm.read_page(2).unwrap();
let leaf: LeafNode<Uuid> = LeafNode::from_bytes(&buf);
assert_eq!(leaf.num_entries, 0);
assert_eq!(leaf.next_leaf, 0);
assert_eq!(leaf.prev_leaf, 0);
}
#[test]
fn test_vec_btree_create_and_open() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("var_index.db");
{
let tree = BTree::<Vec<u8>>::create_with(&path, 64).unwrap();
assert_eq!(tree.len(), 0);
assert!(tree.is_empty());
assert_eq!(tree.height(), 1);
assert_eq!(tree.config(), 64);
}
{
let tree = BTree::<Vec<u8>>::open(&path).unwrap();
assert_eq!(tree.len(), 0);
assert_eq!(tree.height(), 1);
assert_eq!(tree.config(), 64);
}
}
}