use libc::{off_t, size_t};
use std::alloc::{GlobalAlloc, Layout};
use std::cell::Cell;
use std::os::raw::{c_int, c_long, c_void};
use std::ptr;
#[derive(Debug, Clone, Copy)]
pub struct MmapAllocator;
impl Default for MmapAllocator {
#[inline]
fn default() -> Self {
Self
}
}
impl MmapAllocator {
#[inline]
pub const fn new() -> Self {
Self
}
}
unsafe impl GlobalAlloc for MmapAllocator {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
const ADDR: *mut c_void = ptr::null_mut::<c_void>();
let length = layout.size() as size_t;
const PROT: c_int = libc::PROT_READ | libc::PROT_WRITE;
const FLAGS: c_int = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS;
const FD: c_int = -1; const OFFSET: off_t = 0;
match mmap(ADDR, length, PROT, FLAGS, FD, OFFSET) {
libc::MAP_FAILED => ptr::null_mut::<u8>(),
ret => {
let ptr = ret as usize;
assert_eq!(0, ptr % layout.align());
ret as *mut u8
}
}
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let addr = ptr as *mut c_void;
let length = layout.size() as size_t;
munmap(addr, length);
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
self.alloc(layout)
}
#[cfg(linux)]
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let old_address = ptr as *mut c_void;
let old_size = layout.size() as size_t;
let new_size = new_size as size_t;
let FLAGS = libc::MREMAP_MAYMOVE;
match mremap(old_address, old_size, new_size, FLAGS) {
libc::MAP_FAILED => ptr::null_mut::<u8>(),
ret => ret as *mut u8,
}
}
}
extern "C" {
fn mmap(
addr: *mut c_void,
length: size_t,
prot: c_int,
flags: c_int,
fd: c_int,
offset: off_t,
) -> *mut c_void;
fn munmap(addr: *mut c_void, length: size_t);
#[cfg(linux)]
fn mremap(
old_address: *mut c_void,
old_size: size_t,
new_size: size_t,
flags: c_int,
) -> *mut c_void;
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
use std::ptr;
const ENOERR: i32 = 0;
fn clear_errno() {
unsafe { *libc::__errno_location() = 0 }
}
fn errno() -> i32 {
unsafe { *libc::__errno_location() }
}
#[test]
fn default() {
let _alloc = MmapAllocator::default();
}
#[test]
fn allocate() {
unsafe {
type T = i64;
let alloc = MmapAllocator::default();
let layout = Layout::new::<i64>();
let ptr = alloc.alloc(layout) as *mut T;
assert_ne!(std::ptr::null(), ptr);
*ptr = 84;
assert_eq!(84, *ptr);
*ptr = *ptr * -2;
assert_eq!(-168, *ptr);
alloc.dealloc(ptr as *mut u8, layout)
}
}
#[test]
fn allocate_too_large() {
unsafe {
clear_errno();
type T = String;
let alloc = MmapAllocator::default();
let align = mem::align_of::<T>();
let size = std::usize::MAX - mem::size_of::<T>();
let layout = Layout::from_size_align(size, align).unwrap();
assert_eq!(ptr::null(), alloc.alloc(layout));
assert_ne!(ENOERR, errno());
}
}
#[test]
fn allocate_zero_size() {
unsafe {
clear_errno();
type T = String;
let alloc = MmapAllocator::default();
let align = mem::align_of::<T>();
let size = 0;
let layout = Layout::from_size_align(size, align).unwrap();
assert_eq!(ptr::null(), alloc.alloc(layout));
assert_ne!(ENOERR, errno());
}
}
#[test]
fn alloc_zeroed() {
unsafe {
type T = [u8; 1025];
let alloc = MmapAllocator::default();
let layout = Layout::new::<T>();
let ptr = alloc.alloc_zeroed(layout) as *mut T;
let s: &[u8] = &*ptr;
for u in s {
assert_eq!(0, *u);
}
alloc.dealloc(ptr as *mut u8, layout);
}
}
#[test]
fn realloc() {
unsafe {
type T = [u8; 1025];
let alloc = MmapAllocator::default();
let layout = Layout::new::<T>();
let ptr = alloc.alloc(layout) as *mut T;
let ts = &mut *ptr;
for t in ts.iter_mut() {
*t = 1;
}
type U = (T, T);
let new_size = mem::size_of::<U>();
let ptr = alloc.realloc(ptr as *mut u8, layout, new_size) as *mut T;
let layout = Layout::from_size_align(new_size, layout.align()).unwrap();
let ts = &mut *ptr;
for t in ts.iter_mut() {
assert_eq!(1, *t);
*t = 2;
}
let new_size = mem::size_of::<u8>();
let ptr = alloc.realloc(ptr as *mut u8, layout, new_size);
let layout = Layout::from_size_align(new_size, layout.align()).unwrap();
assert_eq!(2, *ptr);
alloc.dealloc(ptr, layout);
}
}
#[test]
fn realloc_too_large() {
unsafe {
type T = [u8; 1025];
let alloc = MmapAllocator::default();
let layout = Layout::new::<T>();
let ptr = alloc.alloc(layout) as *mut T;
let ts = &mut *ptr;
for t in ts.iter_mut() {
*t = 1;
}
let new_size = std::usize::MAX - mem::size_of::<T>();
let new_ptr = alloc.realloc(ptr as *mut u8, layout, new_size);
assert!(new_ptr.is_null());
assert_ne!(ENOERR, errno());
for t in ts.iter() {
assert_eq!(1, *t);
}
alloc.dealloc(ptr as *mut u8, layout);
}
}
}
thread_local! {
static PAGE_SIZE: Cell<usize> = Cell::new(0);
}
#[inline]
pub fn page_size() -> usize {
PAGE_SIZE.with(|s| match s.get() {
0 => {
let ret = unsafe { sysconf(libc::_SC_PAGE_SIZE) as usize };
s.set(ret);
ret
}
ret => ret,
})
}
extern "C" {
fn sysconf(name: c_int) -> c_long;
}