1#[cfg(feature = "alloc")]
27extern crate alloc;
28
29use core::ptr::NonNull;
30
31#[cfg(feature = "alloc")]
32use alloc::alloc::{alloc, alloc_zeroed, dealloc, Layout};
33
34pub const ALIGN: usize = 64;
37
38#[cfg(feature = "alloc")]
48pub struct AlignedBuffer {
49 ptr: NonNull<u8>,
50 len: usize,
51 cap: usize, }
53
54#[cfg(feature = "alloc")]
55impl AlignedBuffer {
56 pub(crate) fn new_uninit(len: usize) -> Self {
66 if len == 0 {
67 return Self {
68 ptr: NonNull::dangling(),
69 len: 0,
70 cap: 0,
71 };
72 }
73 let cap = Self::padded_cap(len);
74 let layout = Self::layout(cap);
75 let ptr = unsafe { alloc(layout) };
77 let ptr = NonNull::new(ptr).expect("allocation failed");
78 Self { ptr, len, cap }
79 }
80
81 pub fn zeroed(len: usize) -> Self {
83 if len == 0 {
84 return Self {
85 ptr: NonNull::dangling(),
86 len: 0,
87 cap: 0,
88 };
89 }
90 let cap = Self::padded_cap(len);
91 let layout = Self::layout(cap);
92 let ptr = unsafe { alloc_zeroed(layout) };
94 let ptr = NonNull::new(ptr).expect("allocation failed");
95 Self { ptr, len, cap }
96 }
97
98 pub fn from_slice(src: &[u8]) -> Self {
100 let mut buf = Self::new_uninit(src.len());
101 if !src.is_empty() {
102 unsafe {
106 core::ptr::copy_nonoverlapping(src.as_ptr(), buf.as_mut_ptr(), src.len());
107 }
108 }
109 buf
110 }
111
112 #[inline]
114 pub fn len(&self) -> usize {
115 self.len
116 }
117
118 #[inline]
120 pub fn is_empty(&self) -> bool {
121 self.len == 0
122 }
123
124 #[inline]
126 pub fn as_ptr(&self) -> *const u8 {
127 self.ptr.as_ptr()
128 }
129
130 #[inline]
132 pub fn as_mut_ptr(&mut self) -> *mut u8 {
133 self.ptr.as_ptr()
134 }
135
136 #[inline]
138 pub fn as_slice(&self) -> &[u8] {
139 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
141 }
142
143 #[cfg(feature = "alloc")]
147 pub fn to_vec(&self) -> alloc::vec::Vec<u8> {
148 self.as_slice().to_vec()
149 }
150
151 #[cfg(feature = "alloc")]
155 pub fn into_vec(self) -> alloc::vec::Vec<u8> {
156 self.as_slice().to_vec()
157 }
158
159 #[inline]
161 pub fn as_mut_slice(&mut self) -> &mut [u8] {
162 unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
164 }
165
166 pub fn fill(&mut self, val: u8) {
168 self.as_mut_slice().fill(val);
169 }
170
171 pub fn zero(&mut self) {
173 self.fill(0);
174 }
175
176 #[inline]
179 fn padded_cap(len: usize) -> usize {
180 len.checked_add(ALIGN - 1)
182 .map(|value| value & !(ALIGN - 1))
183 .expect("AlignedBuffer capacity overflow")
184 }
185
186 #[inline]
187 fn layout(cap: usize) -> Layout {
188 Layout::from_size_align(cap, ALIGN).expect("AlignedBuffer layout overflow")
189 }
190}
191
192#[cfg(feature = "alloc")]
195impl core::ops::Deref for AlignedBuffer {
196 type Target = [u8];
197 #[inline]
198 fn deref(&self) -> &[u8] {
199 self.as_slice()
200 }
201}
202
203#[cfg(feature = "alloc")]
204impl core::ops::DerefMut for AlignedBuffer {
205 #[inline]
206 fn deref_mut(&mut self) -> &mut [u8] {
207 self.as_mut_slice()
208 }
209}
210
211#[cfg(feature = "alloc")]
214impl Drop for AlignedBuffer {
215 fn drop(&mut self) {
216 if self.cap > 0 {
217 let layout = Self::layout(self.cap);
218 unsafe { dealloc(self.ptr.as_ptr(), layout) };
220 }
221 }
222}
223
224#[cfg(feature = "alloc")]
226unsafe impl Send for AlignedBuffer {}
227#[cfg(feature = "alloc")]
228unsafe impl Sync for AlignedBuffer {}
229
230#[cfg(feature = "alloc")]
233impl core::fmt::Debug for AlignedBuffer {
234 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
235 write!(
236 f,
237 "AlignedBuffer {{ len: {}, align: {ALIGN}, ptr: {:p} }}",
238 self.len,
239 self.as_ptr()
240 )
241 }
242}
243
244#[cfg(feature = "alloc")]
245impl Clone for AlignedBuffer {
246 fn clone(&self) -> Self {
247 Self::from_slice(self.as_slice())
248 }
249}
250
251#[cfg(test)]
254#[cfg(feature = "alloc")]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn alignment_guarantee() {
260 for len in [1usize, 15, 16, 63, 64, 65, 1024, 65536] {
261 let buf = AlignedBuffer::zeroed(len);
262 assert_eq!(
263 buf.as_ptr() as usize % ALIGN,
264 0,
265 "len={len}: pointer not {ALIGN}-byte aligned"
266 );
267 assert_eq!(buf.len(), len);
268 }
269 }
270
271 #[test]
272 fn zero_size() {
273 let buf = AlignedBuffer::zeroed(0);
274 assert!(buf.is_empty());
275 assert_eq!(buf.len(), 0);
276 }
277
278 #[test]
279 #[should_panic(expected = "AlignedBuffer capacity overflow")]
280 fn oversized_length_panics_before_allocation() {
281 let _ = AlignedBuffer::zeroed(usize::MAX);
282 }
283
284 #[test]
285 fn from_slice_roundtrip() {
286 let data: Vec<u8> = (0u8..128).collect();
287 let buf = AlignedBuffer::from_slice(&data);
288 assert_eq!(buf.as_ptr() as usize % ALIGN, 0);
289 assert_eq!(buf.as_slice(), data.as_slice());
290 }
291
292 #[test]
293 fn clone_is_independent() {
294 let mut a = AlignedBuffer::from_slice(&[1u8, 2, 3, 4]);
295 let b = a.clone();
296 a.as_mut_slice()[0] = 0xFF;
297 assert_eq!(b.as_slice()[0], 1); }
299
300 #[test]
301 fn deref_works_with_kernel() {
302 let x = AlignedBuffer::from_slice(&[0x42u8; 64]);
303 let mut y = AlignedBuffer::zeroed(64);
304 crate::kernel::axpy(0x03, &x, &mut y);
306 let mut y_ref = vec![0u8; 64];
308 crate::kernel::scalar::axpy(0x03, &x, &mut y_ref);
309 assert_eq!(y.as_slice(), y_ref.as_slice());
310 }
311}