Skip to main content

bhc_data_structures/
lib.rs

1//! Common data structures for the BHC compiler.
2//!
3//! This crate re-exports and provides wrappers around commonly used
4//! data structures, ensuring consistent hashing and performance.
5
6#![warn(missing_docs)]
7
8use rustc_hash::FxHasher;
9use std::hash::BuildHasherDefault;
10
11/// A hash map using `FxHasher` for fast hashing.
12pub type FxHashMap<K, V> = std::collections::HashMap<K, V, BuildHasherDefault<FxHasher>>;
13
14/// A hash set using `FxHasher` for fast hashing.
15pub type FxHashSet<T> = std::collections::HashSet<T, BuildHasherDefault<FxHasher>>;
16
17/// An insertion-ordered hash map.
18pub type FxIndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
19
20/// An insertion-ordered hash set.
21pub type FxIndexSet<T> = indexmap::IndexSet<T, BuildHasherDefault<FxHasher>>;
22
23// Re-export commonly used types
24pub use indexmap::{IndexMap, IndexSet};
25pub use parking_lot::{Mutex, RwLock};
26pub use smallvec::SmallVec;
27
28/// A small vector optimized for holding 0-4 elements inline.
29pub type TinyVec<T> = SmallVec<[T; 4]>;
30
31/// A small vector optimized for holding 0-8 elements inline.
32pub type SmallVec8<T> = SmallVec<[T; 8]>;
33
34/// Extension trait for creating `FxHashMap` instances.
35pub trait FxHashMapExt<K, V> {
36    /// Create a new empty map.
37    fn new() -> Self;
38    /// Create a new map with the given capacity.
39    fn with_capacity(capacity: usize) -> Self;
40}
41
42impl<K, V> FxHashMapExt<K, V> for FxHashMap<K, V> {
43    fn new() -> Self {
44        Self::default()
45    }
46
47    fn with_capacity(capacity: usize) -> Self {
48        Self::with_capacity_and_hasher(capacity, BuildHasherDefault::default())
49    }
50}
51
52/// Extension trait for creating `FxHashSet` instances.
53pub trait FxHashSetExt<T> {
54    /// Create a new empty set.
55    fn new() -> Self;
56    /// Create a new set with the given capacity.
57    fn with_capacity(capacity: usize) -> Self;
58}
59
60impl<T> FxHashSetExt<T> for FxHashSet<T> {
61    fn new() -> Self {
62        Self::default()
63    }
64
65    fn with_capacity(capacity: usize) -> Self {
66        Self::with_capacity_and_hasher(capacity, BuildHasherDefault::default())
67    }
68}
69
70/// A work queue for graph traversals.
71#[derive(Debug, Clone)]
72pub struct WorkQueue<T> {
73    queue: std::collections::VecDeque<T>,
74    seen: FxHashSet<T>,
75}
76
77impl<T: std::hash::Hash + Eq + Clone> WorkQueue<T> {
78    /// Create a new empty work queue.
79    #[must_use]
80    pub fn new() -> Self {
81        Self {
82            queue: std::collections::VecDeque::new(),
83            seen: FxHashSet::default(),
84        }
85    }
86
87    /// Add an item to the queue if it hasn't been seen before.
88    pub fn push(&mut self, item: T) -> bool {
89        if self.seen.insert(item.clone()) {
90            self.queue.push_back(item);
91            true
92        } else {
93            false
94        }
95    }
96
97    /// Pop an item from the queue.
98    pub fn pop(&mut self) -> Option<T> {
99        self.queue.pop_front()
100    }
101
102    /// Check if the queue is empty.
103    #[must_use]
104    pub fn is_empty(&self) -> bool {
105        self.queue.is_empty()
106    }
107}
108
109impl<T: std::hash::Hash + Eq + Clone> Default for WorkQueue<T> {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115/// A frozen hash map that becomes immutable after construction.
116#[derive(Debug, Clone)]
117pub struct FrozenMap<K, V> {
118    map: FxHashMap<K, V>,
119}
120
121impl<K: std::hash::Hash + Eq, V> FrozenMap<K, V> {
122    /// Create a frozen map from a hash map.
123    #[must_use]
124    pub fn new(map: FxHashMap<K, V>) -> Self {
125        Self { map }
126    }
127
128    /// Get a value by key.
129    #[must_use]
130    pub fn get(&self, key: &K) -> Option<&V> {
131        self.map.get(key)
132    }
133
134    /// Check if a key exists.
135    #[must_use]
136    pub fn contains_key(&self, key: &K) -> bool {
137        self.map.contains_key(key)
138    }
139
140    /// Get the number of entries.
141    #[must_use]
142    pub fn len(&self) -> usize {
143        self.map.len()
144    }
145
146    /// Check if the map is empty.
147    #[must_use]
148    pub fn is_empty(&self) -> bool {
149        self.map.is_empty()
150    }
151
152    /// Iterate over the entries.
153    pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
154        self.map.iter()
155    }
156}
157
158impl<K: std::hash::Hash + Eq, V> FromIterator<(K, V)> for FrozenMap<K, V> {
159    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
160        Self::new(iter.into_iter().collect())
161    }
162}
163
164/// A union-find (disjoint set) data structure.
165#[derive(Debug, Clone)]
166pub struct UnionFind {
167    parent: Vec<usize>,
168    rank: Vec<usize>,
169}
170
171impl UnionFind {
172    /// Create a new union-find structure with `n` elements.
173    #[must_use]
174    pub fn new(n: usize) -> Self {
175        Self {
176            parent: (0..n).collect(),
177            rank: vec![0; n],
178        }
179    }
180
181    /// Find the representative of the set containing `x`.
182    pub fn find(&mut self, x: usize) -> usize {
183        if self.parent[x] != x {
184            self.parent[x] = self.find(self.parent[x]);
185        }
186        self.parent[x]
187    }
188
189    /// Union the sets containing `x` and `y`.
190    pub fn union(&mut self, x: usize, y: usize) {
191        let rx = self.find(x);
192        let ry = self.find(y);
193
194        if rx == ry {
195            return;
196        }
197
198        match self.rank[rx].cmp(&self.rank[ry]) {
199            std::cmp::Ordering::Less => self.parent[rx] = ry,
200            std::cmp::Ordering::Greater => self.parent[ry] = rx,
201            std::cmp::Ordering::Equal => {
202                self.parent[ry] = rx;
203                self.rank[rx] += 1;
204            }
205        }
206    }
207
208    /// Check if `x` and `y` are in the same set.
209    pub fn same_set(&mut self, x: usize, y: usize) -> bool {
210        self.find(x) == self.find(y)
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn test_work_queue() {
220        let mut wq: WorkQueue<i32> = WorkQueue::new();
221
222        assert!(wq.push(1));
223        assert!(wq.push(2));
224        assert!(!wq.push(1)); // Already seen
225
226        assert_eq!(wq.pop(), Some(1));
227        assert_eq!(wq.pop(), Some(2));
228        assert_eq!(wq.pop(), None);
229    }
230
231    #[test]
232    fn test_union_find() {
233        let mut uf = UnionFind::new(5);
234
235        uf.union(0, 1);
236        uf.union(2, 3);
237        uf.union(1, 3);
238
239        assert!(uf.same_set(0, 3));
240        assert!(!uf.same_set(0, 4));
241    }
242}