From d7520a7a32773bd7018d6c27df7d22fe9926c4df Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 21:54:02 +0100 Subject: [PATCH 01/13] implement DfsIterIntoFiltered --- src/node_mut.rs | 22 ++++ .../depth_first/into_iter_filtered.rs | 124 ++++++++++++++++++ src/traversal/depth_first/mod.rs | 1 + src/traversal/depth_first/traverser_core.rs | 22 ++++ src/traversal/traverser_core.rs | 18 ++- 5 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 src/traversal/depth_first/into_iter_filtered.rs diff --git a/src/node_mut.rs b/src/node_mut.rs index 5b09127..c908385 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -2755,6 +2755,28 @@ where }) } + pub fn into_leaves_with( + self, + traverser: &'a mut T, + ) -> impl Iterator> + where + O: OverMut, + T: Traverser, + { + // T::iter_ptr_with_storage(self.node_ptr(), traverser.storage_mut()) + // .filter(|x| { + // let ptr: &NodePtr = O::Enumeration::node_data(x); + // unsafe { &*ptr.ptr() }.next().is_empty() + // }) + // .map(|x| { + // O::Enumeration::from_element_ptr_mut::<'a, V, M, P, O::NodeItemMut<'a, V, M, P>>( + // self.col(), + // x, + // ) + // }) + core::iter::empty() + } + // recursive /// Recursively sets the data of all nodes belonging to the subtree rooted at this node using the `compute_data` diff --git a/src/traversal/depth_first/into_iter_filtered.rs b/src/traversal/depth_first/into_iter_filtered.rs new file mode 100644 index 0000000..bf2577e --- /dev/null +++ b/src/traversal/depth_first/into_iter_filtered.rs @@ -0,0 +1,124 @@ +use super::DepthFirstEnumeration; +use super::iter_ptr::DfsIterPtr; +use super::stack::Item; +use crate::TreeVariant; +use crate::aliases::Col; +use crate::memory::MemoryPolicy; +use crate::pinned_storage::PinnedStorage; +use alloc::vec::Vec; +use orx_self_or::SoM; +use orx_selfref_col::{NodePtr, Refs}; + +pub struct DfsIterIntoFiltered<'a, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: DepthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + col: &'a mut Col, + root_ptr: NodePtr, + iter: DfsIterPtr, + filter: F, +} + +impl<'a, V, M, P, E, S, F> DfsIterIntoFiltered<'a, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: DepthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + /// # Safety + /// + /// We are creating a mutable iterator over nodes of the collection `col`. + /// This is safe only when the second argument `iter` makes sure that there exists only one mutable + /// reference to the collection. + /// + /// This is the case how this method is used, as follows: + /// * Mutable iterators are created through the `Dfs` traverser's `TraverserMut::iter_mut` method. + /// * This method requires a mutable reference to a mutable node `NodeMut` which is guaranteed to + /// be the only reference to the collection. + /// * Finally, this iterator's lifetime is equal to the borrow duration of the mutable node. + #[allow(clippy::type_complexity)] + pub(crate) unsafe fn from( + (col, iter, root_ptr): (&'a mut Col, DfsIterPtr, NodePtr), + filter: F, + ) -> Self { + let node = unsafe { &mut *root_ptr.ptr_mut() }; + + match node.prev().get() { + Some(parent) => { + let parent = unsafe { &mut *parent.ptr_mut() }; + let sibling_idx = parent.next_mut().remove(unsafe { root_ptr.ptr() as usize }); + debug_assert!(sibling_idx.is_some()); + + node.prev_mut().clear(); + } + None => { + // node_ptr points to the root node + col.ends_mut().clear(); + } + } + + Self { + col, + root_ptr, + iter, + filter, + } + } + + fn take_element(&mut self, element: E::Item>) -> E::Item { + E::map_node_data(element, |ptr| { + let col = unsafe { &mut *(self.col as *mut Col) }; + col.close(ptr) + }) + } +} + +impl Iterator for DfsIterIntoFiltered<'_, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: DepthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + type Item = E::Item; + + fn next(&mut self) -> Option { + loop { + match self.iter.next() { + Some(ptr) => { + if (self.filter)(&ptr) { + return Some(self.take_element(ptr)); + } + } + None => return None, + } + } + } +} + +impl Drop for DfsIterIntoFiltered<'_, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: DepthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + fn drop(&mut self) { + while let Some(element) = self.iter.next() { + self.take_element(element); + } + self.col.reclaim_from_closed_node(self.root_ptr); + } +} diff --git a/src/traversal/depth_first/mod.rs b/src/traversal/depth_first/mod.rs index f763aac..44809f2 100644 --- a/src/traversal/depth_first/mod.rs +++ b/src/traversal/depth_first/mod.rs @@ -3,6 +3,7 @@ mod tests; mod dfs_enumeration; pub(crate) mod into_iter; +pub(crate) mod into_iter_filtered; pub(crate) mod iter_mut; pub(crate) mod iter_ptr; pub(crate) mod iter_ref; diff --git a/src/traversal/depth_first/traverser_core.rs b/src/traversal/depth_first/traverser_core.rs index b006797..2714b34 100644 --- a/src/traversal/depth_first/traverser_core.rs +++ b/src/traversal/depth_first/traverser_core.rs @@ -126,4 +126,26 @@ impl TraverserCore for Dfs { let iter_ptr = DfsIterPtr::::from((storage, root)); unsafe { DfsIterInto::::from((col, iter_ptr, root)) } } + + fn into_iter_with_storage_filtered<'a, V, M, P, MO, F>( + node_mut: NodeMut<'a, V, M, P, MO>, + storage: impl SoM>, + filter: F, + ) -> impl Iterator> + where + V: TreeVariant + 'a, + M: MemoryPolicy, + P: PinnedStorage, + MO: NodeMutOrientation, + O: Over, + F: Fn(&<::Enumeration as Enumeration>::Item>) -> bool, + { + let (col, root) = node_mut.into_inner(); + let mut iter_ptr = DfsIterPtr::::from((storage, root)); + let x = iter_ptr.next().unwrap(); + let ptr = ::node_data(&x); + + let iter_ptr = DfsIterPtr::::from((storage, root)); + unsafe { DfsIterInto::::from((col, iter_ptr, root)) } + } } diff --git a/src/traversal/traverser_core.rs b/src/traversal/traverser_core.rs index 26ad56e..0d5f0bd 100644 --- a/src/traversal/traverser_core.rs +++ b/src/traversal/traverser_core.rs @@ -82,7 +82,6 @@ where V: TreeVariant + 'a, M: MemoryPolicy, P: PinnedStorage; - fn iter_mut_with_storage<'a, V, M, P, MO>( node_mut: &'a mut NodeMut<'a, V, M, P, MO>, storage: impl SoM>, @@ -134,7 +133,6 @@ where P: PinnedStorage, MO: NodeMutOrientation, O: OverMut; - fn into_iter_with_storage<'a, V, M, P, MO>( node_mut: NodeMut<'a, V, M, P, MO>, storage: impl SoM>, @@ -146,6 +144,22 @@ where MO: NodeMutOrientation, O: Over; + fn into_iter_with_storage_filtered<'a, V, M, P, MO, F>( + node_mut: NodeMut<'a, V, M, P, MO>, + storage: impl SoM>, + filter: F, + ) -> impl Iterator> + where + V: TreeVariant + 'a, + M: MemoryPolicy, + P: PinnedStorage, + MO: NodeMutOrientation, + O: Over, + F: Fn(&::Item>) -> bool, + { + core::iter::empty() + } + /// Returns an iterator which: /// /// * traverses all nodes including the `node` and its descendants; i.e., From 91e041173274b4294d81815ffb319c51e098e104 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 21:55:53 +0100 Subject: [PATCH 02/13] fix DfsIterIntoFiltered implementation --- src/traversal/depth_first/into_iter_filtered.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/traversal/depth_first/into_iter_filtered.rs b/src/traversal/depth_first/into_iter_filtered.rs index bf2577e..c80fdc0 100644 --- a/src/traversal/depth_first/into_iter_filtered.rs +++ b/src/traversal/depth_first/into_iter_filtered.rs @@ -96,8 +96,10 @@ where loop { match self.iter.next() { Some(ptr) => { - if (self.filter)(&ptr) { - return Some(self.take_element(ptr)); + let included = (self.filter)(&ptr); + let element = self.take_element(ptr); + if included { + return Some(element); } } None => return None, From 6141bb598c1fc258091c56246135904e503d9b68 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 21:57:18 +0100 Subject: [PATCH 03/13] implement into_iter_with_storage_filtered for Dfs --- src/traversal/depth_first/traverser_core.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/traversal/depth_first/traverser_core.rs b/src/traversal/depth_first/traverser_core.rs index 2714b34..af74c62 100644 --- a/src/traversal/depth_first/traverser_core.rs +++ b/src/traversal/depth_first/traverser_core.rs @@ -8,6 +8,7 @@ use crate::{ pinned_storage::PinnedStorage, traversal::{ Over, OverMut, + depth_first::into_iter_filtered::DfsIterIntoFiltered, enumeration::Enumeration, over::OverItem, over_mut::{OverItemInto, OverItemMut}, @@ -141,11 +142,7 @@ impl TraverserCore for Dfs { F: Fn(&<::Enumeration as Enumeration>::Item>) -> bool, { let (col, root) = node_mut.into_inner(); - let mut iter_ptr = DfsIterPtr::::from((storage, root)); - let x = iter_ptr.next().unwrap(); - let ptr = ::node_data(&x); - let iter_ptr = DfsIterPtr::::from((storage, root)); - unsafe { DfsIterInto::::from((col, iter_ptr, root)) } + unsafe { DfsIterIntoFiltered::::from((col, iter_ptr, root), filter) } } } From ea33ee1e4d66514a59053d5b413908b2038bc99f Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:01:40 +0100 Subject: [PATCH 04/13] implement NodeMut::into_leaves_with with the filter --- src/node_mut.rs | 17 +++++------------ src/traversal/node_item.rs | 4 ---- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index c908385..dd0f2ca 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -11,6 +11,7 @@ use crate::{ Over, OverData, OverMut, enumeration::Enumeration, enumerations::Val, + node_item::NodeItem, over::OverPtr, over_mut::{OverItemInto, OverItemMut}, post_order::iter_ptr::PostOrderIterPtr, @@ -2763,18 +2764,10 @@ where O: OverMut, T: Traverser, { - // T::iter_ptr_with_storage(self.node_ptr(), traverser.storage_mut()) - // .filter(|x| { - // let ptr: &NodePtr = O::Enumeration::node_data(x); - // unsafe { &*ptr.ptr() }.next().is_empty() - // }) - // .map(|x| { - // O::Enumeration::from_element_ptr_mut::<'a, V, M, P, O::NodeItemMut<'a, V, M, P>>( - // self.col(), - // x, - // ) - // }) - core::iter::empty() + T::into_iter_with_storage_filtered(self, traverser.storage_mut(), |x| { + let ptr = ::node_data(&x); + unsafe { &*ptr.ptr() }.next().is_empty() + }) } // recursive diff --git a/src/traversal/node_item.rs b/src/traversal/node_item.rs index 6cedf85..3ca2dc4 100644 --- a/src/traversal/node_item.rs +++ b/src/traversal/node_item.rs @@ -13,7 +13,6 @@ where { fn from_ptr(col: &'a Col, node_ptr: NodePtr) -> Self; - #[cfg(test)] fn node_data(&self) -> &V::Item; } @@ -28,7 +27,6 @@ where Node::new(col, node_ptr) } - #[cfg(test)] #[inline(always)] fn node_data(&self) -> &V::Item { use crate::NodeRef; @@ -48,7 +46,6 @@ where node.data().expect("active tree node has data") } - #[cfg(test)] #[inline(always)] fn node_data(&self) -> &V::Item { self @@ -66,7 +63,6 @@ where node_ptr } - #[cfg(test)] #[inline(always)] fn node_data(&self) -> &V::Item { unsafe { &*self.ptr() } From c9f0bcbf3b553c78e5b78f220995f1b17a484401 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:09:22 +0100 Subject: [PATCH 05/13] implement BfsIterIntoFiltered --- src/node_mut.rs | 50 +++++++ .../breadth_first/into_iter_filtered.rs | 126 ++++++++++++++++++ src/traversal/breadth_first/mod.rs | 1 + 3 files changed, 177 insertions(+) create mode 100644 src/traversal/breadth_first/into_iter_filtered.rs diff --git a/src/node_mut.rs b/src/node_mut.rs index dd0f2ca..ede4cfe 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -3304,3 +3304,53 @@ where self.node().prev().get().map(|p| NodeMut::new(self.col, p)) } } + +#[cfg(test)] +mod tst { + use std::println; + use std::string::ToString; + + use crate::{traversal::traverser_core::TraverserCore, *}; + use alloc::vec; + use alloc::vec::Vec; + + #[test] + fn abc() { + // 1 + // ╱ ╲ + // ╱ ╲ + // 2 3 + // ╱ ╲ ╱ ╲ + // 4 5 6 7 + // | | ╱ ╲ + // 8 9 10 11 + + let mut tree = DynTree::new(1.to_string()); + + let mut root = tree.root_mut(); + let [id2, id3] = root.push_children([2, 3].map(|x| x.to_string())); + + let mut n2 = tree.node_mut(id2); + let [id4, _] = n2.push_children([4, 5].map(|x| x.to_string())); + + tree.node_mut(id4).push_child(8.to_string()); + + let mut n3 = tree.node_mut(id3); + let [id6, id7] = n3.push_children([6, 7].map(|x| x.to_string())); + + tree.node_mut(id6).push_child(9.to_string()); + tree.node_mut(id7) + .push_children([10, 11].map(|x| x.to_string())); + + // create the traverser 'bfs' (or others) only once, use it many times + // to walk over references, mutable references or removed values + // without additional allocation + + let mut t = Dfs::default(); + + let leaves: Vec<_> = tree.root_mut().into_leaves_with(&mut t).collect(); + println!("{leaves:?}"); + + assert_eq!(leaves.len(), 33); + } +} diff --git a/src/traversal/breadth_first/into_iter_filtered.rs b/src/traversal/breadth_first/into_iter_filtered.rs new file mode 100644 index 0000000..e0c4f1b --- /dev/null +++ b/src/traversal/breadth_first/into_iter_filtered.rs @@ -0,0 +1,126 @@ +use super::BreadthFirstEnumeration; +use super::iter_ptr::BfsIterPtr; +use super::queue::Item; +use crate::TreeVariant; +use crate::aliases::Col; +use crate::memory::MemoryPolicy; +use crate::pinned_storage::PinnedStorage; +use alloc::collections::VecDeque; +use orx_self_or::SoM; +use orx_selfref_col::{NodePtr, Refs}; + +pub struct BfsIterIntoFiltered<'a, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: BreadthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + col: &'a mut Col, + root_ptr: NodePtr, + iter: BfsIterPtr, + filter: F, +} + +impl<'a, V, M, P, E, S, F> BfsIterIntoFiltered<'a, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: BreadthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + /// # Safety + /// + /// We are creating a mutable iterator over nodes of the collection `col`. + /// This is safe only when the second argument `iter` makes sure that there exists only one mutable + /// reference to the collection. + /// + /// This is the case how this method is used, as follows: + /// * Mutable iterators are created through the `Dfs` traverser's `TraverserMut::iter_mut` method. + /// * This method requires a mutable reference to a mutable node `NodeMut` which is guaranteed to + /// be the only reference to the collection. + /// * Finally, this iterator's lifetime is equal to the borrow duration of the mutable node. + #[allow(clippy::type_complexity)] + pub(crate) unsafe fn from( + (col, iter, root_ptr): (&'a mut Col, BfsIterPtr, NodePtr), + filter: F, + ) -> Self { + let node = unsafe { &mut *root_ptr.ptr_mut() }; + + match node.prev().get() { + Some(parent) => { + let parent = unsafe { &mut *parent.ptr_mut() }; + let sibling_idx = parent.next_mut().remove(unsafe { root_ptr.ptr() as usize }); + debug_assert!(sibling_idx.is_some()); + + node.prev_mut().clear(); + } + None => { + // node_ptr points to the root node + col.ends_mut().clear(); + } + } + + Self { + col, + root_ptr, + iter, + filter, + } + } + + fn take_element(&mut self, element: E::Item>) -> E::Item { + E::map_node_data(element, |ptr| { + let col = unsafe { &mut *(self.col as *mut Col) }; + col.close(ptr) + }) + } +} + +impl Iterator for BfsIterIntoFiltered<'_, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: BreadthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + type Item = E::Item; + + fn next(&mut self) -> Option { + loop { + match self.iter.next() { + Some(ptr) => { + let included = (self.filter)(&ptr); + let element = self.take_element(ptr); + if included { + return Some(element); + } + } + None => return None, + } + } + } +} + +impl Drop for BfsIterIntoFiltered<'_, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: BreadthFirstEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + fn drop(&mut self) { + while let Some(element) = self.iter.next() { + self.take_element(element); + } + self.col.reclaim_from_closed_node(self.root_ptr); + } +} diff --git a/src/traversal/breadth_first/mod.rs b/src/traversal/breadth_first/mod.rs index b488744..f1038ba 100644 --- a/src/traversal/breadth_first/mod.rs +++ b/src/traversal/breadth_first/mod.rs @@ -3,6 +3,7 @@ mod tests; mod bfs_enumeration; pub(crate) mod into_iter; +pub(crate) mod into_iter_filtered; pub(crate) mod iter_mut; pub(crate) mod iter_ptr; pub(crate) mod iter_ref; From 65637ac1f92529ab736063c406c2c4c0b50a3a49 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:10:46 +0100 Subject: [PATCH 06/13] implement PostOrderIterIntoFiltered --- .../post_order/into_iter_filtered.rs | 126 ++++++++++++++++++ src/traversal/post_order/mod.rs | 1 + 2 files changed, 127 insertions(+) create mode 100644 src/traversal/post_order/into_iter_filtered.rs diff --git a/src/traversal/post_order/into_iter_filtered.rs b/src/traversal/post_order/into_iter_filtered.rs new file mode 100644 index 0000000..bfb9cb0 --- /dev/null +++ b/src/traversal/post_order/into_iter_filtered.rs @@ -0,0 +1,126 @@ +use super::iter_ptr::PostOrderIterPtr; +use super::post_enumeration::PostOrderEnumeration; +use super::states::State; +use crate::TreeVariant; +use crate::aliases::Col; +use crate::memory::MemoryPolicy; +use crate::pinned_storage::PinnedStorage; +use alloc::vec::Vec; +use orx_self_or::SoM; +use orx_selfref_col::{NodePtr, Refs}; + +pub struct PostOrderIterIntoFiltered<'a, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: PostOrderEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + col: &'a mut Col, + root_ptr: NodePtr, + iter: PostOrderIterPtr, + filter: F, +} + +impl<'a, V, M, P, E, S, F> PostOrderIterIntoFiltered<'a, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: PostOrderEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + /// # Safety + /// + /// We are creating a mutable iterator over nodes of the collection `col`. + /// This is safe only when the second argument `iter` makes sure that there exists only one mutable + /// reference to the collection. + /// + /// This is the case how this method is used, as follows: + /// * Mutable iterators are created through the `Dfs` traverser's `TraverserMut::iter_mut` method. + /// * This method requires a mutable reference to a mutable node `NodeMut` which is guaranteed to + /// be the only reference to the collection. + /// * Finally, this iterator's lifetime is equal to the borrow duration of the mutable node. + #[allow(clippy::type_complexity)] + pub(crate) unsafe fn from( + (col, iter, root_ptr): (&'a mut Col, PostOrderIterPtr, NodePtr), + filter: F, + ) -> Self { + let node = unsafe { &mut *root_ptr.ptr_mut() }; + + match node.prev().get() { + Some(parent) => { + let parent = unsafe { &mut *parent.ptr_mut() }; + let sibling_idx = parent.next_mut().remove(unsafe { root_ptr.ptr() as usize }); + debug_assert!(sibling_idx.is_some()); + + node.prev_mut().clear(); + } + None => { + // node_ptr points to the root node + col.ends_mut().clear(); + } + } + + Self { + col, + root_ptr, + iter, + filter, + } + } + + fn take_element(&mut self, element: E::Item>) -> E::Item { + E::map_node_data(element, |ptr| { + let col = unsafe { &mut *(self.col as *mut Col) }; + col.close(ptr) + }) + } +} + +impl Iterator for PostOrderIterIntoFiltered<'_, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: PostOrderEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + type Item = E::Item; + + fn next(&mut self) -> Option { + loop { + match self.iter.next() { + Some(ptr) => { + let included = (self.filter)(&ptr); + let element = self.take_element(ptr); + if included { + return Some(element); + } + } + None => return None, + } + } + } +} + +impl Drop for PostOrderIterIntoFiltered<'_, V, M, P, E, S, F> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + E: PostOrderEnumeration, + S: SoM>>, + F: Fn(&E::Item>) -> bool, +{ + fn drop(&mut self) { + while let Some(element) = self.iter.next() { + self.take_element(element); + } + self.col.reclaim_from_closed_node(self.root_ptr); + } +} diff --git a/src/traversal/post_order/mod.rs b/src/traversal/post_order/mod.rs index b030f27..633fdd4 100644 --- a/src/traversal/post_order/mod.rs +++ b/src/traversal/post_order/mod.rs @@ -2,6 +2,7 @@ mod tests; pub(crate) mod into_iter; +pub(crate) mod into_iter_filtered; pub(crate) mod iter_mut; pub(crate) mod iter_ptr; pub(crate) mod iter_ref; From 19cb058a574ca0667bf179bd1bfb600e88302a8d Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:11:43 +0100 Subject: [PATCH 07/13] implement into_iter_with_storage_filtered for Bfs --- src/traversal/breadth_first/traverser_core.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/traversal/breadth_first/traverser_core.rs b/src/traversal/breadth_first/traverser_core.rs index 488da52..1b68856 100644 --- a/src/traversal/breadth_first/traverser_core.rs +++ b/src/traversal/breadth_first/traverser_core.rs @@ -8,6 +8,7 @@ use crate::{ pinned_storage::PinnedStorage, traversal::{ Over, OverMut, + breadth_first::into_iter_filtered::BfsIterIntoFiltered, enumeration::Enumeration, over::OverItem, over_mut::{OverItemInto, OverItemMut}, @@ -126,4 +127,22 @@ impl TraverserCore for Bfs { let iter_ptr = BfsIterPtr::::from((storage, root)); unsafe { BfsIterInto::::from((col, iter_ptr, root)) } } + + fn into_iter_with_storage_filtered<'a, V, M, P, MO, F>( + node_mut: NodeMut<'a, V, M, P, MO>, + storage: impl SoM>, + filter: F, + ) -> impl Iterator> + where + V: TreeVariant + 'a, + M: MemoryPolicy, + P: PinnedStorage, + MO: NodeMutOrientation, + O: Over, + F: Fn(&<::Enumeration as Enumeration>::Item>) -> bool, + { + let (col, root) = node_mut.into_inner(); + let iter_ptr = BfsIterPtr::::from((storage, root)); + unsafe { BfsIterIntoFiltered::::from((col, iter_ptr, root), filter) } + } } From 1a54fcc5f0dc6515a1d2dd451820315845afb8b8 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:12:17 +0100 Subject: [PATCH 08/13] implement into_iter_with_storage_filtered for PostOrder --- src/traversal/post_order/traverser_core.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/traversal/post_order/traverser_core.rs b/src/traversal/post_order/traverser_core.rs index ecdccfe..ad64a69 100644 --- a/src/traversal/post_order/traverser_core.rs +++ b/src/traversal/post_order/traverser_core.rs @@ -11,6 +11,7 @@ use crate::{ enumeration::Enumeration, over::OverItem, over_mut::{OverItemInto, OverItemMut}, + post_order::into_iter_filtered::PostOrderIterIntoFiltered, traverser_core::TraverserCore, }, }; @@ -126,4 +127,24 @@ impl TraverserCore for PostOrder { let iter_ptr = PostOrderIterPtr::::from((storage, root)); unsafe { PostOrderIterInto::::from((col, iter_ptr, root)) } } + + fn into_iter_with_storage_filtered<'a, V, M, P, MO, F>( + node_mut: NodeMut<'a, V, M, P, MO>, + storage: impl SoM>, + filter: F, + ) -> impl Iterator> + where + V: TreeVariant + 'a, + M: MemoryPolicy, + P: PinnedStorage, + MO: NodeMutOrientation, + O: Over, + F: Fn(&<::Enumeration as Enumeration>::Item>) -> bool, + { + let (col, root) = node_mut.into_inner(); + let iter_ptr = PostOrderIterPtr::::from((storage, root)); + unsafe { + PostOrderIterIntoFiltered::::from((col, iter_ptr, root), filter) + } + } } From 70a1853976e25ff391458b6a3dc32fc9cbcfc421 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:12:37 +0100 Subject: [PATCH 09/13] require into_iter_with_storage_filtered for all traversers --- src/traversal/traverser_core.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/traversal/traverser_core.rs b/src/traversal/traverser_core.rs index 0d5f0bd..7e7f16a 100644 --- a/src/traversal/traverser_core.rs +++ b/src/traversal/traverser_core.rs @@ -155,10 +155,7 @@ where P: PinnedStorage, MO: NodeMutOrientation, O: Over, - F: Fn(&::Item>) -> bool, - { - core::iter::empty() - } + F: Fn(&::Item>) -> bool; /// Returns an iterator which: /// From 82c4dabf54de9231e335ddae045e28d6b7eb4f4d Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:28:00 +0100 Subject: [PATCH 10/13] implement NodeMut::into_leaves --- src/node_mut.rs | 157 ++++++++++++++++++++++++++------ src/traversal/traverser_core.rs | 1 + 2 files changed, 128 insertions(+), 30 deletions(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index ede4cfe..1abfe66 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -2143,7 +2143,7 @@ where /// * [`Dfs`] for (pre-order) depth-first ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search)) /// * [`PostOrder`] for post-order ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Post-order,_LRN)) /// - /// As opposed to [`walk_mut`], this method does require internal allocation. + /// As opposed to [`walk_mut`], this method does not require internal allocation. /// Furthermore, it allows to attach node depths or sibling indices to the yield values. /// Please see the examples below. /// @@ -2372,7 +2372,7 @@ where /// assert!(tree.is_empty()); /// assert_eq!(tree.get_root(), None); /// ``` - pub fn into_walk(self) -> impl Iterator + use<'a, T, V, M, P, MO> + pub fn into_walk(self) -> impl Iterator where T: Traverser, { @@ -2391,7 +2391,7 @@ where /// * [`Dfs`] for (pre-order) depth-first ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search)) /// * [`PostOrder`] for post-order ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Post-order,_LRN)) /// - /// As opposed to [`into_walk`], this method does require internal allocation. + /// As opposed to [`into_walk`], this method does not require internal allocation. /// Furthermore, it allows to attach node depths or sibling indices to the yield values. /// Please see the examples below. /// @@ -2624,7 +2624,7 @@ where /// * [`Dfs`] for (pre-order) depth-first ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search)) /// * [`PostOrder`] for post-order ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Post-order,_LRN)) /// - /// As opposed to [`leaves_mut`], this method does require internal allocation. + /// As opposed to [`leaves_mut`], this method does not require internal allocation. /// Furthermore, it allows to attach node depths or sibling indices to the yield values. /// Please see the examples below. /// @@ -2756,6 +2756,98 @@ where }) } + pub fn into_leaves(self) -> impl Iterator + where + T: Traverser, + { + let storage = T::Storage::::default(); + T::into_iter_with_storage_filtered(self, storage, |x| { + let ptr = <::Enumeration as Enumeration>::node_data(&x); + unsafe { &*ptr.ptr() }.next().is_empty() + }) + } + + /// Creates an iterator that yields owned (removed) data of all leaves of the subtree rooted at this node. + /// + /// Note that once the returned iterator is dropped, regardless of whether it is completely used up or not, + /// the subtree rooted at this node will be **removed** from the tree it belongs to. + /// If this node is the root of the tree, the tree will be left empty. + /// + /// The order of the elements is determined by the type of the `traverser` which implements [`Traverser`]. + /// Available implementations are: + /// * [`Bfs`] for breadth-first ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search)) + /// * [`Dfs`] for (pre-order) depth-first ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search)) + /// * [`PostOrder`] for post-order ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Post-order,_LRN)) + /// + /// As opposed to [`into_leaves`], this method does not require internal allocation. + /// Furthermore, it allows to attach node depths or sibling indices to the yield values. + /// Please see the examples below. + /// + /// [`into_leaves`]: crate::NodeMut::into_leaves + /// [`Bfs`]: crate::Bfs + /// [`Dfs`]: crate::Dfs + /// [`PostOrder`]: crate::PostOrder + /// + /// > **(!)** As a method that removes nodes from the tree, this method might result in invalidating indices that are + /// > cached earlier in the [`Auto`] mode, but not in the [`Lazy`] mode. Please see the documentation of [MemoryPolicy] + /// > for details of node index validity. Specifically, the examples in the "Lazy Memory Claim: Preventing Invalid Indices" + /// > section presents a convenient way that allows us to make sure that the indices are valid. + /// + /// [`Auto`]: crate::Auto + /// [`Lazy`]: crate::Lazy + /// [`MemoryPolicy`]: crate::MemoryPolicy + /// + /// # Examples + /// + /// ``` + /// use orx_tree::*; + /// + /// // 1 + /// // ╱ ╲ + /// // ╱ ╲ + /// // 2 3 + /// // ╱ ╲ ╱ ╲ + /// // 4 5 6 7 + /// // | | ╱ ╲ + /// // 8 9 10 11 + /// + /// let mut tree = DynTree::new(1); + /// let [id2, id3] = tree.root_mut().push_children([2, 3]); + /// let [id4, _] = tree.node_mut(id2).push_children([4, 5]); + /// tree.node_mut(id4).push_child(8); + /// let [id6, id7] = tree.node_mut(id3).push_children([6, 7]); + /// tree.node_mut(id6).push_child(9); + /// tree.node_mut(id7).push_children([10, 11]); + /// + /// // create the traverser 'dfs' only once, use it many times + /// // to walk over references, mutable references or removed values + /// // without additional allocation + /// + /// let mut dfs = Dfs::default(); + /// + /// // keep indices valid during removals + /// let mut tree = tree.into_lazy_reclaim(); + /// + /// let n3 = tree.node_mut(id3); + /// let leaves: Vec<_> = n3.into_leaves_with(&mut dfs).collect(); + /// assert_eq!(leaves, [9, 10, 11]); + /// + /// // 1 + /// // ╱ + /// // ╱ + /// // 2 + /// // ╱ ╲ + /// // 4 5 + /// // | + /// // 8 + /// let remaining: Vec<_> = tree.root().walk_with(&mut dfs).copied().collect(); + /// assert_eq!(remaining, [1, 2, 4, 8, 5]); + /// + /// let leaves: Vec<_> = tree.root_mut().into_leaves_with(&mut dfs).collect(); + /// assert_eq!(leaves, [8, 5]); + /// + /// assert!(tree.is_empty()); + /// ``` pub fn into_leaves_with( self, traverser: &'a mut T, @@ -3307,11 +3399,7 @@ where #[cfg(test)] mod tst { - use std::println; - use std::string::ToString; - - use crate::{traversal::traverser_core::TraverserCore, *}; - use alloc::vec; + use crate::*; use alloc::vec::Vec; #[test] @@ -3325,32 +3413,41 @@ mod tst { // | | ╱ ╲ // 8 9 10 11 - let mut tree = DynTree::new(1.to_string()); - - let mut root = tree.root_mut(); - let [id2, id3] = root.push_children([2, 3].map(|x| x.to_string())); - - let mut n2 = tree.node_mut(id2); - let [id4, _] = n2.push_children([4, 5].map(|x| x.to_string())); + let mut tree = DynTree::new(1); + let [id2, id3] = tree.root_mut().push_children([2, 3]); + let [id4, _] = tree.node_mut(id2).push_children([4, 5]); + tree.node_mut(id4).push_child(8); + let [id6, id7] = tree.node_mut(id3).push_children([6, 7]); + tree.node_mut(id6).push_child(9); + tree.node_mut(id7).push_children([10, 11]); - tree.node_mut(id4).push_child(8.to_string()); - - let mut n3 = tree.node_mut(id3); - let [id6, id7] = n3.push_children([6, 7].map(|x| x.to_string())); - - tree.node_mut(id6).push_child(9.to_string()); - tree.node_mut(id7) - .push_children([10, 11].map(|x| x.to_string())); - - // create the traverser 'bfs' (or others) only once, use it many times + // create the traverser 'dfs' only once, use it many times // to walk over references, mutable references or removed values // without additional allocation - let mut t = Dfs::default(); + let mut dfs = Dfs::default(); - let leaves: Vec<_> = tree.root_mut().into_leaves_with(&mut t).collect(); - println!("{leaves:?}"); + // keep indices valid during removals + let mut tree = tree.into_lazy_reclaim(); - assert_eq!(leaves.len(), 33); + let n3 = tree.node_mut(id3); + let leaves: Vec<_> = n3.into_leaves_with(&mut dfs).collect(); + assert_eq!(leaves, [9, 10, 11]); + + // 1 + // ╱ + // ╱ + // 2 + // ╱ ╲ + // 4 5 + // | + // 8 + let remaining: Vec<_> = tree.root().walk_with(&mut dfs).copied().collect(); + assert_eq!(remaining, [1, 2, 4, 8, 5]); + + let leaves: Vec<_> = tree.root_mut().into_leaves_with(&mut dfs).collect(); + assert_eq!(leaves, [8, 5]); + + assert!(tree.is_empty()); } } diff --git a/src/traversal/traverser_core.rs b/src/traversal/traverser_core.rs index 7e7f16a..0ce4b82 100644 --- a/src/traversal/traverser_core.rs +++ b/src/traversal/traverser_core.rs @@ -133,6 +133,7 @@ where P: PinnedStorage, MO: NodeMutOrientation, O: OverMut; + fn into_iter_with_storage<'a, V, M, P, MO>( node_mut: NodeMut<'a, V, M, P, MO>, storage: impl SoM>, From 0370c97d2fbfefebfe355ce87a2350167ea9a71c Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:29:24 +0100 Subject: [PATCH 11/13] implement NodeMut::into_leaves --- src/node_mut.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index 1abfe66..4160c5f 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -3421,17 +3421,11 @@ mod tst { tree.node_mut(id6).push_child(9); tree.node_mut(id7).push_children([10, 11]); - // create the traverser 'dfs' only once, use it many times - // to walk over references, mutable references or removed values - // without additional allocation - - let mut dfs = Dfs::default(); - // keep indices valid during removals let mut tree = tree.into_lazy_reclaim(); let n3 = tree.node_mut(id3); - let leaves: Vec<_> = n3.into_leaves_with(&mut dfs).collect(); + let leaves: Vec<_> = n3.into_leaves::().collect(); assert_eq!(leaves, [9, 10, 11]); // 1 @@ -3442,10 +3436,10 @@ mod tst { // 4 5 // | // 8 - let remaining: Vec<_> = tree.root().walk_with(&mut dfs).copied().collect(); + let remaining: Vec<_> = tree.root().walk::().copied().collect(); assert_eq!(remaining, [1, 2, 4, 8, 5]); - let leaves: Vec<_> = tree.root_mut().into_leaves_with(&mut dfs).collect(); + let leaves: Vec<_> = tree.root_mut().into_leaves::().collect(); assert_eq!(leaves, [8, 5]); assert!(tree.is_empty()); From 07fa282ab89a699731a7b566a33a9ca2aec5aec1 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:31:57 +0100 Subject: [PATCH 12/13] document and test into_leaves --- src/node_mut.rs | 135 ++++++++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 50 deletions(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index 4160c5f..67d26c6 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -11,7 +11,6 @@ use crate::{ Over, OverData, OverMut, enumeration::Enumeration, enumerations::Val, - node_item::NodeItem, over::OverPtr, over_mut::{OverItemInto, OverItemMut}, post_order::iter_ptr::PostOrderIterPtr, @@ -2756,6 +2755,91 @@ where }) } + /// Creates an iterator that yields owned (removed) data of all leaves of the subtree rooted at this node. + /// + /// Note that once the returned iterator is dropped, regardless of whether it is completely used up or not, + /// the subtree rooted at this node will be **removed** from the tree it belongs to. + /// If this node is the root of the tree, the tree will be left empty. + /// + /// The order of the elements is determined by the generic [`Traverser`] parameter `T`. + /// Available implementations are: + /// * [`Bfs`] for breadth-first ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search)) + /// * [`Dfs`] for (pre-order) depth-first ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search)) + /// * [`PostOrder`] for post-order ([wikipedia](https://en.wikipedia.org/wiki/Tree_traversal#Post-order,_LRN)) + /// + /// See also [`leaves`] and [`leaves_mut`] for iterators over shared and mutable references, respectively. + /// + /// Note that tree traversing methods typically allocate a temporary data structure that is dropped once the + /// iterator is dropped. + /// In use cases where we repeatedly iterate using any of the **leaves** methods over different nodes or different + /// trees, we can avoid the allocation by creating the traverser only once and using [`leaves_with`], [`leaves_mut_with`] + /// and [`into_leaves_with`] methods instead. + /// These methods additionally allow for iterating over nodes rather than data; and yielding node depths and sibling + /// indices in addition to node data. + /// + /// > **(!)** As a method that removes nodes from the tree, this method might result in invalidating indices that are + /// > cached earlier in the [`Auto`] mode, but not in the [`Lazy`] mode. Please see the documentation of [MemoryPolicy] + /// > for details of node index validity. Specifically, the examples in the "Lazy Memory Claim: Preventing Invalid Indices" + /// > section presents a convenient way that allows us to make sure that the indices are valid. + /// + /// [`Auto`]: crate::Auto + /// [`Lazy`]: crate::Lazy + /// [`MemoryPolicy`]: crate::MemoryPolicy + /// + /// [`Bfs`]: crate::Bfs + /// [`Dfs`]: crate::Dfs + /// [`PostOrder`]: crate::PostOrder + /// [`leaves`]: crate::NodeRef::leaves + /// [`leaves_mut`]: crate::NodeMut::walk_mut + /// [`leaves_with`]: crate::NodeRef::leaves_with + /// [`leaves_mut_with`]: crate::NodeMut::leaves_mut_with + /// [`into_leaves_with`]: crate::NodeMut::into_leaves_with + /// + /// # Examples + /// + /// ``` + /// use orx_tree::*; + /// + /// // 1 + /// // ╱ ╲ + /// // ╱ ╲ + /// // 2 3 + /// // ╱ ╲ ╱ ╲ + /// // 4 5 6 7 + /// // | | ╱ ╲ + /// // 8 9 10 11 + /// + /// let mut tree = DynTree::new(1); + /// let [id2, id3] = tree.root_mut().push_children([2, 3]); + /// let [id4, _] = tree.node_mut(id2).push_children([4, 5]); + /// tree.node_mut(id4).push_child(8); + /// let [id6, id7] = tree.node_mut(id3).push_children([6, 7]); + /// tree.node_mut(id6).push_child(9); + /// tree.node_mut(id7).push_children([10, 11]); + /// + /// // keep indices valid during removals + /// let mut tree = tree.into_lazy_reclaim(); + /// + /// let n3 = tree.node_mut(id3); + /// let leaves: Vec<_> = n3.into_leaves::().collect(); + /// assert_eq!(leaves, [9, 10, 11]); + /// + /// // 1 + /// // ╱ + /// // ╱ + /// // 2 + /// // ╱ ╲ + /// // 4 5 + /// // | + /// // 8 + /// let remaining: Vec<_> = tree.root().walk::().copied().collect(); + /// assert_eq!(remaining, [1, 2, 4, 8, 5]); + /// + /// let leaves: Vec<_> = tree.root_mut().into_leaves::().collect(); + /// assert_eq!(leaves, [8, 5]); + /// + /// assert!(tree.is_empty()); + /// ``` pub fn into_leaves(self) -> impl Iterator where T: Traverser, @@ -3396,52 +3480,3 @@ where self.node().prev().get().map(|p| NodeMut::new(self.col, p)) } } - -#[cfg(test)] -mod tst { - use crate::*; - use alloc::vec::Vec; - - #[test] - fn abc() { - // 1 - // ╱ ╲ - // ╱ ╲ - // 2 3 - // ╱ ╲ ╱ ╲ - // 4 5 6 7 - // | | ╱ ╲ - // 8 9 10 11 - - let mut tree = DynTree::new(1); - let [id2, id3] = tree.root_mut().push_children([2, 3]); - let [id4, _] = tree.node_mut(id2).push_children([4, 5]); - tree.node_mut(id4).push_child(8); - let [id6, id7] = tree.node_mut(id3).push_children([6, 7]); - tree.node_mut(id6).push_child(9); - tree.node_mut(id7).push_children([10, 11]); - - // keep indices valid during removals - let mut tree = tree.into_lazy_reclaim(); - - let n3 = tree.node_mut(id3); - let leaves: Vec<_> = n3.into_leaves::().collect(); - assert_eq!(leaves, [9, 10, 11]); - - // 1 - // ╱ - // ╱ - // 2 - // ╱ ╲ - // 4 5 - // | - // 8 - let remaining: Vec<_> = tree.root().walk::().copied().collect(); - assert_eq!(remaining, [1, 2, 4, 8, 5]); - - let leaves: Vec<_> = tree.root_mut().into_leaves::().collect(); - assert_eq!(leaves, [8, 5]); - - assert!(tree.is_empty()); - } -} From 15e3a5b24dec4f19f22793792c0fc9615f1da62e Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 23 Nov 2025 22:34:45 +0100 Subject: [PATCH 13/13] clippy fix --- src/node_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index 67d26c6..b31cf6b 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -2941,7 +2941,7 @@ where T: Traverser, { T::into_iter_with_storage_filtered(self, traverser.storage_mut(), |x| { - let ptr = ::node_data(&x); + let ptr = ::node_data(x); unsafe { &*ptr.ptr() }.next().is_empty() }) }