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}