polymorph-allocator 0.1.0-beta.1

A minimal memory manager for no_std
Documentation
#![feature(const_fn)]
#![feature(allocator_api)]
#![feature(alloc_layout_extra)]
#![cfg_attr(not(test), no_std)]

extern crate alloc;

use core::mem::{self, size_of};
use core::ptr::NonNull;
use alloc::alloc::{AllocErr, Layout};

pub mod region;
pub use self::region::{Region, RegionFlags};
pub mod align;
pub use self::align::*;
pub mod locked;
pub use self::locked::LockedAllocator;
#[cfg(test)]
mod test;

pub struct Allocator {
    pub first_region: usize,
}

impl Allocator {
    pub const fn empty() -> Allocator {
        Allocator {
            first_region: 0,
        }
    }

    pub fn min_allocation_size() -> usize {
        size_of::<usize>() * 2
    }

    pub unsafe fn add_region(&mut self, addr: usize, size: usize) {
        if size <= Region::min_size() {
            return;
        }

        // Construct region header
        let mut region = Region::new(addr, size);

        // Store old "first region" on the new region as "next"
        region.next = self.first_region;

        // Store the region at the right place in memory
        let ptr = addr as *mut Region;
        ptr.write(region);

        // Change our "first region" to the new region
        self.first_region = ptr as usize;
    }

    pub fn get_region_first(&self) -> Option<&mut Region> {
        if self.first_region == 0 {
            return None;
        }

        unsafe { Some(&mut *(self.first_region as *mut Region)) }
    }

    pub fn get_region_first_free(&self, size: usize) -> Option<&mut Region> {
        let mut region = self.get_region_first();
        loop {
            if let Some(ref r) = region {
                if r.get_used() == false && r.size() >= size {
                    break;
                }
            } else {
                break;
            }

            if region.is_some() {
                region = region.unwrap().next();
            } else {
                break;
            }
        }

        region
    }

    pub fn get_region_for_ptr(&self, ptr: *mut u8) -> Option<&mut Region> {
        let mut region = self.get_region_first();
        loop {
            if let Some(ref r) = region {
                if unsafe { r.data_ptr() } == ptr {
                    return region;
                }
            }

            if region.is_some() {
                region = region.unwrap().next();
            } else {
                break;
            }
        }

        None
    }

    pub fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
        let mut size = layout.size();
        if size < Self::min_allocation_size() {
            size = Self::min_allocation_size();
        }

        size = align_up(size, mem::align_of::<Region>());
        let layout = Layout::from_size_align(size, layout.align()).unwrap();
        let size_pad = layout.padding_needed_for(layout.align()) + layout.size();
        let region = self.get_region_first_free(size_pad);

        // No free regions big enough for this allocation
        if region.is_none() {
            return Err(AllocErr);
        }

        // Mark region as used and add data
        let mut region = region.unwrap();
        region.set_used(true);
        region.padding_start = size_pad - layout.size();

        // If there's enough space after this region to create a new region,
        // do that
        if (layout.size() + Region::min_size()) <= region.size() {
            let data_addr = unsafe { region.data_ptr() as usize } + layout.size();
            let new_size = region.size() - layout.size();
            let mut new_region = Region::new(data_addr, new_size);
            new_region.next = region.next;

            // write pointer and update next region pointer
            unsafe {
                let ptr = data_addr as *mut Region;
                ptr.write(new_region);
                region.next = ptr as usize;
            }

            region.size = layout.size();
        }


        // Successful allocation!
        Ok(unsafe {
            NonNull::new_unchecked(region.data_ptr())
        })
    }

    pub fn deallocate(&mut self, ptr: NonNull<u8>, _layout: Layout) {
        let region = self.get_region_for_ptr(ptr.as_ptr())
            .expect("failed to get region for given pointer");

        let size = region.size();
        region.padding_start = 0;
        region.padding_end = 0;
        region.size = size;
        region.set_used(false);

        // TODO: compact regions
    }
}