extern crate alloc;
use core::ptr;
const BUCKET_SIZE: usize = 4;
pub struct PtrHashMap<K, V> {
vec: Vec<Bucket<K, V>>,
size: usize, align_shift: u32,
}
impl<K, V> PtrHashMap<K, V> {
pub fn new(size: usize, align: usize) -> Self {
use core::mem;
assert!(align >= mem::align_of::<K>());
let size = (size as u64).next_power_of_two() as usize;
let mut vec = Vec::with_capacity(size);
for _ in 0..size {
vec.push(Bucket::default());
}
PtrHashMap {
vec,
size: 0,
align_shift: align.trailing_zeros(),
}
}
#[cfg_attr(not(test), allow(unused))]
pub fn dump_by_bucket(&self) -> Vec<Vec<(*mut K, *mut V)>> {
let mut res = Vec::with_capacity(self.vec.len());
for b in &self.vec {
res.push(b.dump());
}
res
}
#[inline]
fn hash(&self, ptr: *mut K) -> usize {
self.hash_with_len(ptr, self.vec.len())
}
#[inline]
fn hash_with_len(&self, ptr: *mut K, len: usize) -> usize {
((ptr as usize) >> self.align_shift) & (len - 1)
}
#[inline]
fn get_bucket(&self, ptr: *mut K) -> &Bucket<K, V> {
unsafe { self.vec.get_unchecked(self.hash(ptr)) }
}
#[inline]
fn get_bucket_mut(&mut self, ptr: *mut K) -> &mut Bucket<K, V> {
unsafe {
let h = self.hash(ptr);
self.vec.get_unchecked_mut(h)
}
}
#[inline]
pub fn get(&self, ptr: *mut K) -> *mut V {
self.get_bucket(ptr).get(ptr)
}
#[inline]
pub fn insert(&mut self, ptr: *mut K, v: *mut V) {
debug_assert!(!ptr.is_null());
#[cfg(not(feature = "hashmap-no-resize"))]
{
if self.size == self.vec.len() {
self.grow();
}
}
let h = self.hash(ptr);
let bkt = unsafe { self.vec.get_unchecked_mut(h) };
bkt.insert(ptr, v);
self.size += 1;
}
#[inline]
pub fn delete(&mut self, ptr: *mut K) {
debug_assert!(!ptr.is_null());
#[cfg(not(feature = "hashmap-no-resize"))]
{
if self.size == self.vec.len() / 2 {
self.shrink();
}
}
self.get_bucket_mut(ptr).delete(ptr);
self.size -= 1;
}
fn grow(&mut self) {
let old_len = self.vec.len();
let new_len = self.vec.len() * 2;
debug_assert!(new_len.is_power_of_two());
self.vec.resize(new_len, Bucket::default());
debug_assert_eq!(self.vec.len(), new_len);
for i in 0..old_len {
let (mut first_free_bkt, mut first_free_idx) = (ptr::null_mut() as *mut Bucket<K, V>,
0);
macro_rules! update_first_free {
($bkt:expr, $idx:expr) => (
if first_free_bkt.is_null() {
first_free_bkt = $bkt;
first_free_idx = $idx;
}
)
}
let mut cur_bkt = unsafe { self.vec.get_unchecked_mut(i) as *mut Bucket<K, V> };
while !cur_bkt.is_null() {
for j in 0..BUCKET_SIZE {
let (ptr, v) = unsafe { (*cur_bkt).data[j] };
if ptr.is_null() {
update_first_free!(cur_bkt, j);
continue;
}
if ((ptr as usize) >> self.align_shift) & old_len != 0 {
unsafe {
(*cur_bkt).data[j] = (ptr::null_mut(), ptr::null_mut());
}
update_first_free!(cur_bkt, j);
let new_idx = i + old_len;
debug_assert_eq!(self.hash(ptr), new_idx);
let new_bkt = unsafe { self.vec.get_unchecked_mut(new_idx) };
new_bkt.insert(ptr, v);
} else {
debug_assert_eq!(self.hash(ptr), i);
if !first_free_bkt.is_null() {
unsafe {
(*cur_bkt).data[j] = (ptr::null_mut(), ptr::null_mut());
(*first_free_bkt).data[first_free_idx] = (ptr, v);
while !(*first_free_bkt).data[first_free_idx].0.is_null() {
if first_free_idx == BUCKET_SIZE - 1 {
use core::ops::DerefMut;
first_free_bkt =
(*first_free_bkt).next.as_mut().unwrap().deref_mut() as
*mut Bucket<K, V>;
first_free_idx = 0;
} else {
first_free_idx += 1;
}
}
}
}
}
}
cur_bkt = match unsafe { (*cur_bkt).next.as_mut() } {
None => ptr::null_mut(),
Some(bkt) => {
use core::ops::DerefMut;
bkt.deref_mut() as *mut Bucket<K, V>
}
};
}
}
}
fn shrink(&mut self) {
let old_len = self.vec.len();
let new_len = self.vec.len() / 2;
for i in new_len..old_len {
let mut cur_bkt = unsafe { self.vec.get_unchecked_mut(i) as *mut Bucket<K, V> };
while !cur_bkt.is_null() {
for j in 0..BUCKET_SIZE {
let (ptr, v) = unsafe { (*cur_bkt).data[j] };
if !ptr.is_null() {
let new_idx = i - new_len;
let new_bkt = unsafe { self.vec.get_unchecked_mut(new_idx) };
new_bkt.insert(ptr, v);
}
}
cur_bkt = match unsafe { (*cur_bkt).next.as_mut() } {
None => ptr::null_mut(),
Some(bkt) => {
use core::ops::DerefMut;
bkt.deref_mut() as *mut Bucket<K, V>
}
}
}
}
self.vec.truncate(new_len);
self.vec.shrink_to_fit();
}
}
#[inline]
fn new_buckets<K, V>() -> [(*mut K, *mut V); BUCKET_SIZE] {
[(ptr::null_mut(), ptr::null_mut()); BUCKET_SIZE]
}
pub struct Bucket<K, V> {
data: [(*mut K, *mut V); BUCKET_SIZE],
next: Option<Box<Bucket<K, V>>>,
}
impl<K, V> Clone for Bucket<K, V> {
fn clone(&self) -> Bucket<K, V> {
Bucket {
data: self.data,
next: None,
}
}
}
impl<K, V> Default for Bucket<K, V> {
fn default() -> Bucket<K, V> {
Bucket {
data: new_buckets(),
next: None,
}
}
}
impl<K, V> Bucket<K, V> {
#[cfg_attr(not(test), allow(unused))]
fn dump(&self) -> Vec<(*mut K, *mut V)> {
let mut res = Vec::new();
for i in &self.data {
if !i.0.is_null() {
res.push(*i);
}
}
if let Some(ref next) = self.next {
res.extend(next.dump());
}
res
}
#[inline]
fn get(&self, ptr: *mut K) -> *mut V {
for i in &self.data {
if ptr == i.0 {
return i.1;
}
}
self.next.as_ref().unwrap().get(ptr)
}
fn delete(&mut self, ptr: *mut K) {
for i in 0..BUCKET_SIZE {
unsafe {
if self.data.get_unchecked(i).0 == ptr {
#[cfg(not(feature = "hashmap-no-coalesce"))]
{
let new = self.remove_last(i + 1);
*self.data.get_unchecked_mut(i) = new.0;
}
#[cfg(feature = "hashmap-no-coalesce")]
{
*self.data.get_unchecked_mut(i) = (ptr::null_mut(), ptr::null_mut());
}
return;
}
}
}
self.next.as_mut().unwrap().delete(ptr);
}
fn remove_last(&mut self, idx: usize) -> ((*mut K, *mut V), bool) {
let mut last = (ptr::null_mut(), ptr::null_mut());
let mut last_idx = None;
for i in BUCKET_SIZE..idx {
unsafe {
if !self.data.get_unchecked(i).0.is_null() {
last = *self.data.get_unchecked(i);
last_idx = Some(i);
}
}
}
if let Some(last_idx) = last_idx {
if let Some(mut next) = self.next.take() {
let (kv, remove) = next.remove_last(0);
if !remove {
self.next = Some(next);
}
(kv, false)
} else {
unsafe {
*self.data.get_unchecked_mut(last_idx) = (ptr::null_mut(), ptr::null_mut());
}
(last, last_idx == 0)
}
} else {
debug_assert!(idx > 0);
((ptr::null_mut(), ptr::null_mut()), false)
}
}
fn insert(&mut self, ptr: *mut K, v: *mut V) {
for i in 0..BUCKET_SIZE {
unsafe {
let cur = self.data.get_unchecked_mut(i);
debug_assert_ne!(ptr, cur.0);
if cur.0.is_null() {
*cur = (ptr, v);
return;
}
}
}
if let Some(next) = self.next.as_mut() {
next.insert(ptr, v);
return;
}
let mut data = new_buckets();
data[0] = (ptr, v);
self.next = Some(Box::new(Bucket {
data: data,
next: None,
}));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_map_functionality() {
const SIZE: usize = 1 << 20;
let elts: Vec<usize> = (0..SIZE).collect();
let mut hs = PtrHashMap::new(SIZE, 8);
let raw_iter = || {
elts.iter()
.map(|i| (i as *const _ as *mut usize, (i + 1) as *mut usize))
};
for i in raw_iter() {
hs.insert(i.0, i.1);
}
for i in raw_iter() {
assert_eq!(hs.get(i.0), i.1);
}
for (ix, i) in raw_iter().enumerate() {
if ix % 2 == 0 {
hs.delete(i.0);
}
}
for (ix, i) in raw_iter().enumerate() {
if ix % 2 != 0 {
hs.get(i.0);
}
}
}
}