Skip to main content

cachekit/ds/
slot_arena.rs

1//! Slot arena with stable [`SlotId`] handles.
2//!
3//! Provides arena-style allocation where elements are stored in a `Vec<Option<T>>`
4//! and freed slots are tracked in a free list for reuse. Handles ([`SlotId`]) remain
5//! stable across insertions and deletions, making this ideal for graph structures,
6//! linked lists, and policy metadata.
7//!
8//! ## Architecture
9//!
10//! ```text
11//! ┌────────────────────────────────────────────────────────────────────────────┐
12//! │                           SlotArena Layout                                 │
13//! │                                                                            │
14//! │   ┌───────────────────────────────────────────────────────────────────┐    │
15//! │   │  slots: Vec<Option<T>>                                            │    │
16//! │   │                                                                   │    │
17//! │   │    index:   0        1        2        3        4        5        │    │
18//! │   │           ┌────┐  ┌────┐  ┌────┐  ┌────┐  ┌────┐  ┌────┐          │    │
19//! │   │           │ T  │  │None│  │ T  │  │None│  │ T  │  │None│          │    │
20//! │   │           │"a" │  │    │  │"c" │  │    │  │"e" │  │    │          │    │
21//! │   │           └────┘  └────┘  └────┘  └────┘  └────┘  └────┘          │    │
22//! │   │              ▲       │       ▲       │       ▲       │            │    │
23//! │   │              │       │       │       │       │       │            │    │
24//! │   │           SlotId(0)  │   SlotId(2)   │   SlotId(4)   │            │    │
25//! │   │                      │               │               │            │    │
26//! │   └──────────────────────┼───────────────┼───────────────┼────────────┘    │
27//! │                          │               │               │                 │
28//! │   ┌──────────────────────┼───────────────┼───────────────┼────────────┐    │
29//! │   │  free_list: Vec<usize>               │               │            │    │
30//! │   │                      │               │               │            │    │
31//! │   │              ┌───────┴───────┬───────┴───────┬───────┴───────┐    │    │
32//! │   │              │       1       │       3       │       5       │    │    │
33//! │   │              └───────────────┴───────────────┴───────────────┘    │    │
34//! │   │                                                                   │    │
35//! │   │              ◄─────── pop() returns 5 for next insert ──────►     │    │
36//! │   └───────────────────────────────────────────────────────────────────┘    │
37//! │                                                                            │
38//! │   len: 3  (number of live entries, not slots.len())                        │
39//! └────────────────────────────────────────────────────────────────────────────┘
40//!
41//! Slot Lifecycle
42//! ──────────────
43//!
44//!   insert("x"):
45//!     ├─► free_list.pop() ──► Some(idx) ──► slots[idx] = Some("x")
46//!     │                                      return SlotId(idx)
47//!     │
48//!     └─► None ──► slots.push(Some("x"))
49//!                  return SlotId(slots.len() - 1)
50//!
51//!   remove(SlotId(2)):
52//!     slots[2].take() ──► Some("c") ──► free_list.push(2)
53//!                                       return Some("c")
54//!
55//!   get(SlotId(2)):
56//!     slots.get(2) ──► Some(&slot) ──► slot.as_ref() ──► Option<&T>
57//!
58//! Arena Variants
59//! ──────────────
60//!
61//!   ┌─────────────────────────────────────────────────────────────────────┐
62//!   │ SlotArena<T>              Single-threaded, direct &T access         │
63//!   ├─────────────────────────────────────────────────────────────────────┤
64//!   │ ConcurrentSlotArena<T>    Thread-safe via RwLock                    │
65//!   │                           Uses closures: get_with(id, |v| ...)      │
66//!   ├─────────────────────────────────────────────────────────────────────┤
67//!   │ ShardedSlotArena<T>       Multiple RwLock-protected arenas          │
68//!   │                           Round-robin insert, ShardedSlotId handle  │
69//!   └─────────────────────────────────────────────────────────────────────┘
70//! ```
71//!
72//! ## Key Components
73//!
74//! - [`SlotId`]: Stable handle wrapping a `usize` index
75//! - [`SlotArena`]: Single-threaded arena with O(1) operations
76//! - [`ConcurrentSlotArena`]: Thread-safe wrapper with closure-based access
77//! - [`ShardedSlotArena`]: Sharded arena for high-concurrency workloads
78//! - [`ShardedSlotId`]: Handle containing shard index + slot id
79//!
80//! ## Operations
81//!
82//! | Operation     | Description                              | Complexity |
83//! |---------------|------------------------------------------|------------|
84//! | `insert`      | Add value, reuse free slot if available  | O(1)       |
85//! | `remove`      | Remove and return value, free the slot   | O(1)       |
86//! | `get`         | Get reference by SlotId                  | O(1)       |
87//! | `get_mut`     | Get mutable reference by SlotId          | O(1)       |
88//! | `contains`    | Check if SlotId is valid                 | O(1)       |
89//! | `iter`        | Iterate over live entries                | O(n)       |
90//!
91//! ## Performance Characteristics
92//!
93//! - **No per-operation allocation**: Slots are reused, Vec grows as needed
94//! - **Cache-friendly**: Elements stored contiguously in memory
95//! - **Stable handles**: SlotId values remain valid until removed
96//! - **O(1) operations**: All single-element operations are constant time
97//!
98//! ## When to Use
99//!
100//! - Intrusive data structures (linked lists, trees, graphs)
101//! - Policy metadata storage (LRU list nodes, frequency counters)
102//! - Any scenario requiring stable handles that survive mutations
103//! - Memory pools where allocation churn is a concern
104//!
105//! ## Example Usage
106//!
107//! ```
108//! use cachekit::ds::SlotArena;
109//!
110//! let mut arena = SlotArena::new();
111//!
112//! // Insert values and get stable handles
113//! let id_a = arena.insert("alice");
114//! let id_b = arena.insert("bob");
115//!
116//! // Access by handle
117//! assert_eq!(arena.get(id_a), Some(&"alice"));
118//!
119//! // Remove frees the slot
120//! assert_eq!(arena.remove(id_b), Some("bob"));
121//!
122//! // Next insert reuses freed slot
123//! let id_c = arena.insert("carol");
124//! assert_eq!(id_b.index(), id_c.index());  // Same underlying index
125//! ```
126//!
127//! ## Thread Safety
128//!
129//! - [`SlotArena`]: Not thread-safe, use in single-threaded contexts
130//! - [`ConcurrentSlotArena`]: Thread-safe via `parking_lot::RwLock`
131//! - [`ShardedSlotArena`]: Thread-safe with per-shard locks
132//!
133//! ## Implementation Notes
134//!
135//! - `SlotId` indices may be reused after removal
136//! - Accessing a stale `SlotId` returns `None` (not undefined behavior)
137//! - `len()` tracks live entries, not `slots.len()`
138//! - `debug_validate_invariants()` available in debug/test builds
139/// Stable handle into a [`SlotArena`].
140///
141/// A lightweight identifier (wrapping a `usize` index) that provides O(1)
142/// access to arena slots. Handles remain valid until the slot is removed;
143/// after removal, the index may be reused by a later `insert`.
144///
145/// # Stability
146///
147/// Unlike raw indices or pointers, `SlotId` values are semantically tied to
148/// the slot they reference. Accessing a removed slot returns `None` rather
149/// than causing undefined behavior.
150///
151/// # Example
152///
153/// ```
154/// use cachekit::ds::SlotArena;
155///
156/// let mut arena = SlotArena::new();
157/// let id = arena.insert(42);
158///
159/// // SlotId provides O(1) access
160/// assert_eq!(arena.get(id), Some(&42));
161///
162/// // Inspect raw index for debugging
163/// println!("Value stored at index {}", id.index());
164///
165/// // After removal, same index may be reused
166/// arena.remove(id);
167/// let new_id = arena.insert(100);
168/// assert_eq!(id.index(), new_id.index());
169/// ```
170#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
171pub struct SlotId(pub(crate) usize);
172
173impl SlotId {
174    /// Returns the underlying slot index.
175    ///
176    /// Useful for debugging, logging, or custom data structures that need
177    /// to work with raw indices.
178    pub fn index(self) -> usize {
179        self.0
180    }
181}
182
183/// Single-threaded arena with stable [`SlotId`] handles and O(1) operations.
184///
185/// Stores values in a `Vec<Option<T>>` and reuses freed slots via a free list.
186/// Ideal for graph structures, linked lists, and policy metadata where stable
187/// handles are needed.
188///
189/// # Example
190///
191/// ```
192/// use cachekit::ds::SlotArena;
193///
194/// let mut arena = SlotArena::new();
195///
196/// // Insert returns stable handles
197/// let a = arena.insert("first");
198/// let b = arena.insert("second");
199/// let c = arena.insert("third");
200///
201/// assert_eq!(arena.len(), 3);
202/// assert_eq!(arena.get(b), Some(&"second"));
203///
204/// // Remove frees the slot
205/// arena.remove(b);
206/// assert_eq!(arena.len(), 2);
207/// assert_eq!(arena.get(b), None);
208///
209/// // Next insert reuses the freed slot
210/// let d = arena.insert("fourth");
211/// assert_eq!(b.index(), d.index());
212///
213/// // Iterate over live entries
214/// for (id, value) in arena.iter() {
215///     println!("{}: {}", id.index(), value);
216/// }
217/// ```
218///
219/// # Use Case: Intrusive Linked List
220///
221/// ```
222/// use cachekit::ds::{SlotArena, SlotId};
223///
224/// struct Node {
225///     value: i32,
226///     next: Option<SlotId>,
227/// }
228///
229/// let mut arena = SlotArena::new();
230///
231/// // Build a linked list: 1 -> 2 -> 3
232/// let c = arena.insert(Node { value: 3, next: None });
233/// let b = arena.insert(Node { value: 2, next: Some(c) });
234/// let a = arena.insert(Node { value: 1, next: Some(b) });
235///
236/// // Traverse
237/// let mut current = Some(a);
238/// while let Some(id) = current {
239///     let node = arena.get(id).unwrap();
240///     println!("{}", node.value);
241///     current = node.next;
242/// }
243/// ```
244#[derive(Debug)]
245pub struct SlotArena<T> {
246    slots: Vec<Option<T>>,
247    free_list: Vec<usize>,
248    len: usize,
249}
250
251impl<T> SlotArena<T> {
252    /// Creates an empty arena.
253    ///
254    /// # Example
255    ///
256    /// ```
257    /// use cachekit::ds::SlotArena;
258    ///
259    /// let arena: SlotArena<String> = SlotArena::new();
260    /// assert!(arena.is_empty());
261    /// ```
262    pub fn new() -> Self {
263        Self {
264            slots: Vec::new(),
265            free_list: Vec::new(),
266            len: 0,
267        }
268    }
269
270    /// Creates an empty arena with pre-allocated capacity.
271    ///
272    /// # Example
273    ///
274    /// ```
275    /// use cachekit::ds::SlotArena;
276    ///
277    /// let arena: SlotArena<i32> = SlotArena::with_capacity(100);
278    /// assert!(arena.capacity() >= 100);
279    /// assert!(arena.is_empty());
280    /// ```
281    pub fn with_capacity(capacity: usize) -> Self {
282        Self {
283            slots: Vec::with_capacity(capacity),
284            free_list: Vec::new(),
285            len: 0,
286        }
287    }
288
289    /// Inserts a value and returns its [`SlotId`].
290    ///
291    /// Reuses a freed slot if available, otherwise grows the internal Vec.
292    ///
293    /// # Example
294    ///
295    /// ```
296    /// use cachekit::ds::SlotArena;
297    ///
298    /// let mut arena = SlotArena::new();
299    /// let id = arena.insert("hello");
300    /// assert_eq!(arena.get(id), Some(&"hello"));
301    /// ```
302    #[inline]
303    pub fn insert(&mut self, value: T) -> SlotId {
304        let idx = if let Some(idx) = self.free_list.pop() {
305            self.slots[idx] = Some(value);
306            idx
307        } else {
308            self.slots.push(Some(value));
309            self.slots.len() - 1
310        };
311        self.len += 1;
312        SlotId(idx)
313    }
314
315    /// Removes and returns the value at `id`, freeing the slot for reuse.
316    ///
317    /// Returns `None` if the slot is empty or out of bounds.
318    ///
319    /// # Example
320    ///
321    /// ```
322    /// use cachekit::ds::SlotArena;
323    ///
324    /// let mut arena = SlotArena::new();
325    /// let id = arena.insert(42);
326    ///
327    /// assert_eq!(arena.remove(id), Some(42));
328    /// assert_eq!(arena.remove(id), None);  // Already removed
329    /// ```
330    #[inline]
331    pub fn remove(&mut self, id: SlotId) -> Option<T> {
332        let slot = self.slots.get_mut(id.0)?;
333        let value = slot.take()?;
334        self.free_list.push(id.0);
335        self.len -= 1;
336        Some(value)
337    }
338
339    /// Returns a shared reference to the value at `id`.
340    ///
341    /// # Example
342    ///
343    /// ```
344    /// use cachekit::ds::SlotArena;
345    ///
346    /// let mut arena = SlotArena::new();
347    /// let id = arena.insert("value");
348    ///
349    /// assert_eq!(arena.get(id), Some(&"value"));
350    /// ```
351    #[inline]
352    pub fn get(&self, id: SlotId) -> Option<&T> {
353        self.slots.get(id.0).and_then(|slot| slot.as_ref())
354    }
355
356    /// Returns a mutable reference to the value at `id`.
357    ///
358    /// # Example
359    ///
360    /// ```
361    /// use cachekit::ds::SlotArena;
362    ///
363    /// let mut arena = SlotArena::new();
364    /// let id = arena.insert(1);
365    ///
366    /// if let Some(v) = arena.get_mut(id) {
367    ///     *v = 2;
368    /// }
369    /// assert_eq!(arena.get(id), Some(&2));
370    /// ```
371    #[inline]
372    pub fn get_mut(&mut self, id: SlotId) -> Option<&mut T> {
373        self.slots.get_mut(id.0).and_then(|slot| slot.as_mut())
374    }
375
376    /// Returns `true` if `id` refers to a live slot.
377    ///
378    /// # Example
379    ///
380    /// ```
381    /// use cachekit::ds::SlotArena;
382    ///
383    /// let mut arena = SlotArena::new();
384    /// let id = arena.insert(1);
385    ///
386    /// assert!(arena.contains(id));
387    /// arena.remove(id);
388    /// assert!(!arena.contains(id));
389    /// ```
390    #[inline]
391    pub fn contains(&self, id: SlotId) -> bool {
392        self.slots
393            .get(id.0)
394            .map(|slot| slot.is_some())
395            .unwrap_or(false)
396    }
397
398    /// Returns `true` if the raw `index` is in bounds and occupied.
399    ///
400    /// # Example
401    ///
402    /// ```
403    /// use cachekit::ds::SlotArena;
404    ///
405    /// let mut arena = SlotArena::new();
406    /// let id = arena.insert("value");
407    ///
408    /// assert!(arena.contains_index(id.index()));
409    /// assert!(!arena.contains_index(999));
410    /// ```
411    pub fn contains_index(&self, index: usize) -> bool {
412        self.slots
413            .get(index)
414            .map(|slot| slot.is_some())
415            .unwrap_or(false)
416    }
417
418    /// Returns the number of live entries.
419    ///
420    /// # Example
421    ///
422    /// ```
423    /// use cachekit::ds::SlotArena;
424    ///
425    /// let mut arena = SlotArena::new();
426    /// assert_eq!(arena.len(), 0);
427    ///
428    /// arena.insert("a");
429    /// arena.insert("b");
430    /// assert_eq!(arena.len(), 2);
431    /// ```
432    pub fn len(&self) -> usize {
433        self.len
434    }
435
436    /// Returns `true` if the arena has no live entries.
437    ///
438    /// # Example
439    ///
440    /// ```
441    /// use cachekit::ds::SlotArena;
442    ///
443    /// let mut arena = SlotArena::new();
444    /// assert!(arena.is_empty());
445    ///
446    /// let id = arena.insert(1);
447    /// assert!(!arena.is_empty());
448    ///
449    /// arena.remove(id);
450    /// assert!(arena.is_empty());
451    /// ```
452    pub fn is_empty(&self) -> bool {
453        self.len == 0
454    }
455
456    /// Returns the backing Vec capacity (slots that fit without reallocation).
457    ///
458    /// # Example
459    ///
460    /// ```
461    /// use cachekit::ds::SlotArena;
462    ///
463    /// let arena: SlotArena<i32> = SlotArena::with_capacity(100);
464    /// assert!(arena.capacity() >= 100);
465    /// ```
466    pub fn capacity(&self) -> usize {
467        self.slots.capacity()
468    }
469
470    /// Reserves capacity for at least `additional` more slots.
471    ///
472    /// # Example
473    ///
474    /// ```
475    /// use cachekit::ds::SlotArena;
476    ///
477    /// let mut arena: SlotArena<i32> = SlotArena::new();
478    /// arena.reserve_slots(100);
479    /// assert!(arena.capacity() >= 100);
480    /// ```
481    pub fn reserve_slots(&mut self, additional: usize) {
482        self.slots.reserve(additional);
483    }
484
485    /// Shrinks internal storage to fit the current state.
486    ///
487    /// # Example
488    ///
489    /// ```
490    /// use cachekit::ds::SlotArena;
491    ///
492    /// let mut arena: SlotArena<i32> = SlotArena::with_capacity(1000);
493    /// arena.insert(1);
494    /// arena.shrink_to_fit();
495    /// // Capacity reduced (exact value is implementation-defined)
496    /// ```
497    pub fn shrink_to_fit(&mut self) {
498        self.slots.shrink_to_fit();
499        self.free_list.shrink_to_fit();
500    }
501
502    /// Clears all entries and shrinks internal storage.
503    ///
504    /// # Example
505    ///
506    /// ```
507    /// use cachekit::ds::SlotArena;
508    ///
509    /// let mut arena = SlotArena::with_capacity(100);
510    /// arena.insert("a");
511    /// arena.insert("b");
512    ///
513    /// arena.clear_shrink();
514    /// assert!(arena.is_empty());
515    /// ```
516    pub fn clear_shrink(&mut self) {
517        self.clear();
518        self.shrink_to_fit();
519    }
520
521    /// Removes all entries and resets internal state.
522    ///
523    /// # Example
524    ///
525    /// ```
526    /// use cachekit::ds::SlotArena;
527    ///
528    /// let mut arena = SlotArena::new();
529    /// let a = arena.insert("a");
530    /// let b = arena.insert("b");
531    ///
532    /// arena.clear();
533    /// assert!(arena.is_empty());
534    /// assert!(!arena.contains(a));
535    /// assert!(!arena.contains(b));
536    /// ```
537    pub fn clear(&mut self) {
538        self.slots.clear();
539        self.free_list.clear();
540        self.len = 0;
541    }
542
543    /// Iterates over live `(SlotId, &T)` pairs.
544    ///
545    /// # Example
546    ///
547    /// ```
548    /// use cachekit::ds::SlotArena;
549    ///
550    /// let mut arena = SlotArena::new();
551    /// arena.insert("a");
552    /// arena.insert("b");
553    ///
554    /// let values: Vec<_> = arena.iter().map(|(_, v)| *v).collect();
555    /// assert_eq!(values, vec!["a", "b"]);
556    /// ```
557    pub fn iter(&self) -> impl Iterator<Item = (SlotId, &T)> {
558        self.slots
559            .iter()
560            .enumerate()
561            .filter_map(|(idx, slot)| slot.as_ref().map(|value| (SlotId(idx), value)))
562    }
563
564    /// Iterates over live [`SlotId`]s only.
565    ///
566    /// # Example
567    ///
568    /// ```
569    /// use cachekit::ds::SlotArena;
570    ///
571    /// let mut arena = SlotArena::new();
572    /// let a = arena.insert("a");
573    /// let b = arena.insert("b");
574    /// arena.remove(a);
575    ///
576    /// let ids: Vec<_> = arena.iter_ids().collect();
577    /// assert_eq!(ids.len(), 1);
578    /// assert!(ids.contains(&b));
579    /// ```
580    pub fn iter_ids(&self) -> impl Iterator<Item = SlotId> + '_ {
581        self.slots
582            .iter()
583            .enumerate()
584            .filter_map(|(idx, slot)| slot.as_ref().map(|_| SlotId(idx)))
585    }
586
587    #[cfg(any(test, debug_assertions))]
588    /// Returns a debug snapshot of arena internals.
589    pub fn debug_snapshot(&self) -> SlotArenaSnapshot {
590        let mut free_list = self.free_list.clone();
591        free_list.sort_unstable();
592        let mut live_ids: Vec<_> = self.iter_ids().collect();
593        live_ids.sort_by_key(|id| id.index());
594        SlotArenaSnapshot {
595            len: self.len,
596            slots_len: self.slots.len(),
597            free_list,
598            live_ids,
599        }
600    }
601
602    /// Returns an approximate memory footprint in bytes.
603    ///
604    /// Includes the struct itself, slot storage, and free list.
605    ///
606    /// # Example
607    ///
608    /// ```
609    /// use cachekit::ds::SlotArena;
610    ///
611    /// let mut arena: SlotArena<u64> = SlotArena::with_capacity(100);
612    /// let bytes = arena.approx_bytes();
613    /// assert!(bytes > 0);
614    /// ```
615    pub fn approx_bytes(&self) -> usize {
616        std::mem::size_of::<Self>()
617            + self.slots.capacity() * std::mem::size_of::<Option<T>>()
618            + self.free_list.capacity() * std::mem::size_of::<usize>()
619    }
620
621    #[cfg(any(test, debug_assertions))]
622    /// Validates internal invariants (debug/test builds only).
623    pub fn debug_validate_invariants(&self) {
624        let live_count = self.slots.iter().filter(|slot| slot.is_some()).count();
625        assert_eq!(self.len, live_count);
626        assert!(self.len <= self.slots.len());
627
628        let mut seen_free = std::collections::HashSet::new();
629        for &idx in &self.free_list {
630            assert!(idx < self.slots.len());
631            assert!(self.slots[idx].is_none());
632            assert!(seen_free.insert(idx));
633        }
634
635        assert_eq!(self.slots.len(), self.free_list.len() + self.len);
636    }
637}
638
639#[cfg(any(test, debug_assertions))]
640#[derive(Debug, Clone, PartialEq, Eq)]
641pub struct SlotArenaSnapshot {
642    pub len: usize,
643    pub slots_len: usize,
644    pub free_list: Vec<usize>,
645    pub live_ids: Vec<SlotId>,
646}
647
648/// Stable handle into a [`ShardedSlotArena`].
649///
650/// Contains both the shard index and the [`SlotId`] within that shard.
651/// Required for O(1) access since the shard must be known to locate the value.
652///
653/// # Example
654///
655/// ```
656/// use cachekit::ds::ShardedSlotArena;
657///
658/// let arena = ShardedSlotArena::new(4);
659/// let id = arena.insert(42);
660///
661/// // Inspect shard and slot
662/// println!("Shard {}, Slot {}", id.shard(), id.slot().index());
663///
664/// // Access value
665/// assert_eq!(arena.get_with(id, |v| *v), Some(42));
666/// ```
667#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
668#[cfg(feature = "concurrency")]
669pub struct ShardedSlotId {
670    shard: usize,
671    slot: SlotId,
672}
673
674#[cfg(feature = "concurrency")]
675impl ShardedSlotId {
676    /// Returns the shard index.
677    ///
678    /// # Example
679    ///
680    /// ```
681    /// use cachekit::ds::ShardedSlotArena;
682    ///
683    /// let arena = ShardedSlotArena::new(4);
684    /// let id = arena.insert(42);
685    /// assert!(id.shard() < 4);
686    /// ```
687    pub fn shard(self) -> usize {
688        self.shard
689    }
690
691    /// Returns the [`SlotId`] within the shard.
692    ///
693    /// # Example
694    ///
695    /// ```
696    /// use cachekit::ds::ShardedSlotArena;
697    ///
698    /// let arena = ShardedSlotArena::new(2);
699    /// let id = arena.insert("value");
700    /// let slot = id.slot();
701    /// // slot.index() is the position within the shard
702    /// ```
703    pub fn slot(self) -> SlotId {
704        self.slot
705    }
706}
707
708/// Thread-safe sharded arena with multiple `RwLock`-protected [`SlotArena`]s.
709///
710/// Distributes inserts across shards in round-robin fashion to reduce
711/// contention. Each shard has its own lock, so operations on different
712/// shards can proceed in parallel.
713///
714/// # Use Cases
715///
716/// - High-concurrency metadata storage
717/// - When a single `ConcurrentSlotArena` becomes a bottleneck
718/// - Workloads with many concurrent writers
719///
720/// # Example
721///
722/// ```
723/// use std::sync::Arc;
724/// use std::thread;
725/// use cachekit::ds::ShardedSlotArena;
726///
727/// let arena = Arc::new(ShardedSlotArena::new(4));
728///
729/// // Spawn writers
730/// let handles: Vec<_> = (0..4).map(|_| {
731///     let arena = Arc::clone(&arena);
732///     thread::spawn(move || {
733///         for i in 0..100 {
734///             arena.insert(i);
735///         }
736///     })
737/// }).collect();
738///
739/// for h in handles {
740///     h.join().unwrap();
741/// }
742///
743/// assert_eq!(arena.len(), 400);
744/// assert_eq!(arena.shard_count(), 4);
745/// ```
746#[cfg(feature = "concurrency")]
747#[derive(Debug)]
748pub struct ShardedSlotArena<T> {
749    shards: Vec<parking_lot::RwLock<SlotArena<T>>>,
750    selector: crate::ds::ShardSelector,
751    next_shard: std::sync::atomic::AtomicUsize,
752}
753
754#[cfg(feature = "concurrency")]
755impl<T> ShardedSlotArena<T> {
756    /// Creates a sharded arena with the specified number of shards.
757    ///
758    /// # Example
759    ///
760    /// ```
761    /// use cachekit::ds::ShardedSlotArena;
762    ///
763    /// let arena: ShardedSlotArena<i32> = ShardedSlotArena::new(8);
764    /// assert_eq!(arena.shard_count(), 8);
765    /// ```
766    pub fn new(shards: usize) -> Self {
767        let shards = shards.max(1);
768        let mut arenas = Vec::with_capacity(shards);
769        for _ in 0..shards {
770            arenas.push(parking_lot::RwLock::new(SlotArena::new()));
771        }
772        Self {
773            shards: arenas,
774            selector: crate::ds::ShardSelector::new(shards, 0),
775            next_shard: std::sync::atomic::AtomicUsize::new(0),
776        }
777    }
778
779    /// Creates a sharded arena with pre-allocated capacity per shard.
780    ///
781    /// # Example
782    ///
783    /// ```
784    /// use cachekit::ds::ShardedSlotArena;
785    ///
786    /// // 4 shards, each pre-allocated for 1000 entries
787    /// let arena: ShardedSlotArena<String> = ShardedSlotArena::with_capacity(4, 1000);
788    /// assert_eq!(arena.shard_count(), 4);
789    /// ```
790    pub fn with_capacity(shards: usize, capacity: usize) -> Self {
791        let shards = shards.max(1);
792        let mut arenas = Vec::with_capacity(shards);
793        for _ in 0..shards {
794            arenas.push(parking_lot::RwLock::new(SlotArena::with_capacity(capacity)));
795        }
796        Self {
797            shards: arenas,
798            selector: crate::ds::ShardSelector::new(shards, 0),
799            next_shard: std::sync::atomic::AtomicUsize::new(0),
800        }
801    }
802
803    /// Alias for [`with_capacity`](Self::with_capacity).
804    pub fn with_shards(shards: usize, capacity_per_shard: usize) -> Self {
805        Self::with_capacity(shards, capacity_per_shard)
806    }
807
808    /// Creates a sharded arena with a custom hash seed for shard selection.
809    ///
810    /// # Example
811    ///
812    /// ```
813    /// use cachekit::ds::ShardedSlotArena;
814    ///
815    /// // Custom seed for deterministic shard selection
816    /// let arena: ShardedSlotArena<i32> = ShardedSlotArena::with_shards_seed(4, 100, 42);
817    /// assert_eq!(arena.shard_count(), 4);
818    /// ```
819    pub fn with_shards_seed(shards: usize, capacity_per_shard: usize, seed: u64) -> Self {
820        let shards = shards.max(1);
821        let mut arenas = Vec::with_capacity(shards);
822        for _ in 0..shards {
823            arenas.push(parking_lot::RwLock::new(SlotArena::with_capacity(
824                capacity_per_shard,
825            )));
826        }
827        Self {
828            shards: arenas,
829            selector: crate::ds::ShardSelector::new(shards, seed),
830            next_shard: std::sync::atomic::AtomicUsize::new(0),
831        }
832    }
833
834    /// Returns the shard index for a key using the configured hash selector.
835    ///
836    /// Useful for co-locating related data in the same shard.
837    ///
838    /// # Example
839    ///
840    /// ```
841    /// use cachekit::ds::ShardedSlotArena;
842    ///
843    /// let arena: ShardedSlotArena<String> = ShardedSlotArena::new(8);
844    /// let shard = arena.shard_for_key(&"my_key");
845    /// assert!(shard < 8);
846    /// ```
847    pub fn shard_for_key<K: std::hash::Hash>(&self, key: &K) -> usize {
848        self.selector.shard_for_key(key)
849    }
850
851    /// Returns the number of shards.
852    ///
853    /// # Example
854    ///
855    /// ```
856    /// use cachekit::ds::ShardedSlotArena;
857    ///
858    /// let arena: ShardedSlotArena<i32> = ShardedSlotArena::new(16);
859    /// assert_eq!(arena.shard_count(), 16);
860    /// ```
861    pub fn shard_count(&self) -> usize {
862        self.shards.len()
863    }
864
865    /// Inserts a value into the next shard (round-robin) and returns its handle.
866    ///
867    /// # Example
868    ///
869    /// ```
870    /// use cachekit::ds::ShardedSlotArena;
871    ///
872    /// let arena = ShardedSlotArena::new(2);
873    /// let a = arena.insert("first");
874    /// let b = arena.insert("second");
875    ///
876    /// // Inserts alternate between shards
877    /// assert_ne!(a.shard(), b.shard());
878    /// ```
879    pub fn insert(&self, value: T) -> ShardedSlotId {
880        let shard = self
881            .next_shard
882            .fetch_add(1, std::sync::atomic::Ordering::Relaxed)
883            % self.shards.len();
884        let mut arena = self.shards[shard].write();
885        let slot = arena.insert(value);
886        ShardedSlotId { shard, slot }
887    }
888
889    /// Removes and returns the value at `id`.
890    ///
891    /// # Example
892    ///
893    /// ```
894    /// use cachekit::ds::ShardedSlotArena;
895    ///
896    /// let arena = ShardedSlotArena::new(2);
897    /// let id = arena.insert(42);
898    ///
899    /// assert_eq!(arena.remove(id), Some(42));
900    /// assert_eq!(arena.remove(id), None);  // Already removed
901    /// ```
902    pub fn remove(&self, id: ShardedSlotId) -> Option<T> {
903        let mut arena = self.shards.get(id.shard)?.write();
904        arena.remove(id.slot)
905    }
906
907    /// Runs a closure on a shared reference to the value at `id`.
908    ///
909    /// # Example
910    ///
911    /// ```
912    /// use cachekit::ds::ShardedSlotArena;
913    ///
914    /// let arena = ShardedSlotArena::new(2);
915    /// let id = arena.insert(vec![1, 2, 3]);
916    ///
917    /// let len = arena.get_with(id, |v| v.len());
918    /// assert_eq!(len, Some(3));
919    /// ```
920    pub fn get_with<R>(&self, id: ShardedSlotId, f: impl FnOnce(&T) -> R) -> Option<R> {
921        let arena = self.shards.get(id.shard)?.read();
922        arena.get(id.slot).map(f)
923    }
924
925    /// Runs a closure on a mutable reference to the value at `id`.
926    ///
927    /// # Example
928    ///
929    /// ```
930    /// use cachekit::ds::ShardedSlotArena;
931    ///
932    /// let arena = ShardedSlotArena::new(2);
933    /// let id = arena.insert(10);
934    ///
935    /// arena.get_mut_with(id, |v| *v += 5);
936    /// assert_eq!(arena.get_with(id, |v| *v), Some(15));
937    /// ```
938    pub fn get_mut_with<R>(&self, id: ShardedSlotId, f: impl FnOnce(&mut T) -> R) -> Option<R> {
939        let mut arena = self.shards.get(id.shard)?.write();
940        arena.get_mut(id.slot).map(f)
941    }
942
943    /// Returns `true` if `id` refers to a live slot.
944    ///
945    /// # Example
946    ///
947    /// ```
948    /// use cachekit::ds::ShardedSlotArena;
949    ///
950    /// let arena = ShardedSlotArena::new(2);
951    /// let id = arena.insert("value");
952    ///
953    /// assert!(arena.contains(id));
954    /// arena.remove(id);
955    /// assert!(!arena.contains(id));
956    /// ```
957    pub fn contains(&self, id: ShardedSlotId) -> bool {
958        self.shards
959            .get(id.shard)
960            .map(|arena| arena.read().contains(id.slot))
961            .unwrap_or(false)
962    }
963
964    /// Returns the total number of live entries across all shards.
965    ///
966    /// # Example
967    ///
968    /// ```
969    /// use cachekit::ds::ShardedSlotArena;
970    ///
971    /// let arena = ShardedSlotArena::new(4);
972    /// arena.insert(1);
973    /// arena.insert(2);
974    /// arena.insert(3);
975    ///
976    /// assert_eq!(arena.len(), 3);
977    /// ```
978    pub fn len(&self) -> usize {
979        self.shards.iter().map(|arena| arena.read().len()).sum()
980    }
981
982    /// Returns `true` if all shards are empty.
983    ///
984    /// # Example
985    ///
986    /// ```
987    /// use cachekit::ds::ShardedSlotArena;
988    ///
989    /// let arena: ShardedSlotArena<i32> = ShardedSlotArena::new(2);
990    /// assert!(arena.is_empty());
991    ///
992    /// arena.insert(1);
993    /// assert!(!arena.is_empty());
994    /// ```
995    pub fn is_empty(&self) -> bool {
996        self.len() == 0
997    }
998
999    /// Reserves capacity in each shard.
1000    ///
1001    /// # Example
1002    ///
1003    /// ```
1004    /// use cachekit::ds::ShardedSlotArena;
1005    ///
1006    /// let arena: ShardedSlotArena<i32> = ShardedSlotArena::new(4);
1007    /// arena.reserve_slots(100);  // Each shard gets 100 additional capacity
1008    /// ```
1009    pub fn reserve_slots(&self, additional: usize) {
1010        for arena in &self.shards {
1011            arena.write().reserve_slots(additional);
1012        }
1013    }
1014
1015    /// Shrinks all shards to fit their current state.
1016    ///
1017    /// # Example
1018    ///
1019    /// ```
1020    /// use cachekit::ds::ShardedSlotArena;
1021    ///
1022    /// let arena: ShardedSlotArena<i32> = ShardedSlotArena::with_capacity(4, 1000);
1023    /// arena.insert(1);
1024    /// arena.shrink_to_fit();
1025    /// ```
1026    pub fn shrink_to_fit(&self) {
1027        for arena in &self.shards {
1028            arena.write().shrink_to_fit();
1029        }
1030    }
1031
1032    /// Clears all shards and shrinks storage.
1033    ///
1034    /// # Example
1035    ///
1036    /// ```
1037    /// use cachekit::ds::ShardedSlotArena;
1038    ///
1039    /// let arena = ShardedSlotArena::new(2);
1040    /// arena.insert(1);
1041    /// arena.insert(2);
1042    ///
1043    /// arena.clear_shrink();
1044    /// assert!(arena.is_empty());
1045    /// ```
1046    pub fn clear_shrink(&self) {
1047        for arena in &self.shards {
1048            arena.write().clear_shrink();
1049        }
1050    }
1051
1052    /// Returns approximate memory footprint across all shards.
1053    ///
1054    /// # Example
1055    ///
1056    /// ```
1057    /// use cachekit::ds::ShardedSlotArena;
1058    ///
1059    /// let arena: ShardedSlotArena<u64> = ShardedSlotArena::with_capacity(4, 100);
1060    /// let bytes = arena.approx_bytes();
1061    /// assert!(bytes > 0);
1062    /// ```
1063    pub fn approx_bytes(&self) -> usize {
1064        self.shards
1065            .iter()
1066            .map(|arena| arena.read().approx_bytes())
1067            .sum()
1068    }
1069}
1070
1071impl<T> Default for SlotArena<T> {
1072    fn default() -> Self {
1073        Self::new()
1074    }
1075}
1076
1077/// Thread-safe [`SlotArena`] wrapper using `parking_lot::RwLock`.
1078///
1079/// Provides the same functionality as [`SlotArena`] but safe for concurrent
1080/// access. Uses closure-based access (`get_with`, `get_mut_with`) since
1081/// references cannot outlive lock guards.
1082///
1083/// For high-contention workloads, consider [`ShardedSlotArena`] instead.
1084///
1085/// # Example
1086///
1087/// ```
1088/// use std::sync::Arc;
1089/// use std::thread;
1090/// use cachekit::ds::ConcurrentSlotArena;
1091///
1092/// let arena = Arc::new(ConcurrentSlotArena::new());
1093///
1094/// // Insert from main thread
1095/// let id = arena.insert(0);
1096///
1097/// // Spawn readers and writers
1098/// let handles: Vec<_> = (0..4).map(|i| {
1099///     let arena = Arc::clone(&arena);
1100///     thread::spawn(move || {
1101///         // Read
1102///         let val = arena.get_with(id, |v| *v);
1103///
1104///         // Write (increment)
1105///         arena.get_mut_with(id, |v| *v += 1);
1106///     })
1107/// }).collect();
1108///
1109/// for h in handles {
1110///     h.join().unwrap();
1111/// }
1112///
1113/// assert_eq!(arena.get_with(id, |v| *v), Some(4));
1114/// ```
1115#[cfg(feature = "concurrency")]
1116#[derive(Debug)]
1117pub struct ConcurrentSlotArena<T> {
1118    inner: parking_lot::RwLock<SlotArena<T>>,
1119}
1120
1121#[cfg(feature = "concurrency")]
1122impl<T> ConcurrentSlotArena<T> {
1123    /// Creates an empty concurrent arena.
1124    ///
1125    /// # Example
1126    ///
1127    /// ```
1128    /// use cachekit::ds::ConcurrentSlotArena;
1129    ///
1130    /// let arena: ConcurrentSlotArena<String> = ConcurrentSlotArena::new();
1131    /// assert!(arena.is_empty());
1132    /// ```
1133    pub fn new() -> Self {
1134        Self {
1135            inner: parking_lot::RwLock::new(SlotArena::new()),
1136        }
1137    }
1138
1139    /// Creates a concurrent arena with pre-allocated capacity.
1140    ///
1141    /// # Example
1142    ///
1143    /// ```
1144    /// use cachekit::ds::ConcurrentSlotArena;
1145    ///
1146    /// let arena: ConcurrentSlotArena<i32> = ConcurrentSlotArena::with_capacity(1000);
1147    /// assert!(arena.capacity() >= 1000);
1148    /// ```
1149    pub fn with_capacity(capacity: usize) -> Self {
1150        Self {
1151            inner: parking_lot::RwLock::new(SlotArena::with_capacity(capacity)),
1152        }
1153    }
1154
1155    /// Inserts a value and returns its [`SlotId`].
1156    ///
1157    /// Acquires write lock.
1158    ///
1159    /// # Example
1160    ///
1161    /// ```
1162    /// use cachekit::ds::ConcurrentSlotArena;
1163    ///
1164    /// let arena = ConcurrentSlotArena::new();
1165    /// let id = arena.insert(42);
1166    /// assert!(arena.contains(id));
1167    /// ```
1168    pub fn insert(&self, value: T) -> SlotId {
1169        let mut arena = self.inner.write();
1170        arena.insert(value)
1171    }
1172
1173    /// Removes and returns the value at `id`.
1174    ///
1175    /// Acquires write lock.
1176    ///
1177    /// # Example
1178    ///
1179    /// ```
1180    /// use cachekit::ds::ConcurrentSlotArena;
1181    ///
1182    /// let arena = ConcurrentSlotArena::new();
1183    /// let id = arena.insert(42);
1184    ///
1185    /// assert_eq!(arena.remove(id), Some(42));
1186    /// assert_eq!(arena.remove(id), None);
1187    /// ```
1188    pub fn remove(&self, id: SlotId) -> Option<T> {
1189        let mut arena = self.inner.write();
1190        arena.remove(id)
1191    }
1192
1193    /// Runs a closure on a shared reference to the value at `id`.
1194    ///
1195    /// Acquires read lock. The closure's return value is returned.
1196    ///
1197    /// # Example
1198    ///
1199    /// ```
1200    /// use cachekit::ds::ConcurrentSlotArena;
1201    ///
1202    /// let arena = ConcurrentSlotArena::new();
1203    /// let id = arena.insert(vec![1, 2, 3]);
1204    ///
1205    /// let sum = arena.get_with(id, |v| v.iter().sum::<i32>());
1206    /// assert_eq!(sum, Some(6));
1207    /// ```
1208    pub fn get_with<R>(&self, id: SlotId, f: impl FnOnce(&T) -> R) -> Option<R> {
1209        let arena = self.inner.read();
1210        arena.get(id).map(f)
1211    }
1212
1213    /// Non-blocking version of [`get_with`](Self::get_with).
1214    ///
1215    /// Returns `None` if the lock cannot be acquired immediately.
1216    ///
1217    /// # Example
1218    ///
1219    /// ```
1220    /// use cachekit::ds::ConcurrentSlotArena;
1221    ///
1222    /// let arena = ConcurrentSlotArena::new();
1223    /// let id = arena.insert(100);
1224    ///
1225    /// // Returns Some if lock is available
1226    /// if let Some(val) = arena.try_get_with(id, |v| *v) {
1227    ///     assert_eq!(val, 100);
1228    /// }
1229    /// ```
1230    pub fn try_get_with<R>(&self, id: SlotId, f: impl FnOnce(&T) -> R) -> Option<R> {
1231        let arena = self.inner.try_read()?;
1232        arena.get(id).map(f)
1233    }
1234
1235    /// Runs a closure on a mutable reference to the value at `id`.
1236    ///
1237    /// Acquires write lock.
1238    ///
1239    /// # Example
1240    ///
1241    /// ```
1242    /// use cachekit::ds::ConcurrentSlotArena;
1243    ///
1244    /// let arena = ConcurrentSlotArena::new();
1245    /// let id = arena.insert(1);
1246    ///
1247    /// arena.get_mut_with(id, |v| *v = 2);
1248    /// assert_eq!(arena.get_with(id, |v| *v), Some(2));
1249    /// ```
1250    pub fn get_mut_with<R>(&self, id: SlotId, f: impl FnOnce(&mut T) -> R) -> Option<R> {
1251        let mut arena = self.inner.write();
1252        arena.get_mut(id).map(f)
1253    }
1254
1255    /// Non-blocking version of [`get_mut_with`](Self::get_mut_with).
1256    ///
1257    /// Returns `None` if the lock cannot be acquired immediately.
1258    ///
1259    /// # Example
1260    ///
1261    /// ```
1262    /// use cachekit::ds::ConcurrentSlotArena;
1263    ///
1264    /// let arena = ConcurrentSlotArena::new();
1265    /// let id = arena.insert(5);
1266    ///
1267    /// // Attempt non-blocking write
1268    /// if arena.try_get_mut_with(id, |v| *v += 1).is_some() {
1269    ///     assert_eq!(arena.get_with(id, |v| *v), Some(6));
1270    /// }
1271    /// ```
1272    pub fn try_get_mut_with<R>(&self, id: SlotId, f: impl FnOnce(&mut T) -> R) -> Option<R> {
1273        let mut arena = self.inner.try_write()?;
1274        arena.get_mut(id).map(f)
1275    }
1276
1277    /// Returns `true` if `id` refers to a live slot.
1278    ///
1279    /// # Example
1280    ///
1281    /// ```
1282    /// use cachekit::ds::ConcurrentSlotArena;
1283    ///
1284    /// let arena = ConcurrentSlotArena::new();
1285    /// let id = arena.insert("value");
1286    ///
1287    /// assert!(arena.contains(id));
1288    /// arena.remove(id);
1289    /// assert!(!arena.contains(id));
1290    /// ```
1291    pub fn contains(&self, id: SlotId) -> bool {
1292        let arena = self.inner.read();
1293        arena.contains(id)
1294    }
1295
1296    /// Returns the number of live entries.
1297    ///
1298    /// # Example
1299    ///
1300    /// ```
1301    /// use cachekit::ds::ConcurrentSlotArena;
1302    ///
1303    /// let arena = ConcurrentSlotArena::new();
1304    /// assert_eq!(arena.len(), 0);
1305    ///
1306    /// arena.insert(1);
1307    /// arena.insert(2);
1308    /// assert_eq!(arena.len(), 2);
1309    /// ```
1310    pub fn len(&self) -> usize {
1311        let arena = self.inner.read();
1312        arena.len()
1313    }
1314
1315    /// Returns `true` if there are no live entries.
1316    ///
1317    /// # Example
1318    ///
1319    /// ```
1320    /// use cachekit::ds::ConcurrentSlotArena;
1321    ///
1322    /// let arena: ConcurrentSlotArena<i32> = ConcurrentSlotArena::new();
1323    /// assert!(arena.is_empty());
1324    ///
1325    /// let id = arena.insert(1);
1326    /// assert!(!arena.is_empty());
1327    /// ```
1328    pub fn is_empty(&self) -> bool {
1329        let arena = self.inner.read();
1330        arena.is_empty()
1331    }
1332
1333    /// Returns the backing Vec capacity.
1334    ///
1335    /// # Example
1336    ///
1337    /// ```
1338    /// use cachekit::ds::ConcurrentSlotArena;
1339    ///
1340    /// let arena: ConcurrentSlotArena<i32> = ConcurrentSlotArena::with_capacity(100);
1341    /// assert!(arena.capacity() >= 100);
1342    /// ```
1343    pub fn capacity(&self) -> usize {
1344        let arena = self.inner.read();
1345        arena.capacity()
1346    }
1347
1348    /// Reserves capacity for additional slots.
1349    ///
1350    /// # Example
1351    ///
1352    /// ```
1353    /// use cachekit::ds::ConcurrentSlotArena;
1354    ///
1355    /// let arena: ConcurrentSlotArena<i32> = ConcurrentSlotArena::new();
1356    /// arena.reserve_slots(100);
1357    /// assert!(arena.capacity() >= 100);
1358    /// ```
1359    pub fn reserve_slots(&self, additional: usize) {
1360        let mut arena = self.inner.write();
1361        arena.reserve_slots(additional);
1362    }
1363
1364    /// Shrinks internal storage to fit current state.
1365    ///
1366    /// # Example
1367    ///
1368    /// ```
1369    /// use cachekit::ds::ConcurrentSlotArena;
1370    ///
1371    /// let arena: ConcurrentSlotArena<i32> = ConcurrentSlotArena::with_capacity(1000);
1372    /// arena.insert(1);
1373    /// arena.shrink_to_fit();
1374    /// ```
1375    pub fn shrink_to_fit(&self) {
1376        let mut arena = self.inner.write();
1377        arena.shrink_to_fit();
1378    }
1379
1380    /// Clears all entries and shrinks storage.
1381    ///
1382    /// # Example
1383    ///
1384    /// ```
1385    /// use cachekit::ds::ConcurrentSlotArena;
1386    ///
1387    /// let arena = ConcurrentSlotArena::with_capacity(100);
1388    /// arena.insert("a");
1389    /// arena.insert("b");
1390    ///
1391    /// arena.clear_shrink();
1392    /// assert!(arena.is_empty());
1393    /// ```
1394    pub fn clear_shrink(&self) {
1395        let mut arena = self.inner.write();
1396        arena.clear_shrink();
1397    }
1398
1399    /// Clears all entries.
1400    ///
1401    /// # Example
1402    ///
1403    /// ```
1404    /// use cachekit::ds::ConcurrentSlotArena;
1405    ///
1406    /// let arena = ConcurrentSlotArena::new();
1407    /// let a = arena.insert(1);
1408    /// let b = arena.insert(2);
1409    ///
1410    /// arena.clear();
1411    /// assert!(arena.is_empty());
1412    /// assert!(!arena.contains(a));
1413    /// ```
1414    pub fn clear(&self) {
1415        let mut arena = self.inner.write();
1416        arena.clear();
1417    }
1418
1419    /// Returns approximate memory footprint in bytes.
1420    ///
1421    /// # Example
1422    ///
1423    /// ```
1424    /// use cachekit::ds::ConcurrentSlotArena;
1425    ///
1426    /// let arena: ConcurrentSlotArena<u64> = ConcurrentSlotArena::with_capacity(100);
1427    /// let bytes = arena.approx_bytes();
1428    /// assert!(bytes > 0);
1429    /// ```
1430    pub fn approx_bytes(&self) -> usize {
1431        let arena = self.inner.read();
1432        arena.approx_bytes()
1433    }
1434}
1435
1436#[cfg(feature = "concurrency")]
1437impl<T> Default for ConcurrentSlotArena<T> {
1438    fn default() -> Self {
1439        Self::new()
1440    }
1441}
1442
1443#[cfg(test)]
1444mod tests {
1445    use super::*;
1446
1447    #[test]
1448    fn slot_arena_insert_remove_reuse() {
1449        let mut arena = SlotArena::new();
1450        let id1 = arena.insert("a");
1451        let id2 = arena.insert("b");
1452        assert_eq!(arena.len(), 2);
1453        assert_eq!(arena.get(id1), Some(&"a"));
1454        assert_eq!(arena.get(id2), Some(&"b"));
1455
1456        assert_eq!(arena.remove(id1), Some("a"));
1457        assert_eq!(arena.len(), 1);
1458
1459        let id3 = arena.insert("c");
1460        assert_eq!(arena.len(), 2);
1461        assert_eq!(arena.get(id3), Some(&"c"));
1462        assert_eq!(id1.index(), id3.index());
1463    }
1464
1465    #[cfg(feature = "concurrency")]
1466    #[test]
1467    fn concurrent_slot_arena_basic_ops() {
1468        let arena = ConcurrentSlotArena::new();
1469        let id = arena.insert(10);
1470        assert_eq!(arena.get_with(id, |v| *v), Some(10));
1471        assert!(arena.contains(id));
1472        assert_eq!(arena.len(), 1);
1473
1474        arena.get_mut_with(id, |v| *v = 20);
1475        assert_eq!(arena.get_with(id, |v| *v), Some(20));
1476        assert_eq!(arena.remove(id), Some(20));
1477        assert!(!arena.contains(id));
1478        assert!(arena.is_empty());
1479    }
1480
1481    #[test]
1482    fn slot_arena_remove_invalid_id_is_none() {
1483        let mut arena: SlotArena<i32> = SlotArena::new();
1484        let id = SlotId(0);
1485        assert_eq!(arena.remove(id), None);
1486        assert!(!arena.contains(id));
1487        assert!(arena.is_empty());
1488    }
1489
1490    #[test]
1491    fn slot_arena_clear_resets_state() {
1492        let mut arena = SlotArena::with_capacity(4);
1493        let a = arena.insert("a");
1494        let b = arena.insert("b");
1495        assert_eq!(arena.len(), 2);
1496        assert!(arena.contains(a));
1497        assert!(arena.contains(b));
1498
1499        arena.clear();
1500        assert_eq!(arena.len(), 0);
1501        assert!(arena.is_empty());
1502        assert!(!arena.contains(a));
1503        assert!(!arena.contains(b));
1504        assert_eq!(arena.iter().count(), 0);
1505    }
1506
1507    #[test]
1508    fn slot_arena_iter_skips_empty_slots() {
1509        let mut arena = SlotArena::new();
1510        let a = arena.insert("a");
1511        let b = arena.insert("b");
1512        let c = arena.insert("c");
1513        assert_eq!(arena.remove(b), Some("b"));
1514
1515        let mut values: Vec<_> = arena.iter().map(|(_, v)| *v).collect();
1516        values.sort();
1517        assert_eq!(values, vec!["a", "c"]);
1518        assert!(arena.contains(a));
1519        assert!(arena.contains(c));
1520    }
1521
1522    #[test]
1523    fn slot_arena_get_mut_updates_value() {
1524        let mut arena = SlotArena::new();
1525        let id = arena.insert(1);
1526        if let Some(value) = arena.get_mut(id) {
1527            *value = 2;
1528        }
1529        assert_eq!(arena.get(id), Some(&2));
1530    }
1531
1532    #[test]
1533    fn slot_arena_capacity_tracking() {
1534        let arena: SlotArena<i32> = SlotArena::with_capacity(16);
1535        assert!(arena.capacity() >= 16);
1536        assert_eq!(arena.len(), 0);
1537    }
1538
1539    #[test]
1540    fn slot_arena_contains_index_and_iter_ids() {
1541        let mut arena = SlotArena::new();
1542        let a = arena.insert("a");
1543        let b = arena.insert("b");
1544        assert!(arena.contains_index(a.index()));
1545        assert!(arena.contains_index(b.index()));
1546        arena.remove(a);
1547        assert!(!arena.contains_index(a.index()));
1548
1549        let ids: Vec<_> = arena.iter_ids().collect();
1550        assert_eq!(ids, vec![b]);
1551    }
1552
1553    #[test]
1554    fn slot_arena_reserve_slots_grows_capacity() {
1555        let mut arena: SlotArena<i32> = SlotArena::new();
1556        let before = arena.capacity();
1557        arena.reserve_slots(32);
1558        assert!(arena.capacity() >= before + 32);
1559    }
1560
1561    #[test]
1562    fn slot_arena_debug_snapshot() {
1563        let mut arena = SlotArena::new();
1564        let a = arena.insert("a");
1565        let b = arena.insert("b");
1566        arena.remove(a);
1567        let snapshot = arena.debug_snapshot();
1568        assert_eq!(snapshot.len, 1);
1569        assert!(snapshot.live_ids.contains(&b));
1570        assert!(snapshot.free_list.contains(&a.index()));
1571    }
1572
1573    #[cfg(feature = "concurrency")]
1574    #[test]
1575    fn sharded_slot_arena_basic_ops() {
1576        let arena = ShardedSlotArena::new(2);
1577        let a = arena.insert(1);
1578        let b = arena.insert(2);
1579        assert!(arena.contains(a));
1580        assert!(arena.contains(b));
1581        assert_eq!(arena.get_with(a, |v| *v), Some(1));
1582        assert_eq!(arena.remove(b), Some(2));
1583        assert!(!arena.contains(b));
1584        assert_eq!(arena.len(), 1);
1585    }
1586
1587    #[cfg(feature = "concurrency")]
1588    #[test]
1589    fn sharded_slot_arena_shard_for_key() {
1590        let arena: ShardedSlotArena<i32> = ShardedSlotArena::new(4);
1591        let shard = arena.shard_for_key(&"alpha");
1592        assert!(shard < arena.shard_count());
1593    }
1594
1595    #[cfg(feature = "concurrency")]
1596    #[test]
1597    fn sharded_slot_arena_with_seed() {
1598        let arena: ShardedSlotArena<i32> = ShardedSlotArena::with_shards_seed(4, 0, 99);
1599        let shard = arena.shard_for_key(&"alpha");
1600        assert!(shard < arena.shard_count());
1601    }
1602
1603    #[cfg(feature = "concurrency")]
1604    #[test]
1605    fn concurrent_slot_arena_try_ops() {
1606        let arena = ConcurrentSlotArena::new();
1607        let id = arena.insert(1);
1608        assert_eq!(arena.try_get_with(id, |v| *v), Some(1));
1609        arena.try_get_mut_with(id, |v| *v = 2);
1610        assert_eq!(arena.get_with(id, |v| *v), Some(2));
1611    }
1612
1613    #[cfg(feature = "concurrency")]
1614    #[test]
1615    fn concurrent_slot_arena_clear_and_accessors() {
1616        let arena = ConcurrentSlotArena::new();
1617        let a = arena.insert(1);
1618        let b = arena.insert(2);
1619        assert_eq!(arena.get_with(a, |v| *v), Some(1));
1620        assert_eq!(arena.get_with(b, |v| *v), Some(2));
1621
1622        arena.clear();
1623        assert!(arena.is_empty());
1624        assert!(!arena.contains(a));
1625        assert!(!arena.contains(b));
1626        assert_eq!(arena.get_with(a, |v| *v), None);
1627    }
1628
1629    #[cfg(feature = "concurrency")]
1630    #[test]
1631    fn concurrent_slot_arena_get_mut_with_updates_value() {
1632        let arena = ConcurrentSlotArena::new();
1633        let id = arena.insert(5);
1634        arena.get_mut_with(id, |v| *v = 10);
1635        assert_eq!(arena.get_with(id, |v| *v), Some(10));
1636    }
1637
1638    #[test]
1639    fn slot_arena_debug_invariants_hold() {
1640        let mut arena = SlotArena::new();
1641        let a = arena.insert(1);
1642        let b = arena.insert(2);
1643        arena.remove(a);
1644        let _ = arena.insert(3);
1645        assert!(arena.contains(b));
1646        arena.debug_validate_invariants();
1647    }
1648}
1649
1650#[cfg(test)]
1651mod property_tests {
1652    use super::*;
1653    use proptest::prelude::*;
1654
1655    // =============================================================================
1656    // Property Tests - Insert Operations
1657    // =============================================================================
1658
1659    proptest! {
1660        /// Property: insert returns valid SlotId
1661        #[cfg_attr(miri, ignore)]
1662        #[test]
1663        fn prop_insert_returns_valid_id(
1664            values in prop::collection::vec(any::<u32>(), 0..50)
1665        ) {
1666            let mut arena = SlotArena::new();
1667
1668            for value in values {
1669                let id = arena.insert(value);
1670
1671                prop_assert_eq!(arena.get(id), Some(&value));
1672                prop_assert!(arena.contains(id));
1673            }
1674        }
1675
1676        /// Property: inserted values can be retrieved
1677        #[cfg_attr(miri, ignore)]
1678        #[test]
1679        fn prop_insert_and_get(
1680            value in any::<u32>()
1681        ) {
1682            let mut arena = SlotArena::new();
1683            let id = arena.insert(value);
1684
1685            prop_assert_eq!(arena.get(id), Some(&value));
1686        }
1687    }
1688
1689    // =============================================================================
1690    // Property Tests - Remove Operations
1691    // =============================================================================
1692
1693    proptest! {
1694        /// Property: remove decreases length by 1
1695        #[cfg_attr(miri, ignore)]
1696        #[test]
1697        fn prop_remove_decreases_length(
1698            values in prop::collection::vec(any::<u32>(), 1..30)
1699        ) {
1700            let mut arena = SlotArena::new();
1701            let mut ids = Vec::new();
1702
1703            for value in &values {
1704                let id = arena.insert(*value);
1705                ids.push(id);
1706            }
1707
1708            for id in ids {
1709                let old_len = arena.len();
1710                let removed = arena.remove(id);
1711
1712                prop_assert!(removed.is_some());
1713                prop_assert_eq!(arena.len(), old_len - 1);
1714                prop_assert!(!arena.contains(id));
1715            }
1716
1717            prop_assert!(arena.is_empty());
1718        }
1719
1720        /// Property: remove returns the correct value
1721        #[cfg_attr(miri, ignore)]
1722        #[test]
1723        fn prop_remove_returns_value(
1724            values in prop::collection::vec(any::<u32>(), 1..30)
1725        ) {
1726            let mut arena = SlotArena::new();
1727            let mut ids = Vec::new();
1728
1729            for value in &values {
1730                let id = arena.insert(*value);
1731                ids.push((id, *value));
1732            }
1733
1734            for (id, expected_value) in ids {
1735                let removed = arena.remove(id);
1736                prop_assert_eq!(removed, Some(expected_value));
1737            }
1738        }
1739
1740        /// Property: removing twice returns None
1741        #[cfg_attr(miri, ignore)]
1742        #[test]
1743        fn prop_remove_twice_returns_none(
1744            value in any::<u32>()
1745        ) {
1746            let mut arena = SlotArena::new();
1747            let id = arena.insert(value);
1748
1749            let first = arena.remove(id);
1750            let second = arena.remove(id);
1751
1752            prop_assert_eq!(first, Some(value));
1753            prop_assert_eq!(second, None);
1754        }
1755    }
1756
1757    // =============================================================================
1758    // Property Tests - SlotId Stability
1759    // =============================================================================
1760
1761    proptest! {
1762        /// Property: SlotId remains valid until removed
1763        #[cfg_attr(miri, ignore)]
1764        #[test]
1765        fn prop_slot_id_stable_until_removed(
1766            values in prop::collection::vec(any::<u32>(), 1..30)
1767        ) {
1768            let mut arena = SlotArena::new();
1769            let mut ids = Vec::new();
1770
1771            for value in &values {
1772                let id = arena.insert(*value);
1773                ids.push((id, *value));
1774            }
1775
1776            // All SlotIds should be valid
1777            for (id, value) in &ids {
1778                prop_assert_eq!(arena.get(*id), Some(value));
1779                prop_assert!(arena.contains(*id));
1780            }
1781
1782            // Remove every other slot
1783            for (idx, (id, _value)) in ids.iter().enumerate() {
1784                if idx % 2 == 0 {
1785                    arena.remove(*id);
1786                }
1787            }
1788
1789            // Remaining slots should still be valid
1790            for (idx, (id, value)) in ids.iter().enumerate() {
1791                if idx % 2 != 0 {
1792                    prop_assert_eq!(arena.get(*id), Some(value));
1793                } else {
1794                    prop_assert_eq!(arena.get(*id), None);
1795                }
1796            }
1797        }
1798    }
1799
1800    // =============================================================================
1801    // Property Tests - Free Slot Reuse
1802    // =============================================================================
1803
1804    proptest! {
1805        /// Property: Freed slots are reused
1806        #[cfg_attr(miri, ignore)]
1807        #[test]
1808        fn prop_free_slot_reuse(
1809            values1 in prop::collection::vec(any::<u32>(), 5..20),
1810            values2 in prop::collection::vec(any::<u32>(), 5..20)
1811        ) {
1812            let mut arena = SlotArena::new();
1813
1814            // Insert and collect indices
1815            let mut ids = Vec::new();
1816            for value in &values1 {
1817                let id = arena.insert(*value);
1818                ids.push(id);
1819            }
1820
1821            let removed_indices: Vec<_> = ids.iter().map(|id| id.index()).collect();
1822
1823            // Remove all slots
1824            for id in ids {
1825                arena.remove(id);
1826            }
1827
1828            prop_assert!(arena.is_empty());
1829
1830            // Insert new values - should reuse freed slots
1831            let mut reused_count = 0;
1832            for value in &values2 {
1833                let id = arena.insert(*value);
1834                if removed_indices.contains(&id.index()) {
1835                    reused_count += 1;
1836                }
1837            }
1838
1839            // At least some slots should have been reused
1840            prop_assert!(reused_count > 0);
1841        }
1842    }
1843
1844    // =============================================================================
1845    // Property Tests - Length Tracking
1846    // =============================================================================
1847
1848    proptest! {
1849        /// Property: len() tracks number of live entries
1850        #[cfg_attr(miri, ignore)]
1851        #[test]
1852        fn prop_len_tracks_entries(
1853            values in prop::collection::vec(any::<u32>(), 0..50)
1854        ) {
1855            let mut arena = SlotArena::new();
1856
1857            for (idx, value) in values.iter().enumerate() {
1858                arena.insert(*value);
1859                prop_assert_eq!(arena.len(), idx + 1);
1860            }
1861
1862            let total = values.len();
1863            let mut ids: Vec<_> = arena.iter_ids().collect();
1864
1865            for (idx, id) in ids.drain(..).enumerate() {
1866                arena.remove(id);
1867                prop_assert_eq!(arena.len(), total - idx - 1);
1868            }
1869
1870            prop_assert_eq!(arena.len(), 0);
1871        }
1872
1873        /// Property: is_empty is consistent with len
1874        #[cfg_attr(miri, ignore)]
1875        #[test]
1876        fn prop_is_empty_consistent(
1877            values in prop::collection::vec(any::<u32>(), 0..30)
1878        ) {
1879            let mut arena = SlotArena::new();
1880
1881            for value in values {
1882                arena.insert(value);
1883
1884                if arena.is_empty() {
1885                    prop_assert_eq!(arena.len(), 0);
1886                } else {
1887                    prop_assert!(!arena.is_empty());
1888                }
1889            }
1890        }
1891    }
1892
1893    // =============================================================================
1894    // Property Tests - Get and Contains
1895    // =============================================================================
1896
1897    proptest! {
1898        /// Property: get returns Some for all inserted values
1899        #[cfg_attr(miri, ignore)]
1900        #[test]
1901        fn prop_get_returns_inserted_values(
1902            values in prop::collection::vec(any::<u32>(), 1..30)
1903        ) {
1904            let mut arena = SlotArena::new();
1905            let mut ids = Vec::new();
1906
1907            for value in &values {
1908                let id = arena.insert(*value);
1909                ids.push((id, *value));
1910            }
1911
1912            for (id, value) in ids {
1913                prop_assert_eq!(arena.get(id), Some(&value));
1914            }
1915        }
1916
1917        /// Property: contains is consistent with get
1918        #[cfg_attr(miri, ignore)]
1919        #[test]
1920        fn prop_contains_consistent_with_get(
1921            values in prop::collection::vec(any::<u32>(), 1..30)
1922        ) {
1923            let mut arena = SlotArena::new();
1924            let mut ids = Vec::new();
1925
1926            for value in &values {
1927                let id = arena.insert(*value);
1928                ids.push(id);
1929            }
1930
1931            for id in ids {
1932                let contains = arena.contains(id);
1933                let get_result = arena.get(id);
1934
1935                if contains {
1936                    prop_assert!(get_result.is_some());
1937                } else {
1938                    prop_assert!(get_result.is_none());
1939                }
1940            }
1941        }
1942    }
1943
1944    // =============================================================================
1945    // Property Tests - Get Mut
1946    // =============================================================================
1947
1948    proptest! {
1949        /// Property: get_mut allows modification
1950        #[cfg_attr(miri, ignore)]
1951        #[test]
1952        fn prop_get_mut_modifies(
1953            value1 in any::<u32>(),
1954            value2 in any::<u32>()
1955        ) {
1956            let mut arena = SlotArena::new();
1957            let id = arena.insert(value1);
1958
1959            if let Some(val) = arena.get_mut(id) {
1960                *val = value2;
1961            }
1962
1963            prop_assert_eq!(arena.get(id), Some(&value2));
1964        }
1965    }
1966
1967    // =============================================================================
1968    // Property Tests - Iterator
1969    // =============================================================================
1970
1971    proptest! {
1972        /// Property: iter returns exactly len() items
1973        #[cfg_attr(miri, ignore)]
1974        #[test]
1975        fn prop_iter_returns_len_items(
1976            values in prop::collection::vec(any::<u32>(), 0..50)
1977        ) {
1978            let mut arena = SlotArena::new();
1979
1980            for value in values {
1981                arena.insert(value);
1982            }
1983
1984            let iter_count = arena.iter().count();
1985            prop_assert_eq!(iter_count, arena.len());
1986        }
1987
1988        /// Property: iter_ids returns exactly len() items
1989        #[cfg_attr(miri, ignore)]
1990        #[test]
1991        fn prop_iter_ids_returns_len_items(
1992            values in prop::collection::vec(any::<u32>(), 0..50)
1993        ) {
1994            let mut arena = SlotArena::new();
1995
1996            for value in values {
1997                arena.insert(value);
1998            }
1999
2000            let ids_count = arena.iter_ids().count();
2001            prop_assert_eq!(ids_count, arena.len());
2002        }
2003
2004        /// Property: iter skips removed slots
2005        #[cfg_attr(miri, ignore)]
2006        #[test]
2007        fn prop_iter_skips_removed(
2008            values in prop::collection::vec(any::<u32>(), 5..30)
2009        ) {
2010            let mut arena = SlotArena::new();
2011            let mut ids = Vec::new();
2012
2013            for value in &values {
2014                let id = arena.insert(*value);
2015                ids.push(id);
2016            }
2017
2018            // Remove every other slot
2019            for (idx, id) in ids.iter().enumerate() {
2020                if idx % 2 == 0 {
2021                    arena.remove(*id);
2022                }
2023            }
2024
2025            let iter_count = arena.iter().count();
2026            let expected_count = values.len() / 2;
2027            prop_assert_eq!(iter_count, expected_count);
2028        }
2029    }
2030
2031    // =============================================================================
2032    // Property Tests - Clear Operations
2033    // =============================================================================
2034
2035    proptest! {
2036        /// Property: clear_shrink resets to empty state
2037        #[cfg_attr(miri, ignore)]
2038        #[test]
2039        fn prop_clear_resets_state(
2040            values in prop::collection::vec(any::<u32>(), 1..30)
2041        ) {
2042            let mut arena = SlotArena::new();
2043
2044            for value in values {
2045                arena.insert(value);
2046            }
2047
2048            arena.clear_shrink();
2049
2050            prop_assert!(arena.is_empty());
2051            prop_assert_eq!(arena.len(), 0);
2052            prop_assert_eq!(arena.iter().count(), 0);
2053        }
2054
2055        /// Property: clear invalidates all SlotIds
2056        #[cfg_attr(miri, ignore)]
2057        #[test]
2058        fn prop_clear_invalidates_ids(
2059            values in prop::collection::vec(any::<u32>(), 1..20)
2060        ) {
2061            let mut arena = SlotArena::new();
2062            let mut ids = Vec::new();
2063
2064            for value in values {
2065                let id = arena.insert(value);
2066                ids.push(id);
2067            }
2068
2069            arena.clear_shrink();
2070
2071            // All previous ids should be invalid
2072            for id in ids {
2073                prop_assert!(!arena.contains(id));
2074                prop_assert_eq!(arena.get(id), None);
2075            }
2076        }
2077
2078        /// Property: usable after clear
2079        #[cfg_attr(miri, ignore)]
2080        #[test]
2081        fn prop_usable_after_clear(
2082            values1 in prop::collection::vec(any::<u32>(), 1..20),
2083            values2 in prop::collection::vec(any::<u32>(), 1..20)
2084        ) {
2085            let mut arena = SlotArena::new();
2086
2087            for value in values1 {
2088                arena.insert(value);
2089            }
2090
2091            arena.clear_shrink();
2092
2093            // Should be usable after clear
2094            for value in &values2 {
2095                arena.insert(*value);
2096            }
2097
2098            prop_assert_eq!(arena.len(), values2.len());
2099        }
2100    }
2101
2102    // =============================================================================
2103    // Property Tests - Capacity
2104    // =============================================================================
2105
2106    proptest! {
2107        /// Property: with_capacity pre-allocates
2108        #[cfg_attr(miri, ignore)]
2109        #[test]
2110        fn prop_with_capacity_preallocates(
2111            capacity in 1usize..100
2112        ) {
2113            let arena: SlotArena<u32> = SlotArena::with_capacity(capacity);
2114
2115            prop_assert!(arena.capacity() >= capacity);
2116            prop_assert_eq!(arena.len(), 0);
2117            prop_assert!(arena.is_empty());
2118        }
2119    }
2120
2121    // =============================================================================
2122    // Property Tests - Reference Implementation Equivalence
2123    // =============================================================================
2124
2125    proptest! {
2126        /// Property: Behavior matches reference HashMap
2127        #[cfg_attr(miri, ignore)]
2128        #[test]
2129        fn prop_matches_hashmap(
2130            operations in prop::collection::vec((0u8..3, any::<u32>()), 0..50)
2131        ) {
2132            let mut arena = SlotArena::new();
2133            let mut reference: std::collections::HashMap<usize, u32> = std::collections::HashMap::new();
2134
2135            for (op, value) in operations {
2136                match op % 3 {
2137                    0 => {
2138                        // insert
2139                        let id = arena.insert(value);
2140                        reference.insert(id.index(), value);
2141                    }
2142                    1 => {
2143                        // remove
2144                        if !reference.is_empty() {
2145                            let keys: Vec<_> = reference.keys().copied().collect();
2146                            let key = keys[0];
2147                            let id = SlotId(key);
2148
2149                            let arena_val = arena.remove(id);
2150                            let ref_val = reference.remove(&key);
2151
2152                            prop_assert_eq!(arena_val, ref_val);
2153                        }
2154                    }
2155                    2 => {
2156                        // verify
2157                        for (&key, &expected_value) in &reference {
2158                            let id = SlotId(key);
2159                            prop_assert_eq!(arena.get(id), Some(&expected_value));
2160                        }
2161                    }
2162                    _ => unreachable!(),
2163                }
2164
2165                // Verify consistency
2166                prop_assert_eq!(arena.len(), reference.len());
2167                prop_assert_eq!(arena.is_empty(), reference.is_empty());
2168            }
2169        }
2170    }
2171}