Skip to main content

mnemosyne_heap/branded_vec/
ops.rs

1use crate::BrandedVec;
2use crate::brand::BrandedCell;
3use melinoe::WritePermit;
4use mnemosyne_core::AllocPolicy;
5use mnemosyne_local::LocalAllocatorSelector;
6use mnemosyne_local::internal::HasSegmentPool;
7
8impl<'brand, 'heap, T, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
9    BrandedVec<'brand, 'heap, T, P, B>
10{
11    /// Pushes an element onto the back of the vector, growing it if necessary.
12    #[inline]
13    pub fn push<Permit>(&mut self, token: &mut Permit, val: T) -> Result<(), T>
14    where
15        for<'token> &'token mut Permit: WritePermit<'brand>,
16    {
17        if core::mem::size_of::<T>() == 0 {
18            self.len = match self.len.checked_add(1) {
19                Some(len) => len,
20                None => return Err(val),
21            };
22            // `push` transfers ownership of `val` into the vector. A ZST has
23            // no storage, so the transfer is recorded purely by the `len`
24            // increment above; `forget` (not drop) realizes it — the value
25            // now logically lives in the vector and is dropped exactly once
26            // by `pop`/`truncate`/`Drop`, which synthesize the element back
27            // via `read` from the dangling sentinel. Dropping `val` here
28            // would run its destructor a second time for the same logical
29            // element. This mirrors `std::vec::Vec`'s ZST push (a zero-byte
30            // `write` is semantically the same forget; `forget` states the
31            // ownership intent directly, without the dangling-pointer write).
32            core::mem::forget(val);
33            return Ok(());
34        }
35
36        if self.len == self.cap {
37            // Capacity policy lives here (initial 4, then amortized doubling);
38            // the alloc/realloc mechanics are the shared `grow_to` SSOT. A failed
39            // grow returns the element to the caller unconsumed.
40            let new_cap = if self.cap == 0 {
41                4
42            } else {
43                match self.cap.checked_mul(2) {
44                    Some(cap) => cap,
45                    None => return Err(val),
46                }
47            };
48            if self.grow_to(token, new_cap).is_err() {
49                return Err(val);
50            }
51        }
52        // SAFETY: the block above guarantees `self.len < self.cap` for non-ZST
53        // `T`, so the slot at offset `self.len` lies within the allocation and is
54        // currently uninitialized. `self.ptr.add(self.len)` stays in bounds of
55        // the live block and is properly aligned for `T`. `write` moves `val`
56        // into that slot without reading or dropping prior contents; the `len`
57        // increment then claims it as initialized.
58        unsafe {
59            self.ptr.as_ptr().add(self.len).write(val);
60        }
61        self.len += 1;
62        Ok(())
63    }
64
65    /// Pops the last element from the vector, returning it or None if empty.
66    #[inline(always)]
67    pub fn pop(&mut self) -> Option<T> {
68        if self.len == 0 {
69            None
70        } else {
71            self.len -= 1;
72            // SAFETY: `self.len` was `> 0` and is decremented above, so the new
73            // `self.len` indexes the last initialized element (for ZST `T`,
74            // `add` is a no-op on the dangling sentinel and `read` reconstructs a
75            // value with no storage). The slot is in bounds and holds an
76            // initialized `T`; `read` moves it out, and the decremented `len`
77            // ensures that slot is never read again (no double-drop).
78            unsafe { Some(self.ptr.as_ptr().add(self.len).read()) }
79        }
80    }
81
82    /// Returns the number of elements in the vector.
83    #[inline(always)]
84    pub fn len(&self) -> usize {
85        self.len
86    }
87
88    /// Returns true if the vector contains no elements.
89    #[inline(always)]
90    pub fn is_empty(&self) -> bool {
91        self.len == 0
92    }
93
94    /// Returns the capacity of the vector.
95    #[inline(always)]
96    pub fn capacity(&self) -> usize {
97        self.cap
98    }
99
100    /// Extracts a slice containing the entire vector.
101    #[inline(always)]
102    pub fn as_slice(&self) -> &[T] {
103        if self.len == 0 {
104            &[]
105        } else {
106            // SAFETY: `self.len > 0` here, so `self.ptr` addresses a live
107            // allocation whose prefix `[0, self.len)` is fully initialized `T`
108            // (for ZST `T`, the dangling sentinel is a valid base for a
109            // zero-sized slice). The pointer is aligned for `T`. `&self` borrows
110            // the vector for the returned slice's lifetime, and `BrandedVec` is
111            // `!Send`/`!Sync`, so no concurrent mutation can occur.
112            unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
113        }
114    }
115
116    /// Extracts a mutable slice containing the entire vector.
117    #[inline(always)]
118    pub fn as_mut_slice(&mut self) -> &mut [T] {
119        if self.len == 0 {
120            &mut []
121        } else {
122            // SAFETY: same validity argument as `as_slice` — `[0, self.len)` is
123            // initialized `T` over a live, aligned allocation. `&mut self` proves
124            // exclusive access for the returned slice's lifetime, so the unique
125            // borrow required by `from_raw_parts_mut` holds.
126            unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
127        }
128    }
129
130    /// Clears the vector, removing all values.
131    ///
132    /// Note that this method has no effect on the allocated capacity of the vector.
133    #[inline]
134    pub fn clear(&mut self) {
135        self.truncate(0);
136    }
137
138    /// Shortens the vector, keeping the first `len` elements and dropping the rest.
139    ///
140    /// If `len` is greater than the vector's current length, this has no effect.
141    #[inline]
142    pub fn truncate(&mut self, len: usize) {
143        if len < self.len {
144            // SAFETY: `len < self.len`, so the tail range `[len, self.len)` lies
145            // within the initialized prefix of the live allocation; `add(len)`
146            // stays in bounds and aligned, and `remaining = self.len - len`
147            // elements are all initialized `T`. `self.len` is truncated to `len`
148            // *before* `drop_in_place`, so the tail is logically removed first: a
149            // panic in an element's `Drop` cannot cause the same slots to be
150            // dropped again. `&mut self` guarantees exclusive access.
151            unsafe {
152                let remaining = self.len - len;
153                let tail = core::slice::from_raw_parts_mut(self.ptr.as_ptr().add(len), remaining);
154                self.len = len;
155                core::ptr::drop_in_place(tail);
156            }
157        }
158    }
159
160    /// Clones and appends all elements in a slice to the vector.
161    ///
162    /// # Errors
163    /// Returns `Err(())` if capacity overflow or allocation fails.
164    #[inline]
165    #[expect(clippy::result_unit_err)] // Preserve the existing allocation-failure API.
166    pub fn extend_from_slice<Permit>(&mut self, token: &mut Permit, other: &[T]) -> Result<(), ()>
167    where
168        for<'token> &'token mut Permit: WritePermit<'brand>,
169        T: Clone,
170    {
171        self.reserve(token, other.len())?;
172        for item in other {
173            self.push(token, item.clone()).map_err(|_| ())?;
174        }
175        Ok(())
176    }
177
178    /// Resizes the vector in-place so that `len` is equal to `new_len`.
179    ///
180    /// If `new_len` is greater than `len`, the vector is extended by the difference,
181    /// with each additional slot filled with a clone of `value`.
182    /// If `new_len` is less than `len`, the vector is truncated.
183    ///
184    /// # Errors
185    /// Returns `Err(())` if capacity overflow or allocation fails.
186    #[inline]
187    #[expect(clippy::result_unit_err)] // Preserve the existing allocation-failure API.
188    pub fn resize<Permit>(&mut self, token: &mut Permit, new_len: usize, value: T) -> Result<(), ()>
189    where
190        for<'token> &'token mut Permit: WritePermit<'brand>,
191        T: Clone,
192    {
193        if new_len > self.len {
194            self.reserve(token, new_len - self.len)?;
195            while self.len < new_len {
196                self.push(token, value.clone()).map_err(|_| ())?;
197            }
198        } else {
199            self.truncate(new_len);
200        }
201        Ok(())
202    }
203
204    /// Extends the vector with the contents of an iterator.
205    ///
206    /// # Errors
207    /// Returns `Err(())` if allocation fails.
208    #[inline]
209    #[expect(clippy::result_unit_err)] // Preserve the existing allocation-failure API.
210    pub fn extend<Permit, I>(&mut self, token: &mut Permit, iter: I) -> Result<(), ()>
211    where
212        for<'token> &'token mut Permit: WritePermit<'brand>,
213        I: IntoIterator<Item = T>,
214    {
215        let iterator = iter.into_iter();
216        let (lower, _) = iterator.size_hint();
217        if lower > 0 {
218            self.reserve(token, lower)?;
219        }
220        for item in iterator {
221            self.push(token, item).map_err(|_| ())?;
222        }
223        Ok(())
224    }
225
226    /// Inserts an element at position `index` within the vector, shifting all elements after it to the right.
227    ///
228    /// # Panics
229    /// Panics if `index > len`.
230    ///
231    /// # Errors
232    /// Returns `Err(element)` if growing the vector fails.
233    #[inline]
234    pub fn insert<Permit>(&mut self, token: &mut Permit, index: usize, element: T) -> Result<(), T>
235    where
236        for<'token> &'token mut Permit: WritePermit<'brand>,
237    {
238        assert!(index <= self.len, "insert index out of bounds");
239        if self.len == self.cap && self.reserve(token, 1).is_err() {
240            return Err(element);
241        }
242        // SAFETY: `index <= self.len` (asserted) and the block above ensured
243        // `self.len < self.cap`, so `self.len + 1 <= self.cap`: both the source
244        // range `[index, self.len)` and the shifted destination
245        // `[index + 1, self.len + 1)` lie within the live, aligned allocation.
246        // `copy` (overlap-safe) moves the `self.len - index` initialized tail
247        // elements up by one, leaving slot `index` logically vacant;
248        // `p.write(element)` fills it without dropping (the prior occupant was
249        // moved, not overwritten in place), and the `len` increment claims the
250        // new element. Each element is bitwise-moved exactly once, so no
251        // double-drop or leak occurs.
252        unsafe {
253            let p = self.ptr.as_ptr().add(index);
254            if index < self.len {
255                core::ptr::copy(p, p.add(1), self.len - index);
256            }
257            p.write(element);
258            self.len += 1;
259        }
260        Ok(())
261    }
262
263    /// Removes and returns the element at position `index` within the vector, shifting all elements after it to the left.
264    ///
265    /// # Panics
266    /// Panics if `index >= len`.
267    #[inline]
268    pub fn remove(&mut self, index: usize) -> T {
269        assert!(index < self.len, "remove index out of bounds");
270        // SAFETY: `index < self.len` (asserted), so slot `index` holds an
271        // initialized `T` within the live, aligned allocation. `read` moves it
272        // out (the slot becomes logically vacant); `self.len` is then
273        // decremented, after which the overlap-safe `copy` shifts the
274        // `self.len - index` initialized tail elements at `[index + 1, old_len)`
275        // down by one into `[index, new_len)`. Every element is bitwise-moved
276        // exactly once and the removed value is returned by move, so no element
277        // is dropped twice or leaked.
278        unsafe {
279            let p = self.ptr.as_ptr().add(index);
280            let val = core::ptr::read(p);
281            self.len -= 1;
282            if index < self.len {
283                core::ptr::copy(p.add(1), p, self.len - index);
284            }
285            val
286        }
287    }
288
289    /// Converts this vector into a shared `BrandedCell` containing a slice.
290    ///
291    /// The memory is shrunk to fit and remains allocated until manually reclaimed.
292    #[inline(always)]
293    pub fn into_cell<Permit>(self, token: &mut Permit) -> BrandedCell<'brand, [T]>
294    where
295        for<'token> &'token mut Permit: WritePermit<'brand>,
296    {
297        self.into_boxed_slice(token).into_cell()
298    }
299}
300
301impl<'brand, 'heap, T: Clone, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
302    BrandedVec<'brand, 'heap, T, P, B>
303{
304    /// Clones the vector using the given allocator write permit.
305    ///
306    /// Returns `None` if allocation fails.
307    #[inline]
308    pub fn clone_in<Permit>(&self, token: &mut Permit) -> Option<Self>
309    where
310        for<'token> &'token mut Permit: WritePermit<'brand>,
311    {
312        let mut new_vec = Self::with_capacity(self.heap, &mut *token, self.len())?;
313        for item in self.as_slice() {
314            if new_vec.push(token, item.clone()).is_err() {
315                return None;
316            }
317        }
318        Some(new_vec)
319    }
320}