#![no_std]
#![forbid(non_ascii_idents)]
#![warn(missing_docs)]
#![warn(let_underscore)]
#![warn(unsafe_code)]
#![warn(clippy::pedantic)]
#![warn(clippy::cargo)]
#![allow(clippy::multiple_crate_versions, reason = "Unresolvable")]
#![warn(clippy::nursery)]
#![warn(clippy::restriction)]
#![allow(clippy::blanket_clippy_restriction_lints, reason = "Conflicting lint")]
#![allow(clippy::allow_attributes, reason = "Conflicting lint")]
#![allow(clippy::pattern_type_mismatch, reason = "Conflicting lint")]
#![allow(clippy::separated_literal_suffix, reason = "Conflicting lint")]
#![allow(clippy::semicolon_outside_block, reason = "Conflicting lint")]
#![allow(
clippy::field_scoped_visibility_modifiers,
reason = "Used by IndependentWeave::from()"
)]
#![allow(
clippy::missing_inline_in_public_items,
reason = "Reasonable candidates have already been inlined"
)]
#![allow(clippy::inline_always, reason = "Performance")]
#![allow(clippy::exhaustive_enums, reason = "API")]
#![allow(clippy::exhaustive_structs, reason = "API")]
#![allow(clippy::little_endian_bytes, reason = "API")]
#![allow(clippy::partial_pub_fields, reason = "API")]
#![allow(clippy::pub_use, reason = "API")]
#![allow(clippy::arbitrary_source_item_ordering, reason = "Readability")]
#![allow(clippy::question_mark_used, reason = "Readability")]
#![allow(clippy::single_call_fn, reason = "Readability")]
#![allow(clippy::single_char_lifetime_names, reason = "Readability")]
#![allow(clippy::else_if_without_else, reason = "Style")]
#![allow(clippy::if_then_some_else_none, reason = "Style")]
#![allow(clippy::implicit_return, reason = "Style")]
#![allow(clippy::min_ident_chars, reason = "Style")]
#![allow(clippy::mod_module_files, reason = "Style")]
#![allow(clippy::module_name_repetitions, reason = "Style")]
#![allow(clippy::multiple_inherent_impl, reason = "Style")]
#![allow(clippy::try_err, reason = "Style")]
#![allow(clippy::allow_attributes_without_reason)] #![allow(clippy::indexing_slicing)] #![allow(clippy::unwrap_in_result)] #![allow(clippy::unwrap_used)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::shadow_unrelated)] #![allow(clippy::shadow_reuse)] #![allow(clippy::shadow_same)]
mod contract;
pub mod dependent;
pub mod independent;
pub mod wrappers;
#[cfg(all(
feature = "layout",
any(target_pointer_width = "32", target_pointer_width = "64")
))]
pub mod layout;
#[cfg(feature = "rkyv")]
pub mod versioning;
pub use contracts;
pub use hashbrown;
pub use indexmap;
#[cfg(feature = "layout")]
pub use glam;
#[cfg(feature = "layout")]
pub use tinyvec;
#[cfg(feature = "rkyv")]
pub use rkyv;
#[cfg(feature = "serde")]
pub use serde;
#[cfg(feature = "loro")]
pub use loro;
extern crate alloc;
use alloc::vec::Vec;
use core::{
cmp::{Ordering, Reverse},
hash::{BuildHasher, Hash},
};
use hashbrown::{HashMap, hash_map::Entry};
use scratchpads::{ScratchpadMap, ScratchpadSet, ScratchpadVec};
#[cfg(feature = "rkyv")]
use rkyv::collections::swiss_table::{ArchivedHashMap, ArchivedIndexSet};
#[must_use]
pub trait Node<K, T>
where
K: Hash + Copy + Eq + Ord,
{
type From;
type To;
#[must_use]
fn id(&self) -> K;
#[must_use]
fn from(&self) -> &Self::From;
#[must_use]
fn to(&self) -> &Self::To;
#[must_use]
fn is_active(&self) -> bool;
#[must_use]
fn contents(&self) -> &T;
}
pub trait DiscreteContents: Sized {
fn split(self, at: usize) -> DiscreteContentResult<Self>;
fn merge(self, value: Self) -> DiscreteContentResult<Self>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_docs, reason = "Enum items are self-explanatory")]
#[must_use]
pub enum DiscreteContentResult<T> {
One(T),
Two(T, T),
}
impl DiscreteContents for () {
fn split(self, _at: usize) -> DiscreteContentResult<Self> {
DiscreteContentResult::Two((), ())
}
fn merge(self, _value: Self) -> DiscreteContentResult<Self> {
DiscreteContentResult::One(())
}
}
pub trait IndependentContents {}
impl IndependentContents for () {}
pub trait DeduplicatableContents {
#[must_use]
fn is_duplicate_of(&self, other: &Self) -> bool;
}
#[must_use]
pub trait Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
type Nodes;
type Roots;
#[must_use]
fn len(&self) -> usize;
#[must_use]
fn is_empty(&self) -> bool;
#[must_use]
fn nodes(&self) -> &Self::Nodes;
#[must_use]
fn roots(&self) -> &Self::Roots;
#[must_use]
fn contains(&self, id: &K) -> bool;
#[must_use]
fn contains_active(&self, id: &K) -> bool;
#[must_use]
fn get(&self, id: &K) -> Option<&N>;
#[must_use]
fn get_parents(&self, id: &K) -> Option<&N::From>;
#[must_use]
fn get_children(&self, id: &K) -> Option<&N::To>;
#[must_use]
fn get_contents(&self, id: &K) -> Option<&T>;
fn get_ordered_identifiers(&mut self, output: &mut Vec<K>);
fn get_ordered_identifiers_from(&mut self, id: &K, output: &mut Vec<K>);
fn get_active_path(&mut self, output: &mut Vec<K>);
fn get_path_from(&mut self, id: &K, output: &mut Vec<K>);
fn insert(&mut self, node: N) -> bool;
fn set_active(&mut self, id: &K, value: bool) -> bool;
fn remove(&mut self, id: &K) -> Option<N>;
fn remove_tracked(&mut self, id: &K, on_removal: impl FnMut(N)) -> bool;
fn clear(&mut self);
}
pub trait MetadataWeave<K, N, T, M>: Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
#[must_use]
fn metadata(&self) -> &M;
fn metadata_mut<O>(&mut self, callback: impl FnOnce(&mut M) -> O) -> O;
}
pub trait BookmarkableWeave<K, N, T>: Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
type Bookmarks;
#[must_use]
fn bookmarks(&self) -> &Self::Bookmarks;
#[must_use]
fn contains_bookmark(&self, id: &K) -> bool;
fn set_bookmarked(&mut self, id: &K, value: bool) -> bool;
}
pub trait SortableWeave<K, N, T>: Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
fn sort_children_by(&mut self, id: &K, cmp: impl FnMut(&N, &N) -> Ordering) -> bool;
fn sort_children_by_id(&mut self, id: &K, cmp: impl FnMut(&K, &K) -> Ordering) -> bool;
fn sort_roots_by(&mut self, cmp: impl FnMut(&N, &N) -> Ordering);
fn sort_roots_by_id(&mut self, cmp: impl FnMut(&K, &K) -> Ordering);
}
pub trait SortableBookmarkableWeave<K, N, T>:
BookmarkableWeave<K, N, T> + SortableWeave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
fn sort_bookmarks_by(&mut self, cmp: impl FnMut(&N, &N) -> Ordering);
fn sort_bookmarks_by_id(&mut self, cmp: impl FnMut(&K, &K) -> Ordering);
}
pub trait ActiveSingularWeave<K, N, T>: Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
#[must_use]
fn active(&self) -> Option<K>;
}
pub trait ActivePathWeave<K, N, T>: Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
type Active;
#[must_use]
fn active(&self) -> &Self::Active;
fn set_active_path(&mut self, active: impl Iterator<Item = K>);
}
pub trait IndependentWeave<K, N, T>: Weave<K, N, T> + SemiIndependentWeave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
T: IndependentContents,
{
fn move_to(&mut self, id: &K, new_parents: &[K]) -> bool;
}
pub trait SemiIndependentWeave<K, N, T>: Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
T: IndependentContents,
{
#[must_use]
fn get_contents_mut<O>(&mut self, id: &K, callback: impl FnOnce(&mut T) -> O) -> Option<O>;
}
pub trait DiscreteWeave<K, N, T>: Weave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
T: DiscreteContents,
{
fn split(&mut self, id: &K, at: usize, new_id: K) -> bool;
fn merge_with_parent(&mut self, id: &K) -> Option<K>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LayoutItem<K, V, P> {
Node {
id: K,
center: V,
size: V,
},
Polyline {
from: K,
to: K,
points: P,
},
}
pub trait Layouter<W, K, N, T, V, P>
where
W: Weave<K, N, T>,
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
fn layout(&mut self, weave: &mut W, sizes: impl FnMut(&K) -> V);
fn size(&self) -> V;
fn view(&mut self, min: V, max: V, callback: impl FnMut(LayoutItem<K, V, P>));
}
#[must_use]
pub trait ImmutableWeave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
type Nodes;
type Roots;
#[must_use]
fn len(&self) -> usize;
#[must_use]
fn is_empty(&self) -> bool;
#[must_use]
fn nodes(&self) -> &Self::Nodes;
#[must_use]
fn roots(&self) -> &Self::Roots;
#[must_use]
fn contains(&self, id: &K) -> bool;
#[must_use]
fn contains_active(&self, id: &K) -> bool;
#[must_use]
fn get(&self, id: &K) -> Option<&N>;
#[must_use]
fn get_parents(&self, id: &K) -> Option<&N::From>;
#[must_use]
fn get_children(&self, id: &K) -> Option<&N::To>;
#[must_use]
fn get_contents(&self, id: &K) -> Option<&T>;
fn get_ordered_identifiers(&self, output: &mut Vec<K>);
fn get_ordered_identifiers_from(&self, id: &K, output: &mut Vec<K>);
fn get_active_path(&self, output: &mut Vec<K>);
fn get_path_from(&self, id: &K, output: &mut Vec<K>);
}
pub trait ImmutableMetadataWeave<K, N, T, M>: ImmutableWeave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
#[must_use]
fn metadata(&self) -> &M;
}
pub trait ImmutableBookmarkableWeave<K, N, T>: ImmutableWeave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
type Bookmarks;
#[must_use]
fn bookmarks(&self) -> &Self::Bookmarks;
#[must_use]
fn contains_bookmark(&self, id: &K) -> bool;
}
pub trait ImmutableActiveSingularWeave<K, N, T>: ImmutableWeave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
#[must_use]
fn active(&self) -> Option<K>;
}
pub trait ImmutableActivePathWeave<K, N, T>: ImmutableWeave<K, N, T>
where
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
type Active;
#[must_use]
fn active(&self) -> &Self::Active;
}
pub trait ImmutableLayouter<W, K, N, T, V, P>
where
W: ImmutableWeave<K, N, T>,
K: Hash + Copy + Eq + Ord,
N: Node<K, T>,
{
fn layout(&mut self, weave: &W, sizes: impl FnMut(&K) -> V);
fn size(&self) -> V;
fn view(&mut self, min: V, max: V, callback: impl FnMut(LayoutItem<K, V, P>));
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum Step<A, B> {
Enter(A),
Exit(B),
}
fn topological_sort<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
roots: impl DoubleEndedIterator<Item = K>,
stack: &mut ScratchpadVec<'_, K>,
mut identifier_callback: impl FnMut(K),
identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T> + 'a,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator + ExactSizeIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
identifier_map.extend(nodes.iter().map(|(&k, n)| (k, n.from().into_iter().len())));
stack.extend(roots.rev());
while let Some(id) = stack.pop() {
identifier_callback(id);
for child in nodes[&id].to().into_iter().rev().copied() {
let remaining = identifier_map.get_mut(&child).unwrap();
#[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
{
*remaining -= 1;
}
if *remaining == 0 {
stack.push(child);
}
}
}
}
#[cfg(feature = "rkyv")]
fn archived_topological_sort<'a, K, N, T, S>(
nodes: &'a ArchivedHashMap<K, N>,
roots: &'a ArchivedIndexSet<K>,
stack: &mut ScratchpadVec<'_, K>,
mut identifier_callback: impl FnMut(K),
identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T, From = ArchivedIndexSet<K>, To = ArchivedIndexSet<K>> + 'a,
S: BuildHasher + Default + Clone,
{
identifier_map.extend(nodes.iter().map(|(&k, n)| (k, n.from().len())));
stack.extend(archived_set_reverse_order(roots));
while let Some(id) = stack.pop() {
identifier_callback(id);
for child in archived_set_reverse_order(nodes[&id].to()).copied() {
let remaining = identifier_map.get_mut(&child).unwrap();
#[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
{
*remaining -= 1;
}
if *remaining == 0 {
stack.push(child);
}
}
}
}
fn topological_sort_subgraph<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
filter: impl Fn(&K) -> bool,
subgraph_root: K,
stack: &mut ScratchpadVec<'_, K>,
mut identifier_callback: impl FnMut(K),
identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T> + 'a,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
stack.push(subgraph_root);
while let Some(id) = stack.pop() {
identifier_callback(id);
for child in nodes[&id].to().into_iter().rev().copied() {
if !filter(&child) {
continue;
}
let remaining = identifier_map.entry(child).or_insert_with(|| {
nodes[&child]
.from()
.into_iter()
.filter(|&parent| filter(parent))
.count()
});
#[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
{
*remaining -= 1;
}
if *remaining == 0 {
stack.push(child);
}
}
}
}
#[cfg(feature = "rkyv")]
fn archived_topological_sort_subgraph<'a, K, N, T, S>(
nodes: &'a ArchivedHashMap<K, N>,
filter: impl Fn(&K) -> bool,
subgraph_root: K,
stack: &mut ScratchpadVec<'_, K>,
mut identifier_callback: impl FnMut(K),
identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T, From = ArchivedIndexSet<K>, To = ArchivedIndexSet<K>> + 'a,
S: BuildHasher + Default + Clone,
{
stack.push(subgraph_root);
while let Some(id) = stack.pop() {
identifier_callback(id);
for child in archived_set_reverse_order(nodes[&id].to()).copied() {
if !filter(&child) {
continue;
}
let remaining = identifier_map.entry(child).or_insert_with(|| {
nodes[&child]
.from()
.iter()
.filter(|&parent| filter(parent))
.count()
});
#[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
{
*remaining -= 1;
}
if *remaining == 0 {
stack.push(child);
}
}
}
}
fn shortest_path_to_ancestor<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
id: &'a K,
target: impl Fn(&'a N) -> bool,
scratchpad: &mut ScratchpadVec<'_, K>,
scratchpad_map: &mut ScratchpadMap<'_, K, K, S>,
path: &mut Vec<K>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T> + 'a,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
scratchpad.push(*id);
scratchpad_map.insert(*id, *id);
let mut head = 0;
while head < scratchpad.len() {
let id = scratchpad[head];
#[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
{
head += 1;
}
let node = &nodes[&id];
if target(node) {
path.push(id);
break;
}
for parent in node.from().into_iter().copied() {
if let Entry::Vacant(entry) = scratchpad_map.entry(parent) {
entry.insert(id);
scratchpad.push(parent);
}
}
}
while let Some(last) = path.last()
&& last != id
{
path.push(scratchpad_map[last]);
}
}
#[cfg(feature = "rkyv")]
fn archived_shortest_path_to_ancestor<'a, K, N, T, S>(
nodes: &'a ArchivedHashMap<K, N>,
id: &'a K,
target: impl Fn(&'a N) -> bool,
scratchpad: &mut ScratchpadVec<'_, K>,
scratchpad_map: &mut ScratchpadMap<'_, K, K, S>,
path: &mut Vec<K>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T, From = ArchivedIndexSet<K>> + 'a,
S: BuildHasher + Default + Clone,
{
scratchpad.push(*id);
scratchpad_map.insert(*id, *id);
let mut head = 0;
while head < scratchpad.len() {
let id = scratchpad[head];
#[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
{
head += 1;
}
let node = &nodes[&id];
if target(node) {
path.push(id);
break;
}
for parent in node.from().iter().copied() {
if let Entry::Vacant(entry) = scratchpad_map.entry(parent) {
entry.insert(id);
scratchpad.push(parent);
}
}
}
while let Some(last) = path.last()
&& last != id
{
path.push(scratchpad_map[last]);
}
}
fn longest_candidate_path_to_root<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
topological_order: &[K],
is_candidate: impl Fn(&K) -> bool,
scratchpad_map: &mut ScratchpadMap<'_, K, (usize, K), S>,
mut reversed_path_callback: impl FnMut(K),
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T> + 'a,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator + ExactSizeIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
let mut longest_distance = None;
for id in topological_order {
if !is_candidate(id) {
continue;
}
let from = nodes[id].from().into_iter();
let has_parents = from.len() != 0;
let best_parent = from
.filter_map(|id| scratchpad_map.get(id).map(|v| (v.0, id)))
.min_by_key(|&(v, _)| Reverse(v));
#[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
let distance = match best_parent {
Some((parent_distance, parent)) => Some((parent_distance + 1, *parent)),
None => {
if has_parents {
None
} else {
Some((0, *id))
}
}
};
if let Some((distance, parent)) = distance {
scratchpad_map.insert(*id, (distance, parent));
if longest_distance.is_none_or(|(value, _)| distance > value) {
longest_distance = Some((distance, *id));
}
}
}
if let Some(mut id) = longest_distance.map(|(_, id)| id) {
loop {
reversed_path_callback(id);
let parent = scratchpad_map[&id].1;
if parent == id {
break;
}
id = parent;
}
}
}
#[cfg(feature = "rkyv")]
fn archived_longest_candidate_path_to_root<'a, K, N, T, S>(
nodes: &'a ArchivedHashMap<K, N>,
topological_order: &'a [K],
is_candidate: impl Fn(&K) -> bool,
scratchpad_map: &mut ScratchpadMap<'_, K, (usize, K), S>,
mut reversed_path_callback: impl FnMut(K),
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T, From = ArchivedIndexSet<K>> + 'a,
S: BuildHasher + Default + Clone,
{
let mut longest_distance = None;
for id in topological_order {
if !is_candidate(id) {
continue;
}
let from = nodes[id].from();
let has_parents = !from.is_empty();
let best_parent = from
.iter()
.filter_map(|id| scratchpad_map.get(id).map(|v| (v.0, id)))
.min_by_key(|&(v, _)| Reverse(v));
#[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
let distance = match best_parent {
Some((parent_distance, parent)) => Some((parent_distance + 1, *parent)),
None => {
if has_parents {
None
} else {
Some((0, *id))
}
}
};
if let Some((distance, parent)) = distance {
scratchpad_map.insert(*id, (distance, parent));
if longest_distance.is_none_or(|(value, _)| distance > value) {
longest_distance = Some((distance, *id));
}
}
}
if let Some(mut id) = longest_distance.map(|(_, id)| id) {
loop {
reversed_path_callback(id);
let parent = scratchpad_map[&id].1;
if parent == id {
break;
}
id = parent;
}
}
}
fn ancestor_subgraph<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
id: K,
stack: &mut ScratchpadVec<'_, K>,
identifiers: &mut ScratchpadSet<'_, K, S>,
mut root_callback: impl FnMut(K),
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T>,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator + ExactSizeIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
if identifiers.insert(id) {
stack.push(id);
}
while let Some(id) = stack.pop() {
let from = nodes[&id].from().into_iter();
if from.len() == 0 {
root_callback(id);
} else {
for parent in from.rev().copied() {
if identifiers.insert(parent) {
stack.push(parent);
}
}
}
}
}
fn ancestor_subgraph_reaches<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
ids: impl DoubleEndedIterator<Item = K>,
target: impl Fn(&K) -> bool,
stack: &mut ScratchpadVec<'_, K>,
identifiers: &mut ScratchpadSet<'_, K, S>,
) -> bool
where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T>,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
for id in ids.rev() {
if identifiers.insert(id) {
if target(&id) {
return true;
}
stack.push(id);
}
}
while let Some(id) = stack.pop() {
for parent in nodes[&id].from().into_iter().rev().copied() {
if identifiers.insert(parent) {
if target(&parent) {
return true;
}
stack.push(parent);
}
}
}
false
}
#[cfg(feature = "rkyv")]
fn archived_ancestor_subgraph<'a, K, N, T, S>(
nodes: &'a ArchivedHashMap<K, N>,
id: K,
stack: &mut ScratchpadVec<'_, K>,
identifiers: &mut ScratchpadSet<'_, K, S>,
mut root_callback: impl FnMut(K),
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T, From = ArchivedIndexSet<K>> + 'a,
S: BuildHasher + Default + Clone,
{
if identifiers.insert(id) {
stack.push(id);
}
while let Some(id) = stack.pop() {
let from = nodes[&id].from();
if from.is_empty() {
root_callback(id);
} else {
for parent in archived_set_reverse_order(from).copied() {
if identifiers.insert(parent) {
stack.push(parent);
}
}
}
}
}
fn descendant_subgraph<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
id: K,
stack: &mut ScratchpadVec<'_, K>,
identifiers: &mut ScratchpadSet<'_, K, S>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T>,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
if identifiers.insert(id) {
stack.push(id);
}
while let Some(id) = stack.pop() {
for child in nodes[&id].to().into_iter().rev().copied() {
if identifiers.insert(child) {
stack.push(child);
}
}
}
}
fn descendant_subgraph_reaches<'a, K, N, T, S>(
nodes: &'a HashMap<K, N, S>,
ids: impl DoubleEndedIterator<Item = K>,
target: impl Fn(&K) -> bool,
stack: &mut ScratchpadVec<'_, K>,
identifiers: &mut ScratchpadSet<'_, K, S>,
) -> bool
where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T>,
<N as Node<K, T>>::From: 'a,
<N as Node<K, T>>::To: 'a,
&'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
&'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
S: BuildHasher + Default + Clone,
{
for id in ids.rev() {
if identifiers.insert(id) {
if target(&id) {
return true;
}
stack.push(id);
}
}
while let Some(id) = stack.pop() {
for child in nodes[&id].to().into_iter().rev().copied() {
if identifiers.insert(child) {
if target(&child) {
return true;
}
stack.push(child);
}
}
}
false
}
#[cfg(feature = "rkyv")]
fn archived_descendant_subgraph<'a, K, N, T, S>(
nodes: &'a ArchivedHashMap<K, N>,
id: K,
stack: &mut ScratchpadVec<'_, K>,
identifiers: &mut ScratchpadSet<'_, K, S>,
) where
K: Hash + Copy + Eq + Ord + 'a,
N: Node<K, T, To = ArchivedIndexSet<K>> + 'a,
S: BuildHasher + Default + Clone,
{
if identifiers.insert(id) {
stack.push(id);
}
while let Some(id) = stack.pop() {
for child in archived_set_reverse_order(nodes[&id].to()).copied() {
if identifiers.insert(child) {
stack.push(child);
}
}
}
}
#[cfg(feature = "rkyv")]
fn archived_set_reverse_order<T>(set: &ArchivedIndexSet<T>) -> impl Iterator<Item = &T> {
(0..set.len())
.rev()
.filter_map(|index| set.get_index(index))
}