1use crate::alignment::AlignmentHint;
51use crate::alloc_free::{alloc_aligned, free_aligned};
52use crate::alloc_result::{AllocResult, AllocationError};
53use libc::madvise;
54use std::ffi::c_void;
55use std::ptr::{null_mut, NonNull};
56
57const ALLOC_FLAGS_NONE: u32 = 0;
59
60const ALLOC_FLAGS_HUGE_PAGES: u32 = 1 << 0;
62
63const ALLOC_FLAGS_SEQUENTIAL: u32 = 1 << 1;
65
66#[derive(Debug)]
90pub struct Memory {
91 pub(crate) flags: u32,
92 pub(crate) num_bytes: usize,
93 pub(crate) address: *mut c_void,
94}
95
96impl Memory {
97 pub fn allocate(
107 num_bytes: usize,
108 sequential: bool,
109 clear: bool,
110 ) -> Result<Self, AllocationError> {
111 if num_bytes == 0 {
112 return Err(AllocationError::EmptyAllocation);
113 }
114
115 let alignment = AlignmentHint::new(num_bytes);
116 let ptr = alloc_aligned(num_bytes, alignment.alignment, clear)?;
117
118 let ptr: *mut c_void = ptr.as_ptr().cast::<c_void>();
119
120 let mut advice = if sequential {
121 libc::MADV_SEQUENTIAL
122 } else {
123 libc::MADV_NORMAL
124 };
125
126 let mut flags = if sequential {
127 ALLOC_FLAGS_SEQUENTIAL
128 } else {
129 ALLOC_FLAGS_NONE
130 };
131
132 if alignment.use_huge_pages {
133 advice |= libc::MADV_HUGEPAGE;
134 flags |= ALLOC_FLAGS_HUGE_PAGES;
135 };
136
137 if advice != 0 {
138 unsafe {
141 madvise(ptr, num_bytes, advice);
142 }
143 }
144
145 Ok(Self::new(AllocResult::Ok, flags, num_bytes, ptr))
146 }
147
148 pub fn free(&mut self) {
152 if self.address.is_null() {
153 return;
154 }
155
156 let alignment = AlignmentHint::new(self.num_bytes);
157
158 debug_assert_ne!(self.address, null_mut());
159 let ptr = core::ptr::NonNull::new(self.address);
160
161 if (self.flags & ALLOC_FLAGS_HUGE_PAGES) == ALLOC_FLAGS_HUGE_PAGES {
162 debug_assert!(alignment.use_huge_pages);
163
164 unsafe {
167 madvise(self.address, self.num_bytes, libc::MADV_FREE);
168 }
169 }
170
171 unsafe {
175 free_aligned(ptr, self.num_bytes, alignment.alignment);
176 }
177
178 self.address = null_mut();
180 self.num_bytes = 0;
181 }
182
183 pub(crate) fn new(
184 status: AllocResult,
185 flags: u32,
186 num_bytes: usize,
187 address: *mut c_void,
188 ) -> Self {
189 debug_assert!(
190 status == AllocResult::Ok && !address.is_null() || address.is_null(),
191 "Found null pointer when allocation status was okay"
192 );
193 Memory {
194 flags,
195 num_bytes,
196 address,
197 }
198 }
199
200 pub(crate) fn from_error(status: AllocResult) -> Self {
201 assert_ne!(status, AllocResult::Ok);
202 Memory {
203 flags: 0,
204 num_bytes: 0,
205 address: null_mut(),
206 }
207 }
208
209 #[inline(always)]
211 pub fn len(&self) -> usize {
212 self.num_bytes
213 }
214
215 pub fn is_empty(&self) -> bool {
217 debug_assert!(self.num_bytes > 0 || self.address.is_null());
218 self.num_bytes == 0
219 }
220
221 #[inline(always)]
223 #[deprecated(note = "Use to_const_ptr or to_ptr instead", since = "0.5.0")]
224 pub fn as_ptr(&self) -> *const c_void {
225 self.to_ptr_const()
226 }
227
228 #[inline(always)]
237 pub fn to_ptr_const(&self) -> *const c_void {
238 self.address.cast_const()
239 }
240
241 #[inline(always)]
243 #[deprecated(note = "Use to_ptr_mut or to_ptr instead", since = "0.5.0")]
244 pub fn as_ptr_mut(&mut self) -> *mut c_void {
245 self.to_ptr_mut()
246 }
247
248 #[inline(always)]
257 pub fn to_ptr_mut(&mut self) -> *mut c_void {
258 self.address
259 }
260
261 #[inline(always)]
292 pub fn to_ptr(&self) -> Option<NonNull<c_void>> {
293 NonNull::new(self.address)
294 }
295}
296
297impl Default for Memory {
298 fn default() -> Self {
299 Memory::from_error(AllocResult::Empty)
300 }
301}
302
303impl Drop for Memory {
304 #[inline(always)]
305 fn drop(&mut self) {
306 self.free()
307 }
308}
309
310macro_rules! impl_asref_slice {
312 ($type:ty) => {
313 impl AsRef<[$type]> for Memory {
314 #[inline(always)]
315 fn as_ref(&self) -> &[$type] {
316 let ptr: *const $type = self.address.cast();
317 let len = self.num_bytes / std::mem::size_of::<$type>();
318 unsafe { &*std::ptr::slice_from_raw_parts(ptr, len) }
319 }
320 }
321
322 impl AsMut<[$type]> for Memory {
323 #[inline(always)]
324 fn as_mut(&mut self) -> &mut [$type] {
325 let ptr: *mut $type = self.address.cast();
326 let len = self.num_bytes / std::mem::size_of::<$type>();
327 unsafe { &mut *std::ptr::slice_from_raw_parts_mut(ptr, len) }
328 }
329 }
330 };
331 ($first:ty, $($rest:ty),+) => {
332 impl_asref_slice!($first);
333 impl_asref_slice!($($rest),+);
334 };
335}
336
337impl_asref_slice!(c_void);
338impl_asref_slice!(i8, u8, i16, u16, i32, u32, i64, u64);
339impl_asref_slice!(isize, usize);
340impl_asref_slice!(f32, f64);
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 const TWO_MEGABYTES: usize = 2 * 1024 * 1024;
347 const SIXTY_FOUR_BYTES: usize = 64;
348
349 #[test]
350 fn alloc_4mb_is_2mb_aligned_hugepage() {
351 const SIZE: usize = TWO_MEGABYTES * 2;
352 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
353
354 assert_ne!(memory.address, null_mut());
355 assert_eq!((memory.address as usize) % TWO_MEGABYTES, 0);
356 assert_eq!(memory.len(), SIZE);
357 assert!(!memory.is_empty());
358 assert_eq!(
359 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
360 ALLOC_FLAGS_HUGE_PAGES
361 );
362 assert_eq!(
363 memory.flags & ALLOC_FLAGS_SEQUENTIAL,
364 ALLOC_FLAGS_SEQUENTIAL
365 );
366 }
367
368 #[test]
369 fn alloc_4mb_nonsequential_is_2mb_aligned_hugepage() {
370 const SIZE: usize = TWO_MEGABYTES * 2;
371 let memory = Memory::allocate(SIZE, false, false).expect("allocation failed");
372
373 assert_ne!(memory.address, null_mut());
374 assert_eq!((memory.address as usize) % TWO_MEGABYTES, 0);
375 assert_eq!(memory.len(), SIZE);
376 assert!(!memory.is_empty());
377 assert_eq!(
378 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
379 ALLOC_FLAGS_HUGE_PAGES
380 );
381 assert_ne!(
382 memory.flags & ALLOC_FLAGS_SEQUENTIAL,
383 ALLOC_FLAGS_SEQUENTIAL
384 );
385 }
386
387 #[test]
388 fn alloc_2mb_is_2mb_aligned_hugepage() {
389 const SIZE: usize = TWO_MEGABYTES;
390 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
391
392 assert_ne!(memory.address, null_mut());
393 assert_eq!((memory.address as usize) % TWO_MEGABYTES, 0);
394 assert_eq!(memory.len(), SIZE);
395 assert!(!memory.is_empty());
396 assert_eq!(
397 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
398 ALLOC_FLAGS_HUGE_PAGES
399 );
400 }
401
402 #[test]
403 fn alloc_1mb_is_64b_aligned() {
404 const SIZE: usize = TWO_MEGABYTES / 2;
405 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
406
407 assert_ne!(memory.address, null_mut());
408 assert_eq!((memory.address as usize) % SIXTY_FOUR_BYTES, 0);
409 assert_eq!(memory.len(), SIZE);
410 assert!(!memory.is_empty());
411 assert_ne!(
412 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
413 ALLOC_FLAGS_HUGE_PAGES
414 );
415 }
416
417 #[test]
418 fn alloc_63kb_is_64b_aligned() {
419 const SIZE: usize = 63 * 1024;
420 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
421
422 assert_ne!(memory.address, null_mut());
423 assert_eq!((memory.address as usize) % SIXTY_FOUR_BYTES, 0);
424 assert_eq!(memory.len(), SIZE);
425 assert!(!memory.is_empty());
426 assert_ne!(
427 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
428 ALLOC_FLAGS_HUGE_PAGES
429 );
430 }
431
432 #[test]
433 fn alloc_64kb_is_64b_aligned() {
434 const SIZE: usize = 64 * 1024;
435 let memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
436
437 assert_ne!(memory.address, null_mut());
438 assert_eq!((memory.address as usize) % SIXTY_FOUR_BYTES, 0);
439 assert_eq!(memory.len(), SIZE);
440 assert!(!memory.is_empty());
441 assert_ne!(
442 memory.flags & ALLOC_FLAGS_HUGE_PAGES,
443 ALLOC_FLAGS_HUGE_PAGES
444 );
445 }
446
447 #[test]
448 fn alloc_0b_is_not_allocated() {
449 const SIZE: usize = 0;
450 let err = Memory::allocate(SIZE, true, true).expect_err("the allocation was empty");
451
452 assert_eq!(err, AllocationError::EmptyAllocation);
453 }
454
455 #[test]
456 fn deref_works() {
457 const SIZE: usize = TWO_MEGABYTES * 2;
458 let mut memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
459
460 let addr: *mut u8 = memory.to_ptr_mut() as *mut u8;
461 unsafe {
462 *addr = 0x42;
463 }
464
465 let reference: &[u8] = memory.as_ref();
466 assert_eq!(reference[0], 0x42);
467 assert_eq!(reference[1], 0x00);
468 assert_eq!(reference.len(), memory.len());
469 }
470
471 #[test]
472 fn deref_mut_works() {
473 const SIZE: usize = TWO_MEGABYTES * 2;
474 let mut memory = Memory::allocate(SIZE, true, true).expect("allocation failed");
475
476 let addr: &mut [f32] = memory.as_mut();
477 addr[0] = 1.234;
478 addr[1] = 5.678;
479
480 let reference: &[f32] = memory.as_ref();
481 assert_eq!(reference[0], 1.234);
482 assert_eq!(reference[1], 5.678);
483 assert_eq!(reference[2], 0.0);
484 assert_eq!(reference.len(), memory.len() / std::mem::size_of::<f32>());
485 }
486}