1use crate::alignment::AlignmentHint;
60use crate::alloc_free::{alloc_aligned, free_aligned};
61use crate::alloc_result::{AllocResult, AllocationError};
62use libc::madvise;
63use std::ffi::c_void;
64use std::ptr::{null_mut, NonNull};
65
66const ALLOC_FLAGS_NONE: u32 = 0;
68
69const ALLOC_FLAGS_HUGE_PAGES: u32 = 1 << 0;
71
72const ALLOC_FLAGS_SEQUENTIAL: u32 = 1 << 1;
74
75#[derive(Debug, Clone, Copy)]
88pub struct AllocationConfig {
89 num_bytes: usize,
90 sequential: bool,
91 zeroed: bool,
92}
93
94impl AllocationConfig {
95 pub fn new(num_bytes: usize) -> Self {
97 Self {
98 num_bytes,
99 sequential: false,
100 zeroed: false,
101 }
102 }
103
104 #[must_use]
106 pub fn sequential(mut self) -> Self {
107 self.sequential = true;
108 self
109 }
110
111 #[must_use]
113 pub fn zeroed(mut self) -> Self {
114 self.zeroed = true;
115 self
116 }
117
118 pub fn allocate(self) -> Result<Memory, AllocationError> {
120 Memory::allocate(self.num_bytes, self.sequential, self.zeroed)
121 }
122}
123
124#[derive(Debug)]
148pub struct Memory {
149 pub(crate) flags: u32,
150 pub(crate) num_bytes: usize,
151 pub(crate) address: *mut c_void,
152}
153
154impl Memory {
155 pub fn builder(num_bytes: usize) -> AllocationConfig {
168 AllocationConfig::new(num_bytes)
169 }
170
171 pub fn allocate(
181 num_bytes: usize,
182 sequential: bool,
183 clear: bool,
184 ) -> Result<Self, AllocationError> {
185 if num_bytes == 0 {
186 return Err(AllocationError::EmptyAllocation);
187 }
188
189 let alignment = AlignmentHint::new(num_bytes);
190 let ptr = alloc_aligned(num_bytes, alignment.alignment, clear)?;
191
192 let ptr: *mut c_void = ptr.as_ptr().cast::<c_void>();
193
194 let mut advice = if sequential {
195 libc::MADV_SEQUENTIAL
196 } else {
197 libc::MADV_NORMAL
198 };
199
200 let mut flags = if sequential {
201 ALLOC_FLAGS_SEQUENTIAL
202 } else {
203 ALLOC_FLAGS_NONE
204 };
205
206 if alignment.use_huge_pages {
207 advice |= libc::MADV_HUGEPAGE;
208 flags |= ALLOC_FLAGS_HUGE_PAGES;
209 };
210
211 if advice != 0 {
212 unsafe {
219 madvise(ptr, num_bytes, advice);
220 }
221 }
222
223 Ok(Self::new(AllocResult::Ok, flags, num_bytes, ptr))
224 }
225
226 pub fn free(&mut self) {
230 if self.address.is_null() {
231 return;
232 }
233
234 let alignment = AlignmentHint::new(self.num_bytes);
235
236 debug_assert_ne!(self.address, null_mut());
237 let ptr = core::ptr::NonNull::new(self.address);
238
239 if (self.flags & ALLOC_FLAGS_HUGE_PAGES) == ALLOC_FLAGS_HUGE_PAGES {
240 debug_assert!(alignment.use_huge_pages);
241
242 unsafe {
245 madvise(self.address, self.num_bytes, libc::MADV_FREE);
246 }
247 }
248
249 unsafe {
253 free_aligned(ptr, self.num_bytes, alignment.alignment);
254 }
255
256 self.address = null_mut();
258 self.num_bytes = 0;
259 }
260
261 pub(crate) fn new(
262 status: AllocResult,
263 flags: u32,
264 num_bytes: usize,
265 address: *mut c_void,
266 ) -> Self {
267 debug_assert!(
268 (status == AllocResult::Ok) != address.is_null(),
269 "Allocation status and pointer nullness must agree"
270 );
271 Memory {
272 flags,
273 num_bytes,
274 address,
275 }
276 }
277
278 pub(crate) fn from_error(status: AllocResult) -> Self {
279 assert_ne!(status, AllocResult::Ok);
280 Memory {
281 flags: 0,
282 num_bytes: 0,
283 address: null_mut(),
284 }
285 }
286
287 pub fn len(&self) -> usize {
289 self.num_bytes
290 }
291
292 pub fn is_empty(&self) -> bool {
294 debug_assert!(self.num_bytes > 0 || self.address.is_null());
295 self.num_bytes == 0
296 }
297
298 #[deprecated(note = "Use to_const_ptr or to_ptr instead", since = "0.5.0")]
300 pub fn as_ptr(&self) -> *const c_void {
301 self.to_ptr_const()
302 }
303
304 pub fn to_ptr_const(&self) -> *const c_void {
313 self.address.cast_const()
314 }
315
316 #[deprecated(note = "Use to_ptr_mut or to_ptr instead", since = "0.5.0")]
318 pub fn as_ptr_mut(&mut self) -> *mut c_void {
319 self.to_ptr_mut()
320 }
321
322 pub fn to_ptr_mut(&mut self) -> *mut c_void {
331 self.address
332 }
333
334 pub fn to_ptr(&self) -> Option<NonNull<c_void>> {
365 NonNull::new(self.address)
366 }
367}
368
369impl Default for Memory {
370 fn default() -> Self {
371 Memory::from_error(AllocResult::Empty)
372 }
373}
374
375impl Drop for Memory {
376 fn drop(&mut self) {
377 self.free()
378 }
379}
380
381unsafe impl Send for Memory {}
385unsafe impl Sync for Memory {}
386
387macro_rules! impl_asref_slice {
389 ($type:ty) => {
390 impl AsRef<[$type]> for Memory {
391 fn as_ref(&self) -> &[$type] {
392 let ptr: *const $type = self.address.cast();
393 let len = self.num_bytes / std::mem::size_of::<$type>();
394 unsafe { &*std::ptr::slice_from_raw_parts(ptr, len) }
395 }
396 }
397
398 impl AsMut<[$type]> for Memory {
399 fn as_mut(&mut self) -> &mut [$type] {
400 let ptr: *mut $type = self.address.cast();
401 let len = self.num_bytes / std::mem::size_of::<$type>();
402 unsafe { &mut *std::ptr::slice_from_raw_parts_mut(ptr, len) }
403 }
404 }
405 };
406 ($first:ty, $($rest:ty),+) => {
407 impl_asref_slice!($first);
408 impl_asref_slice!($($rest),+);
409 };
410}
411
412impl_asref_slice!(c_void);
413impl_asref_slice!(i8, u8, i16, u16, i32, u32, i64, u64);
414impl_asref_slice!(isize, usize);
415impl_asref_slice!(f32, f64);
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 const TWO_MEGABYTES: usize = 2 * 1024 * 1024;
422 const SIXTY_FOUR_BYTES: usize = 64;
423
424 #[test]
425 fn alloc_4mb_is_2mb_aligned_hugepage() {
426 const SIZE: usize = TWO_MEGABYTES * 2;
427 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
428
429 assert_ne!(memory.address, null_mut());
430 assert_eq!((memory.address as usize) % TWO_MEGABYTES, 0);
431 assert_eq!(memory.len(), SIZE);
432 assert!(!memory.is_empty());
433 assert_eq!(
434 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
435 ALLOC_FLAGS_HUGE_PAGES
436 );
437 assert_eq!(
438 memory.flags & ALLOC_FLAGS_SEQUENTIAL,
439 ALLOC_FLAGS_SEQUENTIAL
440 );
441 }
442
443 #[test]
444 fn alloc_4mb_nonsequential_is_2mb_aligned_hugepage() {
445 const SIZE: usize = TWO_MEGABYTES * 2;
446 let memory = Memory::allocate(SIZE, false, false).expect("allocation failed");
447
448 assert_ne!(memory.address, null_mut());
449 assert_eq!((memory.address as usize) % TWO_MEGABYTES, 0);
450 assert_eq!(memory.len(), SIZE);
451 assert!(!memory.is_empty());
452 assert_eq!(
453 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
454 ALLOC_FLAGS_HUGE_PAGES
455 );
456 assert_ne!(
457 memory.flags & ALLOC_FLAGS_SEQUENTIAL,
458 ALLOC_FLAGS_SEQUENTIAL
459 );
460 }
461
462 #[test]
463 fn alloc_2mb_is_2mb_aligned_hugepage() {
464 const SIZE: usize = TWO_MEGABYTES;
465 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
466
467 assert_ne!(memory.address, null_mut());
468 assert_eq!((memory.address as usize) % TWO_MEGABYTES, 0);
469 assert_eq!(memory.len(), SIZE);
470 assert!(!memory.is_empty());
471 assert_eq!(
472 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
473 ALLOC_FLAGS_HUGE_PAGES
474 );
475 }
476
477 #[test]
478 fn alloc_1mb_is_64b_aligned() {
479 const SIZE: usize = TWO_MEGABYTES / 2;
480 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
481
482 assert_ne!(memory.address, null_mut());
483 assert_eq!((memory.address as usize) % SIXTY_FOUR_BYTES, 0);
484 assert_eq!(memory.len(), SIZE);
485 assert!(!memory.is_empty());
486 assert_ne!(
487 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
488 ALLOC_FLAGS_HUGE_PAGES
489 );
490 }
491
492 #[test]
493 fn alloc_63kb_is_64b_aligned() {
494 const SIZE: usize = 63 * 1024;
495 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
496
497 assert_ne!(memory.address, null_mut());
498 assert_eq!((memory.address as usize) % SIXTY_FOUR_BYTES, 0);
499 assert_eq!(memory.len(), SIZE);
500 assert!(!memory.is_empty());
501 assert_ne!(
502 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
503 ALLOC_FLAGS_HUGE_PAGES
504 );
505 }
506
507 #[test]
508 fn alloc_64kb_is_64b_aligned() {
509 const SIZE: usize = 64 * 1024;
510 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
511
512 assert_ne!(memory.address, null_mut());
513 assert_eq!((memory.address as usize) % SIXTY_FOUR_BYTES, 0);
514 assert_eq!(memory.len(), SIZE);
515 assert!(!memory.is_empty());
516 assert_ne!(
517 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
518 ALLOC_FLAGS_HUGE_PAGES
519 );
520 }
521
522 #[test]
523 fn alloc_0b_is_not_allocated() {
524 const SIZE: usize = 0;
525 let err = Memory::allocate(SIZE, true, true).expect_err("the allocation was empty");
526
527 assert_eq!(err, AllocationError::EmptyAllocation);
528 }
529
530 #[test]
531 fn deref_works() {
532 const SIZE: usize = TWO_MEGABYTES * 2;
533 let mut memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
534
535 let addr: *mut u8 = memory.to_ptr_mut() as *mut u8;
536 unsafe {
537 *addr = 0x42;
538 }
539
540 let reference: &[u8] = memory.as_ref();
541 assert_eq!(reference[0], 0x42);
542 assert_eq!(reference[1], 0x00);
543 assert_eq!(reference.len(), memory.len());
544 }
545
546 #[test]
547 fn deref_mut_works() {
548 const SIZE: usize = TWO_MEGABYTES * 2;
549 let mut memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
550
551 let addr: &mut [f32] = memory.as_mut();
552 addr[0] = 1.234;
553 addr[1] = 5.678;
554
555 let reference: &[f32] = memory.as_ref();
556 assert_eq!(reference[0], 1.234);
557 assert_eq!(reference[1], 5.678);
558 assert_eq!(reference[2], 0.0);
559 assert_eq!(reference.len(), memory.len() / std::mem::size_of::<f32>());
560 }
561
562 #[test]
563 fn default_is_empty() {
564 let memory = Memory::default();
565 assert!(memory.is_empty());
566 assert_eq!(memory.len(), 0);
567 assert!(memory.to_ptr().is_none());
568 }
569
570 #[test]
571 fn default_free_is_noop() {
572 let mut memory = Memory::default();
573 memory.free(); assert!(memory.is_empty());
575 }
576
577 #[test]
578 fn double_free_is_noop() {
579 const SIZE: usize = 1024;
580 let mut memory = Memory::allocate(SIZE, false, false).expect("allocation failed");
581 memory.free();
582 memory.free(); assert!(memory.is_empty());
584 assert!(memory.to_ptr().is_none());
585 }
586
587 #[test]
588 fn builder_api_works() {
589 const SIZE: usize = 1024;
590 let memory = Memory::builder(SIZE)
591 .sequential()
592 .zeroed()
593 .allocate()
594 .expect("allocation failed");
595
596 assert_eq!(memory.len(), SIZE);
597 assert!(!memory.is_empty());
598 assert!(memory.to_ptr().is_some());
599 }
600
601 #[test]
602 fn memory_is_send_and_sync() {
603 fn assert_send<T: Send>() {}
604 fn assert_sync<T: Sync>() {}
605 assert_send::<Memory>();
606 assert_sync::<Memory>();
607 }
608}