use crate::tree::{Key, LeafBuilder, LeafHeader, NodeBuilder, NodeHeader};
use uuid::Uuid;
const BYTENR_OFFSET: usize = 48;
const KEY_PTR_SIZE: usize = 33;
const HEADER_SIZE: usize = 101;
const ITEM_SIZE: usize = 25;
pub struct TreeBuilder {
pub nodesize: u32,
pub owner: u64,
pub fsid: Uuid,
pub chunk_tree_uuid: Uuid,
pub generation: u64,
}
pub struct TreeBlocks {
pub root_level: u8,
pub blocks: Vec<TreeBlockBuf>,
}
pub struct TreeBlockBuf {
pub level: u8,
pub first_key: Key,
pub buf: Vec<u8>,
pub child_indices: Vec<usize>,
}
impl TreeBuilder {
#[must_use]
pub fn build(&self, items: &[(Key, Vec<u8>)]) -> TreeBlocks {
if items.is_empty() {
let leaf = LeafBuilder::new(self.nodesize, &self.leaf_header());
return TreeBlocks {
root_level: 0,
blocks: vec![TreeBlockBuf {
level: 0,
first_key: Key::new(0, 0, 0),
buf: leaf.finish(),
child_indices: Vec::new(),
}],
};
}
let mut blocks: Vec<TreeBlockBuf> = Vec::new();
let mut leaf = LeafBuilder::new(self.nodesize, &self.leaf_header());
let mut first_key = items[0].0;
for (key, data) in items {
let needed = ITEM_SIZE + data.len();
if leaf.space_left() < needed && !leaf.is_empty() {
blocks.push(TreeBlockBuf {
level: 0,
first_key,
buf: leaf.finish(),
child_indices: Vec::new(),
});
leaf = LeafBuilder::new(self.nodesize, &self.leaf_header());
first_key = *key;
}
leaf.push(*key, data)
.expect("item too large for empty leaf");
}
blocks.push(TreeBlockBuf {
level: 0,
first_key,
buf: leaf.finish(),
child_indices: Vec::new(),
});
if blocks.len() == 1 {
return TreeBlocks {
root_level: 0,
blocks,
};
}
let max_ptrs = (self.nodesize as usize - HEADER_SIZE) / KEY_PTR_SIZE;
let mut level: u8 = 1;
let mut child_start = 0;
let mut child_count = blocks.len();
while child_count > 1 {
let mut new_nodes: Vec<TreeBlockBuf> = Vec::new();
let mut i = child_start;
let child_end = child_start + child_count;
while i < child_end {
let batch_end = (i + max_ptrs).min(child_end);
let batch_end = if child_end - batch_end > 0
&& child_end - batch_end < max_ptrs / 4
{
i + (child_end - i) / 2
} else {
batch_end
};
let node_first_key = blocks[i].first_key;
let mut node =
NodeBuilder::new(self.nodesize, &self.node_header(level));
let mut children = Vec::new();
for (j, block) in blocks[i..batch_end].iter().enumerate() {
node.push(
block.first_key,
0, self.generation,
)
.expect("too many children for internal node");
children.push(i + j);
}
new_nodes.push(TreeBlockBuf {
level,
first_key: node_first_key,
buf: node.finish(),
child_indices: children,
});
i = batch_end;
}
child_start = blocks.len();
child_count = new_nodes.len();
blocks.extend(new_nodes);
level += 1;
}
TreeBlocks {
root_level: level - 1,
blocks,
}
}
pub fn assign_addresses(
tree: &mut TreeBlocks,
mut alloc: impl FnMut() -> u64,
) {
let mut addrs: Vec<u64> = Vec::with_capacity(tree.blocks.len());
for _ in 0..tree.blocks.len() {
addrs.push(alloc());
}
for (i, block) in tree.blocks.iter_mut().enumerate() {
let addr = addrs[i];
block.buf[BYTENR_OFFSET..BYTENR_OFFSET + 8]
.copy_from_slice(&addr.to_le_bytes());
}
for i in 0..tree.blocks.len() {
if tree.blocks[i].child_indices.is_empty() {
continue;
}
let children: Vec<usize> = tree.blocks[i].child_indices.clone();
for (slot, &child_idx) in children.iter().enumerate() {
let ptr_offset = HEADER_SIZE + slot * KEY_PTR_SIZE + 17;
let child_addr = addrs[child_idx];
tree.blocks[i].buf[ptr_offset..ptr_offset + 8]
.copy_from_slice(&child_addr.to_le_bytes());
}
}
}
#[must_use]
pub fn clone_with_owner(&self, owner: u64) -> Self {
Self {
nodesize: self.nodesize,
owner,
fsid: self.fsid,
chunk_tree_uuid: self.chunk_tree_uuid,
generation: self.generation,
}
}
fn leaf_header(&self) -> LeafHeader {
LeafHeader {
fsid: self.fsid,
chunk_tree_uuid: self.chunk_tree_uuid,
generation: self.generation,
owner: self.owner,
bytenr: 0,
}
}
fn node_header(&self, level: u8) -> NodeHeader {
NodeHeader {
fsid: self.fsid,
chunk_tree_uuid: self.chunk_tree_uuid,
generation: self.generation,
owner: self.owner,
bytenr: 0,
level,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use btrfs_disk::tree::TreeBlock;
fn test_builder() -> TreeBuilder {
TreeBuilder {
nodesize: 4096,
owner: 5,
fsid: Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef")
.unwrap(),
chunk_tree_uuid: Uuid::parse_str(
"cafebabe-cafe-babe-cafe-babecafebabe",
)
.unwrap(),
generation: 1,
}
}
#[test]
fn empty_items_single_leaf() {
let builder = test_builder();
let tree = builder.build(&[]);
assert_eq!(tree.root_level, 0);
assert_eq!(tree.blocks.len(), 1);
match TreeBlock::parse(&tree.blocks[0].buf) {
TreeBlock::Leaf { header, items, .. } => {
assert_eq!(header.nritems, 0);
assert_eq!(items.len(), 0);
}
TreeBlock::Node { .. } => panic!("expected leaf"),
}
}
#[test]
fn few_items_single_leaf() {
let builder = test_builder();
let items: Vec<(Key, Vec<u8>)> = (0..5)
.map(|i| (Key::new(i, 1, 0), vec![0u8; 100]))
.collect();
let tree = builder.build(&items);
assert_eq!(tree.root_level, 0);
assert_eq!(tree.blocks.len(), 1);
}
#[test]
fn many_items_multi_leaf() {
let builder = test_builder();
let items: Vec<(Key, Vec<u8>)> = (0..100)
.map(|i| (Key::new(i, 1, 0), vec![0u8; 100]))
.collect();
let tree = builder.build(&items);
assert_eq!(tree.root_level, 1);
let leaf_count = tree.blocks.iter().filter(|b| b.level == 0).count();
let node_count = tree.blocks.iter().filter(|b| b.level == 1).count();
assert!(leaf_count >= 3);
assert_eq!(node_count, 1);
let root = tree.blocks.last().unwrap();
assert_eq!(root.level, 1);
assert_eq!(root.child_indices.len(), leaf_count);
}
#[test]
fn address_assignment_patches_bytenr() {
let builder = test_builder();
let items: Vec<(Key, Vec<u8>)> = (0..100)
.map(|i| (Key::new(i, 1, 0), vec![0u8; 100]))
.collect();
let mut tree = builder.build(&items);
let mut next_addr = 0x500000u64;
TreeBuilder::assign_addresses(&mut tree, || {
let addr = next_addr;
next_addr += 4096;
addr
});
for (i, block) in tree.blocks.iter().enumerate() {
let expected = 0x500000 + (i as u64) * 4096;
let actual = u64::from_le_bytes(
block.buf[BYTENR_OFFSET..BYTENR_OFFSET + 8]
.try_into()
.unwrap(),
);
assert_eq!(actual, expected);
}
let root = tree.blocks.last().unwrap();
if root.level > 0 {
let parsed = TreeBlock::parse(&root.buf);
match parsed {
TreeBlock::Node { ptrs, .. } => {
for (slot, ptr) in ptrs.iter().enumerate() {
let child_idx = root.child_indices[slot];
let expected_addr =
0x500000 + (child_idx as u64) * 4096;
assert_eq!(ptr.blockptr, expected_addr);
}
}
TreeBlock::Leaf { .. } => panic!("expected node"),
}
}
}
}