mnemosyne_arena/scratch/aligned_vec/length.rs
1//! Operations that change an [`AlignedVec`]'s length or content.
2//!
3//! Length-changing operations use the storage module's doubling growth policy,
4//! so scratch reuse remains amortized while quiescent release handles retention.
5//!
6//! Read-only queries, search, sort, and in-place reordering live in
7//! [`super::query`] so each module has a single responsibility.
8
9use super::AlignedVec;
10use super::ScratchElement;
11
12impl<T: ScratchElement> AlignedVec<T> {
13 /// Creates an `AlignedVec` of exactly `len` zero-initialized elements.
14 ///
15 /// Equivalent to `with_capacity(len)` followed by `ensure_len(len)`, but
16 /// expressed as a single constructor. All elements are zero (valid per the
17 /// [`ScratchElement`] invariant).
18 #[inline]
19 #[must_use]
20 pub fn zeroed(len: usize) -> Self {
21 let mut v = Self::with_capacity(len);
22 v.ensure_len(len);
23 v
24 }
25
26 /// Ensures capacity for at least `min_len` elements. Only grows; never
27 /// shrinks. Only zeroes **newly** allocated elements, not existing ones.
28 ///
29 /// Uses geometric (doubling) growth so repeated calls are amortized. A
30 /// provision that bounds the retained size is enforced by quiescent
31 /// [`release`][super::super::pool] trimming, not by narrowing growth
32 /// itself: an exact-growth variant was evaluated and rejected
33 /// (MN-SCRATCH-GROWTH-COST-2026-09-04) because it drops the amortized
34 /// doubling policy this method exists to provide.
35 #[inline]
36 pub fn ensure_len(&mut self, min_len: usize) {
37 if min_len <= self.len {
38 return;
39 }
40 if min_len > self.capacity {
41 self.grow_geometric(min_len);
42 }
43 // SAFETY: capacity >= min_len after grow; `[self.len, min_len)` is
44 // inside the allocation; all-zero is valid for every `ScratchElement`.
45 unsafe { self.extend_with_zeros(min_len) };
46 }
47
48 /// Zeros the range `[self.len, new_len)` and advances `self.len`.
49 ///
50 /// # Safety
51 ///
52 /// `capacity >= new_len` and `new_len >= self.len`. All-zero must be a
53 /// valid bit pattern for `T` (guaranteed by `ScratchElement`).
54 #[inline(always)]
55 unsafe fn extend_with_zeros(&mut self, new_len: usize) {
56 // SAFETY: the caller guarantees capacity and zero-validity.
57 unsafe {
58 core::ptr::write_bytes(self.ptr.add(self.len), 0, new_len - self.len);
59 }
60 self.len = new_len;
61 }
62
63 /// Creates a buffer of exactly `len` elements, every one equal to `value`.
64 ///
65 /// The zero-valued case has a cheaper path in [`zeroed`][Self::zeroed],
66 /// which writes bytes rather than elements.
67 #[inline]
68 #[must_use]
69 pub fn filled(len: usize, value: T) -> Self {
70 let mut buffer = Self::with_capacity(len);
71 buffer.resize(len, value);
72 buffer
73 }
74
75 /// Creates a buffer holding a copy of `slice`.
76 #[inline]
77 #[must_use]
78 pub fn from_slice(slice: &[T]) -> Self {
79 let mut buffer = Self::with_capacity(slice.len());
80 buffer.extend_from_slice(slice);
81 buffer
82 }
83
84 /// Appends one element, growing the allocation if it is full.
85 ///
86 /// Amortized O(1): a growth doubles capacity, so `n` pushes onto an empty
87 /// buffer perform O(log n) reallocations and O(n) element writes.
88 #[inline]
89 pub fn push(&mut self, value: T) {
90 self.reserve(1);
91 // SAFETY: `reserve(1)` leaves `capacity > len`, so `ptr.add(len)` is
92 // inside the allocation and past the initialized prefix `[0, len)`.
93 // `T: Copy` (a `ScratchElement` supertrait), so the write is the whole
94 // initialization and overwrites no live value.
95 unsafe { core::ptr::write(self.ptr.add(self.len), value) };
96 self.len += 1;
97 }
98
99 /// Appends every element of `slice` in one bulk copy.
100 #[inline]
101 pub fn extend_from_slice(&mut self, slice: &[T]) {
102 if slice.is_empty() {
103 return;
104 }
105 self.reserve(slice.len());
106 // SAFETY: `reserve(n)` leaves `capacity >= len + n`, so the destination
107 // range `[len, len + n)` is inside the allocation and past the
108 // initialized prefix. `slice` is a live initialized `[T]`, and it
109 // cannot overlap that range: it is either a distinct allocation or an
110 // initialized region of this one, which the destination excludes.
111 unsafe {
112 core::ptr::copy_nonoverlapping(slice.as_ptr(), self.ptr.add(self.len), slice.len());
113 }
114 self.len += slice.len();
115 }
116
117 /// Sets the length to `new_len`, filling any new elements with `value`.
118 ///
119 /// Shrinking keeps the allocation; `ScratchElement` is `Copy`, so the
120 /// dropped tail needs no destructor run.
121 #[inline]
122 pub fn resize(&mut self, new_len: usize, value: T) {
123 if new_len > self.len {
124 let additional = new_len - self.len;
125 self.reserve(additional);
126 // SAFETY: `reserve(additional)` leaves `capacity >= new_len`. Each
127 // write targets a distinct index in `[len, new_len)`, inside the
128 // allocation and past the initialized prefix; `T: Copy`, so each
129 // write fully initializes its element.
130 unsafe {
131 let base = self.ptr.add(self.len);
132 for offset in 0..additional {
133 core::ptr::write(base.add(offset), value);
134 }
135 }
136 }
137 self.len = new_len;
138 }
139
140 /// Sets the length to `new_len`, filling any new elements using `f`.
141 ///
142 /// Like [`resize`][Self::resize] but uses a closure for the fill value.
143 /// Shrinking keeps the allocation.
144 #[inline]
145 pub fn resize_with<F: FnMut() -> T>(&mut self, new_len: usize, mut f: F) {
146 if new_len > self.len {
147 let additional = new_len - self.len;
148 self.reserve(additional);
149 // SAFETY: `reserve(additional)` leaves `capacity >= new_len`. Each
150 // write targets a distinct index in `[len, new_len)` inside the
151 // allocation; `T: Copy`, so each write fully initializes its element.
152 unsafe {
153 let base = self.ptr.add(self.len);
154 for offset in 0..additional {
155 core::ptr::write(base.add(offset), f());
156 }
157 }
158 }
159 self.len = new_len;
160 }
161
162 /// Overwrites every initialized element with the value produced by `f`.
163 ///
164 /// Unlike [`fill`][Self::fill] (which takes a `Copy` value), this version
165 /// accepts a closure so non-`Copy` logic can produce each element.
166 #[inline]
167 pub fn fill_with<F: FnMut() -> T>(&mut self, mut f: F) {
168 // SAFETY: `[0, self.len)` is fully initialized; `T: Copy` makes
169 // overwriting sound.
170 for i in 0..self.len {
171 unsafe { core::ptr::write(self.ptr.add(i), f()) };
172 }
173 }
174
175 /// Sets the length to zero, retaining the underlying allocation for reuse.
176 ///
177 /// Elements are not zeroed; subsequent [`push`][Self::push] or
178 /// [`extend_from_slice`][Self::extend_from_slice] calls will overwrite them
179 /// before exposing them through [`as_slice`][Self::as_slice].
180 #[inline]
181 pub fn clear(&mut self) {
182 self.len = 0;
183 }
184
185 /// Shortens the buffer to `new_len` elements, retaining the allocation.
186 ///
187 /// If `new_len >= self.len()`, this is a no-op.
188 #[inline]
189 pub fn truncate(&mut self, new_len: usize) {
190 if new_len < self.len {
191 self.len = new_len;
192 }
193 }
194
195 /// Appends every element produced by an iterator.
196 ///
197 /// Forwards to [`push`][Self::push] per element; an
198 /// [`extend_from_slice`][Self::extend_from_slice] call is preferred when a
199 /// contiguous source slice is available.
200 #[inline]
201 pub fn extend_from_iter(&mut self, iter: impl IntoIterator<Item = T>) {
202 for value in iter {
203 self.push(value);
204 }
205 }
206}
207
208// ── Drain iterator ───────────────────────────────────────────────────────────
209
210/// A draining iterator returned by [`AlignedVec::drain`].
211///
212/// Yields elements in `[start, end)` by value and, on drop, shifts the
213/// tail left to close the gap.
214pub struct Drain<'a, T: ScratchElement> {
215 pub(super) buf: &'a mut AlignedVec<T>,
216 pub(super) start: usize,
217 pub(super) end: usize,
218 pub(super) current: usize,
219}
220
221impl<T: ScratchElement> Iterator for Drain<'_, T> {
222 type Item = T;
223
224 #[inline]
225 fn next(&mut self) -> Option<T> {
226 if self.current < self.end {
227 // SAFETY: `current < end <= buf.len()` — initialized; T: Copy.
228 let val = unsafe { core::ptr::read(self.buf.ptr.add(self.current)) };
229 self.current += 1;
230 Some(val)
231 } else {
232 None
233 }
234 }
235
236 #[inline]
237 fn size_hint(&self) -> (usize, Option<usize>) {
238 let remaining = self.end - self.current;
239 (remaining, Some(remaining))
240 }
241}
242
243impl<T: ScratchElement> ExactSizeIterator for Drain<'_, T> {}
244
245impl<T: ScratchElement> Drop for Drain<'_, T> {
246 fn drop(&mut self) {
247 let drain_len = self.end - self.start;
248 if drain_len == 0 {
249 return;
250 }
251 let tail_len = self.buf.len - self.end;
252 if tail_len > 0 {
253 // SAFETY: `[end, end + tail_len)` → `[start, start + tail_len)`;
254 // may overlap (when drain_len > 0), so we use `copy` (memmove).
255 unsafe {
256 core::ptr::copy(
257 self.buf.ptr.add(self.end),
258 self.buf.ptr.add(self.start),
259 tail_len,
260 );
261 }
262 }
263 self.buf.len -= drain_len;
264 }
265}