diff --git a/.gitignore b/.gitignore index cb14a42..ca98cd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,2 @@ -# Generated by Cargo -# will have compiled files and executables /target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..94d32a6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "vec-arena" +version = "0.0.1" +authors = ["Stjepan Glavina "] +repository = "https://github.com/stjepang/vec-arena.git" +homepage = "https://github.com/stjepang/vec-arena" +license = "MIT" +readme = "README.md" +# documentation = "TODO: url to docs on github" +description = "Fast general-purpose arena" + +[lib] +name = "vec_arena" +path = "src/lib.rs" + +[[bin]] +name = "bench_splay" +path = "benches/splay.rs" diff --git a/README.md b/README.md index bb17f0c..46baae6 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# vec-arena \ No newline at end of file +# VecArena + +Work in progress diff --git a/benches/splay.rs b/benches/splay.rs new file mode 100644 index 0000000..7d9d3af --- /dev/null +++ b/benches/splay.rs @@ -0,0 +1,125 @@ +extern crate vec_arena; + +use vec_arena::VecArena; + +struct Node { + parent: usize, + children: [usize; 2], + value: T, +} + +impl Node { + fn new(value: T) -> Node { + Node { + parent: !0, + children: [!0, !0], + value: value, + } + } +} + +struct Splay { + arena: VecArena>, + root: usize, +} + +impl Splay where T: Ord + Eq + Clone { + fn new() -> Splay { + Splay { + arena: VecArena::new(), + root: !0, + } + } + + fn rotate(&mut self, a: usize, b: usize) { + let x = &mut self.arena; + let p = x[a].parent; + + let dir = if x[a].children[0] == b { 0 } else { 1 }; + let t = x[b].children[dir ^ 1]; + + x[a].children[dir] = t; + if t != !0 { + x[t].parent = a; + } + x[b].children[dir ^ 1] = a; + x[a].parent = b; + + if p == !0 { + self.root = b; + x[b].parent = !0; + } else { + let dir = if x[p].children[0] == a { 0 } else { 1 }; + x[p].children[dir] = b; + x[b].parent = p; + } + } + + fn splay(&mut self, a: usize) { + loop { + let b = self.arena[a].parent; + if b == !0 { + break; + } + + let c = self.arena[b].parent; + if c == !0 { + self.rotate(b, a); + break; + } + + let is_l = self.arena[c].children[0] == b && self.arena[b].children[0] == a; + let is_r = self.arena[c].children[1] == b && self.arena[b].children[1] == a; + + if is_l || is_r { + self.rotate(c, b); + self.rotate(b, a); + } else { + self.rotate(b, a); + self.rotate(c, a); + } + } + } + + fn insert(&mut self, value: T) { + let node = self.arena.push(Node::new(value)); + + if self.root == !0 { + self.root = node; + } else { + let mut curr = self.root; + loop { + let dir = if self.arena[node].value < self.arena[curr].value { 0 } else { 1 }; + let next = self.arena[curr].children[dir]; + + if next == !0 { + self.arena[curr].children[dir] = node; + self.arena[node].parent = curr; + self.splay(node); + break; + } else { + curr = next; + } + } + } + } + + fn print(&self, node: usize, depth: usize) where T: std::fmt::Display { + if node != !0 { + self.print(self.arena[node].children[0], depth + 1); + println!("{:width$}{}", "", self.arena[node].value, width = depth * 3); + self.print(self.arena[node].children[1], depth + 1); + } + } +} + +fn main() { + let mut splay = Splay::new(); + + let mut num = 1u32; + for _ in 0..1000000 { + num = num.wrapping_mul(17).wrapping_add(255); + splay.insert(num); + } + // splay.print(splay.root, 0); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..61b9312 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,214 @@ +use std::marker::PhantomData; +use std::mem; +use std::ops::{Index, IndexMut}; +use std::ptr; + +// TODO: Handle ZST differently +// TODO: Test ZST, make a ZST implementing Drop + +#[inline(always)] +fn bits() -> usize { + std::mem::size_of::() * 8 +} + +// TODO: Move into VecArena? +#[inline(always)] +fn num_blocks(cap: usize) -> usize { + (cap + bits() - 1) / bits() +} + +pub struct VecArena { + elems: *const T, + meta: *mut usize, + cap: usize, + head: usize, + count: usize, + marker: PhantomData, +} + +impl VecArena { + #[inline(always)] + unsafe fn get_alive(&self, block: usize) -> *mut usize { + self.meta.offset(block as isize) + } + + #[inline(always)] + unsafe fn get_next(&self, block: usize) -> *mut usize { + self.meta.offset((num_blocks(self.cap) + block) as isize) + } + + pub fn new() -> Self { + let elems = { + let mut v = Vec::with_capacity(0); + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + }; + let meta = { + let mut v = Vec::with_capacity(0); + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + }; + VecArena { + elems: elems, + meta: meta, + cap: 0, + head: !0, + count: 0, + marker: PhantomData, + } + } + + pub fn push(&mut self, value: T) -> usize { + unsafe { + if self.count == self.cap { + self.grow(); + } + while self.head != !0 && *self.get_alive(self.head) == !0 { + self.head = *self.get_next(self.head); + } + if self.head == !0 { + self.grow(); + } + + let i = (!*self.get_alive(self.head)).trailing_zeros() as usize; + let index = self.head * bits() + i; + + unsafe { + ptr::write(self.elems.offset(index as isize) as *mut T, value); + } + let block = self.head; + *self.get_alive(block) |= 1 << i; + self.count += 1; + index + } + } + + pub fn take(&mut self, index: usize) -> T { + self.validate_index(index); + + let b = index / bits(); + let i = index % bits(); + + unsafe { + self.count -= 1; + *self.get_alive(b) ^= 1 << i; + + if *self.get_alive(b) == 0 { + *self.get_next(b) = self.head; + self.head = b; + } + + ptr::read(self.elems.offset(index as isize) as *mut T) + } + } + + unsafe fn grow(&mut self) { + let new_cap = if self.cap == 0 { 4 } else { self.cap * 2 }; + let blocks = num_blocks(self.cap); + let new_blocks = num_blocks(new_cap); + + let new_elems = { + let mut v = Vec::with_capacity(new_cap); + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + }; + ptr::copy_nonoverlapping(self.elems, new_elems, self.cap); + Vec::from_raw_parts(self.elems as *mut T, 0, self.cap); + self.elems = new_elems; + + let new_meta = { + let mut v = Vec::from_raw_parts(self.meta, 2 * blocks, 2 * blocks); + v.reserve_exact(new_blocks * 2 - blocks * 2); + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + }; + ptr::write_bytes(new_meta.offset(blocks as isize), 0, new_blocks - blocks); + + for i in blocks .. new_blocks { + ptr::write(new_meta.offset((new_blocks + i) as isize), i.wrapping_sub(1)); + } + + self.meta = new_meta; + + self.cap = new_cap; + self.head = new_blocks - 1; + } + + #[inline] + fn validate_index(&self, index: usize) { + let b = index / bits(); + let i = index % bits(); + unsafe { + if index >= self.cap || *self.get_alive(b) >> i & 1 == 0 { + self.panic_invalid_index(index); + } + } + } + + #[inline(never)] + fn panic_invalid_index(&self, index: usize) { + if index >= self.cap { + panic!("index out of bounds: the cap is {} but the index is {}", self.cap, index); + } + panic!("uninitialized memory: the index is {} but it's not allocated", index); + } +} + +impl Drop for VecArena { + fn drop(&mut self) { + unsafe { + for b in 0 .. num_blocks(self.cap) { + let alive = *self.get_alive(b); + if alive != 0 { + for i in 0 .. bits() { + if alive & (1 << i) != 0 { + let index = b * bits() + i; + ptr::drop_in_place(self.elems.offset(index as isize) as *mut T); + } + } + } + } + + let blocks = num_blocks(self.cap); + Vec::from_raw_parts(self.elems as *mut T, 0, self.cap); + Vec::from_raw_parts(self.meta, 0, 2 * blocks); + } + } +} + +impl Index for VecArena { + type Output = T; + + fn index(&self, index: usize) -> &T { + self.validate_index(index); + unsafe { + &*self.elems.offset(index as isize) + } + } +} + +impl IndexMut for VecArena { + fn index_mut(&mut self, index: usize) -> &mut T { + self.validate_index(index); + unsafe { + &mut *(self.elems.offset(index as isize) as *mut T) + } + } +} + +// TODO: impl Default + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let mut arena = VecArena::new(); + arena.alloc(1); + } +}