oxihuman_core/
hash_map_open.rs1#![allow(dead_code)]
4
5const DEFAULT_CAPACITY: usize = 16;
8const LOAD_FACTOR_MAX: f32 = 0.7;
9
10#[derive(Debug, Clone)]
11enum Slot<K, V> {
12 Empty,
13 Deleted,
14 Occupied(K, V),
15}
16
17fn hash_key(key: i64, cap: usize) -> usize {
18 let mut h = key as u64;
19 h ^= h >> 33;
20 h = h.wrapping_mul(0xff51afd7ed558ccd);
21 h ^= h >> 33;
22 h as usize % cap
23}
24
25#[derive(Debug, Clone)]
27#[allow(dead_code)]
28pub struct OpenHashMap {
29 slots: Vec<Slot<i64, i64>>,
30 count: usize,
31 deleted: usize,
32 capacity: usize,
33}
34
35impl Default for OpenHashMap {
36 fn default() -> Self {
37 Self::with_capacity(DEFAULT_CAPACITY)
38 }
39}
40
41impl OpenHashMap {
42 #[allow(dead_code)]
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 #[allow(dead_code)]
50 pub fn with_capacity(cap: usize) -> Self {
51 let cap = cap.max(8);
52 Self {
53 slots: (0..cap).map(|_| Slot::Empty).collect(),
54 count: 0,
55 deleted: 0,
56 capacity: cap,
57 }
58 }
59
60 fn probe(&self, key: i64) -> Option<usize> {
61 let start = hash_key(key, self.capacity);
62 for i in 0..self.capacity {
63 let idx = (start + i) % self.capacity;
64 match &self.slots[idx] {
65 Slot::Occupied(k, _) if *k == key => return Some(idx),
66 Slot::Empty => return None,
67 _ => {}
68 }
69 }
70 None
71 }
72
73 fn probe_insert(&self, key: i64) -> usize {
74 let start = hash_key(key, self.capacity);
75 let mut first_deleted: Option<usize> = None;
76 for i in 0..self.capacity {
77 let idx = (start + i) % self.capacity;
78 match &self.slots[idx] {
79 Slot::Occupied(k, _) if *k == key => return idx,
80 Slot::Empty => return first_deleted.unwrap_or(idx),
81 Slot::Deleted => {
82 if first_deleted.is_none() {
83 first_deleted = Some(idx);
84 }
85 }
86 _ => {}
87 }
88 }
89 first_deleted.unwrap_or(0)
90 }
91
92 fn maybe_resize(&mut self) {
93 let load = (self.count + self.deleted) as f32 / self.capacity as f32;
94 if load > LOAD_FACTOR_MAX {
95 let new_cap = self.capacity * 2;
96 let old_slots =
97 std::mem::replace(&mut self.slots, (0..new_cap).map(|_| Slot::Empty).collect());
98 self.capacity = new_cap;
99 self.count = 0;
100 self.deleted = 0;
101 for slot in old_slots {
102 if let Slot::Occupied(k, v) = slot {
103 self.insert(k, v);
104 }
105 }
106 }
107 }
108
109 #[allow(dead_code)]
111 pub fn insert(&mut self, key: i64, value: i64) {
112 self.maybe_resize();
113 let idx = self.probe_insert(key);
114 match &self.slots[idx] {
115 Slot::Occupied(k, _) if *k == key => {
116 self.slots[idx] = Slot::Occupied(key, value);
117 }
118 Slot::Deleted => {
119 self.slots[idx] = Slot::Occupied(key, value);
120 self.deleted = self.deleted.saturating_sub(1);
121 self.count += 1;
122 }
123 _ => {
124 self.slots[idx] = Slot::Occupied(key, value);
125 self.count += 1;
126 }
127 }
128 }
129
130 #[allow(dead_code)]
132 pub fn get(&self, key: i64) -> Option<i64> {
133 let idx = self.probe(key)?;
134 match &self.slots[idx] {
135 Slot::Occupied(_, v) => Some(*v),
136 _ => None,
137 }
138 }
139
140 #[allow(dead_code)]
142 pub fn contains(&self, key: i64) -> bool {
143 self.probe(key).is_some()
144 }
145
146 #[allow(dead_code)]
148 pub fn remove(&mut self, key: i64) -> bool {
149 if let Some(idx) = self.probe(key) {
150 self.slots[idx] = Slot::Deleted;
151 self.count -= 1;
152 self.deleted += 1;
153 true
154 } else {
155 false
156 }
157 }
158
159 #[allow(dead_code)]
161 pub fn len(&self) -> usize {
162 self.count
163 }
164
165 #[allow(dead_code)]
167 pub fn is_empty(&self) -> bool {
168 self.count == 0
169 }
170
171 #[allow(dead_code)]
173 pub fn load_factor(&self) -> f32 {
174 self.count as f32 / self.capacity as f32
175 }
176
177 #[allow(dead_code)]
179 pub fn pairs(&self) -> Vec<(i64, i64)> {
180 self.slots
181 .iter()
182 .filter_map(|s| {
183 if let Slot::Occupied(k, v) = s {
184 Some((*k, *v))
185 } else {
186 None
187 }
188 })
189 .collect()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn insert_and_get() {
199 let mut m = OpenHashMap::new();
200 m.insert(42, 420);
201 assert_eq!(m.get(42), Some(420));
202 }
203
204 #[test]
205 fn get_missing_is_none() {
206 let m = OpenHashMap::new();
207 assert!(m.get(1).is_none());
208 }
209
210 #[test]
211 fn contains_after_insert() {
212 let mut m = OpenHashMap::new();
213 m.insert(7, 70);
214 assert!(m.contains(7));
215 }
216
217 #[test]
218 fn overwrite_existing() {
219 let mut m = OpenHashMap::new();
220 m.insert(1, 10);
221 m.insert(1, 20);
222 assert_eq!(m.get(1), Some(20));
223 assert_eq!(m.len(), 1);
224 }
225
226 #[test]
227 fn remove_existing() {
228 let mut m = OpenHashMap::new();
229 m.insert(3, 3);
230 assert!(m.remove(3));
231 assert!(!m.contains(3));
232 }
233
234 #[test]
235 fn remove_missing_returns_false() {
236 let mut m = OpenHashMap::new();
237 assert!(!m.remove(99));
238 }
239
240 #[test]
241 fn len_tracks_insertions() {
242 let mut m = OpenHashMap::new();
243 m.insert(1, 1);
244 m.insert(2, 2);
245 assert_eq!(m.len(), 2);
246 }
247
248 #[test]
249 fn is_empty_initially() {
250 let m = OpenHashMap::new();
251 assert!(m.is_empty());
252 }
253
254 #[test]
255 fn many_inserts_all_findable() {
256 let mut m = OpenHashMap::new();
257 for i in 0..50i64 {
258 m.insert(i, i * 2);
259 }
260 for i in 0..50i64 {
261 assert_eq!(m.get(i), Some(i * 2));
262 }
263 }
264
265 #[test]
266 fn pairs_count_matches_len() {
267 let mut m = OpenHashMap::new();
268 m.insert(10, 10);
269 m.insert(20, 20);
270 assert_eq!(m.pairs().len(), m.len());
271 }
272}