1use std::{
4 alloc::{alloc, alloc_zeroed, dealloc, handle_alloc_error, Layout},
5 mem,
6 ptr::NonNull,
7};
8
9use crate::memory_region::{HostBuffer, HostMemoryObject};
10
11pub trait Pod: Copy {}
13
14impl Pod for u8 {}
15impl Pod for u16 {}
16impl Pod for u32 {}
17impl Pod for u64 {}
18impl Pod for i8 {}
19impl Pod for i16 {}
20impl Pod for i32 {}
21impl Pod for i64 {}
22
23#[derive(Debug, PartialEq, Eq)]
25pub struct AlignedMemory<const ALIGN: usize> {
26 mem: AlignedVec<ALIGN>,
27 zero_up_to_max_len: bool,
28}
29
30impl<const ALIGN: usize> AlignedMemory<ALIGN> {
31 pub fn from_slice(data: &[u8]) -> Self {
33 let max_len = data.len();
34 let mut mem = AlignedVec::new(max_len, false);
35 unsafe {
36 core::ptr::copy_nonoverlapping(data.as_ptr(), mem.as_mut_ptr(), max_len);
38 mem.set_len(max_len);
39 }
40 Self {
41 mem,
42 zero_up_to_max_len: false,
43 }
44 }
45
46 pub fn with_capacity(max_len: usize) -> Self {
48 let mem = AlignedVec::new(max_len, false);
49 Self {
50 mem,
51 zero_up_to_max_len: false,
52 }
53 }
54
55 pub fn with_capacity_zeroed(max_len: usize) -> Self {
57 let mem = AlignedVec::new(max_len, true);
58 Self {
59 mem,
60 zero_up_to_max_len: true,
61 }
62 }
63
64 pub fn zero_filled(max_len: usize) -> Self {
66 let mut mem = AlignedVec::new(max_len, true);
67 unsafe {
69 mem.set_len(max_len);
70 }
71 Self {
72 mem,
73 zero_up_to_max_len: true,
74 }
75 }
76
77 pub fn mem_size(&self) -> usize {
79 self.mem.capacity().saturating_add(mem::size_of::<Self>())
80 }
81
82 pub fn len(&self) -> usize {
84 self.mem.len()
85 }
86
87 pub fn is_empty(&self) -> bool {
89 self.mem.is_empty()
90 }
91
92 pub fn write_index(&self) -> usize {
94 self.mem.len()
95 }
96
97 pub fn as_slice(&self) -> &[u8] {
99 self.mem.as_slice()
100 }
101
102 pub fn as_slice_mut(&mut self) -> &mut [u8] {
104 self.mem.as_slice_mut()
105 }
106
107 pub fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
109 let (ptr, new_len) = self.mem.write_ptr_for(num).ok_or_else(|| {
110 std::io::Error::new(
111 std::io::ErrorKind::InvalidInput,
112 "aligned memory fill_write failed",
113 )
114 })?;
115
116 if self.zero_up_to_max_len && value == 0 {
117 } else {
119 unsafe {
120 core::ptr::write_bytes(ptr, value, num);
121 }
122 }
123 unsafe {
124 self.mem.set_len(new_len);
125 }
126 Ok(())
127 }
128
129 pub unsafe fn write_unchecked<T: Pod>(&mut self, value: T) {
135 let pos = self.mem.len();
136 let new_len = pos.saturating_add(mem::size_of::<T>());
137 debug_assert!(new_len <= self.mem.capacity());
138 unsafe {
139 self.mem.write_ptr().cast::<T>().write_unaligned(value);
140 self.mem.set_len(new_len);
141 }
142 }
143
144 pub unsafe fn write_all_unchecked(&mut self, value: &[u8]) {
150 let pos = self.mem.len();
151 let new_len = pos.saturating_add(value.len());
152 debug_assert!(new_len <= self.mem.capacity());
153 core::ptr::copy_nonoverlapping(value.as_ptr(), self.mem.write_ptr(), value.len());
154 self.mem.set_len(new_len);
155 }
156}
157
158impl<const ALIGN: usize> Clone for AlignedMemory<ALIGN> {
162 fn clone(&self) -> Self {
163 AlignedMemory::from_slice(self.as_slice())
164 }
165}
166
167impl<const ALIGN: usize> std::io::Write for AlignedMemory<ALIGN> {
168 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
169 let (ptr, new_len) = self.mem.write_ptr_for(buf.len()).ok_or_else(|| {
170 std::io::Error::new(
171 std::io::ErrorKind::InvalidInput,
172 "aligned memory fill_write failed",
173 )
174 })?;
175 unsafe {
176 core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
177 self.mem.set_len(new_len);
178 }
179 Ok(buf.len())
180 }
181 fn flush(&mut self) -> std::io::Result<()> {
182 Ok(())
183 }
184}
185
186impl<const ALIGN: usize, T: AsRef<[u8]>> From<T> for AlignedMemory<ALIGN> {
187 fn from(bytes: T) -> Self {
188 AlignedMemory::from_slice(bytes.as_ref())
189 }
190}
191
192unsafe impl<const A: usize> HostMemoryObject for &AlignedMemory<A> {
193 fn host(self) -> HostBuffer {
194 HostBuffer::Immutable(std::ptr::slice_from_raw_parts(
195 self.mem.ptr.as_ptr(),
196 self.len(),
197 ))
198 }
199}
200
201unsafe impl<const A: usize> HostMemoryObject for &mut AlignedMemory<A> {
202 fn host(self) -> HostBuffer {
203 HostBuffer::Mutable(std::ptr::slice_from_raw_parts_mut(
204 self.mem.ptr.as_ptr(),
205 self.len(),
206 ))
207 }
208}
209
210pub fn is_memory_aligned(ptr: usize, align: usize) -> bool {
212 ptr.checked_rem(align)
213 .map(|remainder| remainder == 0)
214 .unwrap_or(false)
215}
216
217struct AlignedVec<const ALIGN: usize> {
220 ptr: NonNull<u8>,
221 length: usize,
222 capacity: usize,
223}
224
225impl<const ALIGN: usize> Drop for AlignedVec<ALIGN> {
226 fn drop(&mut self) {
227 if self.capacity == 0 {
228 return;
229 }
230 let ptr = self.ptr.as_ptr();
231 unsafe {
232 let layout = Layout::from_size_align_unchecked(self.capacity, ALIGN);
234 dealloc(ptr, layout);
235 }
236 }
237}
238
239impl<const A: usize> std::fmt::Debug for AlignedVec<A> {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 f.debug_list().entries(self.as_slice()).finish()
242 }
243}
244
245impl<const A: usize> PartialEq for AlignedVec<A> {
246 fn eq(&self, other: &Self) -> bool {
247 self.as_slice() == other.as_slice()
248 }
249}
250
251impl<const A: usize> Eq for AlignedVec<A> {}
252
253impl<const ALIGN: usize> AlignedVec<ALIGN> {
254 fn new(max_len: usize, zeroed: bool) -> Self {
260 assert!(ALIGN != 0, "Alignment must not be zero");
261 if max_len == 0 {
262 return Self::empty();
263 }
264 unsafe {
265 let layout = Layout::from_size_align(max_len, ALIGN).expect("invalid layout");
266 let ptr = if zeroed {
268 alloc_zeroed(layout)
269 } else {
270 alloc(layout)
271 };
272 if ptr.is_null() {
273 handle_alloc_error(layout);
274 }
275 Self {
276 ptr: NonNull::new(ptr).unwrap_or_else(|| handle_alloc_error(layout)),
277 length: 0,
278 capacity: max_len,
279 }
280 }
281 }
282
283 fn as_slice(&self) -> &[u8] {
284 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.length) }
285 }
286
287 fn as_slice_mut(&mut self) -> &mut [u8] {
288 unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.length) }
289 }
290
291 fn empty() -> Self {
292 let layout = Layout::from_size_align(0, ALIGN).expect("invalid layout");
293 Self {
294 ptr: layout.dangling_ptr(),
295 length: 0,
296 capacity: 0,
297 }
298 }
299
300 fn as_mut_ptr(&mut self) -> *mut u8 {
301 self.ptr.as_ptr()
302 }
303
304 fn write_ptr(&mut self) -> *mut u8 {
308 unsafe { self.as_mut_ptr().add(self.len()) }
309 }
310
311 fn write_ptr_for(&mut self, bytes: usize) -> Option<(*mut u8, usize)> {
314 let ptr = self.write_ptr();
315 let new_len = self
316 .len()
317 .checked_add(bytes)
318 .filter(|l| *l <= self.capacity())?;
319 Some((ptr, new_len))
320 }
321
322 fn len(&self) -> usize {
323 self.length
324 }
325
326 fn capacity(&self) -> usize {
327 self.capacity
328 }
329
330 fn is_empty(&self) -> bool {
331 self.len() == 0
332 }
333
334 unsafe fn set_len(&mut self, new_len: usize) {
338 debug_assert!(
339 new_len <= self.capacity,
340 "attempted to grow AlignedVec beyond capacity"
341 );
342 debug_assert!(new_len >= self.length, "attempted to shrink AlignedVec");
343 self.length = new_len;
344 }
345}
346
347unsafe impl<const N: usize> Send for AlignedVec<N> {}
349
350unsafe impl<const N: usize> Sync for AlignedVec<N> {}
352
353#[allow(clippy::arithmetic_side_effects)]
354#[cfg(test)]
355mod tests {
356 use {super::*, std::io::Write};
357
358 fn do_test<const ALIGN: usize>() {
359 let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity(10);
360 let ptr = aligned_memory.mem.as_mut_ptr();
361 assert_eq!(
362 ptr.addr() & (ALIGN - 1),
363 0,
364 "memory is not correctly aligned"
365 );
366
367 assert_eq!(aligned_memory.write(&[42u8; 1]).unwrap(), 1);
368 assert_eq!(aligned_memory.write(&[42u8; 9]).unwrap(), 9);
369 assert_eq!(aligned_memory.as_slice(), &[42u8; 10]);
370 assert_eq!(aligned_memory.write(&[42u8; 0]).unwrap(), 0);
371 assert_eq!(aligned_memory.as_slice(), &[42u8; 10]);
372 aligned_memory.write(&[42u8; 1]).unwrap_err();
373 assert_eq!(aligned_memory.as_slice(), &[42u8; 10]);
374 aligned_memory.as_slice_mut().copy_from_slice(&[84u8; 10]);
375 assert_eq!(aligned_memory.as_slice(), &[84u8; 10]);
376
377 let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity_zeroed(10);
378 aligned_memory.fill_write(5, 0).unwrap();
379 aligned_memory.fill_write(2, 1).unwrap();
380 assert_eq!(aligned_memory.write(&[2u8; 3]).unwrap(), 3);
381 assert_eq!(aligned_memory.as_slice(), &[0, 0, 0, 0, 0, 1, 1, 2, 2, 2]);
382 aligned_memory.fill_write(1, 3).unwrap_err();
383 aligned_memory.write(&[4u8; 1]).unwrap_err();
384 assert_eq!(aligned_memory.as_slice(), &[0, 0, 0, 0, 0, 1, 1, 2, 2, 2]);
385
386 let aligned_memory = AlignedMemory::<ALIGN>::zero_filled(10);
387 assert_eq!(aligned_memory.len(), 10);
388 assert_eq!(aligned_memory.as_slice(), &[0u8; 10]);
389
390 let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity_zeroed(15);
391 unsafe {
392 aligned_memory.write_unchecked::<u8>(42);
393 assert_eq!(aligned_memory.len(), 1);
394 aligned_memory.write_unchecked::<u64>(0xCAFEBADDDEADCAFE);
395 assert_eq!(aligned_memory.len(), 9);
396 aligned_memory.fill_write(3, 0).unwrap();
397 aligned_memory.write_all_unchecked(b"foo");
398 assert_eq!(aligned_memory.len(), 15);
399 }
400 let mem = aligned_memory.as_slice();
401 assert_eq!(mem[0], 42);
402 assert_eq!(
403 unsafe {
404 core::ptr::read_unaligned::<u64>(mem[1..1 + mem::size_of::<u64>()].as_ptr().cast())
405 },
406 0xCAFEBADDDEADCAFE
407 );
408 assert_eq!(&mem[1 + mem::size_of::<u64>()..][..3], &[0, 0, 0]);
409 assert_eq!(&mem[1 + mem::size_of::<u64>() + 3..], b"foo");
410 }
411
412 #[test]
413 fn test_aligned_memory() {
414 do_test::<1>();
415 do_test::<16>();
416 do_test::<32768>();
417 }
418
419 #[cfg(debug_assertions)]
420 #[test]
421 #[should_panic(expected = "<= self.mem.capacity()")]
422 fn test_write_unchecked_debug_assert() {
423 let mut aligned_memory = AlignedMemory::<8>::with_capacity(15);
424 unsafe {
425 aligned_memory.write_unchecked::<u64>(42);
426 aligned_memory.write_unchecked::<u64>(24);
427 }
428 }
429
430 #[cfg(debug_assertions)]
431 #[test]
432 #[should_panic(expected = "<= self.mem.capacity()")]
433 fn test_write_all_unchecked_debug_assert() {
434 let mut aligned_memory = AlignedMemory::<8>::with_capacity(5);
435 unsafe {
436 aligned_memory.write_all_unchecked(b"foo");
437 aligned_memory.write_all_unchecked(b"bar");
438 }
439 }
440
441 const fn assert_send<T: Send>() {}
442 const fn assert_sync<T: Sync>() {}
443 const fn assert_unpin<T: Unpin>() {}
444 const _: () = assert_send::<AlignedMemory<8>>();
445 const _: () = assert_sync::<AlignedMemory<8>>();
446 const _: () = assert_unpin::<AlignedMemory<8>>();
447}