use crate::store::NodeStore;
use super::super::cursor::{TreeError, load_node};
use super::super::node::Node;
use super::super::policy::TreePolicy;
use super::{ChildRef, Entry, Mutation};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Window {
pub(super) start: usize,
pub(super) end: usize,
}
pub(super) fn affected_window(
leaves: &[ChildRef],
mutations: &[Mutation],
) -> Result<Option<Window>, TreeError> {
let Some(min_key) = mutations.iter().map(|m| m.key.as_slice()).min() else {
return Ok(None);
};
let Some(max_key) = mutations.iter().map(|m| m.key.as_slice()).max() else {
return Ok(None);
};
if leaves.is_empty() {
return Err(TreeError::InvalidNode);
}
let start = leaf_index_for_key(leaves, min_key);
let end = leaf_index_for_key(leaves, max_key)
.saturating_add(1)
.min(leaves.len());
Ok(Some(Window { start, end }))
}
fn leaf_index_for_key(leaves: &[ChildRef], key: &[u8]) -> usize {
let mut index = 0;
for (position, (separator, _hash)) in leaves.iter().enumerate() {
if separator.as_slice() <= key {
index = position;
} else {
break;
}
}
index
}
pub(super) fn rebuild_window<S: NodeStore + ?Sized>(
store: &mut S,
leaves: &[ChildRef],
window: Window,
mutations: &[Mutation],
policy: TreePolicy,
) -> Result<Vec<ChildRef>, TreeError> {
let start = extend_left(store, leaves, window.start, policy)?;
let mut entries = load_entries(store, &leaves[start..window.end])?;
apply_mutations(&mut entries, mutations)?;
let mut consumed_end = window.end;
while consumed_end < leaves.len() && last_entry_is_open(&entries, policy) {
let next = load_entries(store, std::slice::from_ref(&leaves[consumed_end]))?;
if policy.is_v2() && next_leaf_is_isolated(next.first(), policy) {
break;
}
entries.extend(next);
consumed_end = consumed_end.saturating_add(1);
}
let new_leaves = super::spine::store_leaf_replacements(store, entries, policy)?;
let mut result =
Vec::with_capacity(start + new_leaves.len() + leaves.len().saturating_sub(consumed_end));
result.extend_from_slice(&leaves[..start]);
result.extend(new_leaves);
result.extend_from_slice(&leaves[consumed_end..]);
Ok(result)
}
fn extend_left<S: NodeStore + ?Sized>(
store: &S,
leaves: &[ChildRef],
start: usize,
policy: TreePolicy,
) -> Result<usize, TreeError> {
if !policy.is_v2() {
return Ok(start);
}
let mut start = start;
while start > 0 {
let prev = load_entries(store, std::slice::from_ref(&leaves[start - 1]))?;
let Some((key, value)) = prev.last() else {
break;
};
if policy.leaf_boundary_after(key.as_slice(), value.len()) {
break;
}
start -= 1;
}
Ok(start)
}
fn last_entry_is_open(entries: &[Entry], policy: TreePolicy) -> bool {
entries
.last()
.is_some_and(|(key, value)| !policy.leaf_boundary_after(key.as_slice(), value.len()))
}
fn next_leaf_is_isolated(first: Option<&Entry>, policy: TreePolicy) -> bool {
first.is_some_and(|(key, value)| policy.leaf_boundary_before(key.len(), value.len()))
}
fn load_entries<S: NodeStore + ?Sized>(
store: &S,
leaves: &[ChildRef],
) -> Result<Vec<Entry>, TreeError> {
let mut entries = Vec::new();
for (_separator, hash) in leaves {
match &*load_node(store, *hash)? {
Node::Leaf(leaf) => entries.extend_from_slice(leaf.entries()),
Node::Internal(_internal) => return Err(TreeError::InvalidNode),
}
}
Ok(entries)
}
fn apply_mutations(entries: &mut Vec<Entry>, mutations: &[Mutation]) -> Result<(), TreeError> {
for mutation in mutations {
let search =
entries.binary_search_by(|(key, _value)| key.as_slice().cmp(mutation.key.as_slice()));
match (&mutation.value, search) {
(Some(value), Ok(index)) => {
let Some((_key, stored_value)) = entries.get_mut(index) else {
return Err(TreeError::InvalidNode);
};
value.clone_into(stored_value);
}
(Some(value), Err(index)) => {
if index > entries.len() {
return Err(TreeError::InvalidNode);
}
entries.insert(index, (mutation.key.clone(), value.clone()));
}
(None, Ok(index)) => {
if index >= entries.len() {
return Err(TreeError::InvalidNode);
}
entries.remove(index);
}
(None, Err(_index)) => {}
}
}
Ok(())
}