use std::mem::{transmute, size_of, transmute_copy, forget};
use std::ops::Deref;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::marker::PhantomData;
use std::marker::Sync;
use std::cell::Cell;
#[cfg(target_pointer_width = "32")]
const PTR_SIZE: usize = 4;
#[cfg(target_pointer_width = "64")]
const PTR_SIZE: usize = 8;
const MAX_WEIGHT_EXP: u8 = PTR_SIZE as u8 * 8 - 1;
const MAX_WEIGHT: usize = 1usize << MAX_WEIGHT_EXP;
pub struct Orc<'a, T: 'a> {
pointer_data: [u8; PTR_SIZE - 1], weight_exp: Cell<u8>,
lifetime_and_type: PhantomData<&'a T>,
}
unsafe impl<'a, T> Sync for Orc<'a, T> {}
impl<'a, T> Drop for Orc<'a, T> {
fn drop(&mut self) {
let slot = construct_pointer::<T>(self.pointer_data, 0);
let weight = two_two_the(self.weight_exp.get());
slot.weight.fetch_sub(weight, Ordering::Release);
}
}
impl<'a, T> Clone for Orc<'a, T> {
fn clone(&self) -> Orc<'a, T> {
if self.weight_exp.get() > 1 {
self.weight_exp.set(self.weight_exp.get() - 1);
return Orc {
weight_exp: Cell::new(self.weight_exp.get()),
pointer_data: self.pointer_data,
lifetime_and_type: PhantomData,
};
}
panic!("not implemented yet");
}
}
impl<'a, T> Deref for Orc<'a, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
let slot = construct_pointer::<T>(self.pointer_data, 0);
match slot.data {
Some(ref d) => d,
None => unreachable!(), }
}
}
struct OrcInner<T> {
weight: AtomicUsize,
data: Option<T>,
}
pub struct OrcHeap<T> {
heap: Vec<OrcInner<T>>,
}
unsafe impl<'a, T> Sync for OrcHeap<T> {}
impl<'a, T> OrcHeap<T> {
pub fn new() -> OrcHeap<T> {
const DEFAULT_HEAP_SIZE: usize = 16;
OrcHeap::<T>::with_capacity(DEFAULT_HEAP_SIZE)
}
pub fn with_capacity(capacity: usize) -> OrcHeap<T> {
let mut heap = Vec::with_capacity(capacity);
for _ in 0..capacity {
heap.push(OrcInner {
weight: AtomicUsize::new(0),
data: None,
});
}
let (_, weight) = deconstruct_pointer(heap.iter().nth(capacity - 1).unwrap());
assert_eq!(weight, 0);
OrcHeap::<T> { heap: heap }
}
pub fn alloc(&'a self, value: T) -> Result<Orc<T>, &'static str> {
let mut position = 0;
loop {
unsafe {
let slot = self.heap.get_unchecked(position);
if slot.weight.compare_and_swap(0, MAX_WEIGHT, Ordering::Relaxed) == 0 {
let ref data: Option<T> = slot.data;
let mut_data: *mut Option<T> = hack_transmute(data);
*mut_data = Some(value);
let (pointer_data, _) = deconstruct_pointer(slot);
return Ok(Orc::<'a, T> {
pointer_data: pointer_data,
weight_exp: Cell::new(MAX_WEIGHT_EXP),
lifetime_and_type: PhantomData,
});
}
}
position += 1;
if position == self.heap.capacity() {
position = 0;
break;
}
}
Err("Out of memory")
}
pub fn collect(&'a self) {
for position in 0..self.heap.capacity() {
unsafe {
let slot = self.heap.get_unchecked(position);
if slot.weight.compare_and_swap(0, MAX_WEIGHT, Ordering::Relaxed) == 0 {
let ref data: Option<T> = slot.data;
let mut_data: *mut Option<T> = hack_transmute(data);
*mut_data = None;
}
}
}
}
}
#[inline(always)]
fn deconstruct_pointer<T>(p: &OrcInner<T>) -> ([u8; PTR_SIZE - 1], u8) {
unsafe {
let p: usize = transmute(p);
transmute(usize::from_le(p)) }
}
#[inline(always)]
fn construct_pointer<'a, T>(pointer: [u8; PTR_SIZE - 1], weight: u8) -> &'a OrcInner<T> {
unsafe {
let p: usize = transmute((pointer, weight));
transmute(usize::from_le(p)) }
}
#[inline(always)]
fn two_two_the(exp: u8) -> usize {
1usize << exp
}
#[inline(always)]
unsafe fn hack_transmute<T, U>(x: T) -> U {
debug_assert_eq!(size_of::<T>(), size_of::<U>());
let y = transmute_copy(&x);
forget(x);
y
}
#[test]
fn test_two_two_the() {
assert_eq!(two_two_the(0), 1);
assert_eq!(two_two_the(1), 2);
assert_eq!(two_two_the(8), 256);
}
#[cfg(test)]
mod test_drop {
use OrcHeap;
use std::cell::Cell;
struct DropTest<'a>(&'a Cell<usize>);
impl<'a> Drop for DropTest<'a> {
fn drop(&mut self) {
let v = self.0.get();
self.0.set(v - 1);
}
}
#[test]
#[allow(unused_variables)]
fn test_drop() {
let test_size = 1000;
let values_in_existence = Cell::new(test_size);
let heap = OrcHeap::with_capacity(test_size);
for _ in 0..test_size {
let o = heap.alloc(DropTest(&values_in_existence)).unwrap();
}
heap.collect();
assert_eq!(values_in_existence.get(), 0);
}
#[test]
#[allow(unused_variables)]
fn test_heap_freed() {
let test_size = 2;
let values_in_existence = Cell::new(5);
let heap = OrcHeap::with_capacity(test_size);
{
let a = heap.alloc(DropTest(&values_in_existence)).unwrap();
let b = heap.alloc(DropTest(&values_in_existence)).unwrap();
}
let c = heap.alloc(DropTest(&values_in_existence)).unwrap();
let d = heap.alloc(DropTest(&values_in_existence)).unwrap();
assert_eq!(values_in_existence.get(), 3);
assert!(heap.alloc(DropTest(&values_in_existence)).is_err())
}
}
#[cfg(test)]
mod test_concurrency {
extern crate crossbeam;
use OrcHeap;
#[test]
fn test_concurrency() {
extern crate crossbeam;
let test_size = 1000;
let heap = OrcHeap::with_capacity(test_size * 10);
crossbeam::scope(|scope| {
for _ in 0..test_size {
scope.spawn(|| {
for j in 0..test_size {
if let Ok(v) = heap.alloc(j) {
assert_eq!(*v, j);
}
}
});
}
});
}
}