1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
//! Traits to replace or supplement the alloc module in `no_std`.
//! 
//! Defines traits, similar to `alloc::GlobalAlloc`, that can be implemented to defined different
//! kinds of allocators. Unlike the standard library one they do not presume global uniqueness and
//! static lifetime of the memory resource provider. In return, the allocators are not required to
//! implement the `Sync` bound and can easily be built without operating system support to be
//! usable.
//! 
//! There are additional independent crates with additional abstractions on-top:
//! * [`static-alloc`]: A simple allocator drawing from a memory region statically
//!   embedded within the compiled binary.
//! * [`alloc-free`]: A set of data structures (`Box`, `Vec`, `Rc`, ...) that can
//!   be allocated from the implementors of the traits defined here.
//! 
//! [`static-alloc`]: https://crates.io/crates/static-alloc
//! [`alloc-free`]: https://crates.io/crates/alloc-free

// Copyright 2019 Andreas Molzer
#![no_std]
#![deny(missing_docs)]

mod layout;

use core::fmt;
use core::marker::PhantomData;
use core::ptr::{copy_nonoverlapping, write_bytes, NonNull};

pub use layout::{Layout, NonZeroLayout};

/// A marker struct denoting a lifetime that is not simply coercible to another.
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Invariant<'lt> {
    marker: PhantomData<&'lt fn(&'lt ())>,
}

/// An allocation valid for a particular lifetime.
///
/// It is advisable to try and deallocate it before the end of the lifetime instead of leaking the
/// allocation.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Allocation<'alloc> {
    /// A pointer to the allocated and potentially uninitialized bytes.
    pub ptr: NonNull<u8>,
    /// The allocated layout.
    pub layout: NonZeroLayout,
    /// The lifetime of the allocation.
    pub lifetime: Invariant<'alloc>,
}

/// An allocator providing memory regions valid for a particular lifetime.
///
/// It is useful to compare this trait to `std::alloc::GlobalAlloc`. Similar to the trait it is
/// required that the implementors adhere to the contract of the methods.
pub unsafe trait LocalAlloc<'alloc> {
    /// Allocate one block of memory.
    ///
    /// The callee guarantees that a successful return contains a pointer that is valid for **at
    /// least** the layout requested by the caller.
    fn alloc(&'alloc self, layout: NonZeroLayout) -> Option<Allocation<'alloc>>;

    /// Deallocate a block previously allocated.
    /// # Safety
    /// The caller must ensure that:
    /// * `alloc` has been previously returned from a call to `alloc`.
    /// * There are no more pointer to the allocation.
    unsafe fn dealloc(&'alloc self, alloc: Allocation<'alloc>);

    /// Allocate a block of memory initialized with zeros.
    ///
    /// The callee guarantees that a successful return contains a pointer that is valid for **at
    /// least** the layout requested by the caller and the contiguous region of bytes, starting at
    /// the pointer and with the size of the returned layout, is initialized and zeroed.
    fn alloc_zeroed(&'alloc self, layout: NonZeroLayout)
        -> Option<Allocation<'alloc>> 
    {
        let allocation = self.alloc(layout)?;
        unsafe {
            write_bytes(allocation.ptr.as_ptr(), 0u8, allocation.layout.size().into());
        }
        Some(allocation)
    }

    /// Change the layout of a block previously allocated.
    ///
    /// The callee guarantees that a successful return contains a pointer that is valid for **at
    /// least** the layout requested by the caller and the contiguous region of bytes, starting at
    /// the pointer and with the size of the returned layout, is initialized with the prefix of the
    /// previous allocation that is still valid.
    unsafe fn realloc(&'alloc self, alloc: Allocation<'alloc>, layout: NonZeroLayout)
        -> Option<Allocation<'alloc>>
    {
        let new_alloc = self.alloc(layout)?;
        copy_nonoverlapping(
            alloc.ptr.as_ptr(),
            new_alloc.ptr.as_ptr(),
            layout.size().min(alloc.layout.size()).into());
        Some(new_alloc)
    }
}

impl fmt::Debug for Invariant<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Invariant")
    }
}