comet/
allocator.rs

1use crate::{
2    block::Block,
3    globals::LINE_SIZE,
4    header::HeapObjectHeader,
5    internal::{block_list::BlockList, space_bitmap::SpaceBitmap},
6};
7
8pub mod normal;
9pub mod overflow;
10
11/// A type alias for the block, the current low and high offset.
12pub type BlockTuple = (*mut Block, u16, u16);
13
14/// Trait for the allocators in the immix space.
15///
16/// Only use `get_all_blocks()` and `allocate()` from outside.
17pub trait Allocator {
18    fn line_bitmap(&self) -> &SpaceBitmap<LINE_SIZE>;
19    /// Get all block managed by the allocator, draining any local
20    /// collections.
21    fn get_all_blocks(&mut self, list: &mut BlockList);
22
23    /// Get the current block to allocate from.
24    fn take_current_block(&mut self) -> Option<BlockTuple>;
25
26    /// Set the current block to allocate from.
27    fn put_current_block(&mut self, block_tuple: BlockTuple);
28
29    /// Get a new block from a block resource.
30    fn get_new_block(&mut self) -> Option<BlockTuple>;
31
32    /// Callback if no hole of `size` bytes was found in the current block.
33    fn handle_no_hole(&mut self, size: usize) -> Option<BlockTuple>;
34
35    /// Callback if the given `block` has no holes left.
36    fn handle_full_block(&mut self, block: *mut Block);
37
38    /// Allocate an object of `size` bytes or return `None`.
39    ///
40    /// This allocation will be aligned (see `HeapObjectHeader.get_size()`). This
41    /// object is not initialized, just the memory chunk is allocated.
42    ///
43    /// This will try to find a hole in the `take_current_block()`. If there
44    /// Is no hole `handle_no_hole()` will be called. If this function returns
45    /// `None` a 'get_new_block()' is requested.
46    fn allocate(&mut self, size: usize) -> Option<*mut HeapObjectHeader> {
47        self.take_current_block()
48            .and_then(|tp| self.scan_for_hole(size, tp))
49            .or_else(|| self.handle_no_hole(size))
50            .or_else(|| self.get_new_block())
51            .map(|tp| self.allocate_from_block(size, tp))
52            .map(|(tp, object)| {
53                self.put_current_block(tp);
54
55                object
56            })
57    }
58
59    /// Scan a block tuple for a hole of `size` bytes and return a matching
60    /// hole.
61    ///
62    /// If no hole was found `handle_full_block()` is called and None
63    /// returned.
64    #[inline]
65    fn scan_for_hole(&mut self, size: usize, block_tuple: BlockTuple) -> Option<BlockTuple> {
66        let (block, low, high) = block_tuple;
67        match (high - low as u16) as usize >= size {
68            true => Some(block_tuple),
69            false => match unsafe { (*block).scan_block(self.line_bitmap(), high) } {
70                None => {
71                    self.handle_full_block(block);
72                    None
73                }
74                Some((low, high)) => self.scan_for_hole(size, (block, low, high)),
75            },
76        }
77    }
78
79    /// Allocate an uninitialized object of `size` bytes from the block tuple.
80    ///
81    /// Returns the block tuple with a modified low offset and the allocated
82    /// object pointer.
83    ///
84    /// _Note_: This must only be called if there is a hole of `size` bytes
85    /// starting at low offset!
86    fn allocate_from_block(
87        &self,
88        size: usize,
89        block_tuple: BlockTuple,
90    ) -> (BlockTuple, *mut HeapObjectHeader) {
91        let (block, low, high) = block_tuple;
92
93        let object = unsafe { (*block).offset(low as usize) };
94
95        ((block, low as u16 + size as u16, high), object.cast())
96    }
97}