diff --git a/src/collections/hash_set/extend.rs b/src/collections/hash_set/extend.rs new file mode 100644 index 0000000..b06e7b8 --- /dev/null +++ b/src/collections/hash_set/extend.rs @@ -0,0 +1,39 @@ +use std::pin::Pin; +use std::hash::{Hash, BuildHasher}; +use std::collections::HashSet; + +use crate::prelude::*; +use crate::stream::{Extend, IntoStream}; + +impl Extend for HashSet +where T: Eq + Hash, + H: BuildHasher + Default { + fn stream_extend<'a, S: IntoStream + 'a>( + &'a mut self, + stream: S, + ) -> Pin + 'a>> { + // The Extend impl for HashSet in the standard library delegates to the internal HashMap. + // Thus, this impl is just a copy of the async Extend impl for HashMap in this crate. + + let stream = stream.into_stream(); + + // The following is adapted from the hashbrown source code: + // https://github.com/rust-lang/hashbrown/blob/d1ad4fc3aae2ade446738eea512e50b9e863dd0c/src/map.rs#L2470-L2491 + // + // Keys may be already present or show multiple times in the stream. Reserve the entire + // hint lower bound if the map is empty. Otherwise reserve half the hint (rounded up), so + // the map will only resize twice in the worst case. + + //TODO: Add this back in when size_hint is added to Stream/StreamExt + //let reserve = if self.is_empty() { + // stream.size_hint().0 + //} else { + // (stream.size_hint().0 + 1) / 2 + //}; + //self.reserve(reserve); + + Box::pin(stream.for_each(move |item| { + self.insert(item); + })) + } +} diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs new file mode 100644 index 0000000..3714ae8 --- /dev/null +++ b/src/collections/hash_set/from_stream.rs @@ -0,0 +1,27 @@ +use std::pin::Pin; +use std::hash::{Hash, BuildHasher}; +use std::collections::HashSet; + +use crate::stream::{Extend, FromStream, IntoStream}; + +impl FromStream for HashSet +where T: Eq + Hash, + H: BuildHasher + Default { + #[inline] + fn from_stream<'a, S: IntoStream>( + stream: S, + ) -> Pin + 'a>> + where + ::IntoStream: 'a, + { + let stream = stream.into_stream(); + + Box::pin(async move { + pin_utils::pin_mut!(stream); + + let mut out = HashSet::with_hasher(Default::default()); + out.stream_extend(stream).await; + out + }) + } +} diff --git a/src/collections/hash_set/mod.rs b/src/collections/hash_set/mod.rs new file mode 100644 index 0000000..5d93a88 --- /dev/null +++ b/src/collections/hash_set/mod.rs @@ -0,0 +1,7 @@ +//! The Rust hash set, implemented as a `HashMap` where the value is `()`. + +mod extend; +mod from_stream; + +#[doc(inline)] +pub use std::collections::HashSet; diff --git a/src/collections/mod.rs b/src/collections/mod.rs index 93a0614..4ed8e35 100644 --- a/src/collections/mod.rs +++ b/src/collections/mod.rs @@ -5,8 +5,10 @@ pub mod vec_deque; pub mod hash_map; +pub mod hash_set; pub mod btree_map; pub use vec_deque::VecDeque; pub use hash_map::HashMap; +pub use hash_set::HashSet; pub use btree_map::BTreeMap;