jlrs 0.23.0

jlrs provides bindings to the Julia C API that enable Julia code to be called from Rust and more.
Documentation
use std::{
    borrow::Borrow,
    collections::HashMap,
    hash::{BuildHasher, Hash, RandomState},
    sync::atomic::{AtomicBool, Ordering},
};

use fnv::FnvBuildHasher;
use rustc_hash::FxBuildHasher;

use crate::{gc_safe::GcSafeRwLock, memory::gc::gc_safe};

pub(crate) struct Cache<K, V, S = RandomState> {
    map: GcSafeRwLock<HashMap<K, V, S>>,
    dirty: AtomicBool,
}

pub(crate) type FnvCache<K, V> = Cache<K, V, FnvBuildHasher>;

pub(crate) const fn new_fnv_cache<K, V>() -> FnvCache<K, V> {
    let hasher = FnvBuildHasher::new();
    Cache {
        map: GcSafeRwLock::new(HashMap::with_hasher(hasher)),
        dirty: AtomicBool::new(false),
    }
}

pub(crate) type FxCache<K, V> = Cache<K, V, FxBuildHasher>;

pub(crate) const fn new_fx_cache<K, V>() -> FxCache<K, V> {
    let hasher = FxBuildHasher;
    Cache {
        map: GcSafeRwLock::new(HashMap::with_hasher(hasher)),
        dirty: AtomicBool::new(false),
    }
}

pub(crate) trait CacheMap<'a, K: 'a + Eq + Hash, V: 'a + Clone, S: BuildHasher> {
    fn insert(&self, key: K, value: V) -> Option<V>;

    fn get<Q>(&'a self, key: &Q) -> Option<V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq + ?Sized;

    fn clear_dirty(&self);

    fn is_dirty(&self) -> bool;

    unsafe fn map(&'a self, func: impl Fn(V));
}

impl<'a, K: 'a + Eq + Hash, V: 'a + Clone> CacheMap<'a, K, V, FxBuildHasher> for FxCache<K, V> {
    fn insert(&self, key: K, value: V) -> Option<V> {
        let res = unsafe { gc_safe(|| self.map.write().insert(key, value)) };
        self.dirty.store(true, Ordering::Relaxed);
        res
    }

    fn get<Q>(&'a self, key: &Q) -> Option<V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq + ?Sized,
    {
        self.map.read().get(key).cloned()
    }

    fn clear_dirty(&self) {
        self.dirty.store(false, Ordering::Relaxed);
    }

    fn is_dirty(&self) -> bool {
        self.dirty.load(Ordering::Relaxed)
    }

    unsafe fn map(&'a self, func: impl Fn(V)) {
        unsafe {
            self.map
                .data_ptr()
                .as_ref()
                .unwrap()
                .iter()
                .map(|(_, v)| func(v.clone()))
                .collect::<()>()
        }
    }
}

impl<'a, K: 'a + Eq + Hash, V: 'a + Clone> CacheMap<'a, K, V, FnvBuildHasher> for FnvCache<K, V> {
    fn insert(&self, key: K, value: V) -> Option<V> {
        let res = self.map.write().insert(key, value);
        self.dirty.store(true, Ordering::Relaxed);
        res
    }

    fn get<Q>(&'a self, key: &Q) -> Option<V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq + ?Sized,
    {
        self.map.read().get(key).cloned()
    }

    fn clear_dirty(&self) {
        self.dirty.store(false, Ordering::Relaxed);
    }

    fn is_dirty(&self) -> bool {
        self.dirty.load(Ordering::Relaxed)
    }

    unsafe fn map(&'a self, func: impl Fn(V)) {
        unsafe {
            self.map
                .data_ptr()
                .as_ref()
                .unwrap()
                .iter()
                .map(|(_, v)| func(v.clone()))
                .collect::<()>()
        }
    }
}

unsafe impl<K, V, S> Send for Cache<K, V, S> {}
unsafe impl<K, V, S> Sync for Cache<K, V, S> {}