bumpish 0.1.0

A set of collections using bump allocations
Documentation
use super::BucketPtr;
use super::entry::{OccupiedEntry, VacantEntry};
use super::*;

pub(super) struct Core<K, V> {
    pub(super) hash_table: UnsafeCell<HashTable<BucketPtr<K, V>>>,
    pub(super) entries: Entries<K, V>,
}

impl<K, V> Core<K, V> {
    pub(super) const fn new() -> Self {
        Self {
            hash_table: UnsafeCell::new(HashTable::new()),
            entries: Entries::new(),
        }
    }

    pub(super) fn with_capacity(n: usize) -> Self {
        Self {
            hash_table: UnsafeCell::new(HashTable::with_capacity(n)),
            entries: Entries::with_capacity(n),
        }
    }

    pub(super) fn capacity(&self) -> usize {
        usize::min(
            self.entries.capacity(),
            unsafe { self.hashtab() }.capacity(),
        )
    }

    pub(super) fn clear(&mut self) {
        self.hash_table.get_mut().clear();
        self.entries.clear();
    }

    pub(super) fn len(&self) -> usize {
        assert_eq!(self.entries.len(), unsafe { self.hashtab() }.len());
        self.entries.len()
    }

    /// > **N.B.** The `Q` type must hash like `K`.
    pub(super) fn contains_key<F, Q>(&self, hasher: F, key: &Q) -> bool
    where
        F: Fn() -> u64,
        Q: ?Sized + Hash + Equivalent<K>,
    {
        let hash_table = unsafe { self.hashtab() };
        if hash_table.is_empty() {
            false
        } else {
            hash_table.find(hasher(), eq_fn(key)).is_some()
        }
    }

    pub(super) fn get_key_value<Q>(&self, hash: u64, key: &Q) -> Option<(&K, &V)>
    where
        Q: ?Sized + Hash + Equivalent<K>,
    {
        let hash_table = unsafe { self.hashtab() };
        if let Some(ptr) = hash_table.find(hash, eq_fn(key)) {
            let bucket = unsafe { ptr.as_ref() };
            Some(bucket.as_tuple())
        } else {
            None
        }
    }

    pub(super) fn get_mut<Q>(&mut self, hash: u64, key: &Q) -> Option<&mut V>
    where
        Q: ?Sized + Hash + Equivalent<K>,
    {
        let hash_table = self.hash_table.get_mut();
        if let Some(ptr) = hash_table.find_mut(hash, eq_fn(key)) {
            let bucket = unsafe { ptr.as_mut() };
            Some(bucket.as_tuple_mut().1)
        } else {
            None
        }
    }

    pub(super) fn insert_unique(&self, hash: u64, key: K, value: V) -> &Bucket<K, V> {
        let bucket = Bucket::new(hash, key, value);
        let bucket_ptr = self.entries.push_ptr(|| bucket);
        let hash_table = unsafe { &mut *self.hash_table.get() };
        hash_table.insert_unique(hash, bucket_ptr, hash_fn());
        unsafe { bucket_ptr.as_ref() }
    }

    unsafe fn hashtab(&self) -> &HashTable<BucketPtr<K, V>> {
        unsafe { &*self.hash_table.get() }
    }
}

impl<K: Eq, V> Core<K, V> {
    pub(super) fn pop(&mut self) -> Option<Bucket<K, V>> {
        if let Some(bucket) = self.entries.pop() {
            let hash_table = self.hash_table.get_mut();
            let entry = hash_table.find_entry(bucket.hash, eq_fn(&bucket.key));
            unsafe { entry.unwrap_unchecked().remove() };
            Some(bucket)
        } else {
            None
        }
    }

    pub fn entry(&self, hash: u64, key: K) -> Entry<'_, K, V> {
        let hash_table = unsafe { &mut *self.hash_table.get() };
        match hash_table.entry(hash, eq_fn(&key), hash_fn()) {
            hash_table::Entry::Occupied(entry) => {
                Entry::Occupied(unsafe { OccupiedEntry::new(*entry.get()) })
            }
            hash_table::Entry::Vacant(_) => Entry::Vacant(VacantEntry::new(hash, key, self)),
        }
    }

    /// Doesn't preallocate memory for a possible new insertion.
    pub(super) fn try_insert(&self, hash: u64, k: K, v: V) -> Result<&V, OccupiedError<'_, K, V>> {
        let hash_table = unsafe { &mut *self.hash_table.get() };
        match hash_table.find_entry(hash, eq_fn(&k)) {
            Ok(occupied_entry) => Err(OccupiedError {
                entry: unsafe { OccupiedEntry::new(*occupied_entry.get()) },
                value: v,
            }),
            Err(absent_entry) => {
                let new_bucket = Bucket::new(hash, k, v);
                let bucket_ptr = self.entries.push_ptr(|| new_bucket);
                let table = absent_entry.into_table();
                table.insert_unique(hash, bucket_ptr, hash_fn());
                Ok(unsafe { &bucket_ptr.as_ref().value })
            }
        }
    }

    /// Can preallocate memory for a possible new insertion.
    pub(super) fn insert(&mut self, hash: u64, k: K, v: V) -> Option<V> {
        let hash_table = self.hash_table.get_mut();
        match hash_table.entry(hash, eq_fn(&k), hash_fn()) {
            hash_table::Entry::Occupied(mut entry) => {
                let ptr = entry.get_mut();
                let bucket = unsafe { ptr.as_mut() };
                let mut new_bucket = Bucket::new(hash, k, v);
                core::mem::swap(&mut bucket.value, &mut new_bucket.value);
                Some(new_bucket.value)
            }
            hash_table::Entry::Vacant(entry) => {
                let new_bucket = Bucket::new(hash, k, v);
                let bucket_ptr = self.entries.push_ptr(|| new_bucket);
                entry.insert(bucket_ptr);
                None
            }
        }
    }
}

fn hash_fn<K, V>() -> impl Fn(&BucketPtr<K, V>) -> u64 {
    |ptr: &BucketPtr<K, V>| -> u64 {
        let bucket = unsafe { ptr.as_ref() };
        bucket.hash
    }
}

fn eq_fn<K, Q, V>(key: &Q) -> impl FnMut(&BucketPtr<K, V>) -> bool
where
    Q: ?Sized + Equivalent<K>,
{
    |ptr: &BucketPtr<K, V>| -> bool {
        let bucket = unsafe { ptr.as_ref() };
        Equivalent::equivalent(key, &bucket.key)
    }
}

impl<K, V> core::clone::Clone for Core<K, V>
where
    K: Clone,
    V: Clone,
{
    fn clone(&self) -> Self {
        let hash_table = unsafe { self.hashtab() };
        Self {
            hash_table: UnsafeCell::new(hash_table.clone()),
            entries: self.entries.clone(),
        }
    }
}