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 if first_deleted.is_none() => {
82 first_deleted = Some(idx);
83 }
84 _ => {}
85 }
86 }
87 first_deleted.unwrap_or(0)
88 }
89
90 fn maybe_resize(&mut self) {
91 let load = (self.count + self.deleted) as f32 / self.capacity as f32;
92 if load > LOAD_FACTOR_MAX {
93 let new_cap = self.capacity * 2;
94 let old_slots =
95 std::mem::replace(&mut self.slots, (0..new_cap).map(|_| Slot::Empty).collect());
96 self.capacity = new_cap;
97 self.count = 0;
98 self.deleted = 0;
99 for slot in old_slots {
100 if let Slot::Occupied(k, v) = slot {
101 self.insert(k, v);
102 }
103 }
104 }
105 }
106
107 #[allow(dead_code)]
109 pub fn insert(&mut self, key: i64, value: i64) {
110 self.maybe_resize();
111 let idx = self.probe_insert(key);
112 match &self.slots[idx] {
113 Slot::Occupied(k, _) if *k == key => {
114 self.slots[idx] = Slot::Occupied(key, value);
115 }
116 Slot::Deleted => {
117 self.slots[idx] = Slot::Occupied(key, value);
118 self.deleted = self.deleted.saturating_sub(1);
119 self.count += 1;
120 }
121 _ => {
122 self.slots[idx] = Slot::Occupied(key, value);
123 self.count += 1;
124 }
125 }
126 }
127
128 #[allow(dead_code)]
130 pub fn get(&self, key: i64) -> Option<i64> {
131 let idx = self.probe(key)?;
132 match &self.slots[idx] {
133 Slot::Occupied(_, v) => Some(*v),
134 _ => None,
135 }
136 }
137
138 #[allow(dead_code)]
140 pub fn contains(&self, key: i64) -> bool {
141 self.probe(key).is_some()
142 }
143
144 #[allow(dead_code)]
146 pub fn remove(&mut self, key: i64) -> bool {
147 if let Some(idx) = self.probe(key) {
148 self.slots[idx] = Slot::Deleted;
149 self.count -= 1;
150 self.deleted += 1;
151 true
152 } else {
153 false
154 }
155 }
156
157 #[allow(dead_code)]
159 pub fn len(&self) -> usize {
160 self.count
161 }
162
163 #[allow(dead_code)]
165 pub fn is_empty(&self) -> bool {
166 self.count == 0
167 }
168
169 #[allow(dead_code)]
171 pub fn load_factor(&self) -> f32 {
172 self.count as f32 / self.capacity as f32
173 }
174
175 #[allow(dead_code)]
177 pub fn pairs(&self) -> Vec<(i64, i64)> {
178 self.slots
179 .iter()
180 .filter_map(|s| {
181 if let Slot::Occupied(k, v) = s {
182 Some((*k, *v))
183 } else {
184 None
185 }
186 })
187 .collect()
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn insert_and_get() {
197 let mut m = OpenHashMap::new();
198 m.insert(42, 420);
199 assert_eq!(m.get(42), Some(420));
200 }
201
202 #[test]
203 fn get_missing_is_none() {
204 let m = OpenHashMap::new();
205 assert!(m.get(1).is_none());
206 }
207
208 #[test]
209 fn contains_after_insert() {
210 let mut m = OpenHashMap::new();
211 m.insert(7, 70);
212 assert!(m.contains(7));
213 }
214
215 #[test]
216 fn overwrite_existing() {
217 let mut m = OpenHashMap::new();
218 m.insert(1, 10);
219 m.insert(1, 20);
220 assert_eq!(m.get(1), Some(20));
221 assert_eq!(m.len(), 1);
222 }
223
224 #[test]
225 fn remove_existing() {
226 let mut m = OpenHashMap::new();
227 m.insert(3, 3);
228 assert!(m.remove(3));
229 assert!(!m.contains(3));
230 }
231
232 #[test]
233 fn remove_missing_returns_false() {
234 let mut m = OpenHashMap::new();
235 assert!(!m.remove(99));
236 }
237
238 #[test]
239 fn len_tracks_insertions() {
240 let mut m = OpenHashMap::new();
241 m.insert(1, 1);
242 m.insert(2, 2);
243 assert_eq!(m.len(), 2);
244 }
245
246 #[test]
247 fn is_empty_initially() {
248 let m = OpenHashMap::new();
249 assert!(m.is_empty());
250 }
251
252 #[test]
253 fn many_inserts_all_findable() {
254 let mut m = OpenHashMap::new();
255 for i in 0..50i64 {
256 m.insert(i, i * 2);
257 }
258 for i in 0..50i64 {
259 assert_eq!(m.get(i), Some(i * 2));
260 }
261 }
262
263 #[test]
264 fn pairs_count_matches_len() {
265 let mut m = OpenHashMap::new();
266 m.insert(10, 10);
267 m.insert(20, 20);
268 assert_eq!(m.pairs().len(), m.len());
269 }
270}