pub mod cursor;
pub mod key;
pub mod node;
pub mod ops;
pub mod var_cursor;
pub mod var_node;
pub mod var_ops;
pub mod var_tree;
use std::path::Path;
use crate::error::Result;
use crate::page::manager::PageManager;
use crate::page::{PAGE_SIZE, PageHeader, PageType};
use self::node::LeafNode;
#[derive(Debug, Clone, Copy)]
pub struct BTreeMeta {
pub root_page_id: u32,
pub height: u32,
pub num_entries: u64,
}
pub struct BTree {
meta_page_id: u32,
pub(crate) pm: PageManager,
pub(crate) meta: BTreeMeta,
}
impl BTree {
const META_PAGE_ID: u32 = 1;
pub fn create(path: impl AsRef<Path>) -> 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::new(root_page_id);
pm.write_page(root_page_id, &root.to_bytes())?;
let meta = BTreeMeta {
root_page_id,
height: 1,
num_entries: 0,
};
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
}
fn write_meta_to_buf(buf: &mut [u8; PAGE_SIZE], meta: &BTreeMeta) {
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());
}
fn read_meta_from_buf(buf: &[u8; PAGE_SIZE]) -> BTreeMeta {
BTreeMeta {
root_page_id: u32::from_le_bytes(buf[32..36].try_into().unwrap()),
height: u32::from_le_bytes(buf[36..40].try_into().unwrap()),
num_entries: u64::from_le_bytes(buf[40..48].try_into().unwrap()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_btree_create_and_open() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("index.db");
{
let btree = BTree::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::open(&path).unwrap();
assert_eq!(btree.len(), 0);
assert_eq!(btree.height(), 1);
assert_eq!(btree.meta.root_page_id, 2);
}
}
#[test]
fn test_btree_meta_persistence() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("index.db");
{
let mut btree = BTree::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::open(&path).unwrap();
assert_eq!(btree.len(), 42);
assert_eq!(btree.height(), 3);
assert_eq!(btree.meta.root_page_id, 7);
}
}
#[test]
fn test_btree_initial_root_is_empty_leaf() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("index.db");
let mut btree = BTree::create(&path).unwrap();
let buf = btree.pm.read_page(2).unwrap();
let leaf = LeafNode::from_bytes(&buf);
assert_eq!(leaf.num_entries, 0);
assert_eq!(leaf.next_leaf, 0);
assert_eq!(leaf.prev_leaf, 0);
}
}