use std::ops::Range;
use std::slice;
use slice_pool::sync::{SliceBox, SlicePool};
use super::search as region_search;
use crate::error::{Error, Result};
pub type Allocation = SliceBox<u8>;
pub struct ProximityAllocator {
pub max_distance: usize,
pub pools: Vec<SlicePool<u8>>,
}
impl ProximityAllocator {
pub fn allocate(&mut self, origin: *const (), size: usize) -> Result<Allocation> {
let memory_range = ((origin as usize).saturating_sub(self.max_distance))
..((origin as usize).saturating_add(self.max_distance));
self.allocate_memory(&memory_range, size).or_else(|_| {
self.allocate_pool(&memory_range, origin, size).map(|pool| {
let allocation = pool.alloc(size).unwrap();
self.pools.push(pool);
allocation
})
})
}
pub fn release(&mut self, value: &Allocation) {
let index = self
.pools
.iter()
.position(|pool| {
let lower = pool.as_ptr() as usize;
let upper = lower + pool.len();
(lower..upper).contains(&(value.as_ptr() as usize))
})
.expect("retrieving associated memory pool");
if self.pools[index].len() == 1 {
self.pools.remove(index);
}
}
fn allocate_memory(&mut self, range: &Range<usize>, size: usize) -> Result<Allocation> {
let is_pool_in_range = |pool: &SlicePool<u8>| {
let lower = pool.as_ptr() as usize;
let upper = lower + pool.len();
range.contains(&lower) && range.contains(&(upper - 1))
};
self
.pools
.iter_mut()
.filter_map(|pool| {
if is_pool_in_range(pool) {
pool.alloc(size)
} else {
None
}
})
.next()
.ok_or(Error::OutOfMemory)
}
fn allocate_pool(
&mut self,
range: &Range<usize>,
origin: *const (),
size: usize,
) -> Result<SlicePool<u8>> {
let before = region_search::before(origin, Some(range.clone()));
let after = region_search::after(origin, Some(range.clone()));
after
.chain(before)
.filter_map(|result| match result {
Ok(address) => Self::allocate_fixed_pool(address, size).map(Ok),
Err(error) => Some(Err(error)),
})
.next()
.unwrap_or(Err(Error::OutOfMemory))
}
fn allocate_fixed_pool(address: *const (), size: usize) -> Option<SlicePool<u8>> {
mmap::MemoryMap::new(
size,
&[
mmap::MapOption::MapReadable,
mmap::MapOption::MapWritable,
mmap::MapOption::MapExecutable,
mmap::MapOption::MapAddr(address as *const _),
],
)
.ok()
.map(SliceableMemoryMap)
.map(SlicePool::new)
}
}
struct SliceableMemoryMap(mmap::MemoryMap);
impl SliceableMemoryMap {
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.0.data(), self.0.len()) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.0.data(), self.0.len()) }
}
}
impl AsRef<[u8]> for SliceableMemoryMap {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl AsMut<[u8]> for SliceableMemoryMap {
fn as_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
unsafe impl Send for SliceableMemoryMap {}
unsafe impl Sync for SliceableMemoryMap {}