Skip to main content

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}