Quellcode durchsuchen

Represent occupied block entries through empty blocks

ticki vor 9 Jahren
Ursprung
Commit
8c7ccdc0af
2 geänderte Dateien mit 43 neuen und 90 gelöschten Zeilen
  1. 14 57
      src/block.rs
  2. 29 33
      src/bookkeeper.rs

+ 14 - 57
src/block.rs

@@ -1,6 +1,6 @@
 //! Memory primitives.
 
-use core::{ops, cmp};
+use core::cmp;
 use core::ptr::Unique;
 
 /// A contigious memory block.
@@ -19,6 +19,19 @@ impl Block {
         }
     }
 
+    /// Is this block free?
+    pub fn is_free(&self) -> bool {
+        self.size != 0
+    }
+
+    /// Set this block as free.
+    ///
+    /// This will not deallocate, but it will simply set the size to zero, which is the
+    /// representation of a freeed block.
+    pub fn set_free(&mut self) {
+        self.size = 0;
+    }
+
     /// Is this block is left to `to`?
     pub fn left_to(&self, to: &Block) -> bool {
         self.size + *self.ptr as usize == *to.ptr as usize
@@ -45,53 +58,6 @@ impl cmp::PartialEq for Block {
 
 impl cmp::Eq for Block {}
 
-/// A block entry.
-///
-/// A block entry is a wrapper around `Block` containing an extra field telling if this block is
-/// free or not.
-#[derive(PartialEq, Eq)]
-pub struct BlockEntry {
-    /// The underlying block.
-    block: Block,
-    /// Is this block free?
-    pub free: bool,
-}
-
-impl ops::Deref for BlockEntry {
-    type Target = Block;
-
-    fn deref(&self) -> &Block {
-        &self.block
-    }
-}
-
-impl ops::DerefMut for BlockEntry {
-    fn deref_mut(&mut self) -> &mut Block {
-        &mut self.block
-    }
-}
-
-impl PartialOrd for BlockEntry {
-    fn partial_cmp(&self, other: &BlockEntry) -> Option<cmp::Ordering> {
-        self.block.partial_cmp(other)
-    }
-}
-
-impl Ord for BlockEntry {
-    fn cmp(&self, other: &BlockEntry) -> cmp::Ordering {
-        self.block.cmp(other)
-    }
-}
-
-impl From<Block> for BlockEntry {
-    fn from(block: Block) -> BlockEntry {
-        BlockEntry {
-            block: block,
-            free: true,
-        }
-    }
-}
-
 #[cfg(test)]
 mod test {
     use super::*;
@@ -116,15 +82,6 @@ mod test {
         assert_eq!(*b.end(), *c.ptr);
     }
 
-    #[test]
-    fn test_from() {
-        let ent: BlockEntry = Block {
-            size: 10,
-            ptr: unsafe { Unique::new(10 as *mut _) },
-        }.into();
-        assert!(ent.free)
-    }
-
     #[test]
     fn test_left_to() {
         let a = Block {

+ 29 - 33
src/bookkeeper.rs

@@ -1,11 +1,9 @@
 //! The memory bookkeeping module.
 //!
 //! Blocks are the main unit for the memory bookkeeping. A block is a simple construct with a
-//! `Unique` pointer and a size. `BlockEntry` contains an additional field, which marks if a block
-//! is free or not. The block list is simply a continuous list of block entries kept in the
-//! bookkeeper.
+//! `Unique` pointer and a size. Occupied (non-free) blocks are represented by a zero-sized block.
 
-use block::{BlockEntry, Block};
+use block::Block;
 use sys;
 
 use core::mem::align_of;
@@ -19,9 +17,9 @@ const EMPTY_HEAP: *mut u8 = 0x1 as *mut _;
 ///
 /// This is the main primitive in ralloc. Its job is to keep track of the free blocks in a
 /// structured manner, such that allocation, reallocation, and deallocation are all efficient.
-/// Parituclarly, it keeps a list of free blocks, commonly called the "block list". This list is
-/// kept. Entries in the block list can be "empty", meaning that you can overwrite the entry
-/// without breaking consistency.
+/// Parituclarly, it keeps a list of blocks, commonly called the "block list". This list is kept.
+/// Entries in the block list can be "empty", meaning that you can overwrite the entry without
+/// breaking consistency.
 ///
 /// For details about the internals, see [`BlockList`](./struct.BlockList.html) (requires the docs
 /// to be rendered with private item exposed).
@@ -34,7 +32,7 @@ pub struct Bookkeeper {
     /// Certain guarantees are made:
     ///
     /// 1. The list is always sorted with respect to the block's pointers.
-    /// 2. No two free blocks overlap.
+    /// 2. No two blocks overlap.
     /// 3. No two free blocks are adjacent.
     block_list: BlockList,
 }
@@ -125,7 +123,7 @@ struct BlockList {
     /// The length of the block list.
     len: usize,
     /// The pointer to the first element in the block list.
-    ptr: Unique<BlockEntry>,
+    ptr: Unique<Block>,
 }
 
 impl BlockList {
@@ -187,7 +185,10 @@ impl BlockList {
         for (n, i) in self.iter_mut().enumerate().rev() {
             let aligner = aligner(*i.ptr, align);
 
-            if i.free && i.size - aligner >= size {
+            if i.size >= size + aligner {
+                // To catch dumb logic errors.
+                debug_assert!(i.is_free(), "Block is not free (What the fuck, Richard?)");
+
                 // Use this block as the one, we use for our allocation.
                 block = Some((n, Block {
                     size: i.size,
@@ -197,7 +198,7 @@ impl BlockList {
                 // Leave the stub behind.
                 if aligner == 0 {
                     // Since the stub is empty, we are not interested in keeping it marked as free.
-                    i.free = false;
+                    i.set_free();
                 } else {
                     i.size = aligner;
                 }
@@ -212,7 +213,7 @@ impl BlockList {
                 self.insert(n, Block {
                     size: b.size - size,
                     ptr: unsafe { Unique::new((*b.ptr as usize + size) as *mut _) },
-                }.into());
+                });
             }
 
             // Check consistency.
@@ -230,7 +231,7 @@ impl BlockList {
     ///
     /// This will append a block entry to the end of the block list. Make sure that this entry has
     /// a value higher than any of the elements in the list, to keep it sorted.
-    fn push(&mut self, block: BlockEntry) {
+    fn push(&mut self, block: Block) {
         let len = self.len;
         // This is guaranteed not to overflow, since `len` is bounded by the address space, since
         // each entry represent at minimum one byte, meaning that `len` is bounded by the address
@@ -249,7 +250,7 @@ impl BlockList {
     ///
     /// If it fails, the value will be where the block could be inserted to keep the list sorted.
     fn search(&mut self, block: &Block) -> Result<usize, usize> {
-        self.binary_search_by(|x| (**x).cmp(block))
+        self.binary_search_by(|x| x.cmp(block))
     }
 
     /// Allocate _fresh_ space.
@@ -276,14 +277,14 @@ impl BlockList {
 
         // Add it to the list. This will not change the order, since the pointer is higher than all
         // the previous blocks.
-        self.push(alignment_block.into());
+        self.push(alignment_block);
 
         // Add the extra space allocated.
         self.push(Block {
             // This won't overflow, since `can_size` is bounded by `size`
             size: can_size - size,
             ptr: res.end(),
-        }.into());
+        });
 
         // Check consistency.
         self.check();
@@ -318,7 +319,7 @@ impl BlockList {
             // The addition of the sizes are guaranteed not to overflow, due to only being reach if the
             // next block is free, effectively asserting that the blocks are disjoint, implying that
             // their sum is bounded by the address space (see the guarantees).
-            if self[ind + 1].free && self[ind].size + self[ind + 1].size >= size {
+            if self[ind + 1].is_free() && self[ind].size + self[ind + 1].size >= size {
                 // Calculate the additional space.
                 //
                 // This is guaranteed to not overflow, since the `if` statement's condition implies
@@ -331,11 +332,6 @@ impl BlockList {
                 };
                 self[ind + 1].size -= additional;
 
-                // Set the excessive block as free if it is empty.
-                if self[ind + 1].size == 0 {
-                    self[ind + 1].free = false;
-                }
-
                 // Check consistency.
                 self.check();
 
@@ -426,7 +422,7 @@ impl BlockList {
                 };
 
                 let cap = self.cap;
-                Unique::new(*self.realloc(block, cap, align_of::<BlockEntry>()) as *mut _)
+                Unique::new(*self.realloc(block, cap, align_of::<Block>()) as *mut _)
             };
 
             // Check consistency.
@@ -483,16 +479,16 @@ impl BlockList {
     fn free(&mut self, block: Block) {
         let ind = self.find(&block);
 
-        debug_assert!(*self[ind].ptr != *block.ptr || !self[ind].free, "Double free.");
+        debug_assert!(*self[ind].ptr != *block.ptr || !self[ind].is_free(), "Double free.");
 
         // Try to merge right.
-        if self[ind].free && ind < self.len - 1 && self[ind].left_to(&block) {
+        if self[ind].is_free() && ind + 1 < self.len && self[ind].left_to(&block) {
             self[ind].size += block.size;
         // Try to merge left. Note that `self[ind]` is not free, by the conditional above.
-        } else if self[ind - 1].free && ind != 0 && self[ind - 1].left_to(&block) {
+        } else if self[ind - 1].is_free() && ind != 0 && self[ind - 1].left_to(&block) {
             self[ind - 1].size += block.size;
         } else {
-            self.insert(ind, block.into());
+            self.insert(ind, block);
         }
 
         // Check consistency.
@@ -555,14 +551,14 @@ impl BlockList {
     /// ```
     ///
     /// The insertion is now completed.
-    fn insert(&mut self, ind: usize, block: BlockEntry) {
+    fn insert(&mut self, ind: usize, block: Block) {
         // TODO consider moving right before searching left.
 
         // Find the next gap, where a used block were.
         let n = self.iter()
             .skip(ind)
             .enumerate()
-            .filter(|&(_, x)| x.free)
+            .filter(|&(_, x)| x.is_free())
             .next().map(|x| x.0)
             .unwrap_or_else(|| {
                 let len = self.len;
@@ -608,7 +604,7 @@ impl BlockList {
         // Check if overlapping.
         let mut prev = *self[0].ptr;
         for (n, i) in self.iter().enumerate().skip(1) {
-            assert!(!i.free || *i.ptr > prev, "Two blocks are overlapping/adjacent at index, {}.", n);
+            assert!(!i.is_free() || *i.ptr > prev, "Two blocks are overlapping/adjacent at index, {}.", n);
             prev = *i.end();
         }
 
@@ -641,16 +637,16 @@ mod test {
 }
 
 impl ops::Deref for BlockList {
-    type Target = [BlockEntry];
+    type Target = [Block];
 
-    fn deref(&self) -> &[BlockEntry] {
+    fn deref(&self) -> &[Block] {
         unsafe {
             slice::from_raw_parts(*self.ptr as *const _, self.len)
         }
     }
 }
 impl ops::DerefMut for BlockList {
-    fn deref_mut(&mut self) -> &mut [BlockEntry] {
+    fn deref_mut(&mut self) -> &mut [Block] {
         unsafe {
             slice::from_raw_parts_mut(*self.ptr, self.len)
         }