dynamis-world 0.7.0

Host scene registry, step orchestration, and readback
Documentation
use dynamis_abi::RowMoveRecord;
use std::collections::HashMap;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum RowIdentity {
    Slot(u32),
    Fresh(u32),
    Empty,
}

impl RowIdentity {
    fn record(self, row: u32) -> RowMoveRecord {
        match self {
            Self::Slot(source) => RowMoveRecord::source(row, source),
            Self::Fresh(index) => RowMoveRecord::fresh(row, index),
            Self::Empty => RowMoveRecord::clear(row),
        }
    }

    fn is_self_at(self, row: u32) -> bool {
        self == Self::Slot(row)
    }
}

pub(crate) struct RowMap {
    identities: Vec<(u32, RowIdentity)>,
    index_of: HashMap<u32, usize>,
    row_of: HashMap<RowIdentity, u32>,
}

impl RowMap {
    pub(crate) fn new() -> Self {
        Self {
            identities: Vec::new(),
            index_of: HashMap::new(),
            row_of: HashMap::new(),
        }
    }

    pub(crate) fn identity_of(&self, row: u32) -> RowIdentity {
        match self.index_of.get(&row) {
            Some(index) => self.identities[*index].1,
            None => RowIdentity::Slot(row),
        }
    }

    fn store(&mut self, row: u32, identity: RowIdentity) {
        match self.index_of.get(&row) {
            Some(index) => self.identities[*index] = (row, identity),
            None => {
                self.index_of.insert(row, self.identities.len());
                self.identities.push((row, identity));
            }
        }
    }

    fn detach(&mut self, row: u32) -> RowIdentity {
        let identity = self.identity_of(row);
        if !identity.is_self_at(row) {
            self.row_of.remove(&identity);
        }
        identity
    }

    fn attach(&mut self, row: u32, identity: RowIdentity) {
        if !identity.is_self_at(row) {
            self.row_of.insert(identity, row);
        }
    }

    pub(crate) fn add(&mut self, row: u32, fresh: u32) {
        self.detach(row);
        let identity = RowIdentity::Fresh(fresh);
        self.store(row, identity);
        self.attach(row, identity);
    }

    pub(crate) fn remove(&mut self, hole: u32, tail: u32) {
        if hole == tail {
            self.detach(hole);
            self.store(hole, RowIdentity::Empty);
            return;
        }
        let inherited = self.detach(tail);
        self.detach(hole);
        self.store(hole, inherited);
        self.store(tail, RowIdentity::Empty);
        self.attach(hole, inherited);
    }

    pub(crate) fn swap(&mut self, first: u32, second: u32) {
        let first_identity = self.detach(first);
        let second_identity = self.detach(second);
        self.store(first, second_identity);
        self.store(second, first_identity);
        self.attach(first, second_identity);
        self.attach(second, first_identity);
    }

    pub(crate) fn row_of(&self, identity: RowIdentity) -> Option<u32> {
        match identity {
            RowIdentity::Empty => None,
            RowIdentity::Fresh(_) => self.row_of.get(&identity).copied(),
            RowIdentity::Slot(slot) => match self.row_of.get(&identity).copied() {
                Some(row) => Some(row),
                None if !self.index_of.contains_key(&slot) => Some(slot),
                None => None,
            },
        }
    }

    pub(crate) fn moves(&self, rows: u32) -> Vec<RowMoveRecord> {
        self.identities
            .iter()
            .filter_map(|(row, identity)| {
                if *row >= rows {
                    return None;
                }
                (!identity.is_self_at(*row)).then(|| identity.record(*row))
            })
            .collect()
    }
}

pub(crate) struct RowJournal<T> {
    buckets: Vec<(RowIdentity, Vec<T>)>,
    index_of: HashMap<RowIdentity, usize>,
}

impl<T> RowJournal<T> {
    pub(crate) fn new() -> Self {
        Self {
            buckets: Vec::new(),
            index_of: HashMap::new(),
        }
    }

    pub(crate) fn push(&mut self, identity: RowIdentity, entry: T) {
        match self.index_of.get(&identity) {
            Some(index) => self.buckets[*index].1.push(entry),
            None => {
                self.index_of.insert(identity, self.buckets.len());
                self.buckets.push((identity, vec![entry]));
            }
        }
    }

    pub(crate) fn iter(&self) -> impl Iterator<Item = (RowIdentity, &[T])> {
        self.buckets
            .iter()
            .map(|(identity, entries)| (*identity, entries.as_slice()))
    }
}