#memory-allocator #allocator #memory #allocation #building-block

nightly allocators

composable memory allocators and utilities for creating more

4 releases

Uses old Rust 2015

0.1.9 Jan 11, 2016
0.1.7 Oct 18, 2015
0.1.6 Oct 16, 2015
0.1.5 Oct 16, 2015

#354 in Memory management

MIT/Apache

39KB
807 lines

Allocators

Build Status Crates.io

Documentation

This crate provides a different memory allocators, as well as an Allocator trait for creating other custom allocators. A main goal of allocators is composability. For this reason, it also provides some composable primitives to be used as building blocks for chained allocators. This crate leans heavily on unsafe/unstable code at the moment, and should be considered very experimental.

Why?

For Rust to fulfill its description as a systems programming language, users need to have more fine-grained control over the way memory is allocated in their programs. This crate is a proof-of-concept that these mechanisms can be implemented in Rust and provide a safe interface to their users.

Allocator Traits

Allocator

This is the core trait for allocators to implement. All a type has to do is implement two unsafe functions: allocate_raw and deallocate_raw. This will likely require reallocate_raw in the future.

BlockOwner

Allocators that implement this can say definitively whether they own a block.

Allocator Types

Scoped Allocator

This is useful for reusing a block of memory for temporary allocations in a tight loop. Scopes can be nested and values allocated in a scope cannot be moved outside it.

#![feature(placement_in_syntax)]
use allocators::{Allocator, Scoped};
#[derive(Debug)]
struct Bomb(u8);
impl Drop for Bomb {
    fn drop(&mut self) {
        println!("Boom! {}", self.0);
    }
}
// new scoped allocator with a kilobyte of memory.
let alloc = Scoped::new(1024).unwrap();
alloc.scope(|inner| {
    let mut bombs = Vec::new();
    // allocate_val makes the value on the stack first.
    for i in 0..100 { bombs.push(inner.allocate(Bomb(i)).unwrap())}
    // watch the bombs go off!
});
// Allocators also have placement-in syntax.
let my_int = in alloc.make_place().unwrap() { 23 };
println!("My int: {}", *my_int);

Free List Allocator

This allocator maintains a list of free blocks of a given size.

use allocators::{Allocator, FreeList};

// create a FreeList allocator with 64 blocks of 1024 bytes.
let alloc = FreeList::new(1024, 64).unwrap();
for _ in 0..10 {
    // allocate every block
    let mut v = Vec::new();
    for i in 0u8..64 {
        v.push(alloc.allocate([i; 1024]).unwrap());
    }
    // no more blocks :(.
    assert!(alloc.allocate([0; 1024]).is_err());

    // all the blocks get pushed back onto the freelist at the end here, 
    // memory gets reused efficiently in the next iteration.
}

This allocator can yield very good performance for situations like the above, where each block's space is being fully used.

Composable Primitives

These are very underdeveloped at the moment, and lack a fluent API as well. They are definitely a back-burner feature at the moment, since the idea of composable allocators hasn't really proved its value yet.

Null Allocator

This will probably get a new name since "Null" has some misleading connotations.

It fails to allocate any request made to it, and panics when deallocated with.

Fallback Allocator

This composes two BlockOwners: a main allocator and a fallback. If the main allocator fails to allocate, it turns to the fallback.

Proxy Allocator

This wraps any allocator and something which implements the ProxyLogger trait, which provides functions to log allocation, deallocation, and reallocation in any arbitrary way. It has practical applications in debug builds for measuring how an allocator is being utilized.

No runtime deps