#![allow(unstable_name_collisions)]
use sptr::Strict;
use crate::internals::lincowcell::LinCowCellCapable;
use std::borrow::Borrow;
use std::collections::{BTreeSet, VecDeque};
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::ptr;
use std::sync::Mutex;
use smallvec::SmallVec;
use super::iter::*;
#[cfg(feature = "ahash")]
use ahash::RandomState;
#[cfg(not(feature = "ahash"))]
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash, Hasher};
#[cfg(feature = "hashtrie_skinny")]
pub(crate) const MAX_HEIGHT: u64 = 8;
#[cfg(feature = "hashtrie_skinny")]
#[cfg(test)]
const ABS_MAX_HEIGHT: u64 = 21;
#[cfg(feature = "hashtrie_skinny")]
pub(crate) const HT_CAPACITY: usize = 8;
#[cfg(feature = "hashtrie_skinny")]
const HASH_MASK: u64 =
0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0111;
#[cfg(feature = "hashtrie_skinny")]
const SHIFT: u64 = 3;
#[cfg(not(feature = "hashtrie_skinny"))]
pub(crate) const MAX_HEIGHT: u64 = 6;
#[cfg(not(feature = "hashtrie_skinny"))]
#[cfg(test)]
const ABS_MAX_HEIGHT: u64 = 16;
#[cfg(not(feature = "hashtrie_skinny"))]
pub(crate) const HT_CAPACITY: usize = 16;
#[cfg(not(feature = "hashtrie_skinny"))]
const HASH_MASK: u64 =
0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_1111;
#[cfg(not(feature = "hashtrie_skinny"))]
const SHIFT: u64 = 4;
const TAG: usize = 0b0011;
const UNTAG: usize = usize::MAX - TAG;
const FLAG_DIRTY: usize = 0b01;
const MARK_CLEAN: usize = usize::MAX - FLAG_DIRTY;
const FLAG_BRANCH: usize = 0b00;
const FLAG_BUCKET: usize = 0b10;
const DEFAULT_BUCKET_ALLOC: usize = 1;
macro_rules! hash_key {
($self:expr, $k:expr) => {{
let mut hasher = $self.build_hasher.build_hasher();
$k.hash(&mut hasher);
hasher.finish()
}};
}
#[cfg(all(test, not(miri)))]
thread_local!(static ALLOC_LIST: Mutex<BTreeSet<Ptr>> = Mutex::new(BTreeSet::new()));
#[cfg(all(test, not(miri)))]
thread_local!(static WRITE_LIST: Mutex<BTreeSet<Ptr>> = Mutex::new(BTreeSet::new()));
#[cfg(test)]
fn assert_released() {
#[cfg(not(miri))]
{
let is_empty = ALLOC_LIST.with(|llist| {
let x = llist.lock().unwrap();
println!("Remaining -> {:?}", x);
x.is_empty()
});
assert!(is_empty);
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct Ptr {
p: *mut i32,
}
impl Debug for Ptr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Ptr")
.field("p", &self.p)
.field("bucket", &self.is_bucket())
.field("dirty", &self.is_dirty())
.field("null", &self.is_null())
.finish()
}
}
impl Ptr {
fn null_mut() -> Self {
debug_assert!(std::mem::size_of::<Ptr>() == std::mem::size_of::<*mut Branch<u64, u64>>());
debug_assert!(std::mem::size_of::<Ptr>() == std::mem::size_of::<*mut Bucket<u64, u64>>());
Ptr { p: ptr::null_mut() }
}
#[inline(always)]
pub(crate) fn is_null(&self) -> bool {
self.p.is_null()
}
#[inline(always)]
pub(crate) fn is_bucket(&self) -> bool {
self.p.addr() & FLAG_BUCKET == FLAG_BUCKET
}
#[inline(always)]
pub(crate) fn is_branch(&self) -> bool {
self.p.addr() & FLAG_BUCKET != FLAG_BUCKET
}
#[inline(always)]
fn is_dirty(&self) -> bool {
self.p.addr() & FLAG_DIRTY == FLAG_DIRTY
}
#[cfg(all(test, not(miri)))]
fn untagged(&self) -> Self {
let p = self.p.map_addr(|a| a & UNTAG);
Ptr { p }
}
#[inline(always)]
fn mark_dirty(&mut self) {
#[cfg(all(test, not(miri)))]
WRITE_LIST.with(|llist| assert!(llist.lock().unwrap().insert(self.untagged())));
self.p = self.p.map_addr(|a| a | FLAG_DIRTY)
}
#[inline(always)]
fn mark_clean(&mut self) {
#[cfg(all(test, not(miri)))]
WRITE_LIST.with(|llist| assert!(llist.lock().unwrap().remove(&(self.untagged()))));
self.p = self.p.map_addr(|a| a & MARK_CLEAN)
}
#[inline(always)]
pub(crate) fn as_bucket<K: Hash + Eq + Clone + Debug, V: Clone>(&self) -> &Bucket<K, V> {
debug_assert!(self.is_bucket());
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged())));
unsafe { &*(self.p.map_addr(|a| a & UNTAG) as *const Bucket<K, V>) }
}
#[inline(always)]
fn as_bucket_raw<K: Hash + Eq + Clone + Debug, V: Clone>(&self) -> *mut Bucket<K, V> {
debug_assert!(self.is_bucket());
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged())));
self.p.map_addr(|a| a & UNTAG) as *mut Bucket<K, V>
}
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub(crate) fn as_bucket_mut<K: Hash + Eq + Clone + Debug, V: Clone>(
&self,
) -> &mut Bucket<K, V> {
debug_assert!(self.is_bucket());
debug_assert!(self.is_dirty());
#[cfg(all(test, not(miri)))]
WRITE_LIST.with(|llist| {
let wlist_guard = llist.lock().unwrap();
assert!(wlist_guard.contains(&self.untagged()))
});
unsafe { &mut *(self.p.map_addr(|a| a & UNTAG) as *mut Bucket<K, V>) }
}
#[inline(always)]
pub(crate) fn as_branch<K: Hash + Eq + Clone + Debug, V: Clone>(&self) -> &Branch<K, V> {
debug_assert!(self.is_branch());
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged())));
unsafe { &*(self.p.map_addr(|a| a & UNTAG) as *const Branch<K, V>) }
}
#[inline(always)]
fn as_branch_raw<K: Hash + Eq + Clone + Debug, V: Clone>(&self) -> *mut Branch<K, V> {
debug_assert!(self.is_branch());
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged())));
self.p.map_addr(|a| a & UNTAG) as *mut Branch<K, V>
}
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub(crate) fn as_branch_mut<K: Hash + Eq + Clone + Debug, V: Clone>(
&self,
) -> &mut Branch<K, V> {
debug_assert!(self.is_branch());
debug_assert!(self.is_dirty());
#[cfg(all(test, not(miri)))]
WRITE_LIST.with(|llist| {
let wlist_guard = llist.lock().unwrap();
assert!(wlist_guard.contains(&self.untagged()))
});
unsafe { &mut *(self.p.map_addr(|a| a & UNTAG) as *mut Branch<K, V>) }
}
#[inline(always)]
#[allow(clippy::mut_from_ref)]
unsafe fn as_branch_mut_nock<K: Hash + Eq + Clone + Debug, V: Clone>(
&self,
) -> &mut Branch<K, V> {
debug_assert!(self.is_branch());
debug_assert!(self.is_dirty());
&mut *(self.p.map_addr(|a| a & UNTAG) as *mut Branch<K, V>)
}
fn free<K: Hash + Eq + Clone + Debug, V: Clone>(&self) {
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged())));
unsafe {
if self.is_bucket() {
let _ = Box::from_raw(self.as_bucket_raw::<K, V>());
} else {
let _ = Box::from_raw(self.as_branch_raw::<K, V>());
}
}
#[cfg(all(test, not(miri)))]
if self.is_dirty() {
WRITE_LIST.with(|llist| assert!(llist.lock().unwrap().remove(&(self.untagged()))))
};
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().remove(&self.untagged())));
}
}
impl<K: Hash + Eq + Clone + Debug, V: Clone> From<Box<Branch<K, V>>> for Ptr {
fn from(b: Box<Branch<K, V>>) -> Self {
let rptr: *mut Branch<K, V> = Box::into_raw(b);
#[allow(clippy::let_and_return)]
let r = Self {
p: rptr.map_addr(|a| a | FLAG_BRANCH) as *mut i32,
};
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| llist.lock().unwrap().insert(r.untagged()));
r
}
}
impl<K: Hash + Eq + Clone + Debug, V: Clone> From<Box<Bucket<K, V>>> for Ptr {
fn from(b: Box<Bucket<K, V>>) -> Self {
let rptr: *mut Bucket<K, V> = Box::into_raw(b);
#[allow(clippy::let_and_return)]
let r = Self {
p: rptr.map_addr(|a| a | FLAG_BUCKET) as *mut i32,
};
#[cfg(all(test, not(miri)))]
ALLOC_LIST.with(|llist| llist.lock().unwrap().insert(r.untagged()));
r
}
}
#[derive(Clone)]
pub struct Datum<K, V>
where
K: Hash + Eq + Clone + Debug,
V: Clone,
{
pub h: u64,
pub k: K,
pub v: V,
}
type Bucket<K, V> = SmallVec<[Datum<K, V>; DEFAULT_BUCKET_ALLOC]>;
fn new_dirty_bucket_ptr<K: Hash + Eq + Clone + Debug, V: Clone>() -> Ptr {
let bkt: Box<Bucket<K, V>> = Box::new(SmallVec::new());
let mut p = Ptr::from(bkt);
p.mark_dirty();
p
}
#[repr(align(64))]
pub(crate) struct Branch<K, V>
where
K: Hash + Eq + Clone + Debug,
V: Clone,
{
pub nodes: [Ptr; HT_CAPACITY],
k: PhantomData<K>,
v: PhantomData<V>,
}
fn new_dirty_branch_ptr<K: Hash + Eq + Clone + Debug, V: Clone>() -> Ptr {
let brch: Box<Branch<K, V>> = Branch::new();
let mut p = Ptr::from(brch);
p.mark_dirty();
debug_assert!(p.is_dirty());
debug_assert!(p.is_branch());
p
}
impl<K: Hash + Eq + Clone + Debug, V: Clone> Branch<K, V> {
fn new() -> Box<Self> {
debug_assert!(std::mem::size_of::<usize>() == std::mem::size_of::<*mut Branch<K, V>>());
Box::new(Branch {
nodes: [
Ptr::null_mut(),
Ptr::null_mut(),
Ptr::null_mut(),
Ptr::null_mut(),
Ptr::null_mut(),
Ptr::null_mut(),
Ptr::null_mut(),
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
#[cfg(not(feature = "hashtrie_skinny"))]
Ptr::null_mut(),
],
k: PhantomData,
v: PhantomData,
})
}
fn clone_dirty(&self) -> Ptr {
let bc: Box<Branch<K, V>> = Box::new(Branch {
nodes: self.nodes,
k: PhantomData,
v: PhantomData,
});
let mut p = Ptr::from(bc);
p.mark_dirty();
p
}
}
#[derive(Debug)]
pub(crate) struct SuperBlock<K, V>
where
K: Hash + Eq + Clone + Debug,
V: Clone,
{
root: Ptr,
length: usize,
txid: u64,
build_hasher: RandomState,
k: PhantomData<K>,
v: PhantomData<V>,
}
impl<K: Hash + Eq + Clone + Debug, V: Clone> SuperBlock<K, V> {
pub unsafe fn new() -> Self {
#[cfg(debug)]
assert!(MAX_HEIGHT <= ABS_MAX_HEIGHT);
let b: Box<Branch<K, V>> = Branch::new();
let root = Ptr::from(b);
SuperBlock {
root,
length: 0,
txid: 1,
build_hasher: RandomState::new(),
k: PhantomData,
v: PhantomData,
}
}
}
impl<K: Hash + Eq + Clone + Debug, V: Clone> LinCowCellCapable<CursorRead<K, V>, CursorWrite<K, V>>
for SuperBlock<K, V>
{
fn create_reader(&self) -> CursorRead<K, V> {
CursorRead::new(self)
}
fn create_writer(&self) -> CursorWrite<K, V> {
CursorWrite::new(self)
}
fn pre_commit(
&mut self,
mut new: CursorWrite<K, V>,
prev: &CursorRead<K, V>,
) -> CursorRead<K, V> {
let mut prev_last_seen = prev.last_seen.lock().unwrap();
debug_assert!((*prev_last_seen).is_empty());
let new_last_seen = &mut new.last_seen;
std::mem::swap(&mut (*prev_last_seen), &mut (*new_last_seen));
debug_assert!((*new_last_seen).is_empty());
new.mark_clean();
new.first_seen.clear();
self.root = new.root;
self.length = new.length;
self.txid = new.txid;
debug_assert!(!self.root.is_dirty());
debug_assert!(self.root.is_branch());
CursorRead::new(self)
}
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> Drop for SuperBlock<K, V> {
fn drop(&mut self) {
let mut first_seen = Vec::with_capacity(16);
let mut stack = VecDeque::new();
stack.push_back(self.root);
while let Some(tgt_ptr) = stack.pop_front() {
first_seen.push(tgt_ptr);
if tgt_ptr.is_branch() {
for n in tgt_ptr.as_branch::<K, V>().nodes.iter() {
if !n.is_null() {
stack.push_back(*n);
}
}
}
}
first_seen.iter().for_each(|p| p.free::<K, V>());
}
}
pub(crate) trait CursorReadOps<K: Clone + Hash + Eq + Debug, V: Clone> {
fn get_root_ptr(&self) -> Ptr;
fn len(&self) -> usize;
fn get_txid(&self) -> u64;
fn hash_key<'a, 'b, Q: ?Sized>(&'a self, k: &'b Q) -> u64
where
K: Borrow<Q>,
Q: Hash + Eq;
fn search<'a, 'b, Q: ?Sized>(&'a self, h: u64, k: &'b Q) -> Option<&'a V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
let mut node = self.get_root_ptr();
for d in 0..MAX_HEIGHT {
let bref: &Branch<K, V> = node.as_branch();
let idx = ((h & (HASH_MASK << (d * SHIFT))) >> (d * SHIFT)) as usize;
debug_assert!(idx < HT_CAPACITY);
let tgt_ptr = bref.nodes[idx];
if tgt_ptr.is_null() {
return None;
} else if tgt_ptr.is_branch() {
node = tgt_ptr;
} else {
for datum in tgt_ptr.as_bucket::<K, V>().iter() {
if datum.h == h && k.eq(datum.k.borrow()) {
let x = &datum.v as *const V;
return Some(unsafe { &*x as &V });
}
}
return None;
}
}
unreachable!();
}
fn kv_iter(&self) -> Iter<K, V> {
Iter::new(self.get_root_ptr(), self.len())
}
fn k_iter(&self) -> KeyIter<K, V> {
KeyIter::new(self.get_root_ptr(), self.len())
}
fn v_iter(&self) -> ValueIter<K, V> {
ValueIter::new(self.get_root_ptr(), self.len())
}
fn verify_inner(&self, expect_clean: bool) {
let root = self.get_root_ptr();
assert!(root.is_branch());
let mut stack = VecDeque::new();
let mut ptr_map = BTreeSet::new();
let mut length = 0;
stack.push_back(root);
while let Some(tgt_ptr) = stack.pop_front() {
assert!(ptr_map.insert(tgt_ptr));
if expect_clean {
assert!(!tgt_ptr.is_dirty());
}
if tgt_ptr.is_branch() {
for n in tgt_ptr.as_branch::<K, V>().nodes.iter() {
if !n.is_null() {
stack.push_back(*n);
}
}
} else {
assert!(tgt_ptr.is_bucket());
length += tgt_ptr.as_bucket::<K, V>().len();
}
}
assert!(length == self.len());
}
fn verify(&self);
}
#[derive(Debug)]
pub(crate) struct CursorWrite<K, V>
where
K: Hash + Eq + Clone + Debug,
V: Clone,
{
txid: u64,
length: usize,
root: Ptr,
last_seen: Vec<Ptr>,
first_seen: Vec<Ptr>,
build_hasher: RandomState,
k: PhantomData<K>,
v: PhantomData<V>,
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> CursorWrite<K, V> {
pub(crate) fn new(sblock: &SuperBlock<K, V>) -> Self {
let txid = sblock.txid + 1;
let length = sblock.length;
let root = sblock.root;
let last_seen = Vec::with_capacity(16);
let first_seen = Vec::with_capacity(16);
let build_hasher = sblock.build_hasher.clone();
CursorWrite {
txid,
length,
root,
last_seen,
first_seen,
build_hasher,
k: PhantomData,
v: PhantomData,
}
}
fn dirty_root(&mut self) {
debug_assert!(self.root.is_branch());
if !self.root.is_dirty() {
let clean_bref: &Branch<K, V> = self.root.as_branch();
self.last_seen.push(self.root);
self.root = clean_bref.clone_dirty();
self.first_seen.push(self.root);
}
}
fn mark_clean(&mut self) {
let root = self.get_root_ptr();
assert!(root.is_branch());
let mut stack: VecDeque<Ptr> = VecDeque::new();
if root.is_dirty() {
stack.push_back(root);
self.root.mark_clean();
}
while let Some(tgt_ptr) = stack.pop_front() {
for n in unsafe { tgt_ptr.as_branch_mut_nock::<K, V>().nodes.iter_mut() } {
if !n.is_null() && n.is_dirty() {
if n.is_branch() {
stack.push_back(*n);
}
n.mark_clean();
}
}
if cfg!(debug_assertions) {
for n in tgt_ptr.as_branch::<K, V>().nodes.iter() {
assert!(n.is_null() || !n.is_dirty());
}
}
}
}
pub(crate) fn insert(&mut self, h: u64, k: K, mut v: V) -> Option<V> {
self.dirty_root();
let mut node = self.root;
for d in 0..MAX_HEIGHT {
let bref: &mut Branch<K, V> = node.as_branch_mut();
let shift = d * SHIFT;
let idx = ((h & (HASH_MASK << shift)) >> shift) as usize;
debug_assert!(idx < HT_CAPACITY);
let tgt_ptr = bref.nodes[idx];
if tgt_ptr.is_null() {
let dbkt_ptr = new_dirty_bucket_ptr::<K, V>();
self.first_seen.push(dbkt_ptr);
dbkt_ptr.as_bucket_mut().push(Datum { h, k, v });
bref.nodes[idx] = dbkt_ptr;
self.length += 1;
return None;
} else if tgt_ptr.is_branch() {
if tgt_ptr.is_dirty() {
node = tgt_ptr;
} else {
self.last_seen.push(tgt_ptr);
let from_bref: &Branch<K, V> = tgt_ptr.as_branch();
let nbrch_ptr = from_bref.clone_dirty();
self.first_seen.push(nbrch_ptr);
bref.nodes[idx] = nbrch_ptr;
node = nbrch_ptr;
}
} else {
if d == (MAX_HEIGHT - 1) {
let bkt_ptr = if tgt_ptr.is_dirty() {
tgt_ptr
} else {
self.last_seen.push(tgt_ptr);
let dbkt_ptr = new_dirty_bucket_ptr::<K, V>();
self.first_seen.push(dbkt_ptr);
let dbkt = dbkt_ptr.as_bucket_mut::<K, V>();
tgt_ptr.as_bucket().iter().for_each(|datum| {
dbkt.push(datum.clone());
});
bref.nodes[idx] = dbkt_ptr;
dbkt_ptr
};
let bkt = bkt_ptr.as_bucket_mut::<K, V>();
for datum in bkt.iter_mut() {
if datum.h == h && k.eq(datum.k.borrow()) {
std::mem::swap(&mut datum.v, &mut v);
return Some(v);
}
}
self.length += 1;
bkt.push(Datum { h, k, v });
return None;
} else {
let bkt_ptr = if tgt_ptr.is_dirty() {
tgt_ptr
} else {
debug_assert!(tgt_ptr.as_bucket::<K, V>().len() == 1);
self.last_seen.push(tgt_ptr);
let dbkt_ptr = new_dirty_bucket_ptr::<K, V>();
self.first_seen.push(dbkt_ptr);
let dbkt = dbkt_ptr.as_bucket_mut::<K, V>();
tgt_ptr.as_bucket().iter().for_each(|datum| {
dbkt.push(datum.clone());
});
bref.nodes[idx] = dbkt_ptr;
dbkt_ptr
};
let nbrch_ptr = new_dirty_branch_ptr::<K, V>();
self.first_seen.push(nbrch_ptr);
let bh = bkt_ptr.as_bucket_mut::<K, V>()[0].h;
let shift = (d + 1) * SHIFT;
let bidx = ((bh & (HASH_MASK << shift)) >> shift) as usize;
debug_assert!(bidx < HT_CAPACITY);
nbrch_ptr.as_branch_mut::<K, V>().nodes[bidx] = bkt_ptr;
bref.nodes[idx] = nbrch_ptr;
node = nbrch_ptr;
}
}
}
unreachable!();
}
pub(crate) fn remove(&mut self, h: u64, k: &K) -> Option<V> {
self.dirty_root();
let mut node = self.root;
for d in 0..MAX_HEIGHT {
debug_assert!(node.is_dirty());
debug_assert!(node.is_branch());
let bref: &mut Branch<K, V> = node.as_branch_mut();
let shift = d * SHIFT;
let idx = ((h & (HASH_MASK << shift)) >> shift) as usize;
debug_assert!(idx < HT_CAPACITY);
let tgt_ptr = bref.nodes[idx];
if tgt_ptr.is_null() {
return None;
} else if tgt_ptr.is_branch() {
if tgt_ptr.is_dirty() {
node = tgt_ptr;
} else {
self.last_seen.push(tgt_ptr);
let from_bref: &Branch<K, V> = tgt_ptr.as_branch();
let nbrch_ptr = from_bref.clone_dirty();
self.first_seen.push(nbrch_ptr);
bref.nodes[idx] = nbrch_ptr;
node = nbrch_ptr;
}
} else {
debug_assert!(!tgt_ptr.as_bucket::<K, V>().is_empty());
if tgt_ptr.as_bucket::<K, V>().len() == 1 {
let tgt_bkt = tgt_ptr.as_bucket::<K, V>();
let datum = &tgt_bkt[0];
if datum.h == h && k.eq(datum.k.borrow()) {
bref.nodes[idx] = Ptr::null_mut();
let v = if tgt_ptr.is_dirty() {
let tgt_bkt_mut = tgt_ptr.as_bucket_mut::<K, V>();
let Datum { v, .. } = tgt_bkt_mut.remove(0);
tgt_ptr.free::<K, V>();
v
} else {
self.last_seen.push(tgt_ptr);
datum.v.clone()
};
self.length -= 1;
return Some(v);
} else {
return None;
}
} else {
let bkt_ptr = if tgt_ptr.is_dirty() {
tgt_ptr
} else {
self.last_seen.push(tgt_ptr);
let dbkt_ptr = new_dirty_bucket_ptr::<K, V>();
self.first_seen.push(dbkt_ptr);
let dbkt = dbkt_ptr.as_bucket_mut::<K, V>();
tgt_ptr.as_bucket().iter().for_each(|datum| {
dbkt.push(datum.clone());
});
bref.nodes[idx] = dbkt_ptr;
dbkt_ptr
};
let bkt = bkt_ptr.as_bucket_mut::<K, V>();
for (i, datum) in bkt.iter().enumerate() {
if datum.h == h && k.eq(datum.k.borrow()) {
let Datum { v, .. } = bkt.remove(i);
self.length -= 1;
return Some(v);
}
}
return None;
}
}
}
unreachable!();
}
pub(crate) unsafe fn get_slot_mut_ref(&mut self, h: u64) -> Option<&mut [Datum<K, V>]> {
self.dirty_root();
let mut node = self.root;
for d in 0..MAX_HEIGHT {
debug_assert!(node.is_dirty());
debug_assert!(node.is_branch());
let bref: &mut Branch<K, V> = node.as_branch_mut();
let shift = d * SHIFT;
let idx = ((h & (HASH_MASK << shift)) >> shift) as usize;
debug_assert!(idx < HT_CAPACITY);
let tgt_ptr = bref.nodes[idx];
if tgt_ptr.is_null() {
return None;
} else if tgt_ptr.is_branch() {
if tgt_ptr.is_dirty() {
node = tgt_ptr;
} else {
self.last_seen.push(tgt_ptr);
let from_bref: &Branch<K, V> = tgt_ptr.as_branch();
let nbrch_ptr = from_bref.clone_dirty();
self.first_seen.push(nbrch_ptr);
bref.nodes[idx] = nbrch_ptr;
node = nbrch_ptr;
}
} else {
debug_assert!(!tgt_ptr.as_bucket::<K, V>().is_empty());
let bkt_ptr = if tgt_ptr.is_dirty() {
tgt_ptr
} else {
self.last_seen.push(tgt_ptr);
let dbkt_ptr = new_dirty_bucket_ptr::<K, V>();
self.first_seen.push(dbkt_ptr);
let dbkt = dbkt_ptr.as_bucket_mut::<K, V>();
tgt_ptr.as_bucket().iter().for_each(|datum| {
dbkt.push(datum.clone());
});
bref.nodes[idx] = dbkt_ptr;
dbkt_ptr
};
let bkt = bkt_ptr.as_bucket_mut::<K, V>();
let x = bkt.as_mut_slice() as *mut [Datum<K, V>];
return Some(&mut *x as &mut [Datum<K, V>]);
}
}
unreachable!();
}
pub(crate) fn get_mut_ref(&mut self, h: u64, k: &K) -> Option<&mut V> {
unsafe { self.get_slot_mut_ref(h) }.and_then(|bkt| {
bkt.iter_mut()
.filter_map(|datum| {
if datum.h == h && k.eq(datum.k.borrow()) {
let x = &mut datum.v as *mut V;
Some(unsafe { &mut *x as &mut V })
} else {
None
}
})
.next()
})
}
pub(crate) fn clear(&mut self) {
let mut stack = VecDeque::new();
stack.push_back(self.root);
while let Some(tgt_ptr) = stack.pop_front() {
self.last_seen.push(tgt_ptr);
if tgt_ptr.is_branch() {
for n in tgt_ptr.as_branch::<K, V>().nodes.iter() {
if !n.is_null() {
self.last_seen.push(*n);
}
}
}
}
let b: Box<Branch<K, V>> = Branch::new();
self.root = Ptr::from(b);
self.root.mark_dirty();
self.length = 0;
}
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> Extend<(K, V)> for CursorWrite<K, V> {
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
iter.into_iter().for_each(|(k, v)| {
let h = self.hash_key(&k);
let _ = self.insert(h, k, v);
});
}
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> Drop for CursorWrite<K, V> {
fn drop(&mut self) {
self.first_seen.iter().for_each(|p| p.free::<K, V>())
}
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> CursorReadOps<K, V> for CursorWrite<K, V> {
fn get_root_ptr(&self) -> Ptr {
self.root
}
fn len(&self) -> usize {
self.length
}
fn get_txid(&self) -> u64 {
self.txid
}
fn hash_key<'a, 'b, Q: ?Sized>(&'a self, k: &'b Q) -> u64
where
K: Borrow<Q>,
Q: Hash + Eq,
{
hash_key!(self, k)
}
fn verify(&self) {
self.verify_inner(false);
}
}
#[derive(Debug)]
pub(crate) struct CursorRead<K, V>
where
K: Hash + Eq + Clone + Debug,
V: Clone,
{
txid: u64,
length: usize,
root: Ptr,
last_seen: Mutex<Vec<Ptr>>,
build_hasher: RandomState,
k: PhantomData<K>,
v: PhantomData<V>,
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> CursorRead<K, V> {
pub(crate) fn new(sblock: &SuperBlock<K, V>) -> Self {
let build_hasher = sblock.build_hasher.clone();
CursorRead {
txid: sblock.txid,
length: sblock.length,
root: sblock.root,
last_seen: Mutex::new(Vec::with_capacity(0)),
build_hasher,
k: PhantomData,
v: PhantomData,
}
}
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> Drop for CursorRead<K, V> {
fn drop(&mut self) {
let last_seen_guard = self
.last_seen
.try_lock()
.expect("Unable to lock, something is horridly wrong!");
last_seen_guard.iter().for_each(|p| p.free::<K, V>());
std::mem::drop(last_seen_guard);
}
}
impl<K: Clone + Hash + Eq + Debug, V: Clone> CursorReadOps<K, V> for CursorRead<K, V> {
fn get_root_ptr(&self) -> Ptr {
self.root
}
fn len(&self) -> usize {
self.length
}
fn get_txid(&self) -> u64 {
self.txid
}
fn hash_key<'a, 'b, Q: ?Sized>(&'a self, k: &'b Q) -> u64
where
K: Borrow<Q>,
Q: Hash + Eq,
{
hash_key!(self, k)
}
fn verify(&self) {
self.verify_inner(true);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hashtrie_cursor_basic() {
let sb: SuperBlock<u64, u64> = unsafe { SuperBlock::new() };
let mut wr = sb.create_writer();
assert!(wr.len() == 0);
assert!(wr.search(0, &0).is_none());
assert!(wr.insert(0, 0, 0).is_none());
assert!(wr.len() == 1);
assert!(wr.search(0, &0).is_some());
assert!(wr.search(1, &1).is_none());
assert!(wr.insert(1, 1, 0).is_none());
assert!(wr.search(1, &1).is_some());
assert!(wr.len() == 2);
std::mem::drop(wr);
std::mem::drop(sb);
assert_released();
}
#[test]
fn test_hashtrie_cursor_insert_max_depth() {
let mut sb: SuperBlock<u64, u64> = unsafe { SuperBlock::new() };
let rdr = sb.create_reader();
let mut wr = sb.create_writer();
assert!(wr.len() == 0);
for i in 0..(ABS_MAX_HEIGHT * 2) {
assert!(wr.insert(0, i, i).is_none());
wr.verify();
}
assert!(wr.len() == (ABS_MAX_HEIGHT * 2) as usize);
for i in 0..(ABS_MAX_HEIGHT * 2) {
assert!(wr.search(0, &i).is_some());
}
for i in 0..(ABS_MAX_HEIGHT * 2) {
assert!(wr.remove(0, &i).is_some());
wr.verify();
}
assert!(wr.len() == 0);
let rdr2 = sb.pre_commit(wr, &rdr);
rdr2.verify();
rdr.verify();
std::mem::drop(rdr);
rdr2.verify();
std::mem::drop(rdr2);
std::mem::drop(sb);
assert_released();
}
#[test]
fn test_hashtrie_cursor_insert_broad() {
let mut sb: SuperBlock<u64, u64> = unsafe { SuperBlock::new() };
let rdr = sb.create_reader();
let mut wr = sb.create_writer();
assert!(wr.len() == 0);
for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) {
assert!(wr.insert(i, i, i).is_none());
wr.verify();
}
assert!(wr.len() == (ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) as usize);
for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) {
assert!(wr.search(i, &i).is_some());
}
for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) {
assert!(wr.remove(i, &i).is_some());
wr.verify();
}
assert!(wr.len() == 0);
let rdr2 = sb.pre_commit(wr, &rdr);
rdr2.verify();
rdr.verify();
std::mem::drop(rdr);
rdr2.verify();
std::mem::drop(rdr2);
std::mem::drop(sb);
assert_released();
}
#[test]
fn test_hashtrie_cursor_insert_multiple_txns() {
let mut sb: SuperBlock<u64, u64> = unsafe { SuperBlock::new() };
let mut rdr = sb.create_reader();
assert!(rdr.len() == 0);
for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) {
let mut wr = sb.create_writer();
assert!(wr.insert(i, i, i).is_none());
wr.verify();
rdr = sb.pre_commit(wr, &rdr);
}
{
let rdr2 = sb.create_reader();
assert!(rdr2.len() == (ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) as usize);
for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) {
assert!(rdr2.search(i, &i).is_some());
}
}
for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) {
let mut wr = sb.create_writer();
assert!(wr.remove(i, &i).is_some());
wr.verify();
rdr = sb.pre_commit(wr, &rdr);
}
assert!(rdr.len() == 0);
rdr.verify();
std::mem::drop(rdr);
std::mem::drop(sb);
assert_released();
}
}