zrx_store/stash.rs
1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Stash.
27
28use ahash::HashMap;
29use slab::Slab;
30use std::borrow::Borrow;
31use std::fmt::{self, Debug};
32use std::marker::PhantomData;
33use std::mem;
34use std::ops::{Index, IndexMut};
35
36use crate::store::adapter::slab::{Iter, IterMut};
37use crate::store::item::{Key, Value};
38use crate::store::{
39 Store, StoreIterable, StoreIterableMut, StoreMut, StoreMutRef,
40};
41
42pub mod items;
43mod iter;
44pub mod slots;
45
46pub use items::Items;
47pub use slots::{Slots, SlotsMut};
48
49// ----------------------------------------------------------------------------
50// Implementations
51// ----------------------------------------------------------------------------
52
53/// Stash.
54///
55/// This data type implements a simple stash, which is a key-value store that
56/// is optimized for fast insertion and retrieval of items by index. It's built
57/// on a [`Slab`], together with a [`Store`] that provides the underlying item
58/// storage. Stashes offer optimal performance for temporary storage.
59///
60/// Iteration happens on the underlying [`Slab`], which means that the order of
61/// items is stable, but not sorted by key. This ensures, that iteration is not
62/// affected by insertions and removals, and cache efficient, since no lookups
63/// need to be performed on the underlying [`Store`] to obtain the items. Note
64/// that the store iterator traits don't allow to return indices for the items,
65/// only references to keys and values. In case indices are required, the stash
66/// can be iterated with [`Stash::slots`] or [`Stash::slots_mut`], since those
67/// return both, indices and references to keys and values.
68///
69/// # Examples
70///
71/// ```
72/// use zrx_store::{Stash, StoreMut};
73///
74/// // Create stash and initial state
75/// let mut stash = Stash::default();
76/// stash.insert("key", 42);
77///
78/// // Create iterator over the stash
79/// for (key, value) in &stash {
80/// println!("{key}: {value}");
81/// }
82/// ```
83#[derive(Clone)]
84pub struct Stash<K, V, S = HashMap<K, usize>> {
85 /// Underlying store.
86 store: S,
87 /// Stash items.
88 items: Slab<(K, V)>,
89 /// Capture types.
90 marker: PhantomData<K>,
91}
92
93// ----------------------------------------------------------------------------
94// Implementations
95// ----------------------------------------------------------------------------
96
97impl<K, V, S> Stash<K, V, S>
98where
99 K: Key,
100 S: Store<K, usize>,
101{
102 /// Creates a stash.
103 ///
104 /// # Examples
105 ///
106 /// ```
107 /// use std::collections::HashMap;
108 /// use zrx_store::Stash;
109 ///
110 /// // Create stash
111 /// let mut stash = Stash::<_, _, HashMap<_, _>>::new();
112 /// stash.insert("key", 42);
113 /// ```
114 #[must_use]
115 pub fn new() -> Self
116 where
117 S: Default,
118 {
119 Self {
120 store: S::default(),
121 items: Slab::new(),
122 marker: PhantomData,
123 }
124 }
125
126 /// Returns the index of the value identified by the key.
127 ///
128 /// # Examples
129 ///
130 /// ```
131 /// use zrx_store::Stash;
132 ///
133 /// // Create stash and initial state
134 /// let mut stash = Stash::default();
135 /// stash.insert("key", 42);
136 ///
137 /// // Obtain index of value
138 /// let index = stash.get(&"key");
139 /// assert_eq!(index, Some(0));
140 /// ```
141 #[inline]
142 pub fn get<Q>(&self, key: &Q) -> Option<usize>
143 where
144 K: Borrow<Q>,
145 Q: Key,
146 {
147 self.store.get(key).copied()
148 }
149
150 /// Returns a reference to the key at the index.
151 ///
152 /// # Examples
153 ///
154 /// ```
155 /// use zrx_store::Stash;
156 ///
157 /// // Create stash and initial state
158 /// let mut stash = Stash::default();
159 /// stash.insert("key", 42);
160 ///
161 /// // Obtain key at index
162 /// let key = stash.key(0);
163 /// assert_eq!(key, Some(&"key"));
164 /// ```
165 #[inline]
166 pub fn key(&self, index: usize) -> Option<&K> {
167 self.items.get(index).map(|(key, _)| key)
168 }
169}
170
171impl<K, V, S> Stash<K, V, S>
172where
173 K: Key,
174 S: StoreMut<K, usize>,
175{
176 /// Inserts the value identified by the key and returns its index.
177 ///
178 /// This method inserts the value and returns an index into the store that
179 /// can be used to retrieve an immutable or mutable reference to the value.
180 ///
181 /// # Examples
182 ///
183 /// ```
184 /// use zrx_store::Stash;
185 ///
186 /// // Create stash
187 /// let mut stash = Stash::default();
188 ///
189 /// // Insert value
190 /// let index = stash.insert("key", 42);
191 /// assert_eq!(index, 0);
192 /// ```
193 #[inline]
194 pub fn insert(&mut self, key: K, value: V) -> usize {
195 if let Some(&index) = self.store.get(&key) {
196 self.items[index].1 = value;
197 index
198 } else {
199 let index = self.items.insert((key.clone(), value));
200 self.store.insert(key, index);
201 index
202 }
203 }
204
205 /// Removes the entry at the index and returns it.
206 ///
207 /// # Examples
208 ///
209 /// ```
210 /// use zrx_store::Stash;
211 ///
212 /// // Create stash and initial state
213 /// let mut stash = Stash::default();
214 /// stash.insert("key", 42);
215 ///
216 /// // Remove and return entry
217 /// let entry = stash.remove(0);
218 /// assert_eq!(entry, Some(("key", 42)));
219 /// ```
220 #[inline]
221 pub fn remove(&mut self, index: usize) -> Option<(K, V)> {
222 if let Some((key, _)) = self.items.get(index) {
223 self.store.remove(key).map(|index| self.items.remove(index))
224 } else {
225 None
226 }
227 }
228}
229
230// ----------------------------------------------------------------------------
231// Trait implementations
232// ----------------------------------------------------------------------------
233
234impl<K, V, S> Store<K, V> for Stash<K, V, S>
235where
236 K: Key,
237 S: Store<K, usize>,
238{
239 /// Returns a reference to the value identified by the key.
240 ///
241 /// # Examples
242 ///
243 /// ```
244 /// use zrx_store::{Stash, Store};
245 ///
246 /// // Create stash and initial state
247 /// let mut stash = Stash::default();
248 /// stash.insert("key", 42);
249 ///
250 /// // Obtain reference to value
251 /// let value = Store::get(&stash, &"key");
252 /// assert_eq!(value, Some(&42));
253 /// ```
254 #[inline]
255 fn get<Q>(&self, key: &Q) -> Option<&V>
256 where
257 K: Borrow<Q>,
258 Q: Key,
259 {
260 match self.store.get(key) {
261 Some(&index) => self.items.get(index).map(|(_, value)| value),
262 None => None,
263 }
264 }
265
266 /// Returns whether the store contains the key.
267 ///
268 /// # Examples
269 ///
270 /// ```
271 /// use zrx_store::{Stash, Store};
272 ///
273 /// // Create stash and initial state
274 /// let mut stash = Stash::default();
275 /// stash.insert("key", 42);
276 ///
277 /// // Ensure presence of key
278 /// let check = stash.contains_key(&"key");
279 /// assert_eq!(check, true);
280 /// ```
281 #[inline]
282 fn contains_key<Q>(&self, key: &Q) -> bool
283 where
284 K: Borrow<Q>,
285 Q: Key,
286 {
287 self.store.contains_key(key)
288 }
289
290 /// Returns the number of items in the store.
291 #[inline]
292 fn len(&self) -> usize {
293 self.store.len()
294 }
295}
296
297impl<K, V, S> StoreMut<K, V> for Stash<K, V, S>
298where
299 K: Key,
300 V: Value,
301 S: StoreMut<K, usize>,
302{
303 /// Inserts the value identified by the key.
304 ///
305 /// # Examples
306 ///
307 /// ```
308 /// use zrx_store::{Stash, StoreMut};
309 ///
310 /// // Create stash
311 /// let mut stash = Stash::default();
312 ///
313 /// // Insert value
314 /// let value = StoreMut::insert(&mut stash, "key", 42);
315 /// assert_eq!(value, None);
316 /// ```
317 #[inline]
318 fn insert(&mut self, key: K, value: V) -> Option<V> {
319 if let Some(&index) = self.store.get(&key) {
320 let prior = &mut self.items[index].1;
321 (prior != &value).then(|| mem::replace(prior, value))
322 } else {
323 let index = self.items.insert((key.clone(), value));
324 self.store.insert(key, index);
325 None
326 }
327 }
328
329 /// Removes the value identified by the key.
330 ///
331 /// # Examples
332 ///
333 /// ```
334 /// use zrx_store::{Stash, StoreMut};
335 ///
336 /// // Create stash and initial state
337 /// let mut stash = Stash::default();
338 /// stash.insert("key", 42);
339 ///
340 /// // Remove and return value
341 /// let value = StoreMut::remove(&mut stash, &"key");
342 /// assert_eq!(value, Some(42));
343 /// ```
344 #[inline]
345 fn remove<Q>(&mut self, key: &Q) -> Option<V>
346 where
347 K: Borrow<Q>,
348 Q: Key,
349 {
350 self.store.remove(key).map(|index| {
351 let (_, value) = self.items.remove(index);
352 value
353 })
354 }
355
356 /// Removes the value identified by the key and returns both.
357 ///
358 /// # Examples
359 ///
360 /// ```
361 /// use zrx_store::{Stash, StoreMut};
362 ///
363 /// // Create stash and initial state
364 /// let mut stash = Stash::default();
365 /// stash.insert("key", 42);
366 ///
367 /// // Remove and return entry
368 /// let entry = stash.remove_entry(&"key");
369 /// assert_eq!(entry, Some(("key", 42)));
370 /// ```
371 #[inline]
372 fn remove_entry<Q>(&mut self, key: &Q) -> Option<(K, V)>
373 where
374 K: Borrow<Q>,
375 Q: Key,
376 {
377 self.store.remove(key).map(|index| self.items.remove(index))
378 }
379
380 /// Clears the stash, removing all items.
381 ///
382 /// # Examples
383 ///
384 /// ```
385 /// use zrx_store::{Stash, Store, StoreMut};
386 ///
387 /// // Create stash and initial state
388 /// let mut stash = Stash::default();
389 /// stash.insert("key", 42);
390 ///
391 /// // Clear stash
392 /// stash.clear();
393 /// assert!(stash.is_empty());
394 /// ```
395 #[inline]
396 fn clear(&mut self) {
397 self.store.clear();
398 self.items.clear();
399 }
400}
401
402impl<K, V, S> StoreMutRef<K, V> for Stash<K, V, S>
403where
404 K: Key,
405 S: StoreMut<K, usize>,
406{
407 /// Returns a mutable reference to the value identified by the key.
408 ///
409 /// # Examples
410 ///
411 /// ```
412 /// use zrx_store::{Stash, StoreMut, StoreMutRef};
413 ///
414 /// // Create stash and initial state
415 /// let mut stash = Stash::default();
416 /// stash.insert("key", 42);
417 ///
418 /// // Obtain mutable reference to value
419 /// let mut value = stash.get_mut(&"key");
420 /// assert_eq!(value, Some(&mut 42));
421 /// ```
422 #[inline]
423 fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
424 where
425 K: Borrow<Q>,
426 Q: Key,
427 {
428 match self.store.get(key) {
429 Some(&index) => self.items.get_mut(index).map(|(_, value)| value),
430 None => None,
431 }
432 }
433
434 /// Returns a mutable reference to the value or creates the default.
435 ///
436 /// # Examples
437 ///
438 /// ```
439 /// use zrx_store::{Stash, StoreMutRef};
440 ///
441 /// // Create stash
442 /// let mut stash = Stash::<_, i32>::default();
443 ///
444 /// // Obtain mutable reference to value
445 /// let value = stash.get_or_insert_default(&"key");
446 /// assert_eq!(value, &mut 0);
447 /// ```
448 #[inline]
449 fn get_or_insert_default(&mut self, key: &K) -> &mut V
450 where
451 V: Default,
452 {
453 if !self.store.contains_key(key) {
454 let n = self.items.insert((key.clone(), V::default()));
455 self.store.insert(key.clone(), n);
456 }
457
458 // We can safely use expect here, as the key is present
459 self.get_mut(key).expect("invariant")
460 }
461}
462
463// ----------------------------------------------------------------------------
464
465impl<K, V, S> Index<usize> for Stash<K, V, S>
466where
467 K: Key,
468 S: Store<K, usize>,
469{
470 type Output = V;
471
472 /// Returns a reference to the value at the index.
473 ///
474 /// # Panics
475 ///
476 /// Panics if the index is out of bounds.
477 ///
478 /// # Examples
479 ///
480 /// ```
481 /// use zrx_store::Stash;
482 ///
483 /// // Create stash and initial state
484 /// let mut stash = Stash::default();
485 /// stash.insert("a", 42);
486 /// stash.insert("b", 84);
487 ///
488 /// // Obtain reference to value
489 /// let value = &stash[1];
490 /// assert_eq!(value, &84);
491 /// ```
492 #[inline]
493 fn index(&self, index: usize) -> &Self::Output {
494 &self.items[index].1
495 }
496}
497
498impl<K, V, S> IndexMut<usize> for Stash<K, V, S>
499where
500 K: Key,
501 S: Store<K, usize>,
502{
503 /// Returns a mutable reference to the value at the index.
504 ///
505 /// # Panics
506 ///
507 /// Panics if the index is out of bounds.
508 ///
509 /// # Examples
510 ///
511 /// ```
512 /// use zrx_store::Stash;
513 ///
514 /// // Create stash and initial state
515 /// let mut stash = Stash::default();
516 /// stash.insert("a", 42);
517 /// stash.insert("b", 84);
518 ///
519 /// // Obtain mutable reference to value
520 /// let value = &mut stash[1];
521 /// assert_eq!(value, &mut 84);
522 /// ```
523 #[inline]
524 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
525 &mut self.items[index].1
526 }
527}
528
529// ----------------------------------------------------------------------------
530
531impl<K, V, S> FromIterator<(K, V)> for Stash<K, V, S>
532where
533 K: Key,
534 S: StoreMut<K, usize> + Default,
535{
536 /// Creates a stash from an iterator.
537 ///
538 /// # Examples
539 ///
540 /// ```
541 /// use std::collections::HashMap;
542 /// use zrx_store::Stash;
543 ///
544 /// // Create a vector of key-value pairs
545 /// let items = vec![
546 /// ("a", 1),
547 /// ("b", 2),
548 /// ("c", 3),
549 /// ("d", 4),
550 /// ];
551 ///
552 /// // Create stash from iterator
553 /// let stash: Stash<_, _, HashMap<_, _>> =
554 /// items.into_iter().collect();
555 ///
556 /// // Create iterator over the stash
557 /// for (key, value) in &stash {
558 /// println!("{key}: {value}");
559 /// }
560 /// ```
561 #[inline]
562 fn from_iter<T>(iter: T) -> Self
563 where
564 T: IntoIterator<Item = (K, V)>,
565 {
566 let mut store = Stash::new();
567 for (key, value) in iter {
568 store.insert(key, value);
569 }
570 store
571 }
572}
573
574#[allow(clippy::into_iter_without_iter)]
575impl<'a, K, V, S> IntoIterator for &'a Stash<K, V, S>
576where
577 K: Key,
578 V: Value,
579 S: StoreIterable<K, usize>,
580{
581 type Item = (&'a K, &'a V);
582 type IntoIter = Iter<'a, K, V>;
583
584 /// Creates an iterator over the stash.
585 ///
586 /// # Examples
587 ///
588 /// ```
589 /// use zrx_store::Stash;
590 ///
591 /// // Create stash and initial state
592 /// let mut stash = Stash::default();
593 /// stash.insert("key", 42);
594 ///
595 /// // Create iterator over the stash
596 /// for (key, value) in &stash {
597 /// println!("{key}: {value}");
598 /// }
599 /// ```
600 #[inline]
601 fn into_iter(self) -> Self::IntoIter {
602 StoreIterable::iter(&self.items)
603 }
604}
605
606#[allow(clippy::into_iter_without_iter)]
607impl<'a, K, V, S> IntoIterator for &'a mut Stash<K, V, S>
608where
609 K: Key,
610 V: Value,
611 S: StoreIterableMut<K, usize>,
612{
613 type Item = (&'a K, &'a mut V);
614 type IntoIter = IterMut<'a, K, V>;
615
616 /// Creates a mutable iterator over the stash.
617 ///
618 /// # Examples
619 ///
620 /// ```
621 /// use zrx_store::Stash;
622 ///
623 /// // Create stash and initial state
624 /// let mut stash = Stash::default();
625 /// stash.insert("key", 42);
626 ///
627 /// // Create iterator over the stash
628 /// for (key, value) in &mut stash {
629 /// println!("{key}: {value}");
630 /// }
631 /// ```
632 #[inline]
633 fn into_iter(self) -> Self::IntoIter {
634 StoreIterableMut::iter_mut(&mut self.items)
635 }
636}
637
638// ----------------------------------------------------------------------------
639
640impl<K, V> Default for Stash<K, V>
641where
642 K: Key,
643{
644 /// Creates a stash with [`HashMap::default`][] as a store.
645 ///
646 /// Note that this method does not allow to customize the [`BuildHasher`][],
647 /// but uses [`ahash`] by default, which is the fastest known hasher.
648 ///
649 /// [`BuildHasher`]: std::hash::BuildHasher
650 /// [`HashMap::default`]: Default::default
651 ///
652 /// # Examples
653 ///
654 /// ```
655 /// use zrx_store::Stash;
656 ///
657 /// // Create stash and initial state
658 /// let mut stash = Stash::default();
659 /// stash.insert("key", 42);
660 /// ```
661 #[inline]
662 fn default() -> Self {
663 Self::new()
664 }
665}
666
667// ----------------------------------------------------------------------------
668
669impl<K, V, S> Debug for Stash<K, V, S>
670where
671 K: Debug,
672 V: Debug,
673 S: Debug,
674{
675 /// Formats the stash for debugging.
676 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
677 f.debug_struct("Stash")
678 .field("store", &self.store)
679 .field("items", &self.items)
680 .finish()
681 }
682}