Skip to main content

turbopack_ecmascript/analyzer/
bump_vec.rs

1//! A minimal growable vector ([`BumpVec`]) backed by a bump allocator.
2//!
3//! `JsValue` analysis allocates all of its nodes into a per-thread [`Bump`](bumpalo::Bump) that is
4//! freed in one shot when analysis finishes. For the list children that grow or are rebuilt after
5//! construction, this module provides [`BumpVec`]: a `Send`/`Sync` growable vector that stores a
6//! raw pointer into the arena, a capacity, and a length.
7
8use std::{
9    alloc::Layout,
10    fmt,
11    hash::{Hash, Hasher},
12    marker::PhantomData,
13    mem::ManuallyDrop,
14    ops::{Deref, DerefMut},
15    ptr::{self, NonNull},
16};
17
18use allocator_api2::alloc::Allocator;
19use bumpalo::{Bump, boxed::Box as BumpBox};
20
21/// A minimal growable vector for the list children of a `JsValue` that grow or are rebuilt after
22/// construction (e.g. `Array.items`, `Object.parts`, `Alternatives.values`, `Add` operands, and the
23/// `Call`/`MemberCall` lists).
24///
25/// It owns its `T` elements like `Vec<T>` but stores them through a raw pointer into the arena,
26/// alongside a capacity and a length. The raw pointer makes it neither `Send` nor `Sync` on its
27/// own, so those are declared via `unsafe impl` (sound because it owns the `T`s exactly like a
28/// `Vec<T>`). The growth methods take the `&'a Bump` to allocate from.
29pub struct BumpVec<'a, T> {
30    /// Points at `cap` allocated (possibly uninitialized) `T` slots; dangling when `cap == 0`.
31    ptr: NonNull<T>,
32    cap: usize,
33    len: usize,
34    /// Binds the arena lifetime and signals ownership/variance over `T` to the compiler.
35    _marker: PhantomData<&'a ()>,
36}
37
38// SAFETY: `BumpVec` owns its `T` elements just like `Vec<T>`; the raw pointer is merely an owning
39// handle into the arena, so it is `Send`/`Sync` exactly when `T` is.
40unsafe impl<T: Send> Send for BumpVec<'_, T> {}
41unsafe impl<T: Sync> Sync for BumpVec<'_, T> {}
42
43impl<T> Default for BumpVec<'_, T> {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl<'a, T> From<BumpBox<'a, [T]>> for BumpVec<'a, T> {
50    fn from(boxed: BumpBox<'a, [T]>) -> Self {
51        let len = boxed.len();
52        let ptr = BumpBox::into_raw(boxed) as *mut T;
53        Self {
54            ptr: NonNull::new(ptr).unwrap_or(NonNull::dangling()),
55            cap: len,
56            len,
57            _marker: PhantomData,
58        }
59    }
60}
61
62impl<'a, T> BumpVec<'a, T> {
63    pub fn new() -> Self {
64        Self {
65            ptr: NonNull::dangling(),
66            cap: 0,
67            len: 0,
68            _marker: PhantomData,
69        }
70    }
71
72    /// Allocate room for `cap` uninitialized `T` slots from the arena, or a dangling pointer when
73    /// `cap == 0` (no allocation needed).
74    fn alloc_uninitialized(bump: &'a Bump, cap: usize) -> NonNull<T> {
75        if cap == 0 {
76            return NonNull::dangling();
77        }
78        let layout = Layout::array::<T>(cap).expect("capacity overflow");
79        bump.allocate(layout)
80            .expect("bump allocation failed")
81            .cast::<T>()
82    }
83
84    pub fn with_capacity_in(bump: &'a Bump, capacity: usize) -> Self {
85        Self {
86            ptr: Self::alloc_uninitialized(bump, capacity),
87            cap: capacity,
88            len: 0,
89            _marker: PhantomData,
90        }
91    }
92
93    /// Collect `iter` into a growable [`BumpVec`].
94    pub fn from_iter_in(bump: &'a Bump, iter: impl IntoIterator<Item = T>) -> Self {
95        let iter = iter.into_iter();
96        let mut vec =
97            Self::with_capacity_in(bump, iter.size_hint().1.unwrap_or(iter.size_hint().0));
98        vec.extend(bump, iter);
99        vec
100    }
101
102    /// Freeze into an arena-allocated boxed slice, handing the existing allocation to the box.
103    pub fn into_boxed_slice(self) -> BumpBox<'a, [T]> {
104        let me = ManuallyDrop::new(self);
105        let slice = ptr::slice_from_raw_parts_mut(me.ptr.as_ptr(), me.len);
106        // SAFETY: `ptr`/`len` describe an initialized arena slice we exclusively own, exactly the
107        // representation `BumpBox<[T]>` expects.
108        unsafe { BumpBox::from_raw(slice) }
109    }
110
111    /// Reallocate the buffer to `new_cap` elements (`new_cap >= len`), moving the live prefix into
112    /// the new arena allocation. The old buffer is abandoned (the arena frees it in bulk); when it
113    /// is the arena's most recent allocation, [`Allocator::grow`] extends it in place.
114    unsafe fn realloc_to(&mut self, bump: &'a Bump, new_cap: usize) {
115        debug_assert!(new_cap >= self.len);
116        let new_layout = Layout::array::<T>(new_cap).expect("capacity overflow");
117        let new_ptr = if self.cap == 0 {
118            // Nothing allocated yet, so there is no prior block to grow from.
119            bump.allocate(new_layout)
120        } else {
121            let old_layout = Layout::array::<T>(self.cap).expect("capacity overflow");
122            // SAFETY: `ptr`/`old_layout` describe the current arena buffer and `new_layout` is
123            // strictly larger, so growing it (and moving the bytes) is sound.
124
125            // re: ptr must denote a block of memory currently allocated via this allocator.
126            // While, this is not true, Bumpalo's implementation of `grow()` does not rely on
127            // this. Instead, a new pointer in the `bump` that `grow()` was called on is
128            // returned even if that was not the `bump` this vector was originally allocated
129            // in. This is relevant to us as we wrap Bump in ThreadLocal<>, therefore, different
130            // threads have different bumps.
131
132            // See the `grow_across_separate_bumps()` test.
133
134            unsafe { bump.grow(self.ptr.cast::<u8>(), old_layout, new_layout) }
135        }
136        .expect("bump allocation failed")
137        .cast::<T>();
138        self.ptr = new_ptr;
139        self.cap = new_cap;
140    }
141
142    pub fn push(&mut self, bump: &'a Bump, value: T) {
143        if self.len == self.cap {
144            unsafe { self.realloc_to(bump, if self.cap == 0 { 4 } else { self.cap * 2 }) };
145        }
146        // SAFETY: slot `len < cap` is allocated and uninitialized; write the value into it.
147        unsafe { self.ptr.as_ptr().add(self.len).write(value) };
148        self.len += 1;
149    }
150
151    /// Ensure there is room for at least `additional` more elements, growing the arena buffer
152    /// (in one allocation) if necessary.
153    pub fn reserve(&mut self, bump: &'a Bump, additional: usize) {
154        let required = self.len + additional;
155        if required > self.cap {
156            unsafe { self.realloc_to(bump, required.max(self.cap * 2)) };
157        }
158    }
159
160    pub fn extend(&mut self, bump: &'a Bump, iter: impl IntoIterator<Item = T>) {
161        let iter = iter.into_iter();
162        // Reserve the lower bound returned by `size_hint()`;
163        // `push` still grows further if the hint is an underestimate.
164        self.reserve(bump, iter.size_hint().1.unwrap_or(iter.size_hint().0));
165        for value in iter {
166            self.push(bump, value);
167        }
168    }
169
170    /// Append a clone of every element in `other`, reserving the needed capacity up front.
171    pub fn extend_from_slice(&mut self, bump: &'a Bump, other: &[T])
172    where
173        T: Clone,
174    {
175        self.reserve(bump, other.len());
176        for value in other {
177            // SAFETY: the reservation above guarantees slot `len < cap` is allocated; incrementing
178            // `len` per write keeps the vec consistent if `T::clone` panics mid-loop.
179            unsafe { self.ptr.as_ptr().add(self.len).write(value.clone()) };
180            self.len += 1;
181        }
182    }
183
184    pub fn pop(&mut self) -> Option<T> {
185        if self.len == 0 {
186            return None;
187        }
188        self.len -= 1;
189        // SAFETY: index `len` was initialized; move it out and logically shrink.
190        Some(unsafe { self.ptr.as_ptr().add(self.len).read() })
191    }
192
193    /// Remove the element at `index` and return it, moving the last element into its slot. This
194    /// does not preserve element order but is O(1). Panics if `index` is out of bounds.
195    pub fn swap_remove(&mut self, index: usize) -> T {
196        assert!(index < self.len, "swap_remove index out of bounds");
197        // Move the last item into the slot and return the displaced one.
198        let last = self.pop().unwrap();
199        if index < self.len {
200            std::mem::replace(&mut self[index], last)
201        } else {
202            last
203        }
204    }
205
206    /// Split the vec in two at `at`: `self` retains the prefix `[0, at)` and the returned vec owns
207    /// the suffix `[at, len)`, moved into a fresh arena allocation.
208    pub fn split_off(&mut self, bump: &'a Bump, at: usize) -> Self {
209        assert!(at <= self.len, "split_off index out of bounds");
210        let tail_len = self.len - at;
211        let mut tail = Self::with_capacity_in(bump, tail_len);
212        // SAFETY: indices `[at, len)` are initialized and `tail` is a fresh, non-overlapping
213        // allocation; move the suffix into it bytewise.
214        unsafe {
215            ptr::copy_nonoverlapping(self.ptr.as_ptr().add(at), tail.ptr.as_ptr(), tail_len);
216        }
217        tail.len = tail_len;
218        self.len = at;
219        tail
220    }
221}
222
223impl<T> Deref for BumpVec<'_, T> {
224    type Target = [T];
225    fn deref(&self) -> &[T] {
226        // SAFETY: `0..len` is initialized and `ptr` is valid for that range (dangling-but-aligned
227        // when `len == 0`, which `from_raw_parts` accepts).
228        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
229    }
230}
231
232impl<T> DerefMut for BumpVec<'_, T> {
233    fn deref_mut(&mut self) -> &mut [T] {
234        // SAFETY: see `deref`.
235        unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
236    }
237}
238
239impl<T> Drop for BumpVec<'_, T> {
240    fn drop(&mut self) {
241        // SAFETY: `0..len` is initialized; drop each element (the arena only frees memory).
242        unsafe { ptr::drop_in_place(&mut **self) }
243    }
244}
245
246impl<'a, T> IntoIterator for BumpVec<'a, T> {
247    type Item = T;
248    type IntoIter = IntoIter<'a, T>;
249    fn into_iter(self) -> IntoIter<'a, T> {
250        // Disable `Drop` (it would also drop the elements) and take over the buffer.
251        let me = ManuallyDrop::new(self);
252        IntoIter {
253            ptr: me.ptr,
254            len: me.len,
255            idx: 0,
256            _marker: PhantomData,
257        }
258    }
259}
260
261impl<'b, T> IntoIterator for &'b BumpVec<'_, T> {
262    type Item = &'b T;
263    type IntoIter = std::slice::Iter<'b, T>;
264    fn into_iter(self) -> Self::IntoIter {
265        self.iter()
266    }
267}
268
269impl<'b, T> IntoIterator for &'b mut BumpVec<'_, T> {
270    type Item = &'b mut T;
271    type IntoIter = std::slice::IterMut<'b, T>;
272    fn into_iter(self) -> Self::IntoIter {
273        self.iter_mut()
274    }
275}
276
277impl<T: PartialEq> PartialEq for BumpVec<'_, T> {
278    fn eq(&self, other: &Self) -> bool {
279        **self == **other
280    }
281}
282impl<T: Eq> Eq for BumpVec<'_, T> {}
283impl<T: Hash> Hash for BumpVec<'_, T> {
284    fn hash<H: Hasher>(&self, state: &mut H) {
285        (**self).hash(state)
286    }
287}
288impl<T: fmt::Debug> fmt::Debug for BumpVec<'_, T> {
289    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290        fmt::Debug::fmt(&**self, f)
291    }
292}
293
294/// By-value iterator returned from [`BumpVec::into_iter`]. Owns the arena buffer's elements and
295/// drops any unconsumed remainder on `Drop`.
296pub struct IntoIter<'a, T> {
297    ptr: NonNull<T>,
298    len: usize,
299    idx: usize,
300    _marker: PhantomData<&'a ()>,
301}
302
303// SAFETY: like `BumpVec`, `IntoIter` owns its `T` elements behind a raw pointer.
304unsafe impl<T: Send> Send for IntoIter<'_, T> {}
305unsafe impl<T: Sync> Sync for IntoIter<'_, T> {}
306
307impl<T> Iterator for IntoIter<'_, T> {
308    type Item = T;
309    fn next(&mut self) -> Option<T> {
310        if self.idx == self.len {
311            return None;
312        }
313        // SAFETY: index `idx < len` is initialized and yielded at most once.
314        let value = unsafe { self.ptr.as_ptr().add(self.idx).read() };
315        self.idx += 1;
316        Some(value)
317    }
318
319    fn size_hint(&self) -> (usize, Option<usize>) {
320        let remaining = self.len - self.idx;
321        (remaining, Some(remaining))
322    }
323}
324
325impl<T> DoubleEndedIterator for IntoIter<'_, T> {
326    fn next_back(&mut self) -> Option<T> {
327        if self.idx == self.len {
328            return None;
329        }
330        self.len -= 1;
331        // SAFETY: index `len` (post-decrement) lies in the initialized `[idx, len)` range.
332        Some(unsafe { self.ptr.as_ptr().add(self.len).read() })
333    }
334}
335
336impl<T> ExactSizeIterator for IntoIter<'_, T> {}
337
338impl<T> Drop for IntoIter<'_, T> {
339    fn drop(&mut self) {
340        // SAFETY: indices `[idx, len)` are still initialized; drop them in place exactly once.
341        unsafe {
342            ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
343                self.ptr.as_ptr().add(self.idx),
344                self.len - self.idx,
345            ));
346        }
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use std::{
353        cell::Cell,
354        collections::hash_map::DefaultHasher,
355        hash::{Hash, Hasher},
356        rc::Rc,
357    };
358
359    use bumpalo::Bump;
360
361    use super::BumpVec;
362
363    #[test]
364    fn push_grows_and_indexes() {
365        let bump = Bump::new();
366        let mut v = BumpVec::new();
367        assert!(v.is_empty());
368        // Grows well past the initial capacity, exercising several reallocations.
369        for i in 0..100 {
370            v.push(&bump, i);
371        }
372        assert_eq!(v.len(), 100);
373        assert_eq!(&*v, &(0..100).collect::<Vec<_>>()[..]);
374        assert_eq!(v[42], 42);
375    }
376
377    #[test]
378    fn with_capacity_extend_and_from_iter() {
379        let bump = Bump::new();
380        let mut v = BumpVec::with_capacity_in(&bump, 4);
381        assert!(v.is_empty());
382        v.extend(&bump, [1, 2, 3]);
383        assert_eq!(&*v, &[1, 2, 3][..]);
384
385        let v2 = BumpVec::from_iter_in(&bump, [10, 20, 30, 40]);
386        assert_eq!(&*v2, &[10, 20, 30, 40][..]);
387    }
388
389    #[test]
390    fn extend_from_slice() {
391        let bump = Bump::new();
392        let mut v = BumpVec::from_iter_in(&bump, [1, 2]);
393        // Appends onto existing contents, growing past the current capacity.
394        v.extend_from_slice(&bump, &[3, 4, 5]);
395        assert_eq!(&*v, &[1, 2, 3, 4, 5][..]);
396        // Extending by an empty slice is a no-op.
397        v.extend_from_slice(&bump, &[]);
398        assert_eq!(&*v, &[1, 2, 3, 4, 5][..]);
399        // Works on a freshly-constructed empty vec too.
400        let mut empty = BumpVec::new();
401        empty.extend_from_slice(&bump, &[7, 8, 9]);
402        assert_eq!(&*empty, &[7, 8, 9][..]);
403    }
404
405    /// Confirms the cross-arena `grow()` behavior documented in `realloc_to`: a buffer allocated in
406    /// one `Bump` can be grown by calling `grow()` on a *different* `Bump`. Bumpalo never treats
407    /// the foreign pointer as its most-recent allocation, so it falls back to allocating fresh
408    /// in the target arena and copying the bytes over — the contents survive intact.
409    #[test]
410    fn grow_across_separate_bumps() {
411        let bump1 = Bump::new();
412        let bump2 = Bump::new();
413
414        // Allocate and fill to capacity entirely within `bump1`.
415        let mut v = BumpVec::with_capacity_in(&bump1, 2);
416        v.push(&bump1, 1);
417        v.push(&bump1, 2);
418
419        // The vec is now full, so this push reallocates via `bump2.grow(ptr, ..)` where `ptr` lives
420        // in `bump1`. The prior elements must be moved across to `bump2` unharmed.
421        v.push(&bump2, 3);
422        v.push(&bump2, 4);
423        v.push(&bump2, 5);
424        assert_eq!(&*v, &[1, 2, 3, 4, 5][..]);
425
426        // The buffer now lives in `bump2`; further growth on `bump2` keeps everything consistent.
427        v.extend(&bump2, 6..=10);
428        assert_eq!(&*v, &(1..=10).collect::<Vec<_>>()[..]);
429    }
430
431    /// Same cross-arena grow, but with a drop-tracked element type to prove nothing is dropped or
432    /// duplicated when the buffer is copied from one arena to another.
433    #[test]
434    fn grow_across_separate_bumps_does_not_drop_during_move() {
435        let bump1 = Bump::new();
436        let bump2 = Bump::new();
437        let counter = Rc::new(Cell::new(0));
438
439        let mut v = BumpVec::with_capacity_in(&bump1, 2);
440        v.push(&bump1, DropCounter(counter.clone()));
441        v.push(&bump1, DropCounter(counter.clone()));
442        // Cross-arena reallocation moves (does not drop) the existing elements.
443        v.push(&bump2, DropCounter(counter.clone()));
444        assert_eq!(counter.get(), 0);
445
446        drop(v);
447        assert_eq!(counter.get(), 3);
448    }
449
450    #[test]
451    fn pop() {
452        let bump = Bump::new();
453        let mut v = BumpVec::from_iter_in(&bump, [1, 2, 3]);
454        assert_eq!(v.pop(), Some(3));
455        assert_eq!(v.pop(), Some(2));
456        assert_eq!(v.pop(), Some(1));
457        assert_eq!(v.pop(), None);
458        assert!(v.is_empty());
459    }
460
461    #[test]
462    fn swap_remove() {
463        let bump = Bump::new();
464        let mut v = BumpVec::from_iter_in(&bump, [1, 2, 3, 4, 5]);
465        // Removing a middle element moves the last element into its slot.
466        assert_eq!(v.swap_remove(1), 2);
467        assert_eq!(&*v, &[1, 5, 3, 4][..]);
468        // Removing the last element is just a pop.
469        assert_eq!(v.swap_remove(3), 4);
470        assert_eq!(&*v, &[1, 5, 3][..]);
471        // Removing the first element.
472        assert_eq!(v.swap_remove(0), 1);
473        assert_eq!(&*v, &[3, 5][..]);
474    }
475
476    #[test]
477    #[should_panic(expected = "swap_remove index out of bounds")]
478    fn swap_remove_out_of_bounds() {
479        let bump = Bump::new();
480        let mut v = BumpVec::from_iter_in(&bump, [1, 2, 3]);
481        v.swap_remove(3);
482    }
483
484    #[test]
485    fn swap_remove_does_not_double_free() {
486        let bump = Bump::new();
487        let counter = Rc::new(Cell::new(0));
488        let mut v = BumpVec::new();
489        for _ in 0..3 {
490            v.push(&bump, DropCounter(counter.clone()));
491        }
492        let removed = v.swap_remove(0);
493        drop(removed);
494        assert_eq!(counter.get(), 1);
495        // The two remaining drop exactly once; the removed slot must not be dropped again.
496        drop(v);
497        assert_eq!(counter.get(), 3);
498    }
499
500    #[test]
501    fn split_off_prefix_and_suffix() {
502        let bump = Bump::new();
503        let mut v = BumpVec::from_iter_in(&bump, [1, 2, 3, 4, 5]);
504        let tail = v.split_off(&bump, 2);
505        assert_eq!(&*v, &[1, 2][..]);
506        assert_eq!(&*tail, &[3, 4, 5][..]);
507
508        // Split at `len` yields an empty tail.
509        let mut v = BumpVec::from_iter_in(&bump, [1, 2]);
510        let tail = v.split_off(&bump, 2);
511        assert_eq!(&*v, &[1, 2][..]);
512        assert!(tail.is_empty());
513
514        // Split at `0` empties `self`.
515        let mut v = BumpVec::from_iter_in(&bump, [1, 2]);
516        let tail = v.split_off(&bump, 0);
517        assert!(v.is_empty());
518        assert_eq!(&*tail, &[1, 2][..]);
519    }
520
521    #[test]
522    fn iterates_by_ref_mut_and_value() {
523        let bump = Bump::new();
524        let mut v = BumpVec::from_iter_in(&bump, [1, 2, 3]);
525
526        let sum: i32 = (&v).into_iter().copied().sum();
527        assert_eq!(sum, 6);
528
529        for x in &mut v {
530            *x *= 2;
531        }
532        assert_eq!(&*v, &[2, 4, 6][..]);
533
534        let collected: Vec<i32> = v.into_iter().collect();
535        assert_eq!(collected, vec![2, 4, 6]);
536    }
537
538    #[test]
539    fn into_iter_double_ended() {
540        let bump = Bump::new();
541        let v = BumpVec::from_iter_in(&bump, [1, 2, 3, 4]);
542        let reversed: Vec<i32> = v.into_iter().rev().collect();
543        assert_eq!(reversed, vec![4, 3, 2, 1]);
544
545        // Front and back cursors meet without overlap.
546        let v = BumpVec::from_iter_in(&bump, [1, 2, 3]);
547        let mut it = v.into_iter();
548        assert_eq!(it.next(), Some(1));
549        assert_eq!(it.next_back(), Some(3));
550        assert_eq!(it.next(), Some(2));
551        assert_eq!(it.next_back(), None);
552        assert_eq!(it.next(), None);
553    }
554
555    #[test]
556    fn double_ended_drops_remainder_once() {
557        let bump = Bump::new();
558        let counter = Rc::new(Cell::new(0));
559        let mut v = BumpVec::new();
560        for _ in 0..5 {
561            v.push(&bump, DropCounter(counter.clone()));
562        }
563        let mut it = v.into_iter();
564        drop(it.next()); // front
565        drop(it.next_back()); // back
566        assert_eq!(counter.get(), 2);
567        drop(it); // remaining three drop exactly once
568        assert_eq!(counter.get(), 5);
569    }
570
571    #[test]
572    fn eq_hash_and_debug() {
573        let bump = Bump::new();
574        let a = BumpVec::from_iter_in(&bump, [1, 2, 3]);
575        let b = BumpVec::from_iter_in(&bump, [1, 2, 3]);
576        let c = BumpVec::from_iter_in(&bump, [1, 2]);
577        assert_eq!(a, b);
578        assert_ne!(a, c);
579        assert_eq!(format!("{a:?}"), "[1, 2, 3]");
580
581        let mut ha = DefaultHasher::new();
582        let mut hb = DefaultHasher::new();
583        a.hash(&mut ha);
584        b.hash(&mut hb);
585        assert_eq!(ha.finish(), hb.finish());
586    }
587
588    /// Increments a shared counter when dropped, to prove elements are dropped exactly once (the
589    /// arena reclaims memory but never drops contents, so `BumpVec` must).
590    struct DropCounter(Rc<Cell<usize>>);
591    impl Drop for DropCounter {
592        fn drop(&mut self) {
593            self.0.set(self.0.get() + 1);
594        }
595    }
596
597    #[test]
598    fn drops_each_element_exactly_once() {
599        let bump = Bump::new();
600        let counter = Rc::new(Cell::new(0));
601        {
602            let mut v = BumpVec::new();
603            for _ in 0..10 {
604                v.push(&bump, DropCounter(counter.clone()));
605            }
606            // Reallocations move elements (never drop them), so nothing has dropped yet.
607            assert_eq!(counter.get(), 0);
608        }
609        assert_eq!(counter.get(), 10);
610    }
611
612    #[test]
613    fn into_iter_drops_unconsumed_remainder() {
614        let bump = Bump::new();
615        let counter = Rc::new(Cell::new(0));
616        let mut v = BumpVec::new();
617        for _ in 0..5 {
618            v.push(&bump, DropCounter(counter.clone()));
619        }
620        let mut iter = v.into_iter();
621        drop(iter.next());
622        drop(iter.next());
623        assert_eq!(counter.get(), 2);
624        drop(iter); // the remaining three must drop exactly once
625        assert_eq!(counter.get(), 5);
626    }
627
628    #[test]
629    fn pop_does_not_double_free() {
630        let bump = Bump::new();
631        let counter = Rc::new(Cell::new(0));
632        let mut v = BumpVec::new();
633        for _ in 0..3 {
634            v.push(&bump, DropCounter(counter.clone()));
635        }
636        let popped = v.pop().unwrap();
637        drop(popped);
638        assert_eq!(counter.get(), 1);
639        // Dropping the vec drops the two remaining; the popped slot must not be dropped again.
640        drop(v);
641        assert_eq!(counter.get(), 3);
642    }
643}