deep_safe_drop/lib.rs
1#![cfg_attr(not(windows), doc = include_str!("../README.md"))]
2#![cfg_attr(windows, doc = include_str!("..\\README.md"))]
3#![no_std]
4
5
6/// Implement this for your tree node type, with `Link` as your tree link type that references or
7/// is your node type.
8///
9/// The `Link` type may be the same as the `Self` type, when possible, which might be convenient.
10/// Or, they can be different.
11///
12/// Many node types should be able to implement these methods without needing any extra state
13/// beyond their normal state, e.g. because their link fields already support some unused state.
14pub trait DeepSafeDrop<Link>
15{
16 /// Take the next child and replace the link to it with a non-link, if the current state of
17 /// `self` has another child that has not been supplied yet. This may return the child at
18 /// index 0 when there is one.
19 #[inline]
20 fn take_next_child_at_any_index(&mut self) -> Option<Link>
21 {
22 self.take_child_at_index_0().or_else(|| self.take_next_child_at_pos_index())
23 }
24
25 /// Take the child at index 0 and replace the link to it with a given replacement that links
26 /// to the parent of `self`.
27 fn set_parent_at_index_0(
28 &mut self,
29 parent: Link,
30 ) -> SetParent<Link>;
31
32 /// Take the child at index 0 and replace the link to it with a non-link.
33 fn take_child_at_index_0(&mut self) -> Option<Link>;
34
35 /// Take the next child at an index greater than or equal to 1 and replace the link to it with
36 /// a non-link, if the current state of `self` has another child at those indices that has not
37 /// been supplied yet. This must not return the child at index 0 when there is one, because
38 /// that is reused to link to the parent.
39 fn take_next_child_at_pos_index(&mut self) -> Option<Link>;
40}
41
42
43/// Result of [`DeepSafeDrop::set_parent_at_index_0`].
44#[derive(Debug)]
45#[allow(clippy::exhaustive_enums)]
46pub enum SetParent<Link>
47{
48 /// There was a child at index 0 and it was replaced by the parent.
49 YesReplacedChild
50 {
51 /// The child at index 0 that was taken.
52 child0: Link,
53 },
54
55 /// The parent was set at index 0 and no child was replaced.
56 Yes,
57
58 /// No setting could be done, because the node has no links, so the parent must be returned
59 /// back.
60 No
61 {
62 /// The same `parent` value that was given to the method call.
63 returned_parent: Link,
64 },
65}
66
67/// Implement this for your tree link type, with `Node` as your tree node type.
68///
69/// The `Node` type may be the same as the `Self` type, when possible, which might be convenient.
70/// Or, they can be different.
71pub trait Link<Node: ?Sized>
72{
73 /// Return a mutable reference to the node that `self` links to.
74 fn get_mut(&mut self) -> &mut Node;
75}
76
77
78/// Exists to do these `debug_assert`s when a node can be immediately dropped because it's a leaf.
79fn drop_leaf<L, N>(mut link: L)
80where
81 L: Link<N>,
82 N: DeepSafeDrop<L> + ?Sized,
83{
84 let node = link.get_mut();
85 debug_assert!(node.take_next_child_at_any_index().is_none(), "must be leaf");
86 debug_assert!(node.take_child_at_index_0().is_none(), "must be leaf");
87 debug_assert!(node.take_next_child_at_pos_index().is_none(), "must be leaf");
88 drop(link);
89}
90
91
92/// A node's link at index 0 is reused as the parent link.
93fn take_parent<L, N>(node: &mut N) -> Option<L>
94where N: DeepSafeDrop<L> + ?Sized
95{
96 let child0 = node.take_child_at_index_0();
97 debug_assert!(node.take_child_at_index_0().is_none(), "must be gone after take");
98 child0
99}
100
101
102/// Return the nearest ancestor that has a next child if any, or the root ancestor even when it
103/// does not have a next child. Drop any ancestors in the upwards path that do not have a child
104/// but that do have a parent.
105fn take_ancestor_next_child<L, N>(parent: L) -> (L, Option<L>)
106where
107 L: Link<N>,
108 N: DeepSafeDrop<L> + ?Sized,
109{
110 let mut ancestor = parent;
111 loop {
112 if let Some(next_child) = ancestor.get_mut().take_next_child_at_pos_index() {
113 break (ancestor, Some(next_child));
114 }
115 else if let Some(grandancestor) = take_parent(ancestor.get_mut()) {
116 drop_leaf(ancestor); // `ancestor` is now a leaf node so drop it here.
117 ancestor = grandancestor;
118 }
119 else {
120 break (ancestor, None);
121 }
122 }
123}
124
125
126/// The main algorithm.
127fn main_deep_safe_drop<L, N>(top: L)
128where
129 L: Link<N>,
130 N: DeepSafeDrop<L> + ?Sized,
131{
132 let mut parent = top;
133
134 if let Some(mut cur) = parent.get_mut().take_next_child_at_any_index() {
135 loop {
136 match cur.get_mut().set_parent_at_index_0(parent) {
137 SetParent::YesReplacedChild { child0 } => {
138 parent = cur;
139 cur = child0;
140 continue;
141 },
142 SetParent::Yes => {
143 let next = cur.get_mut().take_next_child_at_pos_index();
144 parent = cur;
145 if let Some(child) = next {
146 cur = child;
147 continue;
148 }
149 },
150 SetParent::No { returned_parent } => {
151 parent = returned_parent;
152 drop_leaf(cur); // `cur` is now a leaf node so drop it here.
153 },
154 }
155
156 let (ancestor, ancestor_child) = take_ancestor_next_child(parent);
157 parent = ancestor;
158
159 if let Some(ancestor_child) = ancestor_child {
160 cur = ancestor_child;
161 }
162 else {
163 // Done. `parent` is now `top` which is now mutated to no longer have any
164 // children, so, when dropping it is completed, by the implicit compiler-added
165 // code, after this function returns, recursion into children cannot occur and so
166 // stack overflow cannot occur.
167 drop_leaf(parent);
168 break;
169 }
170 }
171 }
172}
173
174
175/// To be called from your [`Drop::drop`] implementations, to ensure that stack overflow is
176/// avoided.
177///
178/// The `RootNode` type may be different than the primary `Node` type, when possible, which might
179/// be convenient. Or, they can be the same.
180#[inline]
181pub fn deep_safe_drop<RootNode, Link, Node>(root: &mut RootNode)
182where
183 RootNode: DeepSafeDrop<Link> + ?Sized,
184 Link: crate::Link<Node>,
185 Node: DeepSafeDrop<Link> + ?Sized,
186{
187 while let Some(next_child) = root.take_next_child_at_any_index() {
188 main_deep_safe_drop(next_child);
189 }
190}