#![feature(
alloc,
coerce_unsized,
drop_in_place,
heap_api,
placement_new_protocol,
placement_in_syntax,
raw,
unique,
unsize,
)]
use std::error::Error as StdError;
use std::fmt;
use std::marker::PhantomData;
use std::ptr::Unique;
use alloc::heap;
extern crate alloc;
mod boxed;
pub mod composable;
pub mod freelist;
pub mod scoped;
pub use boxed::{AllocBox, Place};
pub use composable::*;
pub use freelist::FreeList;
pub use scoped::Scoped;
pub unsafe trait Allocator {
#[inline]
fn allocate<T>(&self, val: T) -> Result<AllocBox<T, Self>, (Error, T)>
where Self: Sized
{
match self.make_place() {
Ok(place) => {
Ok(in place { val })
}
Err(err) => {
Err((err, val))
}
}
}
fn make_place<T>(&self) -> Result<Place<T, Self>, Error>
where Self: Sized
{
boxed::make_place(self)
}
unsafe fn allocate_raw(&self, size: usize, align: usize) -> Result<Block, Error>;
unsafe fn reallocate_raw<'a>(&'a self, block: Block<'a>, new_size: usize) -> Result<Block<'a>, (Error, Block<'a>)>;
unsafe fn deallocate_raw(&self, block: Block);
}
pub trait BlockOwner: Allocator {
fn owns<'a, T, A: Allocator>(&self, val: &AllocBox<'a, T, A>) -> bool {
self.owns_block(& unsafe { val.as_block() })
}
fn owns_block(&self, block: &Block) -> bool;
fn with_fallback<O: BlockOwner>(self, other: O) -> Fallback<Self, O>
where Self: Sized
{
Fallback::new(self, other)
}
}
pub struct Block<'a> {
ptr: Unique<u8>,
size: usize,
align: usize,
_marker: PhantomData<&'a [u8]>,
}
impl<'a> Block<'a> {
pub fn new(ptr: *mut u8, size: usize, align: usize) -> Self {
assert!(!ptr.is_null());
Block {
ptr: unsafe { Unique::new(ptr) },
size: size,
align: align,
_marker: PhantomData,
}
}
pub fn empty() -> Self {
Block {
ptr: unsafe { Unique::new(heap::EMPTY as *mut u8) },
size: 0,
align: 0,
_marker: PhantomData,
}
}
pub fn ptr(&self) -> *mut u8 {
*self.ptr
}
pub fn size(&self) -> usize {
self.size
}
pub fn align(&self) -> usize {
self.align
}
pub fn is_empty(&self) -> bool {
self.size == 0
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
OutOfMemory,
UnsupportedAlignment,
AllocatorSpecific(String),
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(self.description())
}
}
impl StdError for Error {
fn description(&self) -> &str {
use Error::*;
match *self {
OutOfMemory => {
"Allocator out of memory."
}
UnsupportedAlignment => {
"Attempted to allocate with unsupported alignment."
}
AllocatorSpecific(ref reason) => {
reason
}
}
}
}
#[derive(Debug)]
pub struct HeapAllocator;
pub const HEAP: &'static HeapAllocator = &HeapAllocator;
unsafe impl Allocator for HeapAllocator {
#[inline]
unsafe fn allocate_raw(&self, size: usize, align: usize) -> Result<Block, Error> {
if size != 0 {
let ptr = heap::allocate(size, align);
if !ptr.is_null() {
Ok(Block::new(ptr, size, align))
} else {
Err(Error::OutOfMemory)
}
} else {
Ok(Block::empty())
}
}
#[inline]
unsafe fn reallocate_raw<'a>(&'a self, block: Block<'a>, new_size: usize) -> Result<Block<'a>, (Error, Block<'a>)> {
if new_size == 0 {
self.deallocate_raw(block);
Ok(Block::empty())
} else if block.is_empty() {
Err((Error::UnsupportedAlignment, block))
} else {
let new_ptr = heap::reallocate(block.ptr(), block.size(), new_size, block.align());
if new_ptr.is_null() {
Err((Error::OutOfMemory, block))
} else {
Ok(Block::new(new_ptr, new_size, block.align()))
}
}
}
#[inline]
unsafe fn deallocate_raw(&self, block: Block) {
if !block.is_empty() {
heap::deallocate(block.ptr(), block.size(), block.align())
}
}
}
#[inline]
fn align_forward(ptr: *mut u8, align: usize) -> *mut u8 {
((ptr as usize + align - 1) & !(align - 1)) as *mut u8
}
unsafe impl<'a, A: ?Sized + Allocator + 'a> Allocator for Box<A> {
unsafe fn allocate_raw(&self, size: usize, align: usize) -> Result<Block, Error> {
(**self).allocate_raw(size, align)
}
unsafe fn reallocate_raw<'b>(&'b self, block: Block<'b>, new_size: usize) -> Result<Block<'b>, (Error, Block<'b>)> {
(**self).reallocate_raw(block, new_size)
}
unsafe fn deallocate_raw(&self, block: Block) {
(**self).deallocate_raw(block)
}
}
unsafe impl<'a, 'b: 'a, A: ?Sized + Allocator + 'b> Allocator for &'a A {
unsafe fn allocate_raw(&self, size: usize, align: usize) -> Result<Block, Error> {
(**self).allocate_raw(size, align)
}
unsafe fn reallocate_raw<'c>(&'c self, block: Block<'c>, new_size: usize) -> Result<Block<'c>, (Error, Block<'c>)> {
(**self).reallocate_raw(block, new_size)
}
unsafe fn deallocate_raw(&self, block: Block) {
(**self).deallocate_raw(block)
}
}
unsafe impl<'a, 'b: 'a, A: ?Sized + Allocator + 'b> Allocator for &'a mut A {
unsafe fn allocate_raw(&self, size: usize, align: usize) -> Result<Block, Error> {
(**self).allocate_raw(size, align)
}
unsafe fn reallocate_raw<'c>(&'c self, block: Block<'c>, new_size: usize) -> Result<Block<'c>, (Error, Block<'c>)> {
(**self).reallocate_raw(block, new_size)
}
unsafe fn deallocate_raw(&self, block: Block) {
(**self).deallocate_raw(block)
}
}
#[cfg(test)]
mod tests {
use std::any::Any;
use super::*;
#[test]
fn heap_lifetime() {
let my_int;
{
my_int = HEAP.allocate(0i32).unwrap();
}
assert_eq!(*my_int, 0);
}
#[test]
fn heap_in_place() {
let big = in HEAP.make_place().unwrap() { [0u8; 8_000_000] };
assert_eq!(big.len(), 8_000_000);
}
#[test]
fn unsizing() {
#[derive(Debug)]
struct Bomb;
impl Drop for Bomb {
fn drop(&mut self) {
println!("Boom")
}
}
let my_foo: AllocBox<Any, _> = HEAP.allocate(Bomb).unwrap();
let _: AllocBox<Bomb, _> = my_foo.downcast().ok().unwrap();
}
#[test]
fn take_out() {
let _: [u8; 1024] = HEAP.allocate([0; 1024]).ok().unwrap().take();
}
#[test]
fn boxed_allocator() {
#[derive(Debug)]
struct Increment<'a>(&'a mut i32);
impl<'a> Drop for Increment<'a> {
fn drop(&mut self) {
*self.0 += 1;
}
}
let mut i = 0;
let alloc: Box<Allocator> = Box::new(HEAP);
{
let _ = alloc.allocate(Increment(&mut i)).unwrap();
}
assert_eq!(i, 1);
}
}