Skip to main content

auto_hash_map/
tiny_vec.rs

1//! A bounded small-vector with a `u8`-sized header and an optional inline
2//! buffer. Backs both the `List` variant of [`crate::AutoMap`] and, with
3//! `INLINE = 0`, `TaskStorage`'s lazy-fields collection.
4//!
5//! This is functionally a `SmallVec<[T; INLINE]>` that is *bounded* at `MAX`
6//! elements, but with a much smaller header:
7//!
8//! * `SmallVec` stores a `usize` length **and**, in its spilled representation, a heap pointer plus
9//!   a `usize` capacity. Three `usize`s of header.
10//! * Because the element count never exceeds `MAX` (`<= 254`), both the length and the capacity fit
11//!   in a `u8`. `TinyVec` stores `len: NonZeroU8` and `cap: u8` — two bytes of header — and
12//!   overlaps the inline array with the heap pointer in a union.
13//!
14//! The length is stored as `NonZeroU8` (holding `actual_len + 1`) so that `0`
15//! is a forbidden bit pattern. That niche lets the enclosing `AutoMap` enum
16//! fold its `List`/`Map` discriminant in for free — no separate tag word. For
17//! example `AutoMap<TaskId, (), _, 3>` (a `NonZero`-keyed set) shrinks from 32
18//! bytes with `SmallVec` to 24, and `AutoMap<TaskId, (), _, 0>` to 16.
19//!
20//! # Type parameters
21//! * `INLINE` — elements stored inline in the struct before spilling to the heap. `INLINE = 0` (the
22//!   default) is a pure heap vector with a 2-byte header — 16 B on 64-bit, vs 24 B for `Vec`.
23//! * `MAX` — hard cap on the element count. Defaults to [`MAX_LIST_SIZE`](crate::MAX_LIST_SIZE).
24//!   Pushing past `MAX` panics; growth doubles until it would exceed `MAX`, then caps at exactly
25//!   `MAX`.
26//!
27//! # Representation
28//! * `cap == INLINE`: elements live inline in `data.inline[..len]`.
29//! * `cap > INLINE`: elements live on the heap at `data.heap[..len]`, in an allocation of `cap`
30//!   elements. Only reachable once more than `INLINE` elements are inserted; capped at `MAX`.
31
32use std::{
33    alloc::{self, Layout},
34    fmt::{Debug, Formatter},
35    mem::{ManuallyDrop, MaybeUninit},
36    num::NonZeroU8,
37    ops::{Deref, DerefMut},
38    ptr::{self, NonNull, drop_in_place, slice_from_raw_parts_mut},
39    slice::{Iter, IterMut},
40};
41
42use shrink_to_fit::ShrinkToFit;
43
44union Data<T, const INLINE: usize> {
45    inline: ManuallyDrop<[MaybeUninit<T>; INLINE]>,
46    /// Valid only when `cap > INLINE`; points to an allocation of `cap` elements.
47    heap: NonNull<T>,
48}
49
50const MAX_TINY_VEC_SIZE: usize = (u8::MAX - 1) as usize;
51/// Bounded small-vector with an optional inline buffer; see the module docs.
52pub struct TinyVec<T, const INLINE: usize, const MAX: usize = MAX_TINY_VEC_SIZE> {
53    /// `actual_len + 1`. Always in `1..=MAX+1`; never `0` (the niche).
54    len: NonZeroU8,
55    /// Current capacity. `INLINE` while inline, `> INLINE` (and `<= MAX`) while
56    /// spilled to the heap.
57    cap: u8,
58    data: Data<T, INLINE>,
59}
60
61// SAFETY: `TinyVec<T>` owns its `T`s (inline or in a private heap allocation),
62// so it is `Send`/`Sync` exactly when `T` is, just like `Vec<T>`.
63unsafe impl<T: Send, const INLINE: usize, const MAX: usize> Send for TinyVec<T, INLINE, MAX> {}
64unsafe impl<T: Sync, const INLINE: usize, const MAX: usize> Sync for TinyVec<T, INLINE, MAX> {}
65
66impl<T, const INLINE: usize, const MAX: usize> TinyVec<T, INLINE, MAX> {
67    /// Compile-time guards. Referenced from every constructor so violations fail
68    /// to compile rather than corrupting the `len`/`cap` bytes at runtime.
69    const ASSERT: () = {
70        assert!(MAX > 0, "TinyVec MAX must be > 0");
71        assert!(
72            INLINE <= MAX,
73            "TinyVec inline capacity INLINE must be <= MAX"
74        );
75        assert!(
76            MAX <= MAX_TINY_VEC_SIZE,
77            "TinyVec MAX must fit in NonZeroU8 with the +1 offset",
78        );
79    };
80
81    /// Stored length representing an empty vec (`actual_len == 0`).
82    const EMPTY_LEN: NonZeroU8 = match NonZeroU8::new(1) {
83        Some(n) => n,
84        None => unreachable!(),
85    };
86
87    #[inline]
88    pub const fn new() -> Self {
89        let () = Self::ASSERT;
90        Self {
91            len: Self::EMPTY_LEN,
92            cap: INLINE as u8,
93            data: Data {
94                inline: ManuallyDrop::new([const { MaybeUninit::uninit() }; INLINE]),
95            },
96        }
97    }
98
99    /// Allocate with room for at least `capacity` elements (clamped to `MAX`).
100    /// Stays inline when `capacity <= INLINE`.
101    #[inline]
102    pub fn with_capacity(capacity: usize) -> Self {
103        let () = Self::ASSERT;
104        if capacity <= INLINE {
105            return Self::new();
106        }
107        let cap = capacity.min(MAX);
108        let heap = Self::alloc(cap);
109        Self {
110            len: Self::EMPTY_LEN,
111            cap: cap as u8,
112            data: Data { heap },
113        }
114    }
115
116    #[inline]
117    const fn is_spilled(&self) -> bool {
118        self.cap as usize > INLINE
119    }
120
121    #[inline]
122    pub const fn len(&self) -> usize {
123        (self.len.get() - 1) as usize
124    }
125
126    #[inline]
127    pub const fn is_empty(&self) -> bool {
128        self.len.get() == 1
129    }
130
131    #[inline]
132    pub const fn capacity(&self) -> usize {
133        self.cap as usize
134    }
135
136    /// # Safety
137    /// `actual` must be `<= self.capacity()` and the first `actual` elements
138    /// must be initialized.
139    #[inline]
140    fn set_len(&mut self, actual: usize) {
141        debug_assert!(actual <= self.capacity());
142        debug_assert!(actual <= MAX);
143        // actual <= MAX <= 254 => actual + 1 in 1..=255, never zero.
144        self.len = unsafe { NonZeroU8::new_unchecked(actual as u8 + 1u8) };
145    }
146
147    /// Pointer to element storage (inline or heap), valid for `len` reads.
148    #[inline]
149    fn as_ptr(&self) -> *const T {
150        if self.is_spilled() {
151            // SAFETY: spilled => `heap` is the active union field.
152            unsafe { self.data.heap.as_ptr() as *const T }
153        } else {
154            // SAFETY: inline => `inline` is the active union field.
155            unsafe { (*ptr::addr_of!(self.data.inline)).as_ptr().cast::<T>() }
156        }
157    }
158
159    #[inline]
160    fn as_mut_ptr(&mut self) -> *mut T {
161        if self.is_spilled() {
162            // SAFETY: spilled => `heap` is the active union field.
163            unsafe { self.data.heap.as_ptr() }
164        } else {
165            // SAFETY: inline => `inline` is the active union field.
166            unsafe {
167                (*ptr::addr_of_mut!(self.data.inline))
168                    .as_mut_ptr()
169                    .cast::<T>()
170            }
171        }
172    }
173
174    #[inline]
175    pub fn as_slice(&self) -> &[T] {
176        // SAFETY: first `len()` elements are initialized (type invariant).
177        unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) }
178    }
179
180    #[inline]
181    pub fn as_mut_slice(&mut self) -> &mut [T] {
182        let len = self.len();
183        // SAFETY: first `len` elements are initialized (type invariant).
184        unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) }
185    }
186
187    #[inline]
188    pub fn iter(&self) -> Iter<'_, T> {
189        self.as_slice().iter()
190    }
191
192    #[inline]
193    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
194        self.as_mut_slice().iter_mut()
195    }
196
197    #[inline]
198    pub fn last_mut(&mut self) -> Option<&mut T> {
199        self.as_mut_slice().last_mut()
200    }
201
202    // ---- allocation helpers -------------------------------------------------
203
204    #[inline]
205    fn layout(cap: usize) -> Layout {
206        Layout::array::<T>(cap).expect("TinyVec allocation layout overflow")
207    }
208
209    /// Free a heap buffer previously returned by [`Self::alloc`]. No-op for a
210    /// ZST `T` (where `alloc` returned a dangling pointer).
211    ///
212    /// # Safety
213    /// `ptr` must have come from `Self::alloc(cap)` and not been freed yet.
214    #[inline]
215    unsafe fn dealloc(ptr: NonNull<T>, cap: usize) {
216        let layout = Self::layout(cap);
217        if layout.size() == 0 {
218            return;
219        }
220        // SAFETY: `ptr`/`layout` match a live allocation from `alloc` (caller
221        // guarantee); non-ZST so it was really allocated.
222        unsafe { alloc::dealloc(ptr.as_ptr() as *mut u8, layout) }
223    }
224
225    /// Allocate an uninitialized heap buffer of `cap` (`> 0`) elements. For a
226    /// zero-sized `T` returns a dangling-but-aligned pointer (no allocation);
227    /// element reads/writes on a ZST touch no memory.
228    fn alloc(cap: usize) -> NonNull<T> {
229        debug_assert!(cap > 0);
230        let layout = Self::layout(cap);
231        if layout.size() == 0 {
232            // ZST `T`: no real allocation needed.
233            return NonNull::dangling();
234        }
235        // SAFETY: `layout` has non-zero size (checked above).
236        let ptr = unsafe { alloc::alloc(layout) } as *mut T;
237        match NonNull::new(ptr) {
238            Some(p) => p,
239            None => alloc::handle_alloc_error(layout),
240        }
241    }
242
243    /// Ensure capacity for at least one more element, spilling inline -> heap or
244    /// growing the heap allocation as needed. Never exceeds `MAX` (the caller —
245    /// `push` — asserts room before calling).
246    fn grow(&mut self) {
247        let old_cap = self.capacity();
248        debug_assert!(old_cap < MAX, "TinyVec grown past MAX");
249        // Growth schedule: jump off inline capacity, then double, clamped to MAX.
250        let new_cap = (old_cap.max(1) * 2).clamp(INLINE + 1, MAX);
251        let len = self.len();
252
253        let new_heap = Self::alloc(new_cap);
254        // Move existing elements into the new allocation.
255        // SAFETY: source holds `len` initialized elements; dest has room for
256        // `new_cap >= len`. Regions don't overlap (fresh allocation).
257        unsafe {
258            ptr::copy_nonoverlapping(self.as_ptr(), new_heap.as_ptr(), len);
259        }
260        if self.is_spilled() {
261            // Free the old heap buffer (elements already moved out).
262            // SAFETY: old buffer was allocated by `alloc` with `old_cap`.
263            unsafe {
264                let old = self.data.heap;
265                Self::dealloc(old, old_cap);
266            }
267        }
268        // else: inline storage needs no deallocation.
269        self.data = Data { heap: new_heap };
270        self.cap = new_cap as u8;
271    }
272
273    /// Appends `value`. Panics if `len == MAX`.
274    #[inline]
275    pub fn push(&mut self, value: T) {
276        let len = self.len();
277        if len == self.capacity() {
278            // At capacity: either grow (still below MAX) or the container is
279            // saturated. A hard assert (not debug-only) keeps the `NonZeroU8`
280            // length invariant sound in release — `AutoMap` converts List->Map
281            // before this fires; `TaskStorage` relies on the panic.
282            assert!(
283                len < MAX,
284                "TinyVec capacity overflow: already at MAX = {MAX}"
285            );
286            self.grow();
287        }
288        // SAFETY: `len < capacity` now; slot `len` is uninitialized.
289        unsafe {
290            self.as_mut_ptr().add(len).write(value);
291        }
292        self.set_len(len + 1);
293    }
294
295    /// Swap-remove the element at `index` (order not preserved, matching the
296    /// unordered `List` backing an `AutoSet`/`AutoMap`).
297    #[inline]
298    pub fn swap_remove(&mut self, index: usize) -> T {
299        let len = self.len();
300        assert!(index < len, "index out of bounds: {index} >= {len}");
301        let ptr = self.as_mut_ptr();
302        // SAFETY: element `index` is initialized; `len-1` is the last valid idx.
303        unsafe {
304            let out = ptr.add(index).read();
305            if index != len - 1 {
306                let last = ptr.add(len - 1).read();
307                ptr.add(index).write(last);
308            }
309            self.set_len(len - 1);
310            out
311        }
312    }
313
314    #[inline]
315    pub fn clear(&mut self) {
316        let len = self.len();
317        // SAFETY: first `len` elements are initialized; drop them and reset.
318        unsafe {
319            drop_in_place(slice_from_raw_parts_mut(self.as_mut_ptr(), len));
320        }
321        self.set_len(0);
322    }
323
324    /// Drop the heap allocation if spilled (used by `Drop` and when converting
325    /// back to inline). Does **not** drop elements — caller handles those.
326    #[inline]
327    unsafe fn dealloc_if_spilled(&mut self) {
328        if self.is_spilled() {
329            let cap = self.capacity();
330            // SAFETY: heap buffer was allocated by `alloc` with `cap` elements.
331            unsafe {
332                Self::dealloc(self.data.heap, cap);
333            }
334        }
335    }
336
337    /// Shrink a spilled buffer back to inline storage when it fits, or to a
338    /// tighter heap allocation. No-op when already inline.
339    pub fn shrink_to_fit(&mut self) {
340        if !self.is_spilled() {
341            return;
342        }
343        let len = self.len();
344        if len <= INLINE {
345            // Move elements back inline.
346            let mut inline: [MaybeUninit<T>; INLINE] = [const { MaybeUninit::uninit() }; INLINE];
347            // SAFETY: heap holds `len <= INLINE` initialized elements; copy them
348            // into the inline array, then free the heap buffer.
349            unsafe {
350                ptr::copy_nonoverlapping(
351                    self.data.heap.as_ptr(),
352                    inline.as_mut_ptr().cast::<T>(),
353                    len,
354                );
355                let old = self.data.heap;
356                let old_cap = self.capacity();
357                self.data = Data {
358                    inline: ManuallyDrop::new(inline),
359                };
360                self.cap = INLINE as u8;
361                Self::dealloc(old, old_cap);
362            }
363        } else if self.capacity() > len {
364            // Reallocate the heap buffer to exactly `len`.
365            let new_heap = Self::alloc(len);
366            // SAFETY: move `len` elements to the tighter buffer, free the old.
367            unsafe {
368                ptr::copy_nonoverlapping(self.data.heap.as_ptr(), new_heap.as_ptr(), len);
369                let old = self.data.heap;
370                let old_cap = self.capacity();
371                Self::dealloc(old, old_cap);
372                self.data = Data { heap: new_heap };
373                self.cap = len as u8;
374            }
375        }
376    }
377
378    /// Remove all elements and yield them by value, leaving `self` empty.
379    #[inline]
380    pub fn drain(&mut self) -> Drain<'_, T, INLINE, MAX> {
381        let end = self.len();
382        // Logically empty now; `Drain` owns the elements and drops any it does
383        // not yield (panic safety).
384        self.set_len(0);
385        Drain {
386            vec: self,
387            idx: 0,
388            end,
389        }
390    }
391
392    /// Reserve room for at least `additional` more elements, spilling to (or
393    /// growing) the heap as needed. Clamped to `MAX`; panics via `push`/`grow`
394    /// only when actually filled past `MAX`, not here.
395    pub fn reserve(&mut self, additional: usize) {
396        let needed = self.len() + additional;
397        if needed <= self.capacity() {
398            return;
399        }
400        // Grow straight to the target (clamped to MAX) instead of doubling, so
401        // an `extend_exact` of a known size allocates exactly once.
402        let target = needed.min(MAX);
403        if target <= INLINE {
404            return;
405        }
406        let len = self.len();
407        let new_heap = Self::alloc(target);
408        // SAFETY: source holds `len` initialized elements; dest has room for
409        // `target >= needed >= len`. Fresh allocation, so regions don't overlap.
410        unsafe {
411            ptr::copy_nonoverlapping(self.as_ptr(), new_heap.as_ptr(), len);
412        }
413        // SAFETY: free the old buffer if it was heap-allocated (elements moved).
414        unsafe {
415            self.dealloc_if_spilled();
416        }
417        self.data = Data { heap: new_heap };
418        self.cap = target as u8;
419    }
420}
421
422impl<T, const INLINE: usize, const MAX: usize> TinyVec<T, INLINE, MAX> {
423    /// Extend from an exact-sized iterator, reserving exactly once up front
424    /// (avoiding the `size_hint().0` lower-bound dance in the generic `Extend`).
425    pub fn extend_exact<It>(&mut self, iter: It)
426    where
427        It: IntoIterator<Item = T>,
428        It::IntoIter: ExactSizeIterator,
429    {
430        let iter = iter.into_iter();
431        self.reserve(iter.len());
432        for item in iter {
433            self.push(item);
434        }
435    }
436}
437
438impl<T, const INLINE: usize, const MAX: usize> Default for TinyVec<T, INLINE, MAX> {
439    #[inline]
440    fn default() -> Self {
441        Self::new()
442    }
443}
444
445impl<T, const INLINE: usize, const MAX: usize> Drop for TinyVec<T, INLINE, MAX> {
446    #[inline]
447    fn drop(&mut self) {
448        let len = self.len();
449        // SAFETY: first `len` elements are initialized; drop them, then free
450        // any heap allocation.
451        unsafe {
452            drop_in_place(slice_from_raw_parts_mut(self.as_mut_ptr(), len));
453            self.dealloc_if_spilled();
454        }
455    }
456}
457
458impl<T: Clone, const INLINE: usize, const MAX: usize> Clone for TinyVec<T, INLINE, MAX> {
459    fn clone(&self) -> Self {
460        let mut out = Self::with_capacity(self.len());
461        for v in self.iter() {
462            out.push(v.clone());
463        }
464        out
465    }
466}
467
468impl<T, const INLINE: usize, const MAX: usize> ShrinkToFit for TinyVec<T, INLINE, MAX> {
469    #[inline]
470    fn shrink_to_fit(&mut self) {
471        Self::shrink_to_fit(self);
472    }
473}
474
475impl<T: Debug, const INLINE: usize, const MAX: usize> Debug for TinyVec<T, INLINE, MAX> {
476    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
477        f.debug_list().entries(self.iter()).finish()
478    }
479}
480
481impl<T, const INLINE: usize, const MAX: usize> Deref for TinyVec<T, INLINE, MAX> {
482    type Target = [T];
483    #[inline]
484    fn deref(&self) -> &[T] {
485        self.as_slice()
486    }
487}
488
489impl<T, const INLINE: usize, const MAX: usize> DerefMut for TinyVec<T, INLINE, MAX> {
490    #[inline]
491    fn deref_mut(&mut self) -> &mut [T] {
492        self.as_mut_slice()
493    }
494}
495
496impl<'a, T, const INLINE: usize, const MAX: usize> IntoIterator for &'a TinyVec<T, INLINE, MAX> {
497    type Item = &'a T;
498    type IntoIter = Iter<'a, T>;
499    #[inline]
500    fn into_iter(self) -> Self::IntoIter {
501        self.iter()
502    }
503}
504
505impl<'a, T, const INLINE: usize, const MAX: usize> IntoIterator
506    for &'a mut TinyVec<T, INLINE, MAX>
507{
508    type Item = &'a mut T;
509    type IntoIter = IterMut<'a, T>;
510    #[inline]
511    fn into_iter(self) -> Self::IntoIter {
512        self.iter_mut()
513    }
514}
515
516/// By-value iterator returned by [`TinyVec::into_iter`].
517pub struct IntoIter<T, const INLINE: usize, const MAX: usize> {
518    vec: TinyVec<T, INLINE, MAX>,
519    idx: usize,
520    end: usize,
521}
522
523impl<T, const INLINE: usize, const MAX: usize> Iterator for IntoIter<T, INLINE, MAX> {
524    type Item = T;
525    #[inline]
526    fn next(&mut self) -> Option<T> {
527        if self.idx == self.end {
528            return None;
529        }
530        // SAFETY: element `idx` is still initialized and not yet yielded.
531        let v = unsafe { self.vec.as_ptr().add(self.idx).read() };
532        self.idx += 1;
533        Some(v)
534    }
535
536    #[inline]
537    fn size_hint(&self) -> (usize, Option<usize>) {
538        let rem = self.end - self.idx;
539        (rem, Some(rem))
540    }
541}
542
543impl<T, const INLINE: usize, const MAX: usize> ExactSizeIterator for IntoIter<T, INLINE, MAX> {}
544
545impl<T, const INLINE: usize, const MAX: usize> Drop for IntoIter<T, INLINE, MAX> {
546    fn drop(&mut self) {
547        // Drop not-yet-yielded elements; the inner `vec` (len already 0) then
548        // frees any heap allocation without double-dropping.
549        // SAFETY: elements `[idx, end)` are initialized and unyielded.
550        unsafe {
551            let base = self.vec.as_mut_ptr();
552            drop_in_place(slice_from_raw_parts_mut(
553                base.add(self.idx),
554                self.end - self.idx,
555            ));
556        }
557    }
558}
559
560impl<T, const INLINE: usize, const MAX: usize> IntoIterator for TinyVec<T, INLINE, MAX> {
561    type Item = T;
562    type IntoIter = IntoIter<T, INLINE, MAX>;
563    #[inline]
564    fn into_iter(mut self) -> Self::IntoIter {
565        let end = self.len();
566        // Prevent `TinyVec::drop` from dropping elements; `IntoIter` owns them
567        // now. The heap buffer (if any) is freed by `IntoIter`'s inner `vec`.
568        self.set_len(0);
569        IntoIter {
570            vec: self,
571            idx: 0,
572            end,
573        }
574    }
575}
576
577impl<T, const INLINE: usize, const MAX: usize> FromIterator<T> for TinyVec<T, INLINE, MAX> {
578    fn from_iter<It: IntoIterator<Item = T>>(iter: It) -> Self {
579        let iter = iter.into_iter();
580        let (lower, _) = iter.size_hint();
581        let mut out = Self::with_capacity(lower);
582        for v in iter {
583            out.push(v);
584        }
585        out
586    }
587}
588
589impl<T, const INLINE: usize, const MAX: usize> Extend<T> for TinyVec<T, INLINE, MAX> {
590    #[inline]
591    fn extend<It: IntoIterator<Item = T>>(&mut self, iter: It) {
592        for v in iter {
593            self.push(v);
594        }
595    }
596}
597
598/// Draining iterator returned by [`TinyVec::drain`].
599pub struct Drain<'a, T, const INLINE: usize, const MAX: usize> {
600    vec: &'a mut TinyVec<T, INLINE, MAX>,
601    idx: usize,
602    end: usize,
603}
604
605impl<T, const INLINE: usize, const MAX: usize> Iterator for Drain<'_, T, INLINE, MAX> {
606    type Item = T;
607    #[inline]
608    fn next(&mut self) -> Option<T> {
609        if self.idx == self.end {
610            return None;
611        }
612        // SAFETY: element `idx` is initialized and unyielded (vec len is 0).
613        let v = unsafe { self.vec.as_ptr().add(self.idx).read() };
614        self.idx += 1;
615        Some(v)
616    }
617
618    #[inline]
619    fn size_hint(&self) -> (usize, Option<usize>) {
620        let rem = self.end - self.idx;
621        (rem, Some(rem))
622    }
623}
624
625impl<T, const INLINE: usize, const MAX: usize> ExactSizeIterator for Drain<'_, T, INLINE, MAX> {}
626
627impl<T, const INLINE: usize, const MAX: usize> Drop for Drain<'_, T, INLINE, MAX> {
628    fn drop(&mut self) {
629        // SAFETY: elements `[idx, end)` are initialized and unyielded.
630        unsafe {
631            let base = self.vec.as_mut_ptr();
632            drop_in_place(slice_from_raw_parts_mut(
633                base.add(self.idx),
634                self.end - self.idx,
635            ));
636        }
637    }
638}
639
640#[cfg(test)]
641mod tests {
642    use std::{cell::Cell, mem::size_of, num::NonZeroU32, rc::Rc};
643
644    use super::*;
645    use crate::MAX_USEFUL_LINEAR_SCAN;
646
647    /// Enum mirroring `AutoMap`'s layout, to assert the `NonZeroU8` niche folds
648    /// the discriminant in (enum size == tiny-vec size, no extra tag word).
649    #[allow(dead_code)]
650    enum MapLike<T, const INLINE: usize, const MAX: usize = MAX_USEFUL_LINEAR_SCAN> {
651        List(TinyVec<T, INLINE, MAX>),
652        Map(Box<u32>),
653    }
654
655    #[test]
656    #[cfg(target_pointer_width = "64")]
657    fn niche_and_sizes() {
658        type Tid = NonZeroU32;
659        // The whole point: enum is no bigger than the List payload.
660        assert_eq!(size_of::<TinyVec<(Tid, ()), 3>>(), 24);
661        assert_eq!(size_of::<MapLike<(Tid, ()), 3>>(), 24, "niche not folded");
662        assert_eq!(size_of::<MapLike<(Tid, ()), 0>>(), 16);
663        assert_eq!(size_of::<MapLike<(Tid, ()), 6>>(), 32);
664
665        // The `MAX` const param is purely compile-time: shrinking it does not
666        // change the layout (still a 2-byte header + union). This pins the
667        // property that `TaskStorage`'s `TinyVec<_, 0, N>` stays 16 B for any N.
668        assert_eq!(size_of::<TinyVec<u64, 0, 25>>(), 16);
669        assert_eq!(size_of::<TinyVec<u64, 0, 254>>(), 16); // 254 = the largest valid MAX
670        assert_eq!(
671            size_of::<MapLike<(Tid, ()), 0, 8>>(),
672            16,
673            "MAX must not affect layout"
674        );
675    }
676
677    #[test]
678    fn push_spill_and_back() {
679        let mut v: TinyVec<u32, 3> = TinyVec::new();
680        assert_eq!(v.capacity(), 3);
681        // Fill inline.
682        for i in 0..3 {
683            v.push(i);
684        }
685        assert_eq!(v.capacity(), 3);
686        assert_eq!(v.as_slice(), &[0, 1, 2]);
687        // Spill to heap.
688        for i in 3..20 {
689            v.push(i);
690        }
691        assert_eq!(v.len(), 20);
692        assert!(v.capacity() > 3 && v.capacity() <= MAX_USEFUL_LINEAR_SCAN);
693        let got: Vec<u32> = v.iter().copied().collect();
694        assert_eq!(got, (0..20).collect::<Vec<_>>());
695        // Shrink back below inline threshold.
696        while v.len() > 2 {
697            v.swap_remove(v.len() - 1);
698        }
699        v.shrink_to_fit();
700        assert_eq!(v.capacity(), 3, "should return to inline storage");
701        assert_eq!(v.len(), 2);
702    }
703
704    #[test]
705    fn swap_remove_semantics() {
706        let mut v: TinyVec<u32, 4> = TinyVec::new();
707        v.extend([10, 20, 30, 40]);
708        assert_eq!(v.swap_remove(1), 20); // 40 moves into slot 1
709        let mut got: Vec<u32> = v.iter().copied().collect();
710        got.sort();
711        assert_eq!(got, vec![10, 30, 40]);
712    }
713
714    #[test]
715    fn drains_and_reuses() {
716        let mut v: TinyVec<u32, 2> = TinyVec::new();
717        v.extend([1, 2, 3, 4, 5]); // spilled
718        let drained: Vec<u32> = v.drain().collect();
719        assert_eq!(drained, vec![1, 2, 3, 4, 5]);
720        assert!(v.is_empty());
721        // Reuse after drain (storage retained).
722        v.push(99);
723        assert_eq!(v.as_slice(), &[99]);
724    }
725
726    // --- drop accounting: every element dropped exactly once, no leaks ---
727
728    struct DropTok(Rc<Cell<i32>>);
729    impl DropTok {
730        fn new(c: &Rc<Cell<i32>>) -> Self {
731            c.set(c.get() + 1);
732            Self(c.clone())
733        }
734    }
735    impl Drop for DropTok {
736        fn drop(&mut self) {
737            self.0.set(self.0.get() - 1);
738        }
739    }
740
741    fn assert_balanced(f: impl FnOnce(&Rc<Cell<i32>>)) {
742        let live = Rc::new(Cell::new(0));
743        f(&live);
744        assert_eq!(live.get(), 0, "unbalanced drops (leak or double free)");
745    }
746
747    #[test]
748    fn drop_paths() {
749        // plain drop, inline
750        assert_balanced(|c| {
751            let mut v: TinyVec<DropTok, 4> = TinyVec::new();
752            v.push(DropTok::new(c));
753            v.push(DropTok::new(c));
754        });
755        // plain drop, spilled
756        assert_balanced(|c| {
757            let mut v: TinyVec<DropTok, 2> = TinyVec::new();
758            for _ in 0..10 {
759                v.push(DropTok::new(c));
760            }
761        });
762        // clear
763        assert_balanced(|c| {
764            let mut v: TinyVec<DropTok, 2> = TinyVec::new();
765            for _ in 0..6 {
766                v.push(DropTok::new(c));
767            }
768            v.clear();
769            assert!(v.is_empty());
770        });
771        // partial into_iter then drop (spilled)
772        assert_balanced(|c| {
773            let mut v: TinyVec<DropTok, 2> = TinyVec::new();
774            for _ in 0..6 {
775                v.push(DropTok::new(c));
776            }
777            let mut it = v.into_iter();
778            drop(it.next());
779            drop(it.next());
780            // remaining 4 dropped when `it` drops
781        });
782        // partial drain then drop
783        assert_balanced(|c| {
784            let mut v: TinyVec<DropTok, 4> = TinyVec::new();
785            for _ in 0..3 {
786                v.push(DropTok::new(c));
787            }
788            let mut d = v.drain();
789            drop(d.next());
790            drop(d); // remaining 2
791        });
792        // swap_remove returns and drops
793        assert_balanced(|c| {
794            let mut v: TinyVec<DropTok, 4> = TinyVec::new();
795            for _ in 0..4 {
796                v.push(DropTok::new(c));
797            }
798            let x = v.swap_remove(0);
799            drop(x);
800            // 3 remain, dropped with v
801        });
802        // shrink_to_fit heap->inline preserves elements
803        assert_balanced(|c| {
804            let mut v: TinyVec<DropTok, 3> = TinyVec::new();
805            for _ in 0..8 {
806                v.push(DropTok::new(c));
807            }
808            while v.len() > 2 {
809                drop(v.swap_remove(0));
810            }
811            v.shrink_to_fit();
812            assert_eq!(v.capacity(), 3);
813            assert_eq!(v.len(), 2);
814        });
815    }
816
817    #[test]
818    fn clone_matches() {
819        let mut v: TinyVec<u32, 2> = TinyVec::new();
820        v.extend([1, 2, 3, 4]); // spilled
821        let c = v.clone();
822        assert_eq!(
823            v.iter().copied().collect::<Vec<_>>(),
824            c.iter().copied().collect::<Vec<_>>()
825        );
826    }
827
828    #[test]
829    fn zst_elements() {
830        // ZST must never touch the allocator, even when "spilled".
831        let mut v: TinyVec<(), 1> = TinyVec::new();
832        for _ in 0..10 {
833            v.push(());
834        }
835        assert_eq!(v.len(), 10);
836        assert_eq!(v.iter().count(), 10);
837        v.clear();
838        assert!(v.is_empty());
839    }
840
841    // ---- ported from turbo-tasks TinyVec: MAX cap, extend_exact, retain_mut ----
842
843    /// `INLINE = 0` is the pure-heap `TinyVec` shape used by `TaskStorage`.
844    #[test]
845    fn heap_only_push_grows() {
846        let mut v: TinyVec<u32, 0> = TinyVec::new();
847        assert_eq!(v.capacity(), 0);
848        for i in 0..20 {
849            v.push(i);
850        }
851        assert_eq!(v.len(), 20);
852        assert_eq!(
853            v.iter().copied().collect::<Vec<_>>(),
854            (0..20).collect::<Vec<_>>()
855        );
856    }
857
858    #[test]
859    fn extend_exact_reserves_once() {
860        let mut v: TinyVec<u32, 0> = TinyVec::new();
861        v.extend_exact(0..10);
862        assert_eq!(v.len(), 10);
863        // Reserved exactly 10 (not a doubling artifact), since 10 <= MAX.
864        assert_eq!(v.capacity(), 10);
865        v.extend_exact(10..15);
866        assert_eq!(
867            v.iter().copied().collect::<Vec<_>>(),
868            (0..15).collect::<Vec<_>>()
869        );
870    }
871
872    /// A tight `MAX` caps growth exactly and panics past it.
873    #[test]
874    fn tight_max_caps_growth_exactly() {
875        let mut v: TinyVec<u32, 0, 5> = TinyVec::new();
876        for i in 0..5 {
877            v.push(i);
878        }
879        assert_eq!(v.len(), 5);
880        assert_eq!(v.capacity(), 5, "growth must cap at MAX, not overshoot");
881    }
882
883    #[test]
884    #[should_panic(expected = "TinyVec capacity overflow")]
885    fn tight_max_panics_at_limit() {
886        let mut v: TinyVec<u32, 0, 3> = TinyVec::new();
887        for i in 0..3 {
888            v.push(i);
889        }
890        v.push(3); // 4th push exceeds MAX = 3
891    }
892
893    /// Growth doubles from the inline threshold, then caps at MAX. For
894    /// `INLINE = 0`: 0 -> 2 -> 4 -> 8 -> 10 (last step clamps to MAX rather than
895    /// overshooting to 16).
896    #[test]
897    fn tight_max_growth_schedule() {
898        let mut v: TinyVec<u32, 0, 10> = TinyVec::new();
899        let mut last = 0;
900        let mut changes = Vec::new();
901        for i in 0..10 {
902            v.push(i);
903            if v.capacity() != last {
904                changes.push(v.capacity());
905                last = v.capacity();
906            }
907        }
908        assert_eq!(changes, vec![2, 4, 8, 10]);
909    }
910}