Skip to main content

kevy_map/
set.rs

1//! `HashSet`-shaped wrapper over [`KevyMap<K, ()>`] — same per-shard /
2//! single-trust-domain assumptions; exposes the underlying map's bucket-addr
3//! API via [`KevySet::as_map`].
4
5use core::borrow::Borrow;
6use core::fmt;
7
8use kevy_hash::KevyHash;
9
10use crate::into_iter::IntoIter;
11use crate::iter::Iter;
12use crate::map::KevyMap;
13
14/// `HashSet`-shaped wrapper over [`KevyMap<K, ()>`].
15///
16/// Carries the same per-shard / single-trust-domain assumptions; differs from
17/// `std::HashSet` only by hashing through [`KevyHash`] (one-call inlinable)
18/// and exposing the underlying `KevyMap`'s bucket-address API via
19/// [`KevySet::as_map`] for callers that want prefetch.
20///
21/// ```
22/// use kevy_map::KevySet;
23/// let mut s: KevySet<Vec<u8>> = KevySet::new();
24/// assert!(s.insert(b"a".to_vec()));
25/// assert!(!s.insert(b"a".to_vec()), "already a member");
26/// assert!(s.contains(b"a".as_slice()));
27/// assert!(s.remove(b"a".as_slice()));
28/// assert!(s.is_empty());
29/// ```
30#[derive(Clone)]
31pub struct KevySet<K>(KevyMap<K, ()>);
32
33impl<K> KevySet<K> {
34    /// Construct an empty set without allocating.
35    pub fn new() -> Self {
36        Self(KevyMap::new())
37    }
38
39    /// Construct a set sized for `cap_hint` members without growing.
40    pub fn with_capacity(cap_hint: usize) -> Self {
41        Self(KevyMap::with_capacity(cap_hint))
42    }
43
44    /// Live member count.
45    #[inline]
46    pub fn len(&self) -> usize {
47        self.0.len()
48    }
49    /// Whether `len() == 0`.
50    #[inline]
51    pub fn is_empty(&self) -> bool {
52        self.0.is_empty()
53    }
54    /// Allocated slot count of the underlying map.
55    #[inline]
56    pub fn capacity(&self) -> usize {
57        self.0.capacity()
58    }
59    /// Drop every member and reset the metadata. Keeps the allocation.
60    pub fn clear(&mut self) {
61        self.0.clear();
62    }
63
64    /// `&K` iterator over all members (unspecified order).
65    pub fn iter(&self) -> SetIter<'_, K> {
66        SetIter(self.0.iter())
67    }
68
69    /// An iterator that begins at slot `start` and wraps once around the whole
70    /// table. Take the first element for an arbitrary member in O(1) expected
71    /// time — the pattern SPOP and SRANDMEMBER need, and the one Redis's
72    /// `dictGetRandomKey` uses.
73    ///
74    /// Not perfectly uniform: a member sitting after a long run of empty slots
75    /// is likelier to be picked than one in a dense cluster. Redis has the same
76    /// bias for the same reason, and the contract is "arbitrary", not "uniform".
77    /// What it is NOT is the identical member every single time, which is what
78    /// `iter().next()` gives you.
79    pub fn iter_from_slot(&self, start: usize) -> impl Iterator<Item = &K> {
80        let cap = self.0.capacity();
81        let start = if cap == 0 { 0 } else { start % cap };
82        self.0.iter_from_bucket(start).chain(self.0.iter().take(start)).map(|(k, ())| k)
83    }
84
85    /// The backing map, for callers that want its bucket addresses (prefetch).
86    pub fn as_map(&self) -> &KevyMap<K, ()> {
87        &self.0
88    }
89}
90
91impl<K: KevyHash + Eq> KevySet<K> {
92    /// Insert `key`. Returns `true` if newly added, `false` if it was already
93    /// present (matches `HashSet::insert`).
94    pub fn insert(&mut self, key: K) -> bool {
95        self.0.insert(key, ()).is_none()
96    }
97}
98
99impl<K> KevySet<K> {
100    /// Whether `key` is a member of the set.
101    pub fn contains<Q>(&self, key: &Q) -> bool
102    where
103        K: Borrow<Q>,
104        Q: KevyHash + Eq + ?Sized,
105    {
106        self.0.contains_key(key)
107    }
108
109    /// Remove `key`; returns `true` if it was present (matches `HashSet::remove`).
110    pub fn remove<Q>(&mut self, key: &Q) -> bool
111    where
112        K: Borrow<Q>,
113        Q: KevyHash + Eq + ?Sized,
114    {
115        self.0.remove(key).is_some()
116    }
117}
118
119impl<K> Default for KevySet<K> {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125impl<K: fmt::Debug> fmt::Debug for KevySet<K> {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        f.debug_set().entries(self.iter()).finish()
128    }
129}
130
131/// `&K` iterator over all members of a [`KevySet`]; order unspecified.
132///
133/// ```
134/// let s: kevy_map::KevySet<u64> = [2, 4].into_iter().collect();
135/// let mut v: Vec<u64> = s.iter().copied().collect();
136/// v.sort();
137/// assert_eq!(v, [2, 4]);
138/// ```
139#[derive(Debug)]
140pub struct SetIter<'a, K>(Iter<'a, K, ()>);
141
142/// `K` iterator that consumes a [`KevySet`]; order unspecified.
143///
144/// ```
145/// let s: kevy_map::KevySet<u64> = [3, 1].into_iter().collect();
146/// let mut v: Vec<u64> = s.into_iter().collect();
147/// v.sort();
148/// assert_eq!(v, [1, 3]);
149/// ```
150#[derive(Debug)]
151pub struct SetIntoIter<K>(IntoIter<K, ()>);
152
153impl<K> Iterator for SetIntoIter<K> {
154    type Item = K;
155    fn next(&mut self) -> Option<K> {
156        self.0.next().map(|(k, ())| k)
157    }
158    fn size_hint(&self) -> (usize, Option<usize>) {
159        self.0.size_hint()
160    }
161}
162
163impl<K> ExactSizeIterator for SetIntoIter<K> {}
164
165impl<K> IntoIterator for KevySet<K> {
166    type Item = K;
167    type IntoIter = SetIntoIter<K>;
168    fn into_iter(self) -> SetIntoIter<K> {
169        SetIntoIter(self.0.into_iter())
170    }
171}
172
173/// Two sets are equal when they hold the same members.
174///
175/// ```
176/// let a: kevy_map::KevySet<u64> = [1, 2].into_iter().collect();
177/// let b: kevy_map::KevySet<u64> = [2, 1].into_iter().collect();
178/// assert_eq!(a, b);
179/// ```
180impl<K: KevyHash + Eq> PartialEq for KevySet<K> {
181    fn eq(&self, other: &Self) -> bool {
182        self.0 == other.0
183    }
184}
185
186impl<K: KevyHash + Eq> Eq for KevySet<K> {}
187
188impl<'a, K> Iterator for SetIter<'a, K> {
189    type Item = &'a K;
190    fn next(&mut self) -> Option<Self::Item> {
191        self.0.next().map(|(k, ())| k)
192    }
193}
194
195impl<'a, K> IntoIterator for &'a KevySet<K> {
196    type Item = &'a K;
197    type IntoIter = SetIter<'a, K>;
198    fn into_iter(self) -> Self::IntoIter {
199        self.iter()
200    }
201}
202
203impl<K: KevyHash + Eq> FromIterator<K> for KevySet<K> {
204    fn from_iter<I: IntoIterator<Item = K>>(iter: I) -> Self {
205        let iter = iter.into_iter();
206        let mut s = match iter.size_hint() {
207            (lo, Some(hi)) if hi <= lo.saturating_mul(2) => Self::with_capacity(hi),
208            (lo, _) => Self::with_capacity(lo),
209        };
210        for k in iter {
211            s.insert(k);
212        }
213        s
214    }
215}
216
217impl<K: KevyHash + Eq> Extend<K> for KevySet<K> {
218    fn extend<I: IntoIterator<Item = K>>(&mut self, iter: I) {
219        for k in iter {
220            self.insert(k);
221        }
222    }
223}