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
106
107
108
use crate::{
    block::Block,
    block_allocator::BlockAllocator,
    globals::{IMMIX_BLOCK_SIZE, LINE_SIZE},
    internal::{block_list::BlockList, space_bitmap::SpaceBitmap},
};

use super::{Allocator, BlockTuple};

/// The `NormalAllocator` is the standard allocator to allocate objects within
/// the immix space.
///
/// Objects smaller than `MEDIUM_OBJECT` bytes are
pub struct NormalAllocator {
    /// The global `BlockAllocator` to get new blocks from.
    pub(crate) block_allocator: *mut BlockAllocator,

    /// The exhausted blocks.
    unavailable_blocks: BlockList,

    /// The blocks with holes to recycle before requesting new blocks..
    pub(crate) recyclable_blocks: BlockList,

    /// The current block to allocate from.
    current_block: Option<BlockTuple>,
    pub(crate) line_bitmap: *const SpaceBitmap<LINE_SIZE>,
}
impl NormalAllocator {
    /// Create a new `NormalAllocator` backed by the given `BlockAllocator`.
    pub fn new(
        block_allocator: *mut BlockAllocator,
        bitmap: *const SpaceBitmap<LINE_SIZE>,
    ) -> NormalAllocator {
        NormalAllocator {
            block_allocator: block_allocator,
            unavailable_blocks: BlockList::new(),
            recyclable_blocks: BlockList::new(),
            current_block: None,
            line_bitmap: bitmap,
        }
    }

    /// Set the recyclable blocks.
    pub fn set_recyclable_blocks(&mut self, blocks: BlockList) {
        self.recyclable_blocks = blocks;
    }
}

impl Allocator for NormalAllocator {
    fn get_all_blocks(&mut self, list: &mut BlockList) {
        while !self.unavailable_blocks.is_empty() {
            list.push(self.unavailable_blocks.pop());
        }
        while !self.recyclable_blocks.is_empty() {
            list.push(self.recyclable_blocks.pop());
        }
        if let Some(block) = self.current_block.take() {
            list.push((block).0);
        }
    }

    fn take_current_block(&mut self) -> Option<BlockTuple> {
        self.current_block.take()
    }

    fn put_current_block(&mut self, block_tuple: BlockTuple) {
        self.current_block = Some(block_tuple);
    }

    fn get_new_block(&mut self) -> Option<BlockTuple> {
        unsafe {
            let block = (*self.block_allocator).get_block();
            if block.is_null() {
                return None;
            }
            (*block).set_allocated();

            Some((block, LINE_SIZE as u16, IMMIX_BLOCK_SIZE as u16 - 1))
        }
    }
    fn line_bitmap(&self) -> &SpaceBitmap<LINE_SIZE> {
        unsafe { &*self.line_bitmap }
    }
    fn handle_no_hole(&mut self, size: usize) -> Option<BlockTuple> {
        if size >= LINE_SIZE {
            None
        } else {
            match self.recyclable_blocks.pop() {
                x if x.is_null() => None,
                block => {
                    match unsafe { (*block).scan_block(&*self.line_bitmap, LINE_SIZE as u16 - 1) } {
                        None => {
                            self.handle_full_block(block);
                            self.handle_no_hole(size)
                        }
                        Some((low, high)) => self
                            .scan_for_hole(size, (block, low, high))
                            .or_else(|| self.handle_no_hole(size)),
                    }
                }
            }
        }
    }

    fn handle_full_block(&mut self, block: *mut Block) {
        self.unavailable_blocks.push(block);
    }
}