use twox_hash::{RandomXxHashBuilder64, XxHash64};
use core::hash::BuildHasher;
const MAX_LOAD_FACTOR: usize = 100_000;
const DEFAULT_BUCKETS_SIZE: usize = 4096 / std::mem::size_of::<usize>();
const DEFAULT_SLOT_SIZE: usize = 8;
pub struct ArrayHashBuilder<H> {
hasher: H,
buckets_size: usize,
max_load_factor: usize,
slot_size: usize
}
impl Default for ArrayHashBuilder<XxHash64> {
fn default() -> ArrayHashBuilder<XxHash64> {
ArrayHashBuilder {
hasher: RandomXxHashBuilder64::default().build_hasher(),
buckets_size: DEFAULT_BUCKETS_SIZE,
max_load_factor: MAX_LOAD_FACTOR,
slot_size: DEFAULT_SLOT_SIZE
}
}
}
impl<H> ArrayHashBuilder<H> where H: core::hash::Hasher {
pub fn with_hasher(hasher: H) -> ArrayHashBuilder<H> {
ArrayHashBuilder {
hasher: hasher,
buckets_size: DEFAULT_BUCKETS_SIZE,
max_load_factor: MAX_LOAD_FACTOR,
slot_size: DEFAULT_SLOT_SIZE
}
}
pub fn hasher<H2>(self, hasher: H2) -> ArrayHashBuilder<H2> {
ArrayHashBuilder {
hasher,
buckets_size: self.buckets_size,
max_load_factor: self.max_load_factor,
slot_size: self.slot_size
}
}
pub fn buckets_size(mut self, size: usize) -> Self {
self.buckets_size = size;
self
}
pub fn max_load_factor(mut self, factor: usize) -> Self {
self.max_load_factor = factor;
self
}
pub fn slot_size(mut self, size: usize) -> Self {
self.slot_size = size;
self
}
pub fn build<K, V>(self) -> ArrayHash<H, K, V> where H: core::hash::Hasher + Clone, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
ArrayHash {
buckets: Some(vec![Vec::with_capacity(self.slot_size); self.buckets_size].into_boxed_slice()),
hasher: self.hasher,
capacity: self.buckets_size,
max_load_factor: self.max_load_factor,
size: 0
}
}
}
#[derive(Clone, Debug)]
pub struct ArrayHash<H, K, V> where H: core::hash::Hasher + Clone, K: core::hash::Hash + PartialEq + Clone, V: Clone {
buckets: Option<Box<[Vec<(K, V)>]>>,
hasher: H,
capacity: usize,
max_load_factor: usize,
size: usize
}
impl<H, K, V> ArrayHash<H, K, V> where H: core::hash::Hasher + Clone, K: core::hash::Hash + PartialEq + Clone, V: Clone {
pub fn put(&mut self, key: K, value: V) -> Option<(K, V)> {
let mut index = self.make_key(&key);
let result;
if let Some(i) = self.buckets.as_mut().unwrap()[index].iter().position(|(k, _)| *k == key) {
result = Some(self.buckets.as_mut().unwrap()[index].swap_remove(i));
} else {
self.size += 1;
if self.maybe_expand() {
index = self.make_key(&key);
}
result = None
}
self.buckets.as_mut().unwrap()[index].push((key.clone(), value));
result
}
pub fn try_put(&mut self, key: K, value: V) -> Option<&V> {
let mut index = self.make_key(&key);
if let Some(i) = self.buckets.as_ref().unwrap()[index].iter().position(|(k, _)| *k == key) {
Some(&self.buckets.as_ref().unwrap()[index][i].1)
} else {
self.size += 1;
if self.maybe_expand() {
index = self.make_key(&key);
}
self.buckets.as_mut().unwrap()[index].push((key, value));
None
}
}
pub fn get(&self, key: &K) -> Option<&V> {
let index = self.make_key(key);
let slot = &self.buckets.as_ref().unwrap()[index];
for (ref k, ref v) in slot.iter() {
if *k == *key {
return Some(v)
}
}
None
}
pub fn smart_get<T, Q>(&self, key: Q) -> Option<&V> where Q: core::ops::Deref<Target=T>, K: core::ops::Deref<Target=T>, T: core::hash::Hash + core::cmp::PartialEq + ?Sized {
let mut local_hasher = self.hasher.clone();
key.hash(&mut local_hasher);
let index = local_hasher.finish() as usize % self.capacity;
let slot = &self.buckets.as_ref().unwrap()[index];
for (ref k, ref v) in slot.iter() {
if **k == *key {
return Some(v)
}
}
None
}
pub fn remove(&mut self, key: &K) -> Option<(K, V)> {
let slot_idx = self.make_key(key);
let slot = self.buckets.as_mut().unwrap();
let entry_idx = slot[slot_idx].iter().position(|(k, _)| {*k == *key});
if let Some(i) = entry_idx {
self.size -= 1;
Some(slot[slot_idx].remove(i))
} else {
None
}
}
pub fn len(&self) -> usize {
self.size
}
pub fn iter(&self) -> ArrayHashIterator<'_, K, V> {
let slots = self.buckets.as_ref().unwrap();
ArrayHashIterator {
buckets: &slots,
current_iterator: slots[0].iter(),
remain_slots: slots.len() - 1,
slot_cursor: 0,
size: self.size
}
}
pub fn iter_mut(&mut self) -> ArrayHashIterMut<'_, K, V> {
if self.size > 0 {
let buckets: Box<[core::slice::IterMut<(K, V)>]> = self.buckets.as_mut().unwrap().iter_mut().filter_map(|slot| {
if slot.len() > 0 {Some(slot.iter_mut())} else {None}
}).collect();
let remain_slots = buckets.len() - 1;
ArrayHashIterMut {
buckets,
remain_slots,
slot_cursor: 0,
size: self.size
}
} else {
ArrayHashIterMut {
buckets: vec![[].iter_mut()].into_boxed_slice(),
remain_slots: 0,
slot_cursor: 0,
size: self.size
}
}
}
pub fn drain(&mut self) -> DrainIter<K, V> {
let mut bucket_iter = self.buckets.as_mut().unwrap().iter_mut();
let current_slot = bucket_iter.next();
self.size = 0;
DrainIter {
bucket_iter,
current_slot,
size: self.size
}
}
pub fn drain_with<F>(&mut self, pred: F) -> DrainWithIter<F, K, V> where F: Fn(&(K, V)) -> bool {
let mut bucket_iter = self.buckets.as_mut().unwrap().iter_mut();
let current_slot = bucket_iter.next();
let size = self.size;
DrainWithIter {
bucket_iter,
cur_size: &mut self.size,
current_slot,
predicate: pred,
size
}
}
pub fn split_by<F>(&mut self, pred: F) -> ArrayHash<H, K, V> where F: Fn(&(K, V)) -> bool {
let mut other = ArrayHashBuilder::with_hasher(self.hasher.clone())
.buckets_size(self.buckets.as_ref().unwrap().len())
.max_load_factor(self.max_load_factor)
.build();
let buckets = self.buckets.as_mut().unwrap();
for i in 0..buckets.len() {
let mut j = 0;
loop {
if pred(&buckets[i][j]) {
other.buckets.as_mut().unwrap()[i].push(buckets[i].swap_remove(j));
other.size += 1;
self.size -= 1;
} else {
j += 1;
}
if j >= buckets[i].len() {
break;
}
}
}
other
}
#[inline(always)]
fn make_key<Q>(&self, key: Q) -> usize where Q: core::ops::Deref<Target=K> {
let mut local_hasher = self.hasher.clone();
key.hash(&mut local_hasher);
local_hasher.finish() as usize % self.capacity
}
#[inline(always)]
fn maybe_expand(&mut self) -> bool {
if self.size < self.max_load_factor {
return false
}
let old_buckets = self.buckets.take().unwrap().to_vec();
let new_capacity = self.capacity * 2;
self.capacity = new_capacity;
self.max_load_factor *= 2;
let mut buckets = vec!(Vec::with_capacity(old_buckets[0].len()); new_capacity);
old_buckets.into_iter().for_each(|slot| {
for (key, value) in slot {
let index = self.make_key(&key);
buckets[index % new_capacity].push((key, value));
}
});
self.buckets = Some(buckets.into_boxed_slice());
true
}
}
#[derive(Debug)]
pub struct ArrayHashIterator<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
buckets: &'a Box<[Vec<(K, V)>]>,
current_iterator: core::slice::Iter<'a, (K, V)>,
slot_cursor: usize,
remain_slots: usize,
size: usize
}
impl<'a, K, V> Iterator for ArrayHashIterator<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=&'a (K, V);
fn next(&mut self) -> Option<Self::Item> {
let mut result = self.current_iterator.next();
while result.is_none() {
if self.slot_cursor < self.remain_slots {
self.slot_cursor += 1;
self.current_iterator = self.buckets[self.slot_cursor].iter();
result = self.current_iterator.next();
} else {
break
}
}
result
}
}
impl<'a, K, V> core::iter::FusedIterator for ArrayHashIterator<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {}
impl<'a, K, V> core::iter::ExactSizeIterator for ArrayHashIterator<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
fn len(&self) -> usize {
self.size
}
}
#[derive(Debug)]
pub struct ArrayHashIterMut<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
buckets: Box<[core::slice::IterMut<'a, (K, V)>]>,
remain_slots: usize,
slot_cursor: usize,
size: usize
}
impl<'a, K, V> Iterator for ArrayHashIterMut<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=&'a mut (K, V);
fn next(&mut self) -> Option<Self::Item> {
let mut result = self.buckets[self.slot_cursor].next();
while result.is_none() {
if self.slot_cursor < self.remain_slots {
self.slot_cursor += 1;
result = self.buckets[self.slot_cursor].next();
} else {
break;
}
}
result
}
}
impl<'a, K, V> core::iter::FusedIterator for ArrayHashIterMut<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {}
impl<'a, K, V> core::iter::ExactSizeIterator for ArrayHashIterMut<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
fn len(&self) -> usize {
self.size
}
}
#[derive(Debug)]
pub struct ArrayHashIntoIter<K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
buckets: std::vec::IntoIter<Vec<(K, V)>>,
current_iterator: std::vec::IntoIter<(K, V)>,
size: usize
}
impl<K, V> Iterator for ArrayHashIntoIter<K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=(K, V);
fn next(&mut self) -> Option<Self::Item> {
let mut result = self.current_iterator.next();
while result.is_none() {
if let Some(slot) = self.buckets.next() {
if slot.len() > 0 {
self.current_iterator = slot.into_iter();
result = self.current_iterator.next();
}
} else {
break
}
}
result
}
}
impl<K, V> core::iter::FusedIterator for ArrayHashIntoIter<K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {}
impl<K, V> core::iter::ExactSizeIterator for ArrayHashIntoIter<K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
fn len(&self) -> usize {
self.size
}
}
impl<H, K, V> IntoIterator for ArrayHash<H, K, V> where H: core::hash::Hasher + Clone, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=(K, V);
type IntoIter=ArrayHashIntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
if self.size >= 1 {
let mut buckets = self.buckets.unwrap().to_vec().into_iter();
let current_iterator = buckets.next().unwrap().into_iter();
ArrayHashIntoIter {
buckets,
current_iterator,
size: self.size
}
} else {
let mut emptied_bucket = vec![vec![]].into_iter();
let emptied_iterator = emptied_bucket.next().unwrap().into_iter();
ArrayHashIntoIter {
buckets: emptied_bucket,
current_iterator: emptied_iterator,
size: 0
}
}
}
}
impl<'a, H, K, V> IntoIterator for &'a ArrayHash<H, K, V> where H: core::hash::Hasher + Clone, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=&'a(K, V);
type IntoIter=ArrayHashIterator<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, H, K, V> IntoIterator for &'a mut ArrayHash<H, K, V> where H: core::hash::Hasher + Clone, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=&'a mut (K, V);
type IntoIter=ArrayHashIterMut<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[derive(Debug)]
pub struct DrainIter<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
bucket_iter: core::slice::IterMut<'a, Vec<(K, V)>>,
current_slot: Option<&'a mut Vec<(K, V)>>,
size: usize,
}
impl<'a, K, V> Iterator for DrainIter<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=(K, V);
fn next(&mut self) -> Option<Self::Item> {
let mut result = self.current_slot.as_mut().unwrap().pop();
while result.is_none() {
self.current_slot = self.bucket_iter.next();
if self.current_slot.is_some() {
result = self.current_slot.as_mut().unwrap().pop();
} else {
break;
}
}
result
}
}
impl<'a, K, V> core::iter::FusedIterator for DrainIter<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {}
impl<'a, K, V> core::iter::ExactSizeIterator for DrainIter<'a, K, V> where K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
fn len(&self) -> usize {
self.size
}
}
#[derive(Debug)]
pub struct DrainWithIter<'a, F, K, V> where F: for<'r> Fn(&'r (K, V)) -> bool, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
bucket_iter: core::slice::IterMut<'a, Vec<(K, V)>>,
cur_size: &'a mut usize,
current_slot: Option<&'a mut Vec<(K, V)>>,
predicate: F,
size: usize
}
impl<'a, F, K, V> Iterator for DrainWithIter<'a, F, K, V> where F: for<'r> Fn(&'r (K, V)) -> bool, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
type Item=(K, V);
fn next(&mut self) -> Option<Self::Item> {
while let Some(ref mut v) = self.current_slot {
for i in 0..v.len() {
if (self.predicate)(&v[i]) {
*self.cur_size -= 1;
return Some(v.swap_remove(i))
}
}
loop {
self.current_slot = self.bucket_iter.next();
if self.current_slot.is_some() {
if self.current_slot.as_ref().unwrap().len() == 0 {
continue;
} else {
break;
}
} else {
return None
}
}
}
None
}
}
impl<'a, F, K, V> core::iter::FusedIterator for DrainWithIter<'a, F, K, V> where F: for<'r> Fn(&'r (K, V)) -> bool, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {}
impl<'a, F, K, V> core::iter::ExactSizeIterator for DrainWithIter<'a, F, K, V> where F: for<'r> Fn(&'r (K, V)) -> bool, K: core::hash::Hash + core::cmp::PartialEq + Clone, V: Clone {
fn len(&self) -> usize {
self.size
}
}
#[cfg(test)]
mod tests;