cranpose-core 0.0.60

Core runtime for a Jetpack Compose inspired UI framework in Rust
Documentation
use super::dense_id_map::DenseIdMap;
use crate::collections::map::HashMap;
use std::mem;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct GenerationalSlot<S> {
    pub(crate) generation: u32,
    pub(crate) state: S,
}

pub(crate) trait RegistryState: Copy {
    fn is_active(self) -> bool;
    fn is_detached(self) -> bool;
}

pub(crate) struct GenerationalRegistryStorage<S> {
    dense_states: DenseIdMap<GenerationalSlot<S>>,
    sparse_states: HashMap<usize, GenerationalSlot<S>>,
    active_count: usize,
    detached_count: usize,
}

impl<S> Default for GenerationalRegistryStorage<S> {
    fn default() -> Self {
        Self {
            dense_states: DenseIdMap::new(),
            sparse_states: HashMap::default(),
            active_count: 0,
            detached_count: 0,
        }
    }
}

impl<S> GenerationalRegistryStorage<S> {
    const DENSE_STORAGE_ID_LIMIT: usize = 65_536;

    pub(crate) fn new() -> Self {
        Self::default()
    }

    pub(crate) fn slot(&self, id: usize) -> Option<&GenerationalSlot<S>> {
        if Self::uses_dense_storage(id) {
            self.dense_states.get(id)
        } else {
            self.sparse_states.get(&id)
        }
    }

    pub(crate) fn slot_mut(&mut self, id: usize) -> Option<&mut GenerationalSlot<S>> {
        if Self::uses_dense_storage(id) {
            self.dense_states.get_mut(id)
        } else {
            self.sparse_states.get_mut(&id)
        }
    }

    pub(crate) fn slots(&self) -> impl Iterator<Item = (usize, &GenerationalSlot<S>)> + '_ {
        self.dense_states
            .iter()
            .chain(self.sparse_states.iter().map(|(&id, slot)| (id, slot)))
    }

    pub(crate) fn slot_len(&self) -> usize {
        self.dense_states.len() + self.sparse_states.len()
    }

    pub(crate) fn sparse_slot_len(&self) -> usize {
        self.sparse_states.len()
    }

    pub(crate) fn capacity(&self) -> usize {
        self.dense_states.capacity() + self.sparse_states.capacity()
    }

    pub(crate) fn heap_bytes(&self) -> usize {
        self.dense_states.capacity() * mem::size_of::<Option<GenerationalSlot<S>>>()
            + self.sparse_states.capacity() * mem::size_of::<(usize, GenerationalSlot<S>)>()
    }

    pub(crate) fn clear(&mut self) {
        self.dense_states.clear();
        self.sparse_states.clear();
        self.active_count = 0;
        self.detached_count = 0;
    }

    pub(crate) fn shrink_to_fit(&mut self) {
        self.dense_states.shrink_to_fit();
        self.sparse_states.shrink_to_fit();
    }

    pub(crate) fn maybe_shrink_sparse_storage(&mut self) -> bool {
        if self.capacity() <= Self::DENSE_STORAGE_ID_LIMIT {
            return false;
        }
        if self.slot_len().saturating_mul(4) >= self.capacity() {
            return false;
        }
        self.shrink_to_fit();
        true
    }

    #[cfg(test)]
    pub(crate) fn dense_capacity(&self) -> usize {
        self.dense_states.capacity()
    }

    fn insert_slot(&mut self, id: usize, generation: u32, state: S) -> Option<GenerationalSlot<S>> {
        let slot = GenerationalSlot { generation, state };
        if Self::uses_dense_storage(id) {
            self.dense_states.insert(id, slot)
        } else {
            self.sparse_states.insert(id, slot)
        }
    }

    fn remove_slot(&mut self, id: usize) -> Option<GenerationalSlot<S>> {
        if Self::uses_dense_storage(id) {
            self.dense_states.remove(id)
        } else {
            self.sparse_states.remove(&id)
        }
    }

    fn uses_dense_storage(id: usize) -> bool {
        id <= Self::DENSE_STORAGE_ID_LIMIT
    }
}

impl<S: RegistryState> GenerationalRegistryStorage<S> {
    pub(crate) fn active_len(&self) -> usize {
        self.active_count
    }

    pub(crate) fn detached_len(&self) -> usize {
        self.detached_count
    }

    pub(crate) fn invalidated_len(&self) -> usize {
        self.slot_len()
            .saturating_sub(self.active_count + self.detached_count)
    }

    pub(crate) fn set_state(&mut self, id: usize, generation: u32, state: S) -> Option<S> {
        if let Some(slot) = self.slot(id) {
            if slot.generation != generation {
                return None;
            }
        }
        let previous = self.insert_slot(id, generation, state);
        self.adjust_state_counts(previous.map(|slot| slot.state), Some(state));
        previous.map(|slot| slot.state)
    }

    pub(crate) fn remove_state(&mut self, id: usize) -> Option<GenerationalSlot<S>> {
        let removed = self.remove_slot(id);
        if let Some(slot) = removed {
            self.adjust_state_counts(Some(slot.state), None);
        }
        removed
    }

    fn adjust_state_counts(&mut self, previous: Option<S>, next: Option<S>) {
        if previous.is_some_and(|state| state.is_active()) {
            self.active_count -= 1;
        }
        if previous.is_some_and(|state| state.is_detached()) {
            self.detached_count -= 1;
        }
        if next.is_some_and(|state| state.is_active()) {
            self.active_count += 1;
        }
        if next.is_some_and(|state| state.is_detached()) {
            self.detached_count += 1;
        }
    }
}