diff --git a/Cargo.lock b/Cargo.lock index d9f2a22..8ab86bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1368,6 +1368,15 @@ dependencies = [ "serde", ] +[[package]] +name = "smolset" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a73542b3021a40b49f29e6ce18b65ff38dbafbf7df8be0fa9f9305305f62ee" +dependencies = [ + "smallvec", +] + [[package]] name = "socket2" version = "0.3.19" @@ -1779,6 +1788,7 @@ dependencies = [ name = "yuurei" version = "0.1.0" dependencies = [ + "cfg-if 1.0.0", "color-eyre", "cryptohelpers", "difference", @@ -1793,6 +1803,7 @@ dependencies = [ "serde", "sha2", "smallvec", + "smolset", "tokio", "uuid", "warp", diff --git a/Cargo.toml b/Cargo.toml index 2465dd0..c326a7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ default = ["nightly"] nightly = ["smallvec/const_generics"] [dependencies] +cfg-if = "1.0.0" color-eyre = {version = "0.5.10", default-features=false} cryptohelpers = {version = "1.7.1", features=["full"]} difference = "2.0.0" @@ -25,6 +26,7 @@ pretty_env_logger = "0.4.0" serde = {version = "1.0.118", features=["derive"]} sha2 = "0.9.2" smallvec = {version = "1.6.0", features= ["union", "serde", "write"]} +smolset = "1.1.0" tokio = {version = "0.2", features=["full"] } uuid = {version = "0.8.1", features=["v4","serde"]} warp = "0.2.5" diff --git a/src/ext.rs b/src/ext.rs index ba65a97..8c99137 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -578,3 +578,80 @@ impl Clone for DynCloneable } +mod maybe_iter +{ + #[derive(Debug, Clone)] + enum Inner + { + Some(I), + One(std::iter::Once), + None, + } + /// An iterator that may yield 0, 1, or more values. + #[derive(Debug, Clone)] + #[repr(transparent)] + pub struct MaybeIter(Inner); + + impl MaybeIter + where I: Iterator + { + /// Create a single element iterator + pub fn one>(from: U) -> Self + { + Self(Inner::One(std::iter::once(from.into()))) + } + /// Create a new instance from an iterator + pub fn many>(from: U) -> Self + { + Self(Inner::Some(from.into_iter())) + } + /// Create a new instance that yields 0 items + #[inline(always)] pub fn none() -> Self + { + Self(Inner::None) + } + } + + impl From> for MaybeIter + where I: Iterator, + U: IntoIterator + { + fn from(from: Option) -> Self + { + match from { + Some(many) => Self::many(many), + _ => Self::none() + } + } + } + + + impl Iterator for MaybeIter + where I: Iterator + { + type Item = T; + fn next(&mut self) -> Option + { + match &mut self.0 { + Inner::Some(x) => x.next(), + Inner::One(x) => x.next(), + Inner::None => None, + } + } + + #[inline] fn size_hint(&self) -> (usize, Option) { + match &self.0 + { + Inner::One(c) => c.size_hint(), + Inner::Some(x) => x.size_hint(), + Inner::None => (0, Some(0)) + } + } + } + + impl> std::iter::FusedIterator for MaybeIter + where I: std::iter::FusedIterator{} + impl> ExactSizeIterator for MaybeIter + where I: ExactSizeIterator{} +} +pub use maybe_iter::MaybeIter; diff --git a/src/main.rs b/src/main.rs index f5799df..820af5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ #[macro_use] extern crate lazy_static; #[macro_use] extern crate log; #[macro_use] extern crate mopa; +#[macro_use] extern crate cfg_if; use std::convert::{TryFrom, TryInto}; use color_eyre::{ diff --git a/src/state/service.rs b/src/state/service.rs index d5964f2..3911ef4 100644 --- a/src/state/service.rs +++ b/src/state/service.rs @@ -15,7 +15,7 @@ use crate::service::{ use std::{error, fmt}; use std::sync::Weak; use std::any::Any; -use std::collections::BTreeMap; +use std::collections::{BTreeMap}; id_type!(ServiceSubID; "Optional ID for filtering directed broadcast messages"); @@ -80,36 +80,16 @@ pub(super) struct Supervisor /// Object sent through the broadcast channel. /// -/// These objects can be cloned and downcasted, but can also be atomically refcounted if that is more desireable. -/// -/// # Cloning -/// The implementation of `Clone` for this instance clones the inner object, not the refcount. Use `clone_ref()` to *just* clone the refcount. -/// To downcast and clone the inner object without an extra `Arc` allocation, use `downcast_clone()` or `downcast().map(Clone::clone)`. -pub struct ServiceEventObject(Arc); +/// These objects can be cloned and downcasted, becaause they are atomically refcounted if that is more desireable. +#[derive(Clone)] +pub struct ServiceEventObject(Arc); shim_debug!(ServiceEventObject); + /// A weak reference to a `ServiceEventObject`. #[derive(Clone)] -pub struct ServiceEventObjectRef(Weak); +pub struct ServiceEventObjectRef(Weak); shim_debug!(ServiceEventObjectRef); -/// Wrapper used when broadcasting to prevent useless inner object clones. -#[derive(Debug)] -struct EventObjectRefCloneWrap(ServiceEventObject); - -impl Clone for EventObjectRefCloneWrap -{ - fn clone(&self) -> Self { - Self(self.0.clone_ref()) - } -} - -impl Clone for ServiceEventObject -{ - fn clone(&self) -> Self { - Self(Arc::from(self.0.as_ref().clone_dyn())) - } -} - impl ServiceEventObjectRef { /// Try to upgrade to a concrete reference, and then clone the inner object. @@ -135,12 +115,6 @@ impl ServiceEventObjectRef impl ServiceEventObject { - /// Get an owned reference counted handle to the object, without cloning the object itself. - pub fn clone_ref(&self) -> Self - { - Self(Arc::clone(&self.0)) - } - /// Get a weak reference counted handle to the object, without cloning the object itself. pub fn clone_weak(&self) -> ServiceEventObjectRef { @@ -164,8 +138,11 @@ impl ServiceEventObject { match Arc::downcast(self.0) { - Ok(v) => v, - Err(e) => Self(e), + Ok(v) => match Arc::try_unwrap(v) { + Ok(v) => Ok(v), + Err(s) => Err(Self(s)), + }, + Err(e) => Err(Self(e)), } } @@ -176,14 +153,14 @@ impl ServiceEventObject } /// Try to downcast the object into a concrete type - #[inline] pub fn is(&self) -> Option<&T> + #[inline] pub fn is(&self) -> bool { - self.0.is() + self.0.as_ref().is::() } /// Try to downcast the object into a concrete type #[inline] pub fn downcast_ref(&self) -> Option<&T> { - self.0.downcast_ref() + self.0.as_ref().downcast_ref::() } } @@ -199,13 +176,23 @@ pub enum ServiceEventKind KeepAlive, } +cfg_if!{ + if #[cfg(debug_assertions)] { + /// Type used for directed array. + /// Currently testing `smolset` over eagerly allocating. + type SESet = smolset::SmolSet<[T; 1]>; + } else { + type SESet = std::collections::HashSet; + } +} + /// An event outputted from a state service's broadcast stream #[derive(Debug, Clone)] pub struct ServiceEvent { kind: ServiceEventKind, - directed: Option>, - obj: Option, + directed: Option>, + obj: Option, } impl ServiceEvent @@ -220,7 +207,7 @@ impl ServiceEvent pub fn is_directed_for(&self, whom: &ServiceSubID) -> bool { if let Some(yes) = self.directed.as_ref() { - yes == whom + yes.contains(whom) } else { false } @@ -234,25 +221,25 @@ impl ServiceEvent /// Check who this event is directed to. /// /// If it is not directed, an empty slice will be returned. - pub fn directed_to(&self) -> &[&ServiceSubID] + pub fn directed_to(&self) -> impl Iterator + '_ { match self.directed.as_ref() { - Some(yes) => &yes[..], - None => &[], + Some(yes) => MaybeIter::many(yes.iter()), + None => MaybeIter::none(), } } /// Get a reference to the object, if there is one. pub fn obj_ref(&self) -> Option<&ServiceEventObject> { - self.obj.as_ref().map(|x| x.0) + self.obj.as_ref() } /// Get a mutable reference to the object, if there is one. pub fn obj_mut(&mut self) -> Option<&mut ServiceEventObject> { - self.obj.as_mut().map(|x| x.0) + self.obj.as_mut() } /// Try to consume into the inner object. If there is no object, return self. @@ -266,6 +253,7 @@ impl ServiceEvent } } + impl From for Option { #[inline] fn from(from: ServiceEvent) -> Self @@ -282,7 +270,7 @@ impl TryFrom for ServiceEventObject { match from.obj { - Some(obj) => Ok(obj.0), + Some(obj) => Ok(obj), None => Err(NoObjectError), } }