Skip to main content

omp_core/
sparse_set.rs

1//! Sparse set backed by bitmap storage.
2//!
3//! `SparseSet<K>` stores sets where keys map to indices, using a bitmap for
4//! presence tracking. Provides fast membership tests and iteration over present
5//! keys.
6
7use std::{fmt, iter::FusedIterator, marker::PhantomData};
8
9use serde::{Deserialize, Serialize};
10use smol_bitmap::SmolBitmap;
11
12use crate::sparse_index::TrySparseIndex;
13
14/// A sparse set of keys convertible to indices.
15///
16/// [`SparseSet`] provides an efficient storage mechanism for sets
17/// where keys can be converted to `usize` indices. It uses a bitmap to track
18/// which indices are present, achieving both memory efficiency and fast lookup
19/// times.
20///
21/// The key type `K` must implement [`Into<usize>`] and [`From<usize>`] to
22/// convert between keys and indices. This makes it ideal for enum keys,
23/// small integers, or other types with a natural index representation.
24///
25/// # Examples
26///
27/// ```
28/// use omp_core::{sparse_index::TrySparseIndex, sparse_set::SparseSet};
29///
30/// #[repr(usize)]
31/// #[derive(Copy, Clone, Debug, PartialEq)]
32/// enum Status {
33/// 	Active  = 0,
34/// 	Pending = 1,
35/// 	Closed  = 2,
36/// }
37///
38/// #[derive(Debug)]
39/// struct StatusError(&'static str);
40///
41/// impl std::fmt::Display for StatusError {
42/// 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43/// 		write!(f, "{}", self.0)
44/// 	}
45/// }
46///
47/// impl std::error::Error for StatusError {}
48///
49/// impl TrySparseIndex for Status {
50/// 	type Error = StatusError;
51///
52/// 	fn index(&self) -> usize {
53/// 		*self as usize
54/// 	}
55///
56/// 	fn try_from_index(index: usize) -> Result<Self, Self::Error> {
57/// 		match index {
58/// 			0 => Ok(Status::Active),
59/// 			1 => Ok(Status::Pending),
60/// 			2 => Ok(Status::Closed),
61/// 			_ => Err(StatusError("Invalid status value")),
62/// 		}
63/// 	}
64/// }
65///
66/// let mut set = SparseSet::new();
67/// set.insert(Status::Active);
68/// set.insert(Status::Closed);
69///
70/// assert!(set.contains(Status::Active));
71/// assert!(!set.contains(Status::Pending));
72/// ```
73#[repr(transparent)]
74pub struct SparseSet<K> {
75	/// Bitmap tracking which indices are present
76	bits:     SmolBitmap,
77	/// Phantom data to maintain type information for keys
78	_phantom: PhantomData<K>,
79}
80
81impl<K> Clone for SparseSet<K> {
82	fn clone(&self) -> Self {
83		Self { bits: self.bits.clone(), _phantom: PhantomData }
84	}
85}
86
87impl<K: TrySparseIndex + fmt::Debug> fmt::Debug for SparseSet<K> {
88	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89		let mut s = f.debug_set();
90		for key in self {
91			s.entry(&key);
92		}
93		s.finish()
94	}
95}
96
97impl<K: TrySparseIndex + Serialize> Serialize for SparseSet<K> {
98	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99	where
100		S: serde::Serializer,
101	{
102		use serde::ser::SerializeSeq;
103
104		if serializer.is_human_readable() {
105			let mut seq = serializer.serialize_seq(Some(self.len()))?;
106			for key in self {
107				seq.serialize_element(&key)?;
108			}
109			seq.end()
110		} else {
111			self.bits.serialize(serializer)
112		}
113	}
114}
115impl<'de, K: TrySparseIndex + Deserialize<'de>> Deserialize<'de> for SparseSet<K> {
116	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117	where
118		D: serde::Deserializer<'de>,
119	{
120		if deserializer.is_human_readable() {
121			use std::fmt;
122
123			use serde::de::{SeqAccess, Visitor};
124
125			struct SparseSetVisitor<K> {
126				_phantom: PhantomData<K>,
127			}
128
129			impl<'de, K: TrySparseIndex + Deserialize<'de>> Visitor<'de> for SparseSetVisitor<K> {
130				type Value = SparseSet<K>;
131
132				fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
133					formatter.write_str("a sequence of indices")
134				}
135
136				fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
137				where
138					A: SeqAccess<'de>,
139				{
140					let mut set = SparseSet::new();
141					if let Some(len) = seq.size_hint() {
142						set.reserve(len);
143					}
144					let mut prev_index = None;
145					while let Some(key) = seq.next_element::<K>()? {
146						let index = key.index();
147						if prev_index.is_some_and(|prev| prev >= index) {
148							return Err(serde::de::Error::invalid_value(
149								serde::de::Unexpected::Unsigned(index as u64),
150								&"indices must be in ascending order",
151							));
152						}
153						prev_index = Some(index);
154						set.bits.insert(index);
155					}
156					Ok(set)
157				}
158			}
159
160			deserializer.deserialize_seq(SparseSetVisitor { _phantom: PhantomData })
161		} else {
162			let bits = Deserialize::deserialize(deserializer)?;
163			let set = Self { bits, _phantom: PhantomData };
164			K::validate_sorted(set.bits.iter()).map_err(serde::de::Error::custom)?;
165			Ok(set)
166		}
167	}
168}
169
170impl<K> Default for SparseSet<K> {
171	fn default() -> Self {
172		Self::new()
173	}
174}
175
176impl<K> PartialEq for SparseSet<K> {
177	fn eq(&self, other: &Self) -> bool {
178		self.bits == other.bits
179	}
180}
181
182impl<K> Eq for SparseSet<K> {}
183
184impl<K> SparseSet<K> {
185	/// Creates a new empty sparse index set.
186	pub const fn new() -> Self {
187		Self { bits: SmolBitmap::new(), _phantom: PhantomData }
188	}
189
190	/// Creates a new sparse index set with the specified capacity.
191	///
192	/// # Arguments
193	///
194	/// * `capacity` - The maximum index that might be stored
195	pub fn with_capacity(capacity: usize) -> Self {
196		Self { bits: SmolBitmap::with_capacity(capacity), _phantom: PhantomData }
197	}
198
199	/// Returns the number of elements in the set.
200	#[inline]
201	pub fn len(&self) -> usize {
202		self.bits.len()
203	}
204
205	/// Returns `true` if the set contains no elements.
206	#[inline]
207	pub fn is_empty(&self) -> bool {
208		self.bits.is_empty()
209	}
210
211	/// Returns the capacity of the underlying bitmap.
212	#[inline]
213	pub const fn capacity(&self) -> usize {
214		self.bits.capacity()
215	}
216
217	/// Clears the set, removing all elements.
218	pub fn clear(&mut self) {
219		self.bits.clear();
220	}
221
222	/// Shrinks the capacity of the set as much as possible.
223	pub fn shrink_to_fit(&mut self) {
224		self.bits.shrink_to_fit();
225	}
226
227	/// Reserves capacity for at least `additional` more elements to be
228	/// inserted in the set.
229	pub fn reserve(&mut self, additional: usize) {
230		self.bits.reserve(additional);
231	}
232
233	/// Decomposes the set into its raw bitmap.
234	///
235	/// # Returns
236	///
237	/// The underlying `SmolBitmap`
238	#[inline]
239	pub fn into_parts(self) -> SmolBitmap {
240		self.bits
241	}
242
243	/// Constructs a sparse set from its raw bitmap.
244	///
245	/// # Arguments
246	///
247	/// * `bits` - The bitmap tracking which indices are present
248	#[inline]
249	pub const fn from_parts(bits: SmolBitmap) -> Self {
250		Self { bits, _phantom: PhantomData }
251	}
252
253	/// Returns `true` if the set is a subset of another.
254	pub fn is_subset(&self, other: &Self) -> bool {
255		self.bits.is_subset(&other.bits)
256	}
257
258	/// Returns `true` if the set is a superset of another.
259	pub fn is_superset(&self, other: &Self) -> bool {
260		self.bits.is_superset(&other.bits)
261	}
262
263	/// Returns `true` if the set has no elements in common with another.
264	pub fn is_disjoint(&self, other: &Self) -> bool {
265		self.bits.is_disjoint(&other.bits)
266	}
267
268	/// Computes the union with another set.
269	pub fn union(&self, other: &Self) -> Self {
270		Self { bits: self.bits.union(&other.bits), _phantom: PhantomData }
271	}
272
273	/// Computes the intersection with another set.
274	pub fn intersection(&self, other: &Self) -> Self {
275		Self { bits: self.bits.intersection(&other.bits), _phantom: PhantomData }
276	}
277
278	/// Computes the difference with another set.
279	pub fn difference(&self, other: &Self) -> Self {
280		Self { bits: self.bits.difference(&other.bits), _phantom: PhantomData }
281	}
282
283	/// Computes the symmetric difference with another set.
284	pub fn symmetric_difference(&self, other: &Self) -> Self {
285		Self { bits: self.bits.symmetric_difference(&other.bits), _phantom: PhantomData }
286	}
287}
288
289impl<K: TrySparseIndex> SparseSet<K> {
290	/// Returns `true` if the set contains the specified key.
291	///
292	/// # Arguments
293	///
294	/// * `key` - The key to check for
295	#[inline]
296	pub fn contains(&self, key: K) -> bool {
297		self.bits.get(key.index())
298	}
299
300	/// Adds a key to the set.
301	///
302	/// If the set did not have this key present, `true` is returned.
303	/// If the set did have this key present, `false` is returned.
304	///
305	/// # Arguments
306	///
307	/// * `key` - The key to insert
308	///
309	/// # Returns
310	///
311	/// `true` if the key was newly inserted, `false` if it was already present
312	pub fn insert(&mut self, key: K) -> bool {
313		self.bits.insert(key.index())
314	}
315
316	/// Removes a key from the set.
317	///
318	/// # Arguments
319	///
320	/// * `key` - The key to remove
321	///
322	/// # Returns
323	///
324	/// `true` if the key was present, `false` otherwise
325	pub fn remove(&mut self, key: K) -> bool {
326		self.bits.remove(key.index())
327	}
328
329	/// Retains only the elements specified by the predicate.
330	///
331	/// In other words, remove all keys `k` such that `f(&k)` returns `false`.
332	pub fn retain<F>(&mut self, mut f: F)
333	where
334		F: FnMut(K) -> bool,
335	{
336		self.bits.retain(|idx| f(K::from_index(idx)));
337	}
338
339	/// Returns an iterator over the keys of the set.
340	///
341	/// The iterator yields keys in the order of their index values.
342	#[define_opaque(Iter)]
343	pub fn iter(&self) -> Iter<'_, K> {
344		self.bits.iter().map(K::from_index)
345	}
346
347	/// Returns the minimum (first) element in the set, or [`None`] if the set is
348	/// empty.
349	pub fn first(&self) -> Option<K> {
350		self.bits.first().map(K::from_index)
351	}
352
353	/// Returns the maximum (last) element in the set, or [`None`] if the set is
354	/// empty.
355	pub fn last(&self) -> Option<K> {
356		self.bits.last().map(K::from_index)
357	}
358
359	/// Returns `true` if the set has holes (gaps) in its indices.
360	///
361	/// A set is considered sparse if there are missing indices between the
362	/// first and last elements. An empty set or a set with a single element
363	/// is considered non-sparse.
364	pub fn is_sparse(&self) -> bool {
365		match (self.bits.first(), self.bits.last()) {
366			(Some(first), Some(last)) => {
367				// If we have all consecutive indices from first to last,
368				// the count should equal (last - first + 1)
369				self.len() < (last - first + 1)
370			},
371			_ => false, // Empty or single element sets are not sparse
372		}
373	}
374}
375
376/// Iterator over members in key order; see [`SparseSet::iter`].
377pub type Iter<'a, K: TrySparseIndex> =
378	impl DoubleEndedIterator<Item = K> + ExactSizeIterator + FusedIterator + Clone;
379/// Owning iterator over members in key order.
380pub type IntoIter<K: TrySparseIndex> =
381	impl DoubleEndedIterator<Item = K> + ExactSizeIterator + FusedIterator + Clone;
382
383impl<'a, K: TrySparseIndex> IntoIterator for &'a SparseSet<K> {
384	type IntoIter = Iter<'a, K>;
385	type Item = K;
386
387	fn into_iter(self) -> Self::IntoIter {
388		self.iter()
389	}
390}
391
392impl<K: TrySparseIndex> IntoIterator for SparseSet<K> {
393	type IntoIter = IntoIter<K>;
394	type Item = K;
395
396	#[define_opaque(IntoIter)]
397	fn into_iter(self) -> Self::IntoIter {
398		self.bits.into_iter().map(K::from_index)
399	}
400}
401
402impl<K: TrySparseIndex> FromIterator<K> for SparseSet<K> {
403	fn from_iter<T: IntoIterator<Item = K>>(iter: T) -> Self {
404		let iter = iter.into_iter();
405		let mut set = Self::with_capacity(iter.size_hint().0);
406		for key in iter {
407			set.insert(key);
408		}
409		set
410	}
411}
412
413impl<K: TrySparseIndex> Extend<K> for SparseSet<K> {
414	fn extend<T: IntoIterator<Item = K>>(&mut self, iter: T) {
415		let iter = iter.into_iter();
416		self.reserve(iter.size_hint().0);
417		for key in iter {
418			self.insert(key);
419		}
420	}
421}