1#![allow(clippy::manual_map)]
2
3use core::{
4 hash::{BuildHasher, Hash},
5 hint::unreachable_unchecked,
6 iter::FusedIterator,
7};
8use std::{
9 collections::hash_map::RandomState,
10 fmt::{self, Debug},
11};
12
13use hashbrown::HashMap;
14use inline::Inline;
15
16#[macro_use]
17mod macros;
18mod inline;
19mod raw;
20
21pub use hashbrown::Equivalent;
22
23#[cfg(feature = "serde")]
24mod serde;
25
26#[cfg(feature = "fxhash")]
27pub type FxSmallMap<const N: usize, K, V> =
28 SmallMap<N, K, V, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
29#[cfg(feature = "ahash")]
30pub type ASmallMap<const N: usize, K, V> =
31 SmallMap<N, K, V, core::hash::BuildHasherDefault<ahash::AHasher>>;
32
33#[derive(Clone)]
34pub enum SmallMap<const N: usize, K, V, S = RandomState> {
35 Heap(HashMap<K, V, S>),
36 Inline(Inline<N, K, V, S>),
37}
38
39impl<const N: usize, K, V, S> Debug for SmallMap<N, K, V, S>
40where
41 K: Debug,
42 V: Debug,
43{
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.debug_map().entries(self.iter()).finish()
46 }
47}
48
49impl<const N: usize, K, V, S: Default> Default for SmallMap<N, K, V, S> {
50 #[inline]
67 fn default() -> Self {
68 Self::with_hasher(Default::default())
69 }
70}
71
72impl<const N: usize, K, V, S: Default> SmallMap<N, K, V, S> {
73 #[inline]
75 pub fn new() -> Self {
76 Self::with_hasher(Default::default())
77 }
78
79 #[inline]
84 pub fn with_capacity(capacity: usize) -> Self {
85 if capacity > N {
86 Self::Heap(HashMap::with_capacity_and_hasher(
87 capacity,
88 Default::default(),
89 ))
90 } else {
91 Self::Inline(Inline::new(Default::default()))
92 }
93 }
94}
95
96impl<const N: usize, K, V, S> SmallMap<N, K, V, S> {
97 #[inline]
114 pub const fn with_hasher(hash_builder: S) -> Self {
115 Self::Inline(Inline::new(hash_builder))
116 }
117
118 #[inline]
124 pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self {
125 if capacity > N {
126 Self::Heap(HashMap::with_capacity_and_hasher(capacity, hash_builder))
127 } else {
128 Self::Inline(Inline::new(hash_builder))
129 }
130 }
131
132 #[inline]
134 pub fn is_inline(&self) -> bool {
135 matches!(self, Self::Inline(..))
136 }
137
138 #[inline]
157 pub fn capacity(&self) -> usize {
158 match self {
159 SmallMap::Heap(inner) => inner.capacity(),
160 SmallMap::Inline(_) => N,
161 }
162 }
163}
164
165impl<const N: usize, K, V, S> SmallMap<N, K, V, S>
166where
167 K: Eq + Hash,
168 S: BuildHasher,
169{
170 #[inline]
171 pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
172 where
173 Q: Hash + Equivalent<K>,
174 {
175 match self {
176 SmallMap::Heap(inner) => inner.get(k),
177 SmallMap::Inline(inner) => inner.get(k),
178 }
179 }
180
181 #[inline]
183 pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
184 where
185 Q: Hash + Equivalent<K>,
186 {
187 match self {
188 SmallMap::Heap(inner) => inner.get_mut(k),
189 SmallMap::Inline(inner) => inner.get_mut(k),
190 }
191 }
192
193 #[inline]
195 pub fn get_key_value<Q: ?Sized>(&self, k: &Q) -> Option<(&K, &V)>
196 where
197 Q: Hash + Equivalent<K>,
198 {
199 match self {
200 SmallMap::Heap(inner) => inner.get_key_value(k),
201 SmallMap::Inline(inner) => inner.get_key_value(k),
202 }
203 }
204
205 #[inline]
206 pub fn insert(&mut self, key: K, value: V) -> Option<V> {
207 match self {
208 SmallMap::Heap(inner) => inner.insert(key, value),
209 SmallMap::Inline(inner) => {
210 if raw::util::likely(!inner.is_full()) {
211 return inner.insert(key, value);
212 }
213 let heap = HashMap::with_capacity_and_hasher(N + 1, unsafe { inner.take_hasher() });
214
215 let heap = match core::mem::replace(self, SmallMap::Heap(heap)) {
216 SmallMap::Heap(_) => unsafe { unreachable_unchecked() },
217 SmallMap::Inline(inline) => {
218 let heap = match self {
219 SmallMap::Heap(heap) => heap,
220 SmallMap::Inline(_) => unsafe { unreachable_unchecked() },
221 };
222 for (k, v) in inline.into_iter() {
223 heap.insert_unique_unchecked(k, v);
224 }
225 heap
226 }
227 };
228 heap.insert(key, value)
229 }
230 }
231 }
232
233 #[inline]
234 pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
235 where
236 Q: Hash + Equivalent<K>,
237 {
238 match self.remove_entry(k) {
240 Some((_, v)) => Some(v),
241 None => None,
242 }
243 }
244
245 #[inline]
246 pub fn remove_entry<Q: ?Sized>(&mut self, k: &Q) -> Option<(K, V)>
247 where
248 Q: Hash + Equivalent<K>,
249 {
250 match self {
251 SmallMap::Heap(inner) => inner.remove_entry(k),
252 SmallMap::Inline(inner) => inner.remove_entry(k),
253 }
254 }
255}
256
257pub enum Iter<'a, const N: usize, K, V> {
258 Heap(hashbrown::hash_map::Iter<'a, K, V>),
259 Inline(inline::Iter<'a, N, K, V>),
260}
261
262impl<'a, const N: usize, K, V> Iterator for Iter<'a, N, K, V> {
263 type Item = (&'a K, &'a V);
264
265 #[inline]
266 fn next(&mut self) -> Option<(&'a K, &'a V)> {
267 match self {
268 Iter::Heap(inner) => inner.next(),
269 Iter::Inline(inner) => inner.next(),
270 }
271 }
272 #[inline]
273 fn size_hint(&self) -> (usize, Option<usize>) {
274 match self {
275 Iter::Heap(inner) => inner.size_hint(),
276 Iter::Inline(inner) => inner.size_hint(),
277 }
278 }
279}
280
281impl<const N: usize, K, V> ExactSizeIterator for Iter<'_, N, K, V> {
282 #[inline]
283 fn len(&self) -> usize {
284 match self {
285 Iter::Heap(inner) => inner.len(),
286 Iter::Inline(inner) => inner.len(),
287 }
288 }
289}
290
291pub enum IntoIter<const N: usize, K, V> {
292 Heap(hashbrown::hash_map::IntoIter<K, V>),
293 Inline(inline::IntoIter<N, K, V>),
294}
295
296impl<const N: usize, K, V> Iterator for IntoIter<N, K, V> {
297 type Item = (K, V);
298
299 #[inline]
300 fn next(&mut self) -> Option<(K, V)> {
301 match self {
302 IntoIter::Heap(inner) => inner.next(),
303 IntoIter::Inline(inner) => inner.next(),
304 }
305 }
306 #[inline]
307 fn size_hint(&self) -> (usize, Option<usize>) {
308 match self {
309 IntoIter::Heap(inner) => inner.size_hint(),
310 IntoIter::Inline(inner) => inner.size_hint(),
311 }
312 }
313}
314impl<const N: usize, K, V> ExactSizeIterator for IntoIter<N, K, V> {
315 #[inline]
316 fn len(&self) -> usize {
317 match self {
318 IntoIter::Heap(inner) => inner.len(),
319 IntoIter::Inline(inner) => inner.len(),
320 }
321 }
322}
323impl<const N: usize, K, V> FusedIterator for IntoIter<N, K, V> {}
324
325impl<const N: usize, K, V, S> IntoIterator for SmallMap<N, K, V, S> {
326 type Item = (K, V);
327 type IntoIter = IntoIter<N, K, V>;
328
329 #[inline]
333 fn into_iter(self) -> IntoIter<N, K, V> {
334 match self {
335 SmallMap::Heap(inner) => IntoIter::Heap(inner.into_iter()),
336 SmallMap::Inline(inner) => IntoIter::Inline(inner.into_iter()),
337 }
338 }
339}
340
341impl<'a, const N: usize, K, V, S> IntoIterator for &'a SmallMap<N, K, V, S> {
342 type Item = (&'a K, &'a V);
343 type IntoIter = Iter<'a, N, K, V>;
344
345 #[inline]
348 fn into_iter(self) -> Iter<'a, N, K, V> {
349 match self {
350 SmallMap::Heap(inner) => Iter::Heap(inner.iter()),
351 SmallMap::Inline(inner) => Iter::Inline(inner.iter()),
352 }
353 }
354}
355
356impl<const N: usize, K, V, S> SmallMap<N, K, V, S> {
357 #[inline]
358 pub fn len(&self) -> usize {
359 match self {
360 SmallMap::Heap(inner) => inner.len(),
361 SmallMap::Inline(inner) => inner.len(),
362 }
363 }
364
365 #[inline]
366 pub fn is_empty(&self) -> bool {
367 match self {
368 SmallMap::Heap(inner) => inner.is_empty(),
369 SmallMap::Inline(inner) => inner.is_empty(),
370 }
371 }
372
373 #[inline]
374 pub fn iter(&self) -> Iter<'_, N, K, V> {
375 match self {
376 SmallMap::Heap(inner) => Iter::Heap(inner.iter()),
377 SmallMap::Inline(inner) => Iter::Inline(inner.iter()),
378 }
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use std::{cell::RefCell, rc::Rc};
385
386 use ahash::RandomState;
387 use hashbrown::HashMap;
388 use rand::Rng;
389
390 use crate::SmallMap;
391
392 #[test]
393 fn basic_op() {
394 let mut map = SmallMap::<16, String, String>::default();
395 map.insert("hello".to_string(), "world".to_string());
396 assert_eq!(map.len(), 1);
397 assert_eq!(map.get("hello").unwrap(), "world");
398 map.insert("hello2".to_string(), "world2".to_string());
399 assert_eq!(map.get("hello2").unwrap(), "world2");
400 assert_eq!(map.len(), 2);
401
402 assert_eq!(
403 map.remove_entry("hello").unwrap(),
404 ("hello".to_string(), "world".to_string())
405 );
406 assert_eq!(map.len(), 1);
407 assert_eq!(map.get("hello2").unwrap(), "world2");
408 assert_eq!(map.remove("hello2").unwrap(), "world2".to_string());
409 assert_eq!(map.len(), 0);
410 assert!(map.get("hello").is_none());
411 }
412
413 #[test]
414 fn multi_group_with_padding() {
415 let mut map = SmallMap::<33, i32, i32>::default();
416 for i in 0..33 {
417 map.insert(i, i * 2);
418 }
419 for i in 0..33 {
420 assert_eq!(*map.get(&i).unwrap(), i * 2);
421 }
422 assert!(map.is_inline());
423
424 for i in 33..64 {
425 map.insert(i, i * 2);
426 }
427 assert!(!map.is_inline());
428 for i in 0..64 {
429 assert_eq!(*map.get(&i).unwrap(), i * 2);
430 }
431 }
432
433 #[test]
434 fn fuzzing() {
435 let mut smallmap = SmallMap::<16, i32, i32>::default();
436 let mut hashmap = HashMap::<i32, i32, RandomState>::default();
437 for _ in 0..1000000 {
438 let op = Operation::random();
439 op.exec(&mut smallmap, &mut hashmap);
440 }
441
442 enum Operation {
443 Insert(i32, i32),
444 Remove(i32),
445 Get(i32),
446 ModifyIfExist(i32, i32),
447 }
448 impl Operation {
449 fn random() -> Self {
450 let mut rng = rand::thread_rng();
451
452 let choice: u8 = rng.gen();
453 match choice % 4 {
454 0 => Operation::Insert(rng.gen_range(0..32), rng.gen()),
455 1 => Operation::Remove(rng.gen_range(0..32)),
456 2 => Operation::Get(rng.gen_range(0..32)),
457 3 => Operation::ModifyIfExist(rng.gen_range(0..32), rng.gen()),
458 _ => unreachable!(),
459 }
460 }
461
462 fn exec<const N: usize, S1: core::hash::BuildHasher, S2: core::hash::BuildHasher>(
463 self,
464 sm: &mut SmallMap<N, i32, i32, S1>,
465 hm: &mut HashMap<i32, i32, S2>,
466 ) {
467 match self {
468 Operation::Insert(k, v) => {
469 if sm.len() == sm.capacity() {
470 return;
471 }
472 assert_eq!(sm.insert(k, v), hm.insert(k, v));
473 assert!(sm.is_inline());
474 }
475 Operation::Remove(k) => {
476 assert_eq!(sm.remove(&k), hm.remove(&k));
477 }
478 Operation::Get(k) => {
479 assert_eq!(sm.get(&k), hm.get(&k));
480 }
481 Operation::ModifyIfExist(k, nv) => {
482 let (sv, hv) = (sm.get_mut(&k), hm.get_mut(&k));
483 assert_eq!(sv, hv);
484 if let Some(v) = sv {
485 *v = nv;
486 }
487 if let Some(v) = hv {
488 *v = nv;
489 }
490 }
491 }
492 }
493 }
494 }
495
496 #[test]
497 fn drop_chk() {
498 let (probe1, checker1) = drop_checker();
499 let (probe2, checker2) = drop_checker();
500 let (probe3, checker3) = drop_checker();
501 let mut map = SmallMap::<8, _, _>::default();
502 map.insert(1, probe1);
503 map.insert(2, probe2);
504 map.insert(3, probe3);
505 assert_eq!(map.len(), 3);
506 let mut it = map.into_iter();
507 drop(it.next());
508 drop(it);
509 checker1.assert_drop();
510 checker2.assert_drop();
511 checker3.assert_drop();
512
513 fn drop_checker() -> (DropProbe, DropChecker) {
514 let flag = Rc::new(RefCell::new(false));
515 (DropProbe { flag: flag.clone() }, DropChecker { flag })
516 }
517
518 struct DropChecker {
519 flag: Rc<RefCell<bool>>,
520 }
521
522 impl DropChecker {
523 fn assert_drop(self) {
524 assert!(*self.flag.borrow())
525 }
526 }
527
528 struct DropProbe {
529 flag: Rc<RefCell<bool>>,
530 }
531
532 impl Drop for DropProbe {
533 fn drop(&mut self) {
534 *self.flag.borrow_mut() = true;
535 }
536 }
537 }
538}