use std::sync::Arc;
use bytes::Bytes;
use crate::Result;
use crate::errors::PagedbError;
use crate::vfs::Vfs;
use crate::btree::leaf::{Leaf, LeafValue};
use crate::btree::node;
use crate::btree::overflow;
use crate::btree::tree::core::BTree;
use super::levels::{Child, LevelAccum};
fn leaf_record_cost(key_len: usize, value: &LeafValue) -> Result<usize> {
2usize
.checked_add(2)
.and_then(|size| size.checked_add(key_len))
.and_then(|size| size.checked_add(value.encoded_size()))
.ok_or(PagedbError::PayloadTooLarge)
}
pub struct BulkLoader<'a, V: Vfs> {
tree: &'a mut BTree<V>,
body_cap: usize,
inline_threshold: usize,
last_key: Option<Vec<u8>>,
leaf_records: Vec<(Vec<u8>, LeafValue)>,
leaf_used: usize,
leaf_page_id: Option<u64>,
left_sibling: u64,
leaf_count: u64,
first_leaf_page_id: u64,
levels: Vec<LevelAccum>,
}
impl<'a, V: Vfs> BulkLoader<'a, V> {
pub(crate) fn new(tree: &'a mut BTree<V>) -> Self {
let body_cap = node::body_capacity(tree.page_size);
let inline_threshold = overflow::inline_value_threshold(tree.page_size);
Self {
tree,
body_cap,
inline_threshold,
last_key: None,
leaf_records: Vec::new(),
leaf_used: node::HEADER_LEN,
leaf_page_id: None,
left_sibling: 0,
leaf_count: 0,
first_leaf_page_id: 0,
levels: Vec::new(),
}
}
pub async fn push(&mut self, key: Vec<u8>, value: Bytes) -> Result<()> {
if let Some(last) = &self.last_key {
if key <= *last {
return Err(PagedbError::BulkLoadNotMonotonic);
}
}
self.tree.validate_insert_record_fits(&key, &value)?;
let stored = if value.len() > self.inline_threshold {
let total_len = value.len() as u64;
let pager = Arc::clone(&self.tree.pager);
let realm_id = self.tree.realm_id;
let page_size = self.tree.page_size;
let tree = &mut *self.tree;
let root_page_id =
overflow::write_chain(&pager, realm_id, &value, page_size, &mut || {
tree.allocate_page()
})
.await?;
LeafValue::Overflow {
total_len,
root_page_id,
}
} else {
LeafValue::Inline(value)
};
let cost = leaf_record_cost(key.len(), &stored)?;
let projected = self
.leaf_used
.checked_add(cost)
.ok_or(PagedbError::PayloadTooLarge)?;
if projected > self.body_cap && !self.leaf_records.is_empty() {
self.close_leaf(true).await?;
}
self.leaf_used = self
.leaf_used
.checked_add(cost)
.ok_or(PagedbError::PayloadTooLarge)?;
match &mut self.last_key {
Some(buffered) => {
buffered.clear();
buffered.extend_from_slice(&key);
}
None => self.last_key = Some(key.clone()),
}
self.leaf_records.push((key, stored));
Ok(())
}
pub async fn finish(mut self) -> Result<()> {
if !self.leaf_records.is_empty() {
self.close_leaf(false).await?;
}
if self.leaf_count == 0 {
return Ok(());
}
if self.leaf_count == 1 {
self.tree.root_page_id = self.first_leaf_page_id;
return Ok(());
}
let mut level = 0usize;
while level < self.levels.len() {
if let Some(page_id) = self.levels[level].sole_child() {
self.tree.root_page_id = page_id;
return Ok(());
}
let Some((node, separator)) = self.levels[level].take_node() else {
break;
};
let page_id = self.tree.allocate_page();
self.tree.write_internal(page_id, &node).await?;
self.push_child(level + 1, (page_id, separator)).await?;
level += 1;
}
Err(PagedbError::Io(std::io::Error::other(
"bulk load: internal levels ended without a root",
)))
}
async fn close_leaf(&mut self, more_records: bool) -> Result<()> {
let page_id = match self.leaf_page_id.take() {
Some(reserved) => reserved,
None => self.tree.allocate_page(),
};
let right_sibling = if more_records {
let next = self.tree.allocate_page();
self.leaf_page_id = Some(next);
next
} else {
0
};
let records = std::mem::take(&mut self.leaf_records);
let first_key = records
.first()
.map(|(key, _)| key.clone())
.unwrap_or_default();
let leaf = Leaf {
left_sibling: self.left_sibling,
right_sibling,
records,
};
self.tree.write_leaf(page_id, &leaf).await?;
self.left_sibling = page_id;
self.leaf_used = node::HEADER_LEN;
if self.leaf_count == 0 {
self.first_leaf_page_id = page_id;
}
self.leaf_count += 1;
self.push_child(0, (page_id, first_key)).await
}
async fn push_child(&mut self, mut level: usize, mut child: Child) -> Result<()> {
let body_cap = self.body_cap;
loop {
while self.levels.len() <= level {
self.levels.push(LevelAccum::new());
}
let (node, separator) = {
let accum = &mut self.levels[level];
if accum.try_push(&child, body_cap)? {
return Ok(());
}
let Some(full) = accum.take_node() else {
return Err(PagedbError::Io(std::io::Error::other(
"bulk load: a full internal level held no node",
)));
};
accum.restart(child);
full
};
let page_id = self.tree.allocate_page();
self.tree.write_internal(page_id, &node).await?;
child = (page_id, separator);
level += 1;
}
}
}
impl<V: Vfs> BTree<V> {
pub fn bulk_loader(&mut self) -> Result<BulkLoader<'_, V>> {
if self.root_page_id != 0 {
return Err(PagedbError::Io(std::io::Error::other(
"bulk_load: tree must be empty",
)));
}
Ok(BulkLoader::new(self))
}
#[cfg(any(test, not(target_arch = "wasm32")))]
pub async fn bulk_load(&mut self, pairs: Vec<(Vec<u8>, Bytes)>) -> Result<()> {
let mut loader = self.bulk_loader()?;
for (key, value) in pairs {
loader.push(key, value).await?;
}
loader.finish().await
}
}