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