Struct maskerad_memory_allocators::DoubleEndedStackAllocator [] [src]

pub struct DoubleEndedStackAllocator { /* fields omitted */ }

A double-ended allocator for data implementing the Drop trait.

It manages two MemoryChunks to:

  • Allocate bytes in a stack-like fashion.

  • Store different types of objects in the same storage.

  • Store data needed for a long period of time in one MemoryChunk, and store temporary data in the other.

  • Drop the content of the MemoryChunk when needed.

Instantiation

When instantiated, the memory chunk pre-allocate the given number of bytes, half in the first MemoryChunk, half in the other.

Allocation

When an object is allocated in memory, the StackAllocator:

  • Asks a pointer to a memory address to its memory chunk,

  • Place the object in this memory address,

  • Update the first unused memory address of the memory chunk according to an offset,

  • And return a mutable reference to the object which has been placed in the memory chunk.

This offset is calculated by the size of the object, the size of a TypeDescription structure, its memory-alignment and an offset to align the object in memory.

Roll-back

This structure allows you to get a marker, the index to the first unused memory address of a memory chunk. A stack allocator can be reset to a marker, or reset entirely.

When the allocator is reset to a marker, the memory chunk will drop all the content lying between the marker and the first unused memory address, and set the first unused memory address to the marker.

When the allocator is reset completely, the memory chunk will drop everything and set the first unused memory address to the bottom of its stack.

Purpose

Suppose you want to load data A, and this data need the temporary data B. You need to load B before A in order to create it.

After A is loaded, B is no longer needed. However, since this allocator is a stack, you need to free A before freeing B.

That's why this allocator has two memory chunks, one for temporary data, one for the resident data who need temporary data to be created.

Example

use maskerad_memory_allocators::DoubleEndedStackAllocator;
use maskerad_memory_allocators::ChunkType;
use maskerad_memory_allocators::StackAllocator;

struct LevelConfig {
    data: u32,
}

impl LevelConfig {
    pub fn new(data: u32) -> Self {
        LevelConfig {
            data,
        }
    }

    pub fn data(&self) -> u32 {
        self.data
    }
}

impl Drop for LevelConfig {
    fn drop(&mut self) {
        println!("LevelConfig dropped.");
    }
}

struct Level {
    number: u32
}

impl Level {
    pub fn from_config(config: &mut LevelConfig) -> Self {
        Level {
            number: config.data(),
        }
    }
}

impl Drop for Level {
    fn drop(&mut self) {
        println!("Level dropped.");
    }
}

//50 bytes for each memory chunk.
let double_ended_allocator = DoubleEndedStackAllocator::with_capacity(100);
let allocator = StackAllocator::with_capacity(100);


//we want to load data from a config file, in order to load a level, let's try with a standard stack allocator !

//Get a marker to the current top of the stack, we will reset later to drop the LevelConfig.
let allocator_marker = allocator.marker();

let my_config = allocator.alloc(|| {
    LevelConfig::new(42)
});

//Now create the level.
let my_level = allocator.alloc(|| {
    Level::from_config(my_config)
});

//We got our level, let's drop the config allocated in the allocator !
allocator.reset_to_marker(allocator_marker);

// "Level dropped."
// "LevelConfig dropped."

//Ouch... if we want to drop the LevelConfig, we must drop the Level too, since it is a stack.


let my_config = double_ended_allocator.alloc(&ChunkType::TempData, || {
    LevelConfig::new(42)
});

let my_level = double_ended_allocator.alloc(&ChunkType::ResidentData, || {
    Level::from_config(my_config)
});

double_ended_allocator.reset(&ChunkType::TempData);
// "LevelConfig dropped."

//That's the purpose of the double ended allocator.

Methods

impl DoubleEndedStackAllocator
[src]

[src]

Creates a DoubleEndedStackAllocator with the given capacity, in bytes.

Example

#![feature(alloc)]
use maskerad_memory_allocators::DoubleEndedStackAllocator;

let allocator = DoubleEndedStackAllocator::with_capacity(100);
assert_eq!(allocator.temp_storage().borrow().capacity(), 50);
assert_eq!(allocator.resident_storage().borrow().capacity(), 50);

[src]

Returns an immutable reference to the memory chunk used for resident allocation.

[src]

Returns an immutable reference to the memory chunk used for temporary allocation.

[src]

Allocates data in the allocator's memory.

Panics

This function will panic if the allocation exceeds the maximum storage capacity of the allocator.

Example

use maskerad_memory_allocators::DoubleEndedStackAllocator;
use maskerad_memory_allocators::ChunkType;

struct Monster {
    hp: u32,
    level: u32,
}

impl Default for Monster {
    fn default() -> Self {
        Monster {
        hp: 1,
        level: 1,
        }
    }
}

impl Drop for Monster {
    fn drop(&mut self) {
        println!("I'm dying !");
    }
}

let allocator = DoubleEndedStackAllocator::with_capacity(100);

let my_monster = allocator.alloc(&ChunkType::TempData, || {
    Monster::default()
});

let another_monster = allocator.alloc(&ChunkType::ResidentData, || {
    Monster::default()
});

assert_eq!(my_monster.level, 1);
assert_eq!(another_monster.hp, 1);

[src]

Returns the index of the first unused memory address.

Example

use maskerad_memory_allocators::DoubleEndedStackAllocator;
use maskerad_memory_allocators::ChunkType;

let allocator = DoubleEndedStackAllocator::with_capacity(100); //50 bytes for each memory chunk.

//Get the raw pointer to the bottom of the memory chunk used for temp data.
let start_allocator_temp = allocator.temp_storage().borrow().as_ptr();

//Get the index of the first unused memory address in the memory chunk used for temp data.
let index_temp = allocator.marker(&ChunkType::TempData);

//Calling offset() on a raw pointer is an unsafe operation.
unsafe {
    //Get the raw pointer, with the index.
    let current_top = start_allocator_temp.offset(index_temp as isize);

    //Nothing has been allocated in the memory chunk used for temp data,
    //the top of the stack is the bottom of the memory chunk.
    assert_eq!(current_top, start_allocator_temp);
}

[src]

Reset the allocator, dropping all the content residing inside it.

Example

use maskerad_memory_allocators::DoubleEndedStackAllocator;
use maskerad_memory_allocators::ChunkType;

struct Monster {
    _hp :u32,
}

impl Default for Monster {
    fn default() -> Self {
        Monster {
        _hp: 1,
        }
    }
}

impl Drop for Monster {
    fn drop(&mut self) {
        println!("Monster is dying !");
    }
}

struct Dragon {
    _level: u8,
}

impl Default for Dragon {
    fn default() -> Self {
        Dragon {
            _level: 1,
        }
    }
}

impl Drop for Dragon {
    fn drop(&mut self) {
        println!("Dragon is dying !");
    }
}

let allocator = DoubleEndedStackAllocator::with_capacity(100); // 50 bytes for each memory chunk.

//When nothing has been allocated, the first unused memory address is at index 0.
assert_eq!(allocator.marker(&ChunkType::TempData), 0);
assert_eq!(allocator.marker(&ChunkType::ResidentData), 0);

let my_monster = allocator.alloc(&ChunkType::TempData, || {
    Monster::default()
});

assert_ne!(allocator.marker(&ChunkType::TempData), 0);

let my_dragon = allocator.alloc(&ChunkType::TempData, || {
    Dragon::default()
});

allocator.reset(&ChunkType::TempData);

//The memory chunk for temp data has been totally reset, and all its content has been dropped.

//"Dragon is dying!".
//"Monster is dying!".

assert_eq!(allocator.marker(&ChunkType::TempData), 0);

[src]

Reset partially the allocator, dropping all the content residing between the marker and the first unused memory address of the allocator.

Example

use maskerad_memory_allocators::DoubleEndedStackAllocator;
use maskerad_memory_allocators::ChunkType;

struct Monster {
    _hp :u32,
}

impl Default for Monster {
    fn default() -> Self {
        Monster {
        _hp: 1,
        }
    }
}

impl Drop for Monster {
    fn drop(&mut self) {
        println!("Monster is dying !");
    }
}

struct Dragon {
    _level: u8,
}

impl Default for Dragon {
    fn default() -> Self {
        Dragon {
            _level: 1,
        }
    }
}

impl Drop for Dragon {
    fn drop(&mut self) {
        println!("Dragon is dying !");
    }
}

let allocator = DoubleEndedStackAllocator::with_capacity(100); // 100 bytes.

//When nothing has been allocated, the first unused memory address is at index 0.
assert_eq!(allocator.marker(&ChunkType::TempData), 0);

let my_monster = allocator.alloc(&ChunkType::TempData, || {
    Monster::default()
});

//After the monster allocation, get the index of the first unused memory address in the memory chunk used for temp data.
let index_current_temp = allocator.marker(&ChunkType::TempData);
assert_ne!(index_current_temp, 0);

let my_dragon = allocator.alloc(&ChunkType::TempData, || {
    Dragon::default()
});

assert_ne!(allocator.marker(&ChunkType::TempData), index_current_temp);

allocator.reset_to_marker(&ChunkType::TempData, index_current_temp);

//The allocator has been partially reset, and all the content lying between the marker and
//the first unused memory address has been dropped.

//"Dragon is dying!".

assert_eq!(allocator.marker(&ChunkType::TempData), index_current_temp);

Trait Implementations

impl Drop for DoubleEndedStackAllocator
[src]

[src]

Executes the destructor for this type. Read more