Browse Source

Implement generic searching.

This prepares ralloc for first-fit search.

We implement generic searching by a trait which represents some
searching method.
ticki 8 years ago
parent
commit
99ea74d425
4 changed files with 85 additions and 25 deletions
  1. 1 0
      src/bk/mod.rs
  2. 34 14
      src/bk/pool.rs
  3. 49 0
      src/bk/search.rs
  4. 1 11
      src/bk/shortcut.rs

+ 1 - 0
src/bk/mod.rs

@@ -27,5 +27,6 @@
 
 mod lv;
 mod node;
+mod search;
 mod seek;
 mod shortcut;

+ 34 - 14
src/bk/pool.rs

@@ -1,8 +1,7 @@
-use prelude::*;
-
 use core::{cmp, mem};
 
 use arena::Arena;
+use bk::search::{self, Search};
 use random;
 
 struct Pool {
@@ -17,9 +16,6 @@ impl Pool {
     /// overshoot, then we repeat on the next level starting at the last non-overshot shortcut from
     /// the previous level.
     ///
-    /// The returned seek contains the shortcutted nodes ("lookback") and other data found while
-    /// searching. This can be used to manipulate the found node/block.
-    ///
     /// # Example
     ///
     /// If we look for 8, we start in the top level and follow until we hit 9.
@@ -30,14 +26,29 @@ impl Pool {
     fn search(&mut self, block: &Block) -> Seek {
         log!(DEBUG, "Searching the block pool for block {:?}...", block);
 
+        // Use `BlockSearcher` for this.
+        self.search_with(search::BlockSearcher {
+            needle: block,
+        }).unwrap()
+        // TODO: Find a way to fix this unwrap.
+    }
+
+    /// Search the block pool with a particular searcher.
+    ///
+    /// The outline of the algorithm is this: We start by shortcutting from the top level until we
+    /// need to refine (which is determined by the serarcher), then we repeat on the next level
+    /// starting at the last refined shortcut from the previous level. At the lowest level, we go
+    /// forward until we find a match.
+    ///
+    /// A "lookback", i.e. the refined nodes of every level is stored in the returned value.
+    fn search_with<S: Search>(&mut self, searcher: S) -> Result<Seek, ()> {
         // We start by an uninitialized value, which we fill out.
         let mut seek = unsafe { mem::uninitialized() };
 
         // Start at the highest (least dense) level.
         let mut iter = self.head.follow_shortcut(lv::Level::max());
-        // Go forward until we overshoot.
-        while let Some(shortcut_taken) = iter.take_while(|x| x < block).last() {
-
+        // Go forward until we can refine (e.g. we overshoot).
+        while let Some(shortcut_taken) = iter.find(|x| searcher.refine(x)) {
             // Decrement the level.
             let lv::Level(lv) = iter.decrement_level();
             log!(INTERNAL, "Going from level {} to level {}.", lv, lv - 1);
@@ -56,17 +67,26 @@ impl Pool {
             }
         }
 
-        // We're now at the bottom layer, in which we will iterate over to find the last element,
-        // below our needle.
-        // FIXME: These unwraps can be eliminated, find an approach which does not significantly
-        // increase CLOC of this function.
-        seek.node = iter.unwrap_node().iter().take_while(|x| x < block).last().unwrap();
+        // We're now at the bottom layer, and we need to find a match by iterating over the nodes
+        // of this layer.
+        if let Some(shortcut) = iter.next() {
+            if let Some(found) = shortcut.node.iter().find(|x| searcher.is_match(x)) {
+                // Set the seek's found node to the first match (as defined by the searcher).
+                seek.node = found;
+            } else {
+                // No match was found, return error.
+                return Err(());
+            }
+        } else {
+            // We reached the end of iterator.
+            return Err(());
+        }
 
         seek.check();
 
         // Everything have been initialized, including all the back look cells (i.e. every level
         // have been visited).
-        seek
+        Ok(seek)
     }
 }
 

+ 49 - 0
src/bk/search.rs

@@ -0,0 +1,49 @@
+//! Primitives for searching the block pool.
+
+use bk::node::Node;
+use bk::shortcut::Shortcut;
+use block::Block;
+
+pub trait Search {
+    /// Determine if this shortcut skips the needle.
+    ///
+    /// If this shortcut spans the needle (target), this method should return `true`. This is used
+    /// to refine shortcuts. If a shortcut is determined to not skip the needle, we will simply
+    /// progress to the next shortcut and not the lower one.
+    fn refine(self, shortcut: &Shortcut) -> bool;
+    /// Determine if some node is a match.
+    ///
+    /// This is used at the bottom layer to determine if we have completeed our search yet.
+    fn is_match(self, node: &Node) -> bool;
+}
+
+#[derive(Copy, Clone)]
+pub struct BlockSearcher<'a> {
+    /// The target block.
+    needle: &'a Block,
+}
+
+impl<'a> Search for BlockSearcher {
+    fn refine(self, shortcut: &Shortcut) -> bool {
+        if let Some(next) = shortcut.next {
+            // We refine if the next block is above our needle, and hence not satisfying our
+            // search condition.
+            next.block > self.needle
+        } else {
+            // If the shortcut has no successor, we have to refine.
+            true
+        }
+    }
+
+    fn is_match(self, node: &Node) -> bool {
+        if let Some(next) = node.next {
+            // We refine if the next block is above our needle, and hence not satisfying our
+            // search condition.
+            next.block > self.needle
+        } else {
+            // If the shortcut has no successor (i.e. it is the last block in the pool), we have to
+            // refine.
+            true
+        }
+    }
+}

+ 1 - 11
src/bk/shortcut.rs

@@ -110,21 +110,11 @@ impl<'a> ShortcutIter<'a> {
     /// This will make the iterator skip approximately half of the elements of the previous state
     /// of the iterator.
     #[inline]
-    fn decrement_level(&mut self) -> Level {
+    pub fn decrement_level(&mut self) -> Level {
         let lv = self.lv;
         self.lv -= Level(1);
         lv
     }
-
-    /// Unwrap the inner node (of the shortcut that the iterator is currently on).
-    ///
-    /// # Panics
-    ///
-    /// This will panic if the iterator is over (i.e. no node is left)
-    #[inline]
-    fn unwrap_node(self) -> &Node {
-        self.shortcut.unwrap().node
-    }
 }
 
 impl<'a> Iterator for ShortcutIter<'a> {