changeset 1:ca4d8694fb33

Implement SkipMap (some methods still missing)
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 08 Jun 2016 20:58:50 +0200
parents b5f1f6458ee2
children 0c8c5df504fd
files src/lib.rs src/skipmap.rs
diffstat 2 files changed, 247 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib.rs	Wed Jun 08 20:58:50 2016 +0200
@@ -0,0 +1,10 @@
+
+extern crate rand;
+
+mod skipmap;
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skipmap.rs	Wed Jun 08 20:58:50 2016 +0200
@@ -0,0 +1,237 @@
+#![allow(dead_code)]
+
+use rand::{Rng, SeedableRng, StdRng};
+use std::cmp::Ordering;
+
+use std::mem::{replace, transmute_copy};
+
+const MAX_HEIGHT: usize = 12;
+
+pub trait Comparator {
+    fn cmp(&Vec<u8>, &Vec<u8>) -> Ordering;
+}
+
+pub struct StandardComparator;
+
+impl Comparator for StandardComparator {
+    fn cmp(a: &Vec<u8>, b: &Vec<u8>) -> Ordering {
+        a.cmp(b)
+    }
+}
+
+struct Node {
+    skips: Vec<Option<*mut Node>>,
+    // skips[0] points to the element in next; next provides proper ownership
+    next: Option<Box<Node>>,
+    key: Vec<u8>,
+    value: Vec<u8>,
+}
+
+pub struct SkipMap<C: Comparator> {
+    head: Box<Node>,
+    rand: StdRng,
+    cmp: C,
+    len: usize,
+}
+
+impl SkipMap<StandardComparator> {
+    fn new() -> SkipMap<StandardComparator> {
+        SkipMap::new_with_cmp(StandardComparator {})
+    }
+}
+
+
+impl<C: Comparator> SkipMap<C> {
+    fn new_with_cmp(c: C) -> SkipMap<C> {
+        let mut s = Vec::new();
+        s.resize(MAX_HEIGHT, None);
+
+        SkipMap {
+            head: Box::new(Node {
+                skips: s,
+                next: None,
+                key: Vec::new(),
+                value: Vec::new(),
+            }),
+            rand: StdRng::from_seed(&[0xde, 0xad, 0xbe, 0xef]),
+            cmp: c,
+            len: 0,
+        }
+    }
+
+    fn len(&self) -> usize {
+        self.len
+    }
+    fn random_height(&mut self) -> usize {
+        1 + (self.rand.next_u32() as usize % (MAX_HEIGHT - 1))
+    }
+
+    fn contains(&mut self, key: &Vec<u8>) -> bool {
+        if key.is_empty() {
+            return false;
+        }
+
+        // Start at the highest skip link of the head node, and work down from there
+        let mut current: *const Node = unsafe { transmute_copy(&self.head.as_ref()) };
+        let mut level = self.head.skips.len() - 1;
+
+        loop {
+            unsafe {
+                if let Some(next) = (*current).skips[level] {
+                    let ord = C::cmp(&(*next).key, key);
+
+                    match ord {
+                        Ordering::Less => {
+                            current = next;
+                            continue;
+                        }
+                        Ordering::Equal => return true,
+                        Ordering::Greater => (),
+                    }
+                }
+            }
+            if level == 0 {
+                break;
+            }
+            level -= 1;
+        }
+        false
+    }
+
+    fn insert(&mut self, key: Vec<u8>, val: Vec<u8>) {
+        assert!(!key.is_empty());
+        assert!(!val.is_empty());
+
+        // Keeping track of skip entries that will need to be updated
+        let new_height = self.random_height();
+        let mut prevs: Vec<Option<*mut Node>> = Vec::with_capacity(new_height);
+
+        let mut level = MAX_HEIGHT - 1;
+        let mut current: *mut Node = unsafe { transmute_copy(&self.head.as_mut()) };
+        // Initialize all prevs entries with *head
+        prevs.resize(new_height, Some(current));
+
+        // Find the node after which we want to insert the new node; this is the node with the key
+        // immediately smaller than the key to be inserted.
+        loop {
+            unsafe {
+                if let Some(next) = (*current).skips[level] {
+                    // If the wanted position is after the current node
+                    let ord = C::cmp(&(*next).key, &key);
+
+                    assert!(ord != Ordering::Equal, "No duplicates allowed");
+
+                    if ord == Ordering::Less {
+                        current = next;
+                        continue;
+                    }
+                }
+            }
+
+            if level < new_height {
+                prevs[level] = Some(current);
+            }
+
+            if level == 0 {
+                break;
+            } else {
+                level -= 1;
+            }
+        }
+
+        // Construct new node
+        let mut new_skips = Vec::with_capacity(new_height);
+        new_skips.resize(new_height, None);
+        let mut new = Box::new(Node {
+            skips: new_skips,
+            next: None,
+            key: key,
+            value: val,
+        });
+        let newp = unsafe { transmute_copy(&(new.as_mut())) };
+
+        for i in 0..new_height {
+            if let Some(prev) = prevs[i] {
+                unsafe {
+                    new.skips[i] = (*prev).skips[i];
+                    (*prev).skips[i] = Some(newp);
+                }
+            }
+        }
+
+        // Insert new node by first replacing the previous element's next field with None and
+        // assigning its value to new.next...
+        new.next = unsafe { replace(&mut (*current).next, None) };
+        // ...and then setting the previous element's next field to the new node
+        unsafe { replace(&mut (*current).next, Some(new)) };
+
+        self.len += 1;
+    }
+
+
+    // Runs through the skipmap and prints everything including addresses
+    fn dbg_print(&self) {
+        let mut current: *const Node = unsafe { transmute_copy(&self.head.as_ref()) };
+        loop {
+            unsafe {
+                println!("{:?} {:?}/{:?} - {:?}",
+                         current,
+                         (*current).key,
+                         (*current).value,
+                         (*current).skips);
+                if let Some(next) = (*current).skips[0].clone() {
+                    current = next;
+                } else {
+                    break;
+                }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::SkipMap;
+    #[test]
+    fn test_insert() {
+        let mut skm = SkipMap::new();
+        skm.insert("abc".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abd".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abe".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abf".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        assert_eq!(skm.len(), 4);
+        skm.dbg_print();
+    }
+
+    #[test]
+    #[should_panic]
+    fn test_no_dupes() {
+        let mut skm = SkipMap::new();
+        skm.insert("abc".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abd".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abe".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abi".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        // this should panic
+        skm.insert("abc".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abf".as_bytes().to_vec(), "def".as_bytes().to_vec());
+    }
+
+    #[test]
+    fn test_contains() {
+        let mut skm = SkipMap::new();
+        skm.insert("abc".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abd".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abe".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abi".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abx".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("aby".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abz".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        skm.insert("abm".as_bytes().to_vec(), "def".as_bytes().to_vec());
+        assert!(skm.contains(&"aby".as_bytes().to_vec()));
+        assert!(skm.contains(&"abc".as_bytes().to_vec()));
+        assert!(skm.contains(&"abz".as_bytes().to_vec()));
+        assert!(!skm.contains(&"123".as_bytes().to_vec()));
+        assert!(!skm.contains(&"abg".as_bytes().to_vec()));
+        assert!(!skm.contains(&"456".as_bytes().to_vec()));
+    }
+}