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