impl drop for free

This commit is contained in:
janis 2025-09-18 19:29:00 +02:00
parent 81f92738db
commit 216485e448
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8

View file

@ -13,18 +13,40 @@
// tree.find('i') -> None
use alloc::boxed::Box;
use core::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
use core::{
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
ptr::NonNull,
};
use crate::tree::subtree::Subtree;
mod marker {
#![allow(dead_code)]
use core::marker::PhantomData;
pub trait NodeType {}
#[derive(Debug)]
pub struct Internal;
#[derive(Debug)]
pub struct Leaf;
#[derive(Debug)]
pub struct LeafOrInternal;
impl NodeType for Internal {}
impl NodeType for Leaf {}
impl NodeType for LeafOrInternal {}
pub trait HandleType {}
#[derive(Debug)]
pub struct Edge;
#[derive(Debug)]
pub struct Value;
impl HandleType for Edge {}
impl HandleType for Value {}
#[derive(Debug)]
pub struct Owned;
#[derive(Debug)]
@ -54,6 +76,7 @@ mod marker {
}
#[derive(Debug)]
#[repr(C)]
pub(crate) struct LeafNode<K, V> {
parent: Option<BoxedNode<K, V>>,
parent_idx: MaybeUninit<u16>,
@ -484,7 +507,7 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V> {
}
pub(super) fn capacity(&self) -> usize {
unsafe { usize::from((*Self::as_leaf_ptr(self)).len) }
unsafe { usize::from((*Self::as_leaf_ptr(self)).capacity) }
}
}
@ -493,6 +516,10 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V> {
unsafe { (*Self::as_leaf_ptr(self)).len == 0 }
}
pub(crate) fn has_descendants(&self) -> bool {
self.as_leaf().len > 0
}
/// Temporarily takes out another, immutable reference to the same node.
pub(super) fn reborrow(&self) -> NodeRef<marker::Immut<'_>, K, V> {
NodeRef {
@ -651,7 +678,8 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V> {
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V> {
pub(super) fn grow_node(&mut self) {
// grow the node
let Some(new_capacity) = self.capacity_mut().checked_mul(2) else {
let capacity = self.capacity();
let Some(new_capacity) = capacity.checked_mul(2) else {
panic!("Node capacity overflow");
};
@ -679,8 +707,8 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V> {
// dealloc old keys and edges.
// This doesn't drop because the keys and edges were moved.
_ = Box::from_non_null(old_keys);
_ = Box::from_non_null(old_edges);
_ = Box::<[_]>::from_non_null(NonNull::slice_from_raw_parts(old_keys, capacity));
_ = Box::<[_]>::from_non_null(NonNull::slice_from_raw_parts(old_edges, capacity));
}
}
@ -716,6 +744,7 @@ impl<K, V> NodeRef<marker::Owned, K, V> {
// insert new key and child node.
// SAFETY: we just grew the allocations.
unsafe {
assert!(new_len == parent.idx + 1);
slice_insert(parent.node.key_area_mut(..new_len), parent.idx, key);
slice_insert(parent.node.edge_area_mut(..new_len), parent.idx, self.node);
}
@ -733,6 +762,25 @@ impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V>, marker::Edge> {
pub(super) fn into_value(self) -> Handle<NodeRef<BorrowType, K, V>, marker::Value> {
unsafe { Handle::new_value(self.node) }
}
pub(crate) fn right_edge(
self,
) -> Result<Handle<NodeRef<BorrowType, K, V>, marker::Edge>, Self> {
let len = self.node.len();
if self.idx + 1 < len {
Ok(unsafe { Handle::new_edge(self.node, self.idx + 1) })
} else {
Err(self)
}
}
pub(crate) fn left_edge(self) -> Result<Handle<NodeRef<BorrowType, K, V>, marker::Edge>, Self> {
if self.idx > 0 {
Ok(unsafe { Handle::new_edge(self.node, self.idx - 1) })
} else {
Err(self)
}
}
}
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V>, marker::Edge> {
@ -1304,6 +1352,12 @@ pub struct Tree<K, V> {
_marker: PhantomData<alloc::boxed::Box<(K, V)>>,
}
impl<K, V> Default for Tree<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<'a, K: core::fmt::Debug + 'a, V: core::fmt::Debug + 'a> core::fmt::Debug for &'a Tree<K, V> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut map = f.debug_map();
@ -1472,7 +1526,7 @@ mod range {
use crate::tree::{Handle, NodeRef};
enum LeafHandle<BorrowType, K, V> {
pub(crate) enum LeafHandle<BorrowType, K, V> {
Root(NodeRef<BorrowType, K, V>),
Edge(Handle<NodeRef<BorrowType, K, V>, marker::Edge>),
}
@ -1500,8 +1554,85 @@ mod range {
}
}
impl<K, V> Handle<NodeRef<marker::Dying, K, V>, marker::Edge> {
unsafe fn deallocating_next(
self,
) -> Option<Handle<NodeRef<marker::Dying, K, V>, marker::Edge>> {
let mut edge = self;
loop {
// if this node has descendants, we want to drop them first.
if edge.node.has_descendants() {
return Some(edge.descend().first_edge());
}
let mut last_edge = edge;
edge = loop {
// no more descendants: deallocate this node and ascend.
// after ascending, check if we need to go right and drop
// more descendants of the parent.
// otherwise, continue deallocating and ascending
match last_edge.node.deallocate_and_ascend() {
Some(parent) => match parent.right_edge() {
Ok(next) => break next,
Err(last) => last_edge = last,
},
None => return None,
}
};
}
}
unsafe fn dellocating_next_back(
self,
) -> Option<Handle<NodeRef<marker::Dying, K, V>, marker::Edge>> {
let mut edge = self;
loop {
if edge.node.has_descendants() {
match edge.left_edge() {
Ok(next) => return Some(next),
Err(node) => edge = node,
}
} else {
match edge.node.deallocate_and_ascend() {
Some(parent) => edge = parent,
None => return None,
}
}
}
}
pub(crate) unsafe fn deallocating_next_unchecked(&mut self) {
super::replace(self, |edge| {
(unsafe { edge.deallocating_next().unwrap() }, ())
})
}
pub(crate) unsafe fn try_deallocating_next(&mut self) -> bool {
super::maybe_replace(self, |edge| unsafe {
let err = core::ptr::read(&edge);
edge.deallocating_next().map(|e| (e, ())).ok_or(err)
})
.is_some()
}
unsafe fn deallocating_next_back_unchecked(&mut self) {
super::replace(self, |edge| {
(unsafe { edge.dellocating_next_back().unwrap() }, ())
})
}
pub(crate) fn deallocating_end(self) {
let mut edge = self;
while let Some(parent) = edge.node.deallocate_and_ascend() {
edge = parent;
}
}
}
impl<BorrowType: marker::BorrowType, K, V> LeafRange<BorrowType, K, V> {
fn init_front(&mut self) -> Option<&mut Handle<NodeRef<BorrowType, K, V>, marker::Edge>> {
pub(crate) fn init_front(
&mut self,
) -> Option<&mut Handle<NodeRef<BorrowType, K, V>, marker::Edge>> {
if let Some(LeafHandle::Root(root)) = &self.front {
self.front = Some(LeafHandle::Edge(
unsafe { ptr::read(root) }.first_leaf_edge(),
@ -1511,6 +1642,7 @@ mod range {
match &mut self.front {
None => None,
Some(LeafHandle::Edge(edge)) => Some(edge),
// if it was root, we've just replaced it with an edge.
Some(LeafHandle::Root(_)) => unreachable!(),
}
}
@ -1525,33 +1657,126 @@ mod range {
match &mut self.back {
None => None,
Some(LeafHandle::Edge(edge)) => Some(edge),
// if it was root, we've just replaced it with an edge.
Some(LeafHandle::Root(_)) => unreachable!(),
}
}
}
impl<K, V> LeafRange<marker::Dying, K, V> {
pub(crate) fn dying_next(&mut self) -> Option<()> {
let front = self.init_front().unwrap();
unsafe { front.try_deallocating_next().then_some(()) }
}
}
pub(crate) fn full_range<BorrowType, K, V>(
start: Option<NodeRef<BorrowType, K, V>>,
end: Option<NodeRef<BorrowType, K, V>>,
) -> LeafRange<BorrowType, K, V> {
LeafRange {
front: start.map(LeafHandle::Root),
back: end.map(LeafHandle::Root),
}
}
}
/// This replaces the value behind the `v` unique reference by calling the
/// relevant function, and returns a result obtained along the way.
///
/// If a panic occurs in the `change` closure, the entire process will be aborted.
#[inline]
pub(super) fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {
use core::{mem, ptr};
struct PanicGuard;
impl Drop for PanicGuard {
fn drop(&mut self) {
panic!()
}
}
let guard = PanicGuard;
let value = unsafe { ptr::read(v) };
let (new_value, ret) = change(value);
unsafe {
ptr::write(v, new_value);
}
mem::forget(guard);
ret
}
pub(crate) fn maybe_replace<T, R>(
v: &mut T,
change: impl FnOnce(T) -> Result<(T, R), T>,
) -> Option<R> {
use core::{mem, ptr};
struct PanicGuard;
impl Drop for PanicGuard {
fn drop(&mut self) {
panic!()
}
}
let guard = PanicGuard;
let value = unsafe { ptr::read(v) };
let (new_value, ret) = match change(value) {
Err(old_value) => (old_value, None),
Ok((new_value, ret)) => (new_value, Some(ret)),
};
unsafe {
ptr::write(v, new_value);
}
mem::forget(guard);
ret
}
struct IntoIter<K, V> {
range: range::LeafRange<marker::Dying, K, V>,
}
impl<K, V> IntoIter<K, V> {
fn new(tree: Tree<K, V>) -> Self {
let mut tree = ManuallyDrop::new(tree);
if let Some(root) = tree.root.take() {
let root = root.into_dying();
let root2 = unsafe { core::ptr::read(&root) };
Self {
range: range::full_range(Some(root), Some(root2)),
}
} else {
Self {
range: range::full_range(None, None),
}
}
}
}
impl<K, V> Drop for IntoIter<K, V> {
fn drop(&mut self) {
while let Some(_) = self.range.dying_next() {}
}
}
impl<K, V> Drop for Tree<K, V> {
fn drop(&mut self) {
drop(unsafe { IntoIter::new(core::ptr::read(self)) });
}
}
#[cfg(test)]
mod tests {
use super::*;
fn build_tree() -> Tree<char, i32> {
let mut tree = Tree::new();
tree.entry("asdf".chars()).or_insert(1);
tree.entry("asd".chars()).or_insert(2);
tree.entry("asdg".chars()).or_insert(3);
tree
}
#[test]
fn promote_leaf() {
#[derive(Debug, PartialEq, Eq)]
struct Test(&'static str);
impl Drop for Test {
fn drop(&mut self) {
std::eprintln!("Dropping: {}", self.0);
}
}
let mut leaf = NodeRef::<_, (), Test>::new();
leaf.borrow_mut().as_leaf_mut().value = Some(Test("test"));
let mut root = NodeRef::new();
let mut leaf = leaf.reparent(unsafe { Handle::new_edge(root.borrow_mut(), 0) }, ());
assert_eq!(leaf.as_leaf_mut().value, Some(Test("test")));
fn drop_tree() {
let tree = build_tree();
}
}