diff --git a/Cargo.lock b/Cargo.lock index 5ec15a039a..e13decc999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2753,6 +2753,7 @@ dependencies = [ "futures", "guardian", "hydration_context", + "indexmap", "or_poisoned", "pin-project-lite", "rustc-hash 2.1.1", diff --git a/reactive_graph/Cargo.toml b/reactive_graph/Cargo.toml index 0a5c6f890d..0c9fcd7a19 100644 --- a/reactive_graph/Cargo.toml +++ b/reactive_graph/Cargo.toml @@ -27,6 +27,7 @@ async-lock = { workspace = true, default-features = true } send_wrapper = { features = [ "futures", ], workspace = true, default-features = true } +indexmap = { workspace = true, default-features = true } [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] web-sys = { version = "0.3.77", features = ["console"] } diff --git a/reactive_graph/src/graph/sets.rs b/reactive_graph/src/graph/sets.rs index f607473caa..6ce696edfc 100644 --- a/reactive_graph/src/graph/sets.rs +++ b/reactive_graph/src/graph/sets.rs @@ -6,11 +6,14 @@ //! a linear search is not significantly more expensive than a hash and lookup. use super::{AnySource, AnySubscriber, Source}; -use core::slice; -use std::{mem, vec::IntoIter}; +use indexmap::IndexSet; +use rustc_hash::FxHasher; +use std::{hash::BuildHasherDefault, mem}; + +type FxIndexSet = IndexSet>; #[derive(Default, Clone, Debug)] -pub struct SourceSet(Vec); +pub struct SourceSet(FxIndexSet); impl SourceSet { pub fn new() -> Self { @@ -18,16 +21,14 @@ impl SourceSet { } pub fn insert(&mut self, source: AnySource) { - self.0.push(source); + self.0.insert(source); } pub fn remove(&mut self, source: &AnySource) { - if let Some(pos) = self.0.iter().position(|s| s == source) { - self.0.remove(pos); - } + self.0.shift_remove(source); } - pub fn take(&mut self) -> Vec { + pub fn take(&mut self) -> FxIndexSet { mem::take(&mut self.0) } @@ -44,7 +45,7 @@ impl SourceSet { impl IntoIterator for SourceSet { type Item = AnySource; - type IntoIter = IntoIter; + type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() @@ -53,40 +54,36 @@ impl IntoIterator for SourceSet { impl<'a> IntoIterator for &'a SourceSet { type Item = &'a AnySource; - type IntoIter = slice::Iter<'a, AnySource>; + type IntoIter = <&'a FxIndexSet as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.iter() } } #[derive(Debug, Default, Clone)] -pub struct SubscriberSet(Vec); +pub struct SubscriberSet(FxIndexSet); impl SubscriberSet { pub fn new() -> Self { - Self(Vec::with_capacity(2)) + Self(FxIndexSet::with_capacity_and_hasher(2, Default::default())) } pub fn subscribe(&mut self, subscriber: AnySubscriber) { - if !self.0.contains(&subscriber) { - self.0.push(subscriber); - } + self.0.insert(subscriber); } pub fn unsubscribe(&mut self, subscriber: &AnySubscriber) { - if let Some(pos) = self.0.iter().position(|s| s == subscriber) { - // note: do not use `.swap_remove()` here. - // using `.remove()` is slower because it shifts other items - // but it maintains the order of the subscribers, which is important - // to correctness when you're using this to drive something like a UI, - // which can have nested effects, where the inner one assumes the outer - // has already run (for example, an outer effect that checks .is_some(), - // and an inner effect that unwraps) - self.0.remove(pos); - } + // note: do not use `.swap_remove()` here. + // using `.remove()` is slower because it shifts other items + // but it maintains the order of the subscribers, which is important + // to correctness when you're using this to drive something like a UI, + // which can have nested effects, where the inner one assumes the outer + // has already run (for example, an outer effect that checks .is_some(), + // and an inner effect that unwraps) + self.0.shift_remove(subscriber); } - pub fn take(&mut self) -> Vec { + pub fn take(&mut self) -> FxIndexSet { mem::take(&mut self.0) } @@ -97,7 +94,7 @@ impl SubscriberSet { impl IntoIterator for SubscriberSet { type Item = AnySubscriber; - type IntoIter = IntoIter; + type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() @@ -106,7 +103,7 @@ impl IntoIterator for SubscriberSet { impl<'a> IntoIterator for &'a SubscriberSet { type Item = &'a AnySubscriber; - type IntoIter = slice::Iter<'a, AnySubscriber>; + type IntoIter = <&'a FxIndexSet as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.iter()