1use std::fmt::{Debug, Formatter};
2use std::hash::Hash;
3use std::ptr;
4use std::{collections::HashMap, mem::MaybeUninit};
5
6#[derive(Debug, Clone)]
13pub struct InlineHashMap<K, V, const N: usize>(InlineHashMapInner<K, V, N>);
14
15impl<K, V, const N: usize> InlineHashMap<K, V, N>
16where
17 K: Hash + Eq,
18{
19 pub(crate) fn new() -> Self {
21 Self(InlineHashMapInner::new())
22 }
23
24 #[inline]
26 pub fn len(&self) -> usize {
27 self.0.len()
28 }
29
30 #[inline]
32 pub fn is_empty(&self) -> bool {
33 self.len() == 0
34 }
35
36 #[inline]
42 pub fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_> {
43 self.0.iter()
44 }
45
46 #[inline]
50 #[allow(clippy::type_complexity)]
51 pub fn inline_parts_mut(&mut self) -> Option<(&mut [MaybeUninit<(K, V)>; N], usize)> {
52 self.0.inline_parts_mut()
53 }
54
55 #[inline]
57 pub fn to_map(&self) -> HashMap<K, V>
58 where
59 K: Clone + Hash + Eq,
60 V: Clone,
61 {
62 self.0.to_map()
63 }
64
65 #[inline]
67 pub fn is_heap_allocated(&self) -> bool {
68 self.0.is_heap_allocated()
69 }
70
71 #[inline]
73 pub fn insert(&mut self, key: K, value: V) {
74 self.0.insert(key, value)
75 }
76
77 #[inline]
79 pub fn remove(&mut self, key: &K) -> Option<V> {
80 self.0.remove(key)
81 }
82
83 #[inline]
85 pub fn get(&self, key: &K) -> Option<&V> {
86 self.0.get(key)
87 }
88
89 #[inline]
91 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
92 self.0.get_mut(key)
93 }
94
95 #[inline]
97 pub fn contains_key(&self, key: &K) -> bool {
98 self.0.contains_key(key)
99 }
100}
101
102enum InlineHashMapInner<K, V, const N: usize> {
103 Inline {
104 len: usize,
105 data: [MaybeUninit<(K, V)>; N],
106 },
107 Heap(HashMap<K, V>),
108}
109
110impl<K, V, const N: usize> Debug for InlineHashMapInner<K, V, N>
111where
112 K: Debug,
113 V: Debug,
114{
115 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
116 write!(f, "InlineHashMap<{} items>", self.len())
117 }
118}
119
120impl<K, V, const N: usize> Clone for InlineHashMapInner<K, V, N>
121where
122 K: Clone,
123 V: Clone,
124{
125 fn clone(&self) -> Self {
126 match self {
127 Self::Heap(m) => Self::Heap(m.clone()),
128 Self::Inline { len, data } => {
129 let mut new_data = super::uninit_array();
130
131 let iter = data.iter().take(*len).enumerate();
132
133 for (idx, element) in iter {
134 let element = unsafe { &*element.as_ptr() };
135 let (key, value) = element.clone();
136 new_data[idx] = MaybeUninit::new((key, value));
137 }
138
139 Self::Inline {
140 len: *len,
141 data: new_data,
142 }
143 }
144 }
145 }
146}
147
148impl<K, V, const N: usize> Drop for InlineHashMapInner<K, V, N> {
149 fn drop(&mut self) {
150 if let Some((data, len)) = self.inline_parts_mut() {
151 for element in data.iter_mut().take(len) {
152 unsafe { ptr::drop_in_place(element.as_mut_ptr()) };
153 }
154 }
155 }
156}
157
158impl<K, V, const N: usize> InlineHashMapInner<K, V, N> {
159 #[inline]
160 pub(crate) fn new() -> Self {
161 Self::Inline {
162 len: 0,
163 data: super::uninit_array(),
164 }
165 }
166
167 #[inline]
168 pub fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_> {
169 match self {
170 Self::Inline { len, data } => {
171 Box::new(unsafe { InlineHashMapIterator::new(data, *len) })
172 }
173 Self::Heap(h) => Box::new(h.iter()),
174 }
175 }
176
177 #[inline]
178 #[allow(clippy::type_complexity)]
179 pub fn inline_parts_mut(&mut self) -> Option<(&mut [MaybeUninit<(K, V)>; N], usize)> {
180 match self {
181 Self::Heap(_) => None,
182 Self::Inline { len, data } => Some((data, *len)),
183 }
184 }
185
186 #[inline]
187 fn to_map(&self) -> HashMap<K, V>
188 where
189 K: Clone + Hash + Eq,
190 V: Clone,
191 {
192 match &self {
193 InlineHashMapInner::Heap(m) => m.clone(),
194 InlineHashMapInner::Inline { len, data } => {
195 let mut new_data = HashMap::with_capacity(*len);
196
197 let iter = data.iter().take(*len);
198
199 for element in iter {
200 let element = unsafe { &*element.as_ptr() };
201 let (key, value) = element.clone();
202 new_data.insert(key, value);
203 }
204
205 new_data
206 }
207 }
208 }
209
210 #[inline]
211 pub fn len(&self) -> usize {
212 match self {
213 Self::Inline { len, .. } => *len,
214 Self::Heap(map) => map.len(),
215 }
216 }
217
218 #[inline]
219 pub fn is_heap_allocated(&self) -> bool {
220 matches!(self, Self::Heap(_))
221 }
222}
223
224impl<K: Eq + Hash, V, const N: usize> InlineHashMapInner<K, V, N> {
225 pub fn get<'m>(&'m self, k: &K) -> Option<&'m V> {
226 match self {
227 Self::Inline { data, len } => unsafe {
228 InlineHashMapIterator::new(data, *len)
229 .find(|(key, _)| key.eq(&k))
230 .map(|(_, value)| value)
231 },
232 Self::Heap(map) => map.get(k),
233 }
234 }
235
236 pub fn get_mut<'m>(&'m mut self, k: &K) -> Option<&'m mut V> {
237 match self {
238 Self::Inline { data, len } => unsafe {
239 InlineHashMapIteratorMut::new(data, *len)
240 .find(|(key, _)| key.eq(k))
241 .map(|(_, value)| value)
242 },
243 Self::Heap(map) => map.get_mut(k),
244 }
245 }
246
247 pub fn remove(&mut self, key: &K) -> Option<V> {
248 match self {
249 Self::Inline { data, len } => {
250 let idx = data
251 .iter()
252 .take(*len)
253 .map(|x| unsafe { &*x.as_ptr() })
254 .position(|x| &x.0 == key)?;
255
256 let element = unsafe {
257 std::mem::replace(data.get_unchecked_mut(idx), MaybeUninit::uninit())
258 };
259
260 data.swap(idx, *len - 1);
263 *len -= 1;
264
265 Some(unsafe { element.assume_init().1 })
266 }
267 Self::Heap(h) => h.remove(key),
268 }
269 }
270
271 pub fn insert(&mut self, k: K, v: V) {
272 let (array, len) = match self {
273 Self::Inline { data, len } => (data, len),
274 Self::Heap(map) => {
275 map.insert(k, v);
276 return;
277 }
278 };
279
280 if *len >= N {
281 let mut map = HashMap::with_capacity(*len);
282
283 for element in array.iter_mut().take(*len) {
285 let element = std::mem::replace(element, MaybeUninit::uninit());
286 let (key, value) = unsafe { element.assume_init() };
287
288 map.insert(key, value);
289 }
290
291 map.insert(k, v);
293 let new_heap = Self::Heap(map);
294
295 unsafe { ptr::write(self, new_heap) };
297 } else {
298 array[*len].write((k, v));
299 *len += 1;
300 }
301 }
302
303 pub fn contains_key(&self, k: &K) -> bool {
304 match self {
305 Self::Inline { data, len } => unsafe {
306 InlineHashMapIterator::new(data, *len).any(|(key, _)| key.eq(k))
307 },
308 Self::Heap(map) => map.contains_key(k),
309 }
310 }
311}
312
313pub struct InlineHashMapIteratorMut<'a, K, V> {
315 array: &'a mut [MaybeUninit<(K, V)>],
316 idx: usize,
317 len: usize,
318}
319
320impl<'a, K, V> InlineHashMapIteratorMut<'a, K, V> {
321 pub(crate) unsafe fn new(array: &'a mut [MaybeUninit<(K, V)>], len: usize) -> Self {
322 Self { array, idx: 0, len }
323 }
324}
325
326impl<'a, K, V> Iterator for InlineHashMapIteratorMut<'a, K, V> {
327 type Item = &'a mut (K, V);
328
329 fn next(&mut self) -> Option<Self::Item> {
330 if self.idx >= self.len {
331 return None;
332 }
333
334 let element = unsafe { &mut *self.array[self.idx].as_mut_ptr() };
335 self.idx += 1;
336
337 Some(element)
338 }
339}
340
341pub struct InlineHashMapIterator<'a, K, V> {
343 array: &'a [MaybeUninit<(K, V)>],
344 idx: usize,
345 len: usize,
346}
347
348impl<'a, K, V> InlineHashMapIterator<'a, K, V> {
349 pub(crate) unsafe fn new(array: &'a [MaybeUninit<(K, V)>], len: usize) -> Self {
350 Self { array, idx: 0, len }
351 }
352}
353
354impl<'a, K, V> Iterator for InlineHashMapIterator<'a, K, V> {
355 type Item = (&'a K, &'a V);
356
357 fn next(&mut self) -> Option<Self::Item> {
358 if self.idx >= self.len {
359 return None;
360 }
361
362 let (k, v) = unsafe { &*self.array[self.idx].as_ptr() };
363 self.idx += 1;
364
365 Some((k, v))
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn inlinehashmap_iter() {
375 let mut x = InlineHashMap::<String, usize, 5>::new();
376 x.insert("foo".into(), 3);
377 x.insert("bar".into(), 6);
378 x.insert("baz".into(), 7);
379 x.insert("qux".into(), 9);
380
381 let mut iter = x.iter();
382
383 assert_eq!(iter.next(), Some((&"foo".into(), &3usize)));
388 assert_eq!(iter.next(), Some((&"bar".into(), &6usize)));
389 assert_eq!(iter.next(), Some((&"baz".into(), &7usize)));
390 assert_eq!(iter.next(), Some((&"qux".into(), &9usize)));
391 }
392
393 #[test]
394 fn inlinehashmap_remove() {
395 let mut x = InlineHashMap::<usize, usize, 4>::new();
396 x.insert(789, 1336);
397 assert_eq!(x.len(), 1);
398 assert_eq!(x.get(&789), Some(&1336));
399 assert_eq!(x.remove(&789), Some(1336));
400 assert_eq!(x.len(), 0);
401
402 assert_eq!(x.remove(&789), None);
403
404 for i in 0..4 {
405 x.insert(i, i * 2);
406 }
407
408 assert!(!x.is_heap_allocated());
409 assert_eq!(x.len(), 4);
410
411 assert_eq!(x.remove(&2), Some(4));
412 assert_eq!(x.len(), 3);
413
414 assert_eq!(x.remove(&3), Some(6));
415 assert_eq!(x.len(), 2);
416
417 assert_eq!(x.remove(&1), Some(2));
418 assert_eq!(x.len(), 1);
419
420 assert_eq!(x.remove(&0), Some(0));
421 assert_eq!(x.len(), 0);
422 assert!(!x.is_heap_allocated());
423
424 for i in 0..8 {
426 x.insert(i, i * 2);
427 }
428 assert!(x.is_heap_allocated());
429 assert_eq!(x.len(), 8);
430
431 assert_eq!(x.remove(&7), Some(14));
432 assert_eq!(x.remove(&0), Some(0));
433 }
434
435 #[test]
436 fn inlinehashmap_remove_heap() {
437 let mut x = InlineHashMap::<usize, String, 4>::new();
438 x.insert(42, "test".into());
439 assert_eq!(x.len(), 1);
440 assert_eq!(x.remove(&42), Some("test".into()));
441 assert_eq!(x.len(), 0);
442 }
443
444 #[test]
445 fn inlinehashmap_clone() {
446 let mut x = InlineHashMapInner::<usize, usize, 4>::new();
447
448 for i in 0..10 {
449 x.insert(i, i * 2);
450 }
451
452 let x = x.clone();
453 assert_eq!(x.len(), 10);
454 assert!(x.is_heap_allocated());
455 assert_eq!(x.get(&9), Some(&18));
456 }
457
458 #[test]
459 fn inlinehashmap_to_map_stack() {
460 let mut x = InlineHashMapInner::<usize, usize, 4>::new();
461
462 for i in 0..4 {
463 x.insert(i, i * 2);
464 }
465
466 assert!(!x.is_heap_allocated());
467 assert_eq!(x.len(), 4);
468
469 let xx = x.to_map();
470 assert_eq!(xx.get(&0), Some(&0));
471 assert_eq!(xx.get(&1), Some(&2));
472 assert_eq!(xx.get(&2), Some(&4));
473 assert_eq!(xx.get(&3), Some(&6));
474 assert_eq!(xx.len(), 4);
475
476 x.insert(42, 1337);
477 assert!(x.is_heap_allocated());
478 assert_eq!(x.len(), 5);
479 assert_eq!(x.get(&42), Some(&1337));
480
481 let xx = x.to_map();
482 assert_eq!(xx.get(&0), Some(&0));
483 assert_eq!(xx.get(&42), Some(&1337));
484 assert_eq!(xx.len(), 5);
485 }
486
487 #[test]
488 fn inlinehashmap_to_map_heap() {
489 let mut x = InlineHashMapInner::<usize, String, 4>::new();
490
491 for i in 0..4 {
492 x.insert(i, i.to_string());
493 }
494
495 assert!(!x.is_heap_allocated());
496 assert_eq!(x.len(), 4);
497
498 let xx = x.to_map();
499 assert_eq!(&*xx[&0], "0");
500 assert_eq!(&*xx[&1], "1");
501 assert_eq!(&*xx[&2], "2");
502 assert_eq!(&*xx[&3], "3");
503 assert_eq!(xx.len(), 4);
504
505 x.insert(42, "1337".into());
506 assert!(x.is_heap_allocated());
507 assert_eq!(x.len(), 5);
508 assert_eq!(x.get(&42).map(|x| &**x), Some("1337"));
509
510 let xx = x.to_map();
511 assert_eq!(&*xx[&0], "0");
512 assert_eq!(&*xx[&42], "1337");
513 assert_eq!(xx.len(), 5);
514 }
515
516 #[test]
517 fn inlinehashmap_drop_stack() {
518 let mut x = InlineHashMapInner::<usize, String, 4>::new();
519
520 for i in 0..3 {
521 x.insert(i, i.to_string());
522 }
523
524 assert_eq!(x.len(), 3);
525 assert!(!x.is_heap_allocated());
526 }
527
528 #[test]
529 fn inlinehashmap_drop_heap() {
530 let mut x = InlineHashMapInner::<usize, String, 4>::new();
531
532 for i in 0..16 {
533 x.insert(i, i.to_string());
534 }
535
536 assert_eq!(x.len(), 16);
537 assert!(x.is_heap_allocated());
538 }
539
540 #[test]
541 fn inlinehashmap() {
542 let mut x = InlineHashMapInner::<&'static str, usize, 4>::new();
543 assert_eq!(x.len(), 0);
544 assert_eq!(x.get(&"hi"), None);
545 assert!(!x.is_heap_allocated());
546
547 x.insert("foo", 1337);
548 assert_eq!(x.len(), 1);
549 assert_eq!(x.get(&"foo"), Some(&1337));
550 assert!(!x.is_heap_allocated());
551
552 x.insert("foo2", 2);
553 x.insert("foo3", 3);
554 x.insert("foo4", 4);
555
556 assert_eq!(x.len(), 4);
557
558 x.insert("foo5", 5);
559 assert_eq!(x.len(), 5);
560 assert!(x.is_heap_allocated());
561
562 x.insert("foo6", 6);
563 x.insert("foo7", 7);
564 x.insert("foo8", 8);
565 x.insert("foo9", 9);
566 x.insert("foo10", 10);
567 x.insert("foo11", 11);
568 }
569}