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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! [`Buddies`](crate): A buddies allocator.
//!
//! This can be used to allocate blocks of different sizes from a single
//! fixed-width block, and is useful in bare-metal physical memory allocation
//! (which is how Linux does things).

#![no_std]

extern crate bitvec;
use bitvec::prelude::*;

/// [`RawBuddies`]: A slightly unsafe buddy allocator.
///
/// A small size and no standard library dependency is traded for an unsafe
/// structure. A safe shell can be constructed around this for built-in
/// allocation of resources as well as a safe allocation result.
pub struct RawBuddies<T> {
    /// The number of buddies.
    num: usize,
    /// A pointer to the first data element (size 2^num).
    data: *mut T,
    /// A pointer to the first bitspace byte.
    bits: *mut u8,
}

impl<T> RawBuddies<T> {
    /// Creates a new [`RawBuddies`].
    ///
    /// ### Conditions
    /// `data` and `bits` are not dropped as long as the instantiation lives.  
    /// `data` is at least of length `2^num`. It may be uninitialized.  
    /// `bits` is at least of length `2^num/8` (i.e it holds `2^num` bits). It
    /// must only contain `0`s (i.e `false`s).
    pub unsafe fn new(num: usize, data: *mut T, bits: *mut u8) -> Self {
        Self {
            num,
            data,
            bits,
        }
    }

    /// Checks if a block of size `2^n` `T`s can be allocated.
    ///
    /// ### Panics
    /// Panics if the block size is too large (`>= buddies`).
    pub fn can_allocate(&self, n: usize) -> bool {
        assert!(n < self.num);
        // Check if matching buddy contains free zones
        self.buddymap_ref(n).any()
    }

    /// Allocates a block of size `2^n` `T`s.
    ///
    /// Note for safe shells: You want to convert the pointer to a slice such
    /// that multiple (mutable) slices can be held simultaneously.
    ///
    /// Returns the reference as well as the block index (for freeing later).
    ///
    /// ### Panics
    /// Panics if the block size is too large (`>= buddies`).
    pub fn allocate(&mut self, n: usize) -> Option<(*mut T, usize)> {
        assert!(n < self.num);
        // Find a free zone for the buddy (auto-exit on fail)
        let pos = self.buddymap_ref(n).iter().position(|s| s)?;
        // Mark the network
        self.set_network(n, pos, true);
        // Return the block
        Some((unsafe { self.data.add(pos << n) }, pos))
    }

    /// Frees a given block by index and size.
    ///
    /// ### Panics
    /// Panics if the block size is too large (`>= buddies`).  
    /// Panics if the index is too large (`>= 2^(buddies-size-1)`).  
    /// Panics if the block was already free (possible double-free).  
    pub fn free(&mut self, n: usize, pos: usize) {
        assert!(n < self.num);
        assert!(pos < (1usize << (self.num - n - 1)));
        assert!(self.buddymap_ref(n)[pos]);
        // Drop the data
        unsafe { core::ptr::drop_in_place(self.data.add(pos << n)); }
        // Free the network
        self.set_network(n, pos, false);
    }

    /// Retrieves a bit slice for a certain buddy immutably.
    ///
    /// Primarily defined for [`can_allocate`].
    fn buddymap_ref(&self, n: usize) -> &BitSlice {
        assert!(n < self.num);
        // Index is 2^(num-n) from end
        let bits: &BitSlice = unsafe {
            core::slice::from_raw_parts(self.bits, 1usize << self.num)
        }.into();
        &bits[
            (1usize << self.num) - (1usize << (self.num - n))
         .. (1usize << self.num) - (1usize << (self.num - n - 1))
        ]
    }

    /// Retrieves a bit slice for a certain buddy mutably.
    fn buddymap_mut(&mut self, n: usize) -> &mut BitSlice {
        assert!(n < self.num);
        // Index is 2^(num-n) from end
        let bits: &mut BitSlice = unsafe {
            core::slice::from_raw_parts_mut(self.bits, 1usize << self.num)
        }.into();
        &mut bits[
            (1usize << self.num) - (1usize << (self.num - n))
         .. (1usize << self.num) - (1usize << (self.num - n - 1))
        ]
    }

    /// Sets a 'network' of bits around a single set.
    ///
    /// Primarily defined for [`allocate`] and [`free`].
    fn set_network(&mut self, n: usize, i: usize, v: bool) {
        assert!(n < self.num);
        assert!(i < (1 << n));
        // Begin by setting the lower network of bits
        for b in 0..n {
            self.buddymap_mut(b)[i << (n-b) .. (i+1) << (n-b)].set_all(v);
        }
        // Set the higher network of bits
        // v == true: Keep setting until we hit another true
        // v == false: Keep setting until the other is true
        if v {
            for b in n .. self.num {
                let map = self.buddymap_mut(b);
                if map[i >> (b-n)] {
                    break;
                }
                map.set(i >> (b-n), true);
            }
        } else {
            for b in n .. self.num {
                let map = self.buddymap_mut(b);
                map.set(i >> (b-n), false);
                if map[(i >> (b-n)) ^ 1] {
                    break;
                }
            }
        }
    }
}