use crate::*;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker::PhantomData;
pub struct HashMap<'a, T>
where
T: SmallFixed,
{
buckets: u64,
root: u64,
ps: &'a mut PageSet,
new_root: bool,
pd: PhantomData<T>,
}
impl<'a, T> HashMap<'a, T>
where
T: SmallFixed,
{
pub fn new(ps: &'a mut PageSet, buckets: u64) -> Self {
let mut pt = PageTree::new(ps.new_page(), 1, ps);
pt.resize(buckets);
Self {
buckets,
root: pt.root,
ps,
new_root: true,
pd: PhantomData,
}
}
pub fn insert<K: Key<T>>(&mut self, key: &K, val: T) {
let hash = self.hash(key);
self.do_insert(val, hash);
}
pub fn get<K: Key<T>>(&mut self, key: &K) -> Option<T> {
let hash = self.hash(key);
let pnum = self.get_page_num(hash, false);
if pnum == 0 {
return None;
}
let (data, changed) = self.ps.load(pnum);
let result = bucket::Reader::new(&data).get(key, hash, self.ps);
self.ps.note(pnum, data, changed);
result
}
pub fn remove<K: Key<T>>(&mut self, key: &K) -> Option<T> {
let hash = self.hash(key);
let pnum = self.get_page_num(hash, false);
if pnum != 0 {
let (mut data, changed) = self.ps.load(pnum);
let md = Arc::make_mut(&mut data);
md.resize(bucket::size::<T>(), 0);
let mut w = bucket::Writer::new(md);
let result = w.remove(key, hash, self.ps);
self.ps.note(pnum, data, changed || result.is_some());
return result;
}
None
}
pub fn root_changed(&self) -> bool {
self.new_root
}
pub fn save(&self) -> (u64, u64) {
(self.root, self.buckets)
}
pub fn restore((root, buckets): (u64, u64), ps: &'a mut PageSet) -> Self {
Self {
root,
buckets,
ps,
new_root: false,
pd: PhantomData,
}
}
pub fn all(&mut self) -> Vec<(T, u64)> {
let mut pt = PageTree::new(self.root, self.buckets, self.ps);
let mut result = Vec::new();
for pix in 0..self.buckets {
let pnum = pt.get(pix, false);
if pnum != 0 {
let (data, changed) = pt.ps.load(pnum);
let r = bucket::Reader::new(&data);
for x in r.iter() {
result.push(x);
}
pt.ps.note(pnum, data, changed);
}
}
result
}
pub fn delete(&mut self) {
let mut pt = PageTree::new(self.root, self.buckets, self.ps);
pt.drop_pages();
self.root = 0;
}
fn get_page_num(&mut self, hash: u64, create: bool) -> u64 {
let pix = hash % self.buckets;
PageTree::new(self.root, self.buckets, self.ps).get(pix, create)
}
fn do_insert(&mut self, mut val: T, hash: u64) {
while let Some(v) = self.try_insert(val, hash) {
val = v;
self.expand();
}
}
fn try_insert(&mut self, val: T, hash: u64) -> Option<T> {
let pnum = self.get_page_num(hash, true);
let (mut data, changed) = self.ps.load(pnum);
let md = Arc::make_mut(&mut data);
md.resize(bucket::size::<T>(), 0);
let mut w = bucket::Writer::new(md);
if w.full() {
self.ps.note(pnum, data, changed);
Some(val)
} else {
w.insert(val, hash);
self.ps.note(pnum, data, true);
None
}
}
fn expand(&mut self) {
let buckets = 1 + self.buckets * 9 / 8;
let list = self.all();
self.delete();
let mut m = HashMap::new(self.ps, buckets);
for (r, h) in list {
m.do_insert(r, h);
}
self.root = m.root;
self.buckets = m.buckets;
self.new_root = true;
}
fn hash<K: Hash>(&self, key: K) -> u64 {
let mut h = fxhash::FxHasher::default();
key.hash(&mut h);
h.finish()
}
}
impl SmallFixed for u64 {
fn size() -> usize {
8
}
fn load(bytes: &[u8]) -> Self {
u64::from_le_bytes(bytes.try_into().unwrap())
}
fn save(&self, bytes: &mut [u8]) {
bytes.copy_from_slice(&self.to_le_bytes());
}
}
impl SmallFixed for i64 {
fn size() -> usize {
8
}
fn load(bytes: &[u8]) -> Self {
i64::from_le_bytes(bytes.try_into().unwrap())
}
fn save(&self, bytes: &mut [u8]) {
bytes.copy_from_slice(&self.to_le_bytes());
}
}
#[cfg(test)]
#[derive(Hash)]
struct TestKey {
value: u64,
}
#[cfg(test)]
impl Key<u64> for TestKey {
fn ok(&self, v: &u64, _ps: &mut PageSet) -> bool {
self.value == *v
}
}
#[cfg(test)]
pub fn test_hash(ps: &mut PageSet) {
let mut hm = HashMap::new(ps, 1);
let last = 800;
for tv in 1..=last {
let key = TestKey { value: tv };
hm.insert(&key, tv);
}
println!("test_hash hm.buckets={}", hm.buckets);
for tv in 1..=last {
let key = TestKey { value: tv };
let x = hm.get(&key);
assert_eq!(x, Some(tv));
}
for tv in 1..=last {
let key = TestKey { value: tv };
let x = hm.remove(&key);
assert_eq!(x, Some(tv));
}
}