diff --git a/commons/zenoh-keyexpr/Cargo.toml b/commons/zenoh-keyexpr/Cargo.toml index 59e71ebee1..41456af1ec 100644 --- a/commons/zenoh-keyexpr/Cargo.toml +++ b/commons/zenoh-keyexpr/Cargo.toml @@ -28,7 +28,6 @@ default = ["std"] std = ["zenoh-result/std", "dep:schemars"] [dependencies] -ahash = { workspace = true } keyed-set = { workspace = true } rand = { workspace = true, features = ["alloc", "getrandom"] } schemars = { workspace = true, optional = true } @@ -41,6 +40,7 @@ hashbrown = { workspace = true } # NOTE: May cause problems when testing no_std stuff. Check this tool: https://docs.rs/crate/cargo-no-dev-deps/0.1.0 [dev-dependencies] +ahash = { workspace = true } criterion = { workspace = true } lazy_static = { workspace = true } rand = { workspace = true, features = ["default"] } diff --git a/commons/zenoh-keyexpr/src/keyexpr_tree/box_tree.rs b/commons/zenoh-keyexpr/src/keyexpr_tree/box_tree.rs index 39efd3af75..60b797bf4a 100644 --- a/commons/zenoh-keyexpr/src/keyexpr_tree/box_tree.rs +++ b/commons/zenoh-keyexpr/src/keyexpr_tree/box_tree.rs @@ -117,6 +117,20 @@ where IterOrOption::Opt(node) } } + + type IncluderItem = ::Item; + type Includer = IterOrOption< + Includer<'a, Children, Box>, Weight>, + &'a Self::Node, + >; + fn nodes_including(&'a self, ke: &'a keyexpr) -> Self::Includer { + if self.wildness.get() || ke.is_wild() { + Includer::new(&self.children, ke).into() + } else { + let node = self.node(ke); + IterOrOption::Opt(node) + } + } } impl< 'a, @@ -218,6 +232,19 @@ where IterOrOption::Opt(node) } } + type IncluderItemMut = ::Item; + type IncluderMut = IterOrOption< + IncluderMut<'a, Children, Box>, Weight>, + &'a mut Self::Node, + >; + fn nodes_including_mut(&'a mut self, ke: &'a keyexpr) -> Self::IncluderMut { + if self.wildness.get() || ke.is_wild() { + IncluderMut::new(&mut self.children, ke).into() + } else { + let node = self.node_mut(ke); + IterOrOption::Opt(node) + } + } fn prune_where bool>(&mut self, mut predicate: F) { let mut wild = false; diff --git a/commons/zenoh-keyexpr/src/keyexpr_tree/iters/includer.rs b/commons/zenoh-keyexpr/src/keyexpr_tree/iters/includer.rs new file mode 100644 index 0000000000..a22d0804b1 --- /dev/null +++ b/commons/zenoh-keyexpr/src/keyexpr_tree/iters/includer.rs @@ -0,0 +1,337 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +use crate::keyexpr_tree::*; +use alloc::vec::Vec; + +struct StackFrame<'a, Children: IChildrenProvider, Node: UIKeyExprTreeNode, Weight> +where + Children::Assoc: IChildren + 'a, + >::Node: 'a, +{ + iterator: >::Iter<'a>, + start: usize, + end: usize, + _marker: core::marker::PhantomData, +} +pub struct Includer<'a, Children: IChildrenProvider, Node: UIKeyExprTreeNode, Weight> +where + Children::Assoc: IChildren + 'a, +{ + key: &'a keyexpr, + ke_indices: Vec, + iterators: Vec>, +} + +impl<'a, Children: IChildrenProvider, Node: UIKeyExprTreeNode, Weight> + Includer<'a, Children, Node, Weight> +where + Children::Assoc: IChildren + 'a, +{ + pub(crate) fn new(children: &'a Children::Assoc, key: &'a keyexpr) -> Self { + let mut ke_indices = Vec::with_capacity(32); + ke_indices.push(0); + let mut iterators = Vec::with_capacity(16); + iterators.push(StackFrame { + iterator: children.children(), + start: 0, + end: 1, + _marker: Default::default(), + }); + Self { + key, + ke_indices, + iterators, + } + } +} + +impl< + 'a, + Children: IChildrenProvider, + Node: UIKeyExprTreeNode + 'a, + Weight, + > Iterator for Includer<'a, Children, Node, Weight> +where + Children::Assoc: IChildren + 'a, +{ + type Item = &'a Node; + fn next(&mut self) -> Option { + loop { + let StackFrame { + iterator, + start, + end, + _marker, + } = self.iterators.last_mut()?; + match iterator.next() { + Some(node) => { + let mut node_matches = false; + let new_start = *end; + let mut new_end = *end; + macro_rules! push { + ($index: expr) => { + let index = $index; + if new_end == new_start + || self.ke_indices[new_start..new_end] + .iter() + .rev() + .all(|c| *c < index) + { + self.ke_indices.push(index); + new_end += 1; + } + }; + } + let chunk = node.chunk(); + unsafe { node.as_node().__keyexpr() }; + let chunk_is_super = chunk == "**"; + if chunk_is_super { + let mut latest_idx = usize::MAX; + 'outer: for i in *start..*end { + let mut kec_start = self.ke_indices[i]; + if kec_start == self.key.len() { + node_matches = true; + break; + } + if latest_idx <= kec_start && latest_idx != usize::MAX { + continue; + } + loop { + push!(kec_start); + latest_idx = kec_start; + let key = &self.key.as_bytes()[kec_start..]; + if key[0] == b'@' { + break; + } + match key.iter().position(|&c| c == b'/') { + Some(kec_end) => kec_start += kec_end + 1, + None => { + node_matches = true; + break 'outer; + } + } + } + } + } else { + for i in *start..*end { + let kec_start = self.ke_indices[i]; + if kec_start == self.key.len() { + break; + } + let key = &self.key.as_bytes()[kec_start..]; + unsafe { keyexpr::from_slice_unchecked(key) }; + match key.iter().position(|&c| c == b'/') { + Some(kec_end) => { + let subkey = + unsafe { keyexpr::from_slice_unchecked(&key[..kec_end]) }; + if chunk.includes(subkey) { + push!(kec_start + kec_end + 1); + } + } + None => { + let key = unsafe { keyexpr::from_slice_unchecked(key) }; + if chunk.includes(key) { + push!(self.key.len()); + node_matches = true; + } + } + } + } + } + if new_end > new_start { + let iterator = unsafe { node.as_node().__children() }.children(); + self.iterators.push(StackFrame { + iterator, + start: new_start, + end: new_end, + _marker: Default::default(), + }) + } + if node_matches { + return Some(node.as_node()); + } + } + None => { + if let Some(StackFrame { start, .. }) = self.iterators.pop() { + self.ke_indices.truncate(start); + } + } + } + } + } +} +struct StackFrameMut<'a, Children: IChildrenProvider, Node: UIKeyExprTreeNode, Weight> +where + Children::Assoc: IChildren + 'a, + >::Node: 'a, +{ + iterator: >::IterMut<'a>, + start: usize, + end: usize, + _marker: core::marker::PhantomData, +} + +pub struct IncluderMut< + 'a, + Children: IChildrenProvider, + Node: UIKeyExprTreeNode, + Weight, +> where + Children::Assoc: IChildren + 'a, +{ + key: &'a keyexpr, + ke_indices: Vec, + iterators: Vec>, +} + +impl<'a, Children: IChildrenProvider, Node: UIKeyExprTreeNode, Weight> + IncluderMut<'a, Children, Node, Weight> +where + Children::Assoc: IChildren + 'a, +{ + pub(crate) fn new(children: &'a mut Children::Assoc, key: &'a keyexpr) -> Self { + let mut ke_indices = Vec::with_capacity(32); + ke_indices.push(0); + let mut iterators = Vec::with_capacity(16); + iterators.push(StackFrameMut { + iterator: children.children_mut(), + start: 0, + end: 1, + _marker: Default::default(), + }); + Self { + key, + ke_indices, + iterators, + } + } +} + +impl< + 'a, + Children: IChildrenProvider, + Node: IKeyExprTreeNodeMut + 'a, + Weight, + > Iterator for IncluderMut<'a, Children, Node, Weight> +where + Children::Assoc: IChildren + 'a, +{ + type Item = &'a mut >::Node; + fn next(&mut self) -> Option { + loop { + let StackFrameMut { + iterator, + start, + end, + _marker, + } = self.iterators.last_mut()?; + match iterator.next() { + Some(node) => { + let mut node_matches = false; + let new_start = *end; + let mut new_end = *end; + macro_rules! push { + ($index: expr) => { + let index = $index; + if new_end == new_start + || self.ke_indices[new_start..new_end] + .iter() + .rev() + .all(|c| *c < index) + { + self.ke_indices.push(index); + new_end += 1; + } + }; + } + let chunk = node.chunk(); + let chunk_is_super = chunk == "**"; + if chunk_is_super { + let mut latest_idx = usize::MAX; + 'outer: for i in *start..*end { + let mut kec_start = self.ke_indices[i]; + if kec_start == self.key.len() { + node_matches = true; + break; + } + if latest_idx <= kec_start && latest_idx != usize::MAX { + continue; + } + loop { + push!(kec_start); + latest_idx = kec_start; + let key = &self.key.as_bytes()[kec_start..]; + if key[0] == b'@' { + break; + } + match key.iter().position(|&c| c == b'/') { + Some(kec_end) => kec_start += kec_end + 1, + None => { + node_matches = true; + break 'outer; + } + } + } + } + } else { + for i in *start..*end { + let kec_start = self.ke_indices[i]; + if kec_start == self.key.len() { + break; + } + let key = &self.key.as_bytes()[kec_start..]; + unsafe { keyexpr::from_slice_unchecked(key) }; + match key.iter().position(|&c| c == b'/') { + Some(kec_end) => { + let subkey = + unsafe { keyexpr::from_slice_unchecked(&key[..kec_end]) }; + if chunk.includes(subkey) { + push!(kec_start + kec_end + 1); + } + } + None => { + let key = unsafe { keyexpr::from_slice_unchecked(key) }; + if chunk.includes(key) { + push!(self.key.len()); + node_matches = true; + } + } + } + } + } + if new_end > new_start { + let iterator = unsafe { &mut *(node.as_node_mut() as *mut Node) } + .children_mut() + .children_mut(); + self.iterators.push(StackFrameMut { + iterator, + start: new_start, + end: new_end, + _marker: Default::default(), + }) + } + if node_matches { + return Some(node); + } + } + None => { + if let Some(StackFrameMut { start, .. }) = self.iterators.pop() { + self.ke_indices.truncate(start); + } + } + } + } + } +} diff --git a/commons/zenoh-keyexpr/src/keyexpr_tree/iters/mod.rs b/commons/zenoh-keyexpr/src/keyexpr_tree/iters/mod.rs index 3550ed0431..19545dbdd7 100644 --- a/commons/zenoh-keyexpr/src/keyexpr_tree/iters/mod.rs +++ b/commons/zenoh-keyexpr/src/keyexpr_tree/iters/mod.rs @@ -18,3 +18,5 @@ mod intersection; pub use intersection::{Intersection, IntersectionMut}; mod inclusion; pub use inclusion::{Inclusion, InclusionMut}; +mod includer; +pub use includer::{Includer, IncluderMut}; diff --git a/commons/zenoh-keyexpr/src/keyexpr_tree/test.rs b/commons/zenoh-keyexpr/src/keyexpr_tree/test.rs index 0a65b46588..cd640a53c5 100644 --- a/commons/zenoh-keyexpr/src/keyexpr_tree/test.rs +++ b/commons/zenoh-keyexpr/src/keyexpr_tree/test.rs @@ -124,8 +124,8 @@ fn test_keyset + Debug>(keys: &[K]) { } } let mut exclone = expected.clone(); - for node in tree.intersecting_nodes(dbg!(target)) { - let ke = dbg!(node.keyexpr()); + for node in tree.intersecting_nodes(target) { + let ke = node.keyexpr(); let weight = node.weight(); assert_eq!( expected @@ -198,6 +198,46 @@ fn test_keyset + Debug>(keys: &[K]) { target.deref(), &exclone ); + for (k, v) in &map { + if k.includes(target) { + assert!(expected.insert(k, v).is_none()); + } + } + exclone = expected.clone(); + for node in tree.nodes_including(dbg!(target)) { + let ke = node.keyexpr(); + let weight = node.weight(); + assert_eq!( + expected + .remove(dbg!(&ke)) + .unwrap_or_else(|| panic!("Couldn't find {ke} in {target}'s expected output")) + .as_ref(), + weight + ) + } + for node in tree.nodes_including_mut(target) { + let ke = node.keyexpr(); + let weight = node.weight(); + assert_eq!( + exclone + .remove(&ke) + .unwrap_or_else(|| panic!("Couldn't find {ke} in {target}'s expected output")) + .as_ref(), + weight + ) + } + assert!( + expected.is_empty(), + "MISSING INCLUDES FOR {}: {:?}", + target.deref(), + &expected + ); + assert!( + exclone.is_empty(), + "MISSING MUTABLE INCLUDES FOR {}: {:?}", + target.deref(), + &exclone + ); #[cfg(feature = "std")] { println!("{} OK", target.deref()); @@ -369,7 +409,7 @@ fn test_keyarctree>(keys: &[K]) { #[test] fn keyed_set_tree() { - let keys: [&keyexpr; 14] = [ + let keys: [&keyexpr; 16] = [ "a/b/**/c/**", "a/b/c", "a/b/c", @@ -384,6 +424,8 @@ fn keyed_set_tree() { "@c/**", "@c/a", "a/@c", + "b/a$*a/b/bb", + "**/b$*/bb", ] .map(into_ke); test_keyset(&keys); diff --git a/commons/zenoh-keyexpr/src/keyexpr_tree/traits/mod.rs b/commons/zenoh-keyexpr/src/keyexpr_tree/traits/mod.rs index a1b0f3aead..1a77d4fea8 100644 --- a/commons/zenoh-keyexpr/src/keyexpr_tree/traits/mod.rs +++ b/commons/zenoh-keyexpr/src/keyexpr_tree/traits/mod.rs @@ -37,6 +37,12 @@ pub trait IKeyExprTree<'a, Weight> { /// /// Note that nodes without a `Weight` will also be yielded by the iterator. fn included_nodes(&'a self, key: &'a keyexpr) -> Self::Inclusion; + type IncluderItem; + type Includer: Iterator; + /// Iterates over all nodes of the tree whose KE is includes the given `key`. + /// + /// Note that nodes without a `Weight` will also be yielded by the iterator. + fn nodes_including(&'a self, key: &'a keyexpr) -> Self::Includer; } /// The basic mutable methods of all all KeTrees @@ -65,6 +71,12 @@ pub trait IKeyExprTreeMut<'a, Weight>: IKeyExprTree<'a, Weight> { /// /// Note that nodes without a `Weight` will also be yielded by the iterator. fn included_nodes_mut(&'a mut self, key: &'a keyexpr) -> Self::InclusionMut; + type IncluderItemMut; + type IncluderMut: Iterator; + /// Iterates over all nodes of the tree whose KE includes the given `key`. + /// + /// Note that nodes without a `Weight` will also be yielded by the iterator. + fn nodes_including_mut(&'a mut self, key: &'a keyexpr) -> Self::IncluderMut; /// Prunes node from the tree where the predicate returns `true`. /// /// Note that nodes that still have children will not be pruned.