use std::ptr::NonNull;
use std::any::TypeId;
use std::marker::PhantomData;
pub struct Allocal {
data: NonNull<u8>,
cap: usize,
locations: Vec<LocationInner>,
}
#[derive(Clone)]
pub struct LocationInner {
offset: usize,
size: usize,
drop_fn: Option<fn(*mut u8)>,
}
pub struct Location<T> {
index: usize,
_m: PhantomData<*const T>,
}
impl<T> Clone for Location<T> {
fn clone(&self) -> Self {
Self {
index: self.index,
_m: PhantomData,
}
}
}
impl<T> Copy for Location<T> {}
impl<T: 'static, U: 'static> PartialEq<Location<U>> for Location<T> {
#[inline(always)]
fn eq(&self, other: &Location<U>) -> bool {
TypeId::of::<T>() == TypeId::of::<U>() && self.index == other.index
}
}
impl Drop for Allocal {
fn drop(&mut self) {
use std::alloc::{dealloc, Layout};
for loc in &self.locations {
if let Some(drop_fn) = loc.drop_fn {
let ptr = unsafe { self.data.as_ptr().add(loc.offset) };
drop_fn(ptr);
}
}
let layout = unsafe { Layout::from_size_align_unchecked(self.cap, 1) };
unsafe { dealloc(self.data.as_ptr(), layout) };
}
}
impl Allocal {
pub fn with_capacity(capacity: usize) -> Self {
use std::alloc::{alloc, Layout};
let layout = unsafe { Layout::from_size_align_unchecked(capacity, 1) };
let data = unsafe { alloc(layout) };
Self {
data: NonNull::new(data).unwrap(),
cap: capacity,
locations: Vec::new(),
}
}
pub fn reserve_items(&mut self, count: usize) {
self.locations.reserve(count);
}
#[inline]
pub fn get_ptr<T>(&self, location: Location<T>) -> Option<*const T> {
let offset = self.locations.get(location.index)?.offset;
unsafe {
Some(self.data.as_ptr().add(offset).cast())
}
}
#[inline]
pub fn get_mut_ptr<T>(&mut self, location: Location<T>) -> Option<*mut T> {
let offset = self.locations.get(location.index)?.offset;
unsafe {
Some(self.data.as_ptr().add(offset).cast())
}
}
#[inline(always)]
pub fn try_get<T>(&self, location: Location<T>) -> Option<&T> {
unsafe {
Some(&*self.get_ptr(location)?)
}
}
#[inline(always)]
pub fn try_get_mut<T>(&mut self, location: Location<T>) -> Option<&mut T> {
unsafe {
Some(&mut *self.get_mut_ptr(location)?)
}
}
#[inline(always)]
pub fn get<T>(&self, location: Location<T>) -> &T {
self.try_get(location).unwrap()
}
#[inline(always)]
pub fn get_mut<T>(&mut self, location: Location<T>) -> &mut T {
self.try_get_mut(location).unwrap()
}
pub fn allocate<T: 'static>(&mut self, item: T) -> Result<Location<T>, ()> {
use std::mem::{align_of, size_of, needs_drop};
let size = size_of::<T>();
if size == 0 {
let location = LocationInner {
size,
offset: usize::MAX,
drop_fn: None,
};
let index = self.locations.len();
self.locations.push(location.clone());
return Ok(Location {
index,
_m: PhantomData,
});
}
let align = align_of::<T>();
let mut offset = (self.data.as_ptr() as usize) % align;
let loc = if self.locations.is_empty() {
if offset + size > self.cap {
return Err(());
}
0usize
} else {
let mut loc = 0usize;
loop {
while offset < self.locations[loc].offset + self.locations[loc].size {
offset += align;
}
loc += 1;
if loc >= self.locations.len() {
if offset + size > self.cap {
return Err(());
}
break;
}
if offset + size < self.locations[loc].offset {
break;
}
}
loc
};
let location = LocationInner {
offset,
size,
drop_fn: if needs_drop::<T>() {
Some(|ptr: *mut u8| unsafe { std::ptr::drop_in_place(ptr as *mut T) })
} else { None },
};
self.locations.insert(loc, location.clone());
unsafe {
let ptr: *mut T = self.data.as_ptr().add(offset).cast();
ptr.write(item);
}
Ok(Location {
index: loc,
_m: PhantomData,
})
}
pub fn deallocate<T>(&mut self, location: Location<T>) -> T {
let location = self.locations.remove(location.index);
unsafe {
let ptr: *mut T = self.data.as_ptr().add(location.offset).cast();
ptr.read()
}
}
}
#[cfg(test)]
#[test]
fn create() {
let mut allocal = Allocal::with_capacity(100);
let mut locations = Vec::new();
for i in 0u8..100 {
println!("{}", i);
locations.push(allocal.allocate(i).unwrap());
}
assert!(allocal.allocate(0u8).is_err());
for (i, loc) in locations.clone().into_iter().enumerate() {
assert_eq!(*allocal.get(loc), i as u8);
}
for (i, loc) in locations.clone().into_iter().enumerate().rev() {
assert_eq!(allocal.deallocate(loc), i as u8);
}
for loc in locations.into_iter() {
assert_eq!(allocal.try_get(loc), None);
}
}
#[cfg(test)]
#[test]
fn alignement() {
use std::mem::align_of;
for _ in 0..100 {
let mut allocal = Allocal::with_capacity(30);
let loc = allocal.allocate(0u128).unwrap();
let ptr = allocal.get_ptr(loc).unwrap() as usize;
assert_eq!(ptr % align_of::<u128>(), 0);
}
}
#[cfg(test)]
#[test]
fn drop() {
use std::rc::Rc;
use std::cell::Cell;
let drop_count = Rc::new(Cell::new(0u8));
struct DropCheck(Rc<Cell<u8>>);
impl Drop for DropCheck {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
{
let mut allocal = Allocal::with_capacity(1024);
for _ in 0..20 {
allocal.allocate(DropCheck(Rc::clone(&drop_count))).unwrap();
}
}
assert_eq!(drop_count.get(), 20);
}