use crate::cell::*;
use crate::dict::{
read_label, rebuild_aug_dict_from_stack, rebuild_dict_from_stack, write_label_parts, AugDictFn,
Branch, DictBound, DictOwnedEntry, Segment,
};
use crate::error::Error;
pub fn dict_remove_owned(
dict: &mut Option<Cell>,
key: &mut CellSlice,
key_bit_len: u16,
allow_subtree: bool,
context: &mut dyn CellContext,
) -> Result<Option<CellSliceParts>, Error> {
if !allow_subtree && key.size_bits() != key_bit_len {
return Err(Error::CellUnderflow);
}
let Some(root) = &dict else {
return Ok(None);
};
let Some((mut stack, removed, prev_key_bit_len)) =
ok!(dict_find_value_to_remove(root, key, context))
else {
return Ok(None);
};
let value = if let Some(last) = stack.pop() {
let (leaf, value) = ok!(last.rebuild_as_removed(key, prev_key_bit_len, context));
*dict = Some(ok!(rebuild_dict_from_stack(stack, leaf, context)));
value
} else {
let value = root.clone();
*dict = None;
value
};
Ok(Some((value, removed)))
}
pub fn aug_dict_remove_owned(
dict: &mut Option<Cell>,
key: &mut CellSlice,
key_bit_len: u16,
allow_subtree: bool,
comparator: AugDictFn,
context: &mut dyn CellContext,
) -> Result<Option<CellSliceParts>, Error> {
if !allow_subtree && key.size_bits() != key_bit_len {
return Err(Error::CellUnderflow);
}
let Some(root) = &dict else {
return Ok(None);
};
let Some((mut stack, removed, prev_key_bit_len)) =
ok!(dict_find_value_to_remove(root, key, context))
else {
return Ok(None);
};
let value = if let Some(last) = stack.pop() {
let (leaf, value) = ok!(last.rebuild_as_removed(key, prev_key_bit_len, context));
*dict = Some(ok!(rebuild_aug_dict_from_stack(
stack, leaf, comparator, context,
)));
value
} else {
let value = root.clone();
*dict = None;
value
};
Ok(Some((value, removed)))
}
fn dict_find_value_to_remove<'a>(
root: &'a Cell,
key: &mut CellSlice,
context: &mut dyn CellContext,
) -> Result<Option<(Vec<Segment<'a>>, CellSliceRange, u16)>, Error> {
let mut data = ok!(context.load_dyn_cell(root.as_ref(), LoadMode::Full));
let mut stack = Vec::<Segment>::new();
let mut prev_key_bit_len = key.size_bits();
let removed = loop {
let mut remaining_data: CellSlice<'_> = ok!(data.as_slice());
let prefix = &mut ok!(read_label(&mut remaining_data, key.size_bits()));
let lcp = key.longest_common_data_prefix(prefix);
match lcp.size_bits().cmp(&key.size_bits()) {
std::cmp::Ordering::Equal => break remaining_data.range(),
std::cmp::Ordering::Less if lcp.size_bits() < prefix.size_bits() => {
return Ok(None);
}
std::cmp::Ordering::Less => {
if data.reference_count() != 2 {
return Err(Error::CellUnderflow);
}
prev_key_bit_len = key.size_bits();
key.skip_first(lcp.size_bits(), 0).ok();
let next_branch = Branch::from(ok!(key.load_bit()));
let child = match data.reference(next_branch as u8) {
Some(child) => ok!(context.load_dyn_cell(child, LoadMode::Full)),
None => return Err(Error::CellUnderflow),
};
stack.push(Segment {
data,
next_branch,
key_bit_len: prev_key_bit_len,
});
data = child;
}
std::cmp::Ordering::Greater => {
debug_assert!(false, "LCP of prefix and key can't be greater than key");
unsafe { std::hint::unreachable_unchecked() };
}
}
};
Ok(Some((stack, removed, prev_key_bit_len)))
}
pub fn dict_remove_bound_owned(
dict: &mut Option<Cell>,
mut key_bit_len: u16,
bound: DictBound,
signed: bool,
context: &mut dyn CellContext,
) -> Result<Option<DictOwnedEntry>, Error> {
let root = match &dict {
Some(data) => ok!(context.load_cell(data.clone(), LoadMode::Full)),
None => return Ok(None),
};
let mut data = root.as_ref();
let mut stack = Vec::<Segment>::new();
let mut direction = None;
let mut key = CellBuilder::new();
let mut prev_key_bit_len = key_bit_len;
let removed = loop {
let mut remaining_data = ok!(data.as_slice());
let prefix = &ok!(read_label(&mut remaining_data, key_bit_len));
if !prefix.is_data_empty() {
ok!(key.store_slice_data(prefix));
}
match key_bit_len.checked_sub(prefix.size_bits()) {
Some(0) => break remaining_data.range(),
Some(remaining) => {
if remaining_data.size_refs() < 2 {
return Err(Error::CellUnderflow);
}
prev_key_bit_len = key_bit_len;
key_bit_len = remaining - 1;
}
None => return Err(Error::CellUnderflow),
};
let next_branch = bound.update_direction(prefix, signed, &mut direction);
ok!(key.store_bit(next_branch.into_bit()));
let child = match data.reference(next_branch as u8) {
Some(cell) => ok!(context.load_dyn_cell(cell, LoadMode::Full)),
None => return Err(Error::CellUnderflow),
};
stack.push(Segment {
data,
next_branch,
key_bit_len: 0,
});
data = child;
};
let (leaf, removed) = if let Some(last) = stack.pop() {
let index = last.next_branch as u8;
let value = match last.data.reference_cloned(index) {
Some(cell) => ok!(context.load_cell(cell, LoadMode::Resolve)),
None => return Err(Error::CellUnderflow),
};
let pfx = {
let mut parent = unsafe { last.data.as_slice_unchecked() };
ok!(read_label(&mut parent, prev_key_bit_len))
};
let mut opposite = match last.data.reference(1 - index) {
Some(cell) => ok!(context
.load_dyn_cell(cell, LoadMode::Full)
.and_then(CellSlice::new)),
None => return Err(Error::CellUnderflow),
};
let rem = ok!(read_label(&mut opposite, key_bit_len));
let mut builder = CellBuilder::new();
ok!(write_label_parts(
&pfx,
index == 0,
&rem,
prev_key_bit_len,
&mut builder
));
ok!(builder.store_slice(opposite));
let leaf = ok!(builder.build_ext(context));
(leaf, (value, removed))
} else {
*dict = None;
return Ok(Some((key, (root, removed))));
};
*dict = Some(ok!(rebuild_dict_from_stack(stack, leaf, context)));
Ok(Some((key, removed)))
}