From ed2942b98adba1275099d693f7358c6558d2129d Mon Sep 17 00:00:00 2001 From: MrElectrify Date: Wed, 6 Mar 2024 22:10:21 -0800 Subject: [PATCH] Implement rbtree iterators, increment version to 0.10.3 --- Cargo.lock | 83 +++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- src/internal/rb_tree/iter.rs | 33 ++++++++++++++ src/internal/rb_tree/mod.rs | 21 +++++++++ src/internal/rb_tree/node.rs | 49 +++++++++++++++++---- src/map/mod.rs | 30 +++++++++++++ src/set/mod.rs | 8 ++++ 7 files changed, 217 insertions(+), 10 deletions(-) create mode 100644 src/internal/rb_tree/iter.rs diff --git a/Cargo.lock b/Cargo.lock index b95ed95..2a6630c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,15 +8,32 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "duplicate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +dependencies = [ + "heck", + "proc-macro-error", +] + [[package]] name = "eastl-rs" -version = "0.10.1" +version = "0.10.3" dependencies = [ + "duplicate", "memoffset", "moveit", "superslice", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "memoffset" version = "0.9.0" @@ -32,8 +49,72 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87d7335204cb6ef7bd647fa6db0be3e4d7aa25b5823a7aa030027ddf512cefba" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + [[package]] name = "superslice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/Cargo.toml b/Cargo.toml index def4d4d..e6da32f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "eastl-rs" authors = ["Andrew Buck"] description = "EASTL binary-compatible Rust implementations" documentation = "https://docs.rs/crate/eastl-rs" -version = "0.10.2" +version = "0.10.3" edition = "2021" license-file = "LICENSE" readme = "README.md" @@ -11,6 +11,7 @@ repository = "https://github.com/MrElectrify/EASTL-rs" keywords = ["EASTL", "STL"] [dependencies] +duplicate = "1.0" moveit = "0.6.0" superslice = "1.0" diff --git a/src/internal/rb_tree/iter.rs b/src/internal/rb_tree/iter.rs new file mode 100644 index 0000000..cb9e9c1 --- /dev/null +++ b/src/internal/rb_tree/iter.rs @@ -0,0 +1,33 @@ +use crate::internal::rb_tree::node::Node; +use std::marker::PhantomData; + +/// An iterator over a Red-Black tree's nodes. +pub struct Iter<'a, K, V> { + pub(super) node: *mut Node, + pub(super) _marker: PhantomData<&'a ()>, +} + +pub struct IterMut<'a, K, V> { + pub(super) node: *mut Node, + pub(super) _marker: PhantomData<&'a mut ()>, +} + +impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option { + unsafe { self.node.as_ref() } + .and_then(Node::next) + .map(|node| (node.key(), node.val())) + } +} + +impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + fn next(&mut self) -> Option { + unsafe { self.node.as_mut() } + .and_then(Node::next_mut) + .map(|node| (&node.pair.0, &mut node.pair.1)) + } +} diff --git a/src/internal/rb_tree/mod.rs b/src/internal/rb_tree/mod.rs index d1bfc51..2a27429 100644 --- a/src/internal/rb_tree/mod.rs +++ b/src/internal/rb_tree/mod.rs @@ -1,10 +1,14 @@ +use crate::internal::rb_tree::iter::{Iter, IterMut}; use crate::{ allocator::Allocator, compare::{Compare, Less}, }; +use duplicate::duplicate_item; +use std::marker::PhantomData; use self::node::Node; +pub mod iter; mod node; #[repr(C)] @@ -154,6 +158,23 @@ impl> RBTree { self.len() == 0 } + /// Returns an iterator over the elements in the tree. + /// + /// # Safety + /// This iterator is not tested as trees are only partially implemented. + #[duplicate_item( + iter Self Iter; + [iter] [&Self] [Iter]; + [iter_mut] [&mut Self] [IterMut]; + )] + #[allow(clippy::needless_arbitrary_self_type)] + pub unsafe fn iter(self: Self) -> Iter { + Iter { + node: self.parent, + _marker: PhantomData, + } + } + /// Returns the number of elements in the red-black tree pub fn len(&self) -> usize { self.size as usize diff --git a/src/internal/rb_tree/node.rs b/src/internal/rb_tree/node.rs index a91bad5..73fe345 100644 --- a/src/internal/rb_tree/node.rs +++ b/src/internal/rb_tree/node.rs @@ -1,4 +1,5 @@ -use std::{fmt::Debug, marker::PhantomData}; +use duplicate::duplicate_item; +use std::{fmt::Debug, marker::PhantomData, ptr}; /// The color of a red-black tree node #[repr(C)] @@ -87,8 +88,7 @@ pub(crate) struct Node { pub right: *mut Node, pub left: *mut Node, pub parent: ParentColor, - key: K, - val: V, + pub(crate) pair: (K, V), } impl Default for Node { @@ -97,8 +97,7 @@ impl Default for Node { right: std::ptr::null_mut(), left: std::ptr::null_mut(), parent: ParentColor::default(), - key: K::default(), - val: V::default(), + pair: (K::default(), V::default()), } } } @@ -193,19 +192,53 @@ impl Node { old_right } + /// Returns the next node in the tree, in increasing order. + /// + /// # Safety + /// This method returns + #[duplicate_item( + next Self Node; + [next] [Self] [Node]; + [next_mut] [mut Self] [mut Node] + )] + pub fn next(mut self: &Self) -> Option<&Node> { + if let Some(mut right_node) = unsafe { self.right.as_mut() } { + // the successor lies in the right subtree. find the smallest value in the greater + // subtree, which is the left-most node. + while let Some(left_node) = unsafe { right_node.left.as_mut() } { + right_node = left_node + } + + Some(right_node) + } else { + // the successor is contained within the ancestors. find the first node that is its + // parent's left node (meaning the parent is the first node greater than the node) + // safety: the parent of a node is always present, because the parent of the root node + // is inside the tree itself + let mut parent = unsafe { &mut *self.parent.ptr() }; + while ptr::eq(self as *const _, parent.right as *const _) { + let parent_parent = unsafe { &mut *parent.parent.ptr() }; + self = parent; + parent = parent_parent; + } + + Some(parent) + } + } + /// The key stored in the node pub fn key(&self) -> &K { - &self.key + &self.pair.0 } /// The value stored in the node pub fn val(&self) -> &V { - &self.val + &self.pair.1 } /// The value stored in the node pub fn val_mut(&mut self) -> &mut V { - &mut self.val + &mut self.pair.1 } } diff --git a/src/map/mod.rs b/src/map/mod.rs index 372c708..68602ed 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -1,8 +1,11 @@ +use crate::internal::rb_tree::iter::{Iter, IterMut}; use crate::{ allocator::Allocator, compare::{Compare, Less}, internal::rb_tree::RBTree, }; +use duplicate::duplicate_item; +use std::fmt::{Debug, Formatter}; /// A map backed by a red-black tree that is always ordered. /// Insertion, lookup, and removal are O(nlgn). If you do not @@ -103,6 +106,20 @@ impl> Map { self.inner.is_empty() } + /// Returns an iterator over the elements in the tree. + /// + /// # Safety + /// This iterator is not tested as trees are only partially implemented. + #[duplicate_item( + iter Self Iter; + [iter] [&Self] [Iter]; + [iter_mut] [&mut Self] [IterMut]; + )] + #[allow(clippy::needless_arbitrary_self_type)] + pub unsafe fn iter(self: Self) -> Iter { + self.inner.iter() + } + /// Returns the number of elements in the map pub fn len(&self) -> usize { self.inner.len() @@ -130,6 +147,19 @@ impl> Map { } } +impl> Debug for Map { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{{{}}}", + unsafe { self.iter() } + .map(|(k, v)| format!("{k:?}: {v:?}")) + .collect::>() + .join(",") + ) + } +} + unsafe impl + Send> Send for Map { diff --git a/src/set/mod.rs b/src/set/mod.rs index 4569ca6..e0bfdaa 100644 --- a/src/set/mod.rs +++ b/src/set/mod.rs @@ -77,6 +77,14 @@ impl> Set { self.inner._insert(key, ()).is_some() } + /// Returns an iterator over the elements in the tree. + /// + /// # Safety + /// This iterator is not tested as trees are only partially implemented. + pub unsafe fn iter(&self) -> impl Iterator { + self.inner.iter().map(|(k, _)| k) + } + /// Returns true if the set contains no elements pub fn is_empty(&self) -> bool { self.inner.is_empty()