mnemosyne_arena/scratch/aligned_buf/ops.rs
1use super::AlignedBuf;
2use crate::scratch::element::ScratchElement;
3use core::mem::MaybeUninit;
4
5impl<T: ScratchElement, const N: usize> AlignedBuf<T, N> {
6 // ── Construction ─────────────────────────────────────────────────────────
7
8 /// Creates an empty buffer with no initialized elements.
9 ///
10 /// `const fn` — usable in statics and const contexts.
11 #[inline]
12 pub const fn new() -> Self {
13 // SAFETY: `[MaybeUninit<T>; N]` has no validity invariant — every bit
14 // pattern is a valid representation of the array type. Calling
15 // `assume_init` on the outer `MaybeUninit` wrapper is therefore always
16 // sound; it does not create or access any `T` value.
17 unsafe {
18 Self {
19 data: MaybeUninit::uninit().assume_init(),
20 len: 0,
21 }
22 }
23 }
24
25 /// Creates a buffer filled with `value` replicated `N` times.
26 #[inline]
27 #[must_use]
28 pub fn filled(value: T) -> Self {
29 let mut buf = Self::new();
30 for i in 0..N {
31 buf.data[i] = MaybeUninit::new(value);
32 }
33 buf.len = N;
34 buf
35 }
36
37 /// Creates a buffer from a fixed-size array, consuming all `N` elements.
38 #[inline]
39 #[must_use]
40 pub fn from_array(arr: [T; N]) -> Self {
41 let mut buf = Self::new();
42 for (i, v) in arr.into_iter().enumerate() {
43 buf.data[i] = MaybeUninit::new(v);
44 }
45 buf.len = N;
46 buf
47 }
48
49 /// Creates a buffer from a slice, copying up to `N` elements.
50 ///
51 /// If `slice.len() > N`, only the first `N` elements are copied.
52 #[inline]
53 #[must_use]
54 pub fn from_slice_truncating(slice: &[T]) -> Self {
55 let n = slice.len().min(N);
56 let mut buf = Self::new();
57 for (i, &v) in slice[..n].iter().enumerate() {
58 buf.data[i] = MaybeUninit::new(v);
59 }
60 buf.len = n;
61 buf
62 }
63
64 // ── Capacity queries ────────────────────────────────────────────────────
65
66 /// Maximum number of elements this buffer can hold (always `N`).
67 #[inline]
68 pub const fn capacity(&self) -> usize {
69 N
70 }
71
72 /// Number of initialized elements currently in the buffer.
73 #[inline]
74 pub fn len(&self) -> usize {
75 self.len
76 }
77
78 /// Returns `true` if no elements have been pushed.
79 #[inline]
80 pub fn is_empty(&self) -> bool {
81 self.len == 0
82 }
83
84 /// Returns `true` when `len == N` and no further push is possible.
85 #[inline]
86 pub fn is_full(&self) -> bool {
87 self.len == N
88 }
89
90 /// Number of remaining slots before the buffer is full.
91 #[inline]
92 pub fn remaining(&self) -> usize {
93 N - self.len
94 }
95
96 // ── Push / pop ───────────────────────────────────────────────────────────
97
98 /// Appends `value`.
99 ///
100 /// # Panics
101 ///
102 /// Panics if the buffer is full (`len == N`).
103 #[inline]
104 pub fn push(&mut self, value: T) {
105 assert!(
106 self.len < N,
107 "AlignedBuf::push: buffer is full (capacity {N})"
108 );
109 self.data[self.len] = MaybeUninit::new(value);
110 self.len += 1;
111 }
112
113 /// Appends `value` without panicking.
114 ///
115 /// Returns `true` on success, `false` if the buffer is full.
116 #[inline]
117 pub fn try_push(&mut self, value: T) -> bool {
118 if self.len < N {
119 self.data[self.len] = MaybeUninit::new(value);
120 self.len += 1;
121 true
122 } else {
123 false
124 }
125 }
126
127 /// Removes and returns the last element, or `None` if empty.
128 #[inline]
129 pub fn pop(&mut self) -> Option<T> {
130 if self.len == 0 {
131 return None;
132 }
133 self.len -= 1;
134 // SAFETY: `self.len` was just decremented, so `data[self.len]` was
135 // initialized by a prior `push` or `try_push` call.
136 Some(unsafe { self.data[self.len].assume_init_read() })
137 }
138
139 // ── Mutation ─────────────────────────────────────────────────────────────
140
141 /// Resets the length to zero.
142 ///
143 /// Slots are not cleared; the next push overwrites them. `T: ScratchElement`
144 /// (no `Drop`) means no resources leak.
145 #[inline]
146 pub fn clear(&mut self) {
147 self.len = 0;
148 }
149
150 /// Shortens to `new_len`. If `new_len >= len()` this is a no-op.
151 #[inline]
152 pub fn truncate(&mut self, new_len: usize) {
153 if new_len < self.len {
154 self.len = new_len;
155 }
156 }
157
158 /// Zero-fills all initialized elements (sets every byte to `0`).
159 ///
160 /// All-zero is a valid bit pattern for every [`ScratchElement`] type by
161 /// the trait's invariant.
162 #[inline]
163 pub fn zero_fill(&mut self) {
164 if self.len == 0 {
165 return;
166 }
167 // SAFETY: `[0, self.len)` of `self.data` was written by push/try_push,
168 // so the pointer and range are valid. All-zero is a valid `T` bit
169 // pattern per `ScratchElement`.
170 unsafe {
171 core::ptr::write_bytes(self.data.as_mut_ptr().cast::<T>(), 0, self.len);
172 }
173 }
174
175 // ── Slice views ──────────────────────────────────────────────────────────
176
177 /// Shared slice of the initialized elements.
178 #[inline]
179 pub fn as_slice(&self) -> &[T] {
180 // SAFETY: `[0, self.len)` is initialized (every element was written by
181 // `push` or `try_push`). `T: ScratchElement` is `Copy` / POD; `&self`
182 // ensures exclusive read access for the slice's lifetime.
183 unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast::<T>(), self.len) }
184 }
185
186 /// Mutable slice of the initialized elements.
187 #[inline]
188 pub fn as_mut_slice(&mut self) -> &mut [T] {
189 // SAFETY: same validity argument as `as_slice`; `&mut self` proves
190 // exclusive access.
191 unsafe { core::slice::from_raw_parts_mut(self.data.as_mut_ptr().cast::<T>(), self.len) }
192 }
193
194 /// Raw pointer to the start of the inline storage.
195 ///
196 /// Valid for `N` slots, of which the first `len()` are initialized.
197 #[inline]
198 pub fn as_ptr(&self) -> *const T {
199 self.data.as_ptr().cast::<T>()
200 }
201
202 /// Mutable raw pointer to the start of the inline storage.
203 #[inline]
204 pub fn as_mut_ptr(&mut self) -> *mut T {
205 self.data.as_mut_ptr().cast::<T>()
206 }
207}