1use std::{
4 alloc::{alloc, alloc_zeroed, dealloc, handle_alloc_error, Layout},
5 mem,
6 ptr::NonNull,
7};
8
9use crate::memory_region::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 const WRITABLE: bool = false;
194 fn host_address(self) -> usize {
195 self.mem.ptr.as_ptr().expose_provenance()
196 }
197 fn byte_length(&self) -> usize {
198 self.len()
199 }
200}
201
202unsafe impl<const A: usize> HostMemoryObject for &mut AlignedMemory<A> {
203 const WRITABLE: bool = true;
204 fn host_address(self) -> usize {
205 self.mem.ptr.as_ptr().expose_provenance()
206 }
207 fn byte_length(&self) -> usize {
208 self.len()
209 }
210}
211
212pub fn is_memory_aligned(ptr: usize, align: usize) -> bool {
214 ptr.checked_rem(align)
215 .map(|remainder| remainder == 0)
216 .unwrap_or(false)
217}
218
219struct AlignedVec<const ALIGN: usize> {
222 ptr: NonNull<u8>,
223 length: usize,
224 capacity: usize,
225}
226
227impl<const ALIGN: usize> Drop for AlignedVec<ALIGN> {
228 fn drop(&mut self) {
229 if self.capacity == 0 {
230 return;
231 }
232 let ptr = self.ptr.as_ptr();
233 unsafe {
234 let layout = Layout::from_size_align_unchecked(self.capacity, ALIGN);
236 dealloc(ptr, layout);
237 }
238 }
239}
240
241impl<const A: usize> std::fmt::Debug for AlignedVec<A> {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 f.debug_list().entries(self.as_slice()).finish()
244 }
245}
246
247impl<const A: usize> PartialEq for AlignedVec<A> {
248 fn eq(&self, other: &Self) -> bool {
249 self.as_slice() == other.as_slice()
250 }
251}
252
253impl<const A: usize> Eq for AlignedVec<A> {}
254
255impl<const ALIGN: usize> AlignedVec<ALIGN> {
256 fn new(max_len: usize, zeroed: bool) -> Self {
262 assert!(ALIGN != 0, "Alignment must not be zero");
263 if max_len == 0 {
264 return Self::empty();
265 }
266 unsafe {
267 let layout = Layout::from_size_align(max_len, ALIGN).expect("invalid layout");
268 let ptr = if zeroed {
270 alloc_zeroed(layout)
271 } else {
272 alloc(layout)
273 };
274 if ptr.is_null() {
275 handle_alloc_error(layout);
276 }
277 Self {
278 ptr: NonNull::new(ptr).unwrap_or_else(|| handle_alloc_error(layout)),
279 length: 0,
280 capacity: max_len,
281 }
282 }
283 }
284
285 fn as_slice(&self) -> &[u8] {
286 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.length) }
287 }
288
289 fn as_slice_mut(&mut self) -> &mut [u8] {
290 unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.length) }
291 }
292
293 fn empty() -> Self {
294 Self {
295 ptr: NonNull::new(ALIGN as *mut u8).expect("alignment may not be zero"),
298 length: 0,
299 capacity: 0,
300 }
301 }
302
303 fn as_mut_ptr(&mut self) -> *mut u8 {
304 self.ptr.as_ptr()
305 }
306
307 fn write_ptr(&mut self) -> *mut u8 {
311 unsafe { self.as_mut_ptr().add(self.len()) }
312 }
313
314 fn write_ptr_for(&mut self, bytes: usize) -> Option<(*mut u8, usize)> {
317 let ptr = self.write_ptr();
318 let new_len = self
319 .len()
320 .checked_add(bytes)
321 .filter(|l| *l <= self.capacity())?;
322 Some((ptr, new_len))
323 }
324
325 fn len(&self) -> usize {
326 self.length
327 }
328
329 fn capacity(&self) -> usize {
330 self.capacity
331 }
332
333 fn is_empty(&self) -> bool {
334 self.len() == 0
335 }
336
337 unsafe fn set_len(&mut self, new_len: usize) {
341 debug_assert!(
342 new_len <= self.capacity,
343 "attempted to grow AlignedVec beyond capacity"
344 );
345 debug_assert!(new_len >= self.length, "attempted to shrink AlignedVec");
346 self.length = new_len;
347 }
348}
349
350unsafe impl<const N: usize> Send for AlignedVec<N> {}
352
353unsafe impl<const N: usize> Sync for AlignedVec<N> {}
355
356#[allow(clippy::arithmetic_side_effects)]
357#[cfg(test)]
358mod tests {
359 use {super::*, std::io::Write};
360
361 fn do_test<const ALIGN: usize>() {
362 let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity(10);
363 let ptr = aligned_memory.mem.as_mut_ptr();
364 assert_eq!(
365 ptr.addr() & (ALIGN - 1),
366 0,
367 "memory is not correctly aligned"
368 );
369
370 assert_eq!(aligned_memory.write(&[42u8; 1]).unwrap(), 1);
371 assert_eq!(aligned_memory.write(&[42u8; 9]).unwrap(), 9);
372 assert_eq!(aligned_memory.as_slice(), &[42u8; 10]);
373 assert_eq!(aligned_memory.write(&[42u8; 0]).unwrap(), 0);
374 assert_eq!(aligned_memory.as_slice(), &[42u8; 10]);
375 aligned_memory.write(&[42u8; 1]).unwrap_err();
376 assert_eq!(aligned_memory.as_slice(), &[42u8; 10]);
377 aligned_memory.as_slice_mut().copy_from_slice(&[84u8; 10]);
378 assert_eq!(aligned_memory.as_slice(), &[84u8; 10]);
379
380 let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity_zeroed(10);
381 aligned_memory.fill_write(5, 0).unwrap();
382 aligned_memory.fill_write(2, 1).unwrap();
383 assert_eq!(aligned_memory.write(&[2u8; 3]).unwrap(), 3);
384 assert_eq!(aligned_memory.as_slice(), &[0, 0, 0, 0, 0, 1, 1, 2, 2, 2]);
385 aligned_memory.fill_write(1, 3).unwrap_err();
386 aligned_memory.write(&[4u8; 1]).unwrap_err();
387 assert_eq!(aligned_memory.as_slice(), &[0, 0, 0, 0, 0, 1, 1, 2, 2, 2]);
388
389 let aligned_memory = AlignedMemory::<ALIGN>::zero_filled(10);
390 assert_eq!(aligned_memory.len(), 10);
391 assert_eq!(aligned_memory.as_slice(), &[0u8; 10]);
392
393 let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity_zeroed(15);
394 unsafe {
395 aligned_memory.write_unchecked::<u8>(42);
396 assert_eq!(aligned_memory.len(), 1);
397 aligned_memory.write_unchecked::<u64>(0xCAFEBADDDEADCAFE);
398 assert_eq!(aligned_memory.len(), 9);
399 aligned_memory.fill_write(3, 0).unwrap();
400 aligned_memory.write_all_unchecked(b"foo");
401 assert_eq!(aligned_memory.len(), 15);
402 }
403 let mem = aligned_memory.as_slice();
404 assert_eq!(mem[0], 42);
405 assert_eq!(
406 unsafe {
407 core::ptr::read_unaligned::<u64>(mem[1..1 + mem::size_of::<u64>()].as_ptr().cast())
408 },
409 0xCAFEBADDDEADCAFE
410 );
411 assert_eq!(&mem[1 + mem::size_of::<u64>()..][..3], &[0, 0, 0]);
412 assert_eq!(&mem[1 + mem::size_of::<u64>() + 3..], b"foo");
413 }
414
415 #[test]
416 fn test_aligned_memory() {
417 do_test::<1>();
418 do_test::<16>();
419 do_test::<32768>();
420 }
421
422 #[cfg(debug_assertions)]
423 #[test]
424 #[should_panic(expected = "<= self.mem.capacity()")]
425 fn test_write_unchecked_debug_assert() {
426 let mut aligned_memory = AlignedMemory::<8>::with_capacity(15);
427 unsafe {
428 aligned_memory.write_unchecked::<u64>(42);
429 aligned_memory.write_unchecked::<u64>(24);
430 }
431 }
432
433 #[cfg(debug_assertions)]
434 #[test]
435 #[should_panic(expected = "<= self.mem.capacity()")]
436 fn test_write_all_unchecked_debug_assert() {
437 let mut aligned_memory = AlignedMemory::<8>::with_capacity(5);
438 unsafe {
439 aligned_memory.write_all_unchecked(b"foo");
440 aligned_memory.write_all_unchecked(b"bar");
441 }
442 }
443
444 const fn assert_send<T: Send>() {}
445 const fn assert_sync<T: Sync>() {}
446 const fn assert_unpin<T: Unpin>() {}
447 const _: () = assert_send::<AlignedMemory<8>>();
448 const _: () = assert_sync::<AlignedMemory<8>>();
449 const _: () = assert_unpin::<AlignedMemory<8>>();
450}