jay-compositor 1.15.0

The Jay compositor
use crate::utils::markers::JayHash;
use crate::utils::numcell::NumCell;
use crate::utils::object_registry::ObjectRegistryCache;
use crate::utils::object_registry::RegisteredObject;
use crate::utils::ptr_ext::MutPtrExt;
use rand::RngExt;
use rand::prelude::SmallRng;
use std::mem;
use std::ptr;
use std::rc::Rc;

pub trait Cache<K, V>: Sized
where
    K: JayHash,
{
    type Serial: Copy + Default + Sized;
    fn serial(&mut self) -> Self::Serial;
    fn insert(
        &mut self,
        v: &Rc<RegisteredObject<K, V, Self>>,
    ) -> Option<Rc<RegisteredObject<K, V, Self>>>;
    fn clear(&mut self) -> impl Sized;
}

impl<K, V, C> ObjectRegistryCache<K, V> for C
where
    K: JayHash,
    C: Cache<K, V>,
{
}

pub struct ObjectRegistryNoCache;

impl<K, V> Cache<K, V> for ObjectRegistryNoCache
where
    K: JayHash,
{
    type Serial = ();

    fn serial(&mut self) -> Self::Serial {}

    fn insert(
        &mut self,
        _v: &Rc<RegisteredObject<K, V, Self>>,
    ) -> Option<Rc<RegisteredObject<K, V, Self>>> {
        None
    }

    fn clear(&mut self) -> impl Sized {}
}

pub struct ObjectRegistryRandomCache<K, V>
where
    K: JayHash,
{
    map: Box<[Option<Rc<RegisteredObject<K, V, Self>>>]>,
    rng: SmallRng,
    serial: NumCell<u64>,
}

impl<K, V> ObjectRegistryRandomCache<K, V>
where
    K: JayHash,
{
    pub(super) fn new(size: usize) -> Self {
        Self {
            map: Self::create_map(size),
            rng: rand::make_rng(),
            serial: Default::default(),
        }
    }

    fn create_map(size: usize) -> Box<[Option<Rc<RegisteredObject<K, V, Self>>>]> {
        let size = size.next_power_of_two().max(16);
        vec![None; size].into_boxed_slice()
    }
}

impl<K, V> Cache<K, V> for ObjectRegistryRandomCache<K, V>
where
    K: JayHash,
{
    type Serial = u64;

    fn serial(&mut self) -> Self::Serial {
        self.serial.add_fetch(1)
    }

    fn insert(
        &mut self,
        value: &Rc<RegisteredObject<K, V, Self>>,
    ) -> Option<Rc<RegisteredObject<K, V, Self>>> {
        let value = value.clone();
        value.serial.set(self.serial());
        let r1 = self.rng.random::<u64>() as usize;
        let r2 = self.rng.random::<u64>() as usize;
        let offsets = [r1, r2, r2.wrapping_add(1)];
        let entries = unsafe {
            let mask = self.map.len() - 1;
            let ptr = self.map.as_mut_ptr();
            offsets.map(|idx| ptr.add(idx & mask))
        };
        let mut min_entry = ptr::null_mut();
        let mut min_serial = u64::MAX;
        for entry in entries {
            match unsafe { entry.deref_mut() } {
                e @ None => {
                    *e = Some(value);
                    return None;
                }
                Some(e) => {
                    let serial = e.serial.get();
                    if serial < min_serial {
                        min_entry = e;
                        min_serial = serial;
                    }
                }
            }
        }
        let old = unsafe { mem::replace(min_entry.deref_mut(), value) };
        Some(old)
    }

    fn clear(&mut self) -> impl Sized {
        let new = Self::create_map(self.map.len());
        mem::replace(&mut self.map, new)
    }
}