ecs-tiny 0.4.0

A minimal ECS supporting entity and component insertion/removal, association, and single-type iteration.
Documentation
const NULL: usize = usize::MAX;

#[derive(Debug, Default)]
pub struct SparseIndex<const PAGE: usize> {
    page_map: Vec<usize>,
    pages: Vec<[usize; PAGE]>,
}

impl<const PAGE: usize> SparseIndex<PAGE> {
    pub fn insert(&mut self, index: usize, value: usize) -> Option<usize> {
        let page_index = index / PAGE;
        for _ in self.page_map.len()..=page_index {
            self.page_map.push(NULL);
        }
        let indirect = self.page_map.get_mut(page_index).unwrap();
        if *indirect == NULL {
            *indirect = self.pages.len();
            self.pages.push([NULL; PAGE]);
        }
        let page = self.pages.get_mut(*indirect).unwrap();
        let target = page.get_mut(index - page_index * PAGE).unwrap();
        let old = std::mem::replace(target, value);
        if old == NULL {
            return None;
        }
        Some(old)
    }

    pub fn remove(&mut self, index: usize) -> Option<usize> {
        let page_index = index / PAGE;
        let indirect = *self.page_map.get(page_index)?;
        if indirect == NULL {
            return None;
        }
        let page = self.pages.get_mut(indirect).unwrap();
        let target = page.get_mut(index - page_index * PAGE).unwrap();
        let old = std::mem::replace(target, NULL);
        if old == NULL {
            return None;
        }
        Some(old)
    }

    pub fn get(&self, index: usize) -> Option<&usize> {
        let page_index = index / PAGE;
        let indirect = *self.page_map.get(page_index)?;
        if indirect == NULL {
            return None;
        }
        let page = self.pages.get(indirect).unwrap();
        let value = page.get(index - page_index * PAGE).unwrap();
        if *value == NULL {
            return None;
        }
        Some(value)
    }

    pub fn get_mut(&mut self, index: usize) -> Option<&mut usize> {
        let page_index = index / PAGE;
        let indirect = *self.page_map.get(page_index)?;
        if indirect == NULL {
            return None;
        }
        let page = self.pages.get_mut(indirect).unwrap();
        let value = page.get_mut(index - page_index * PAGE).unwrap();
        if *value == NULL {
            return None;
        }
        Some(value)
    }
}

#[derive(Debug)]
pub struct BlobStorage {
    data: Vec<u8>,
    t_size: usize,
    t_len: usize,
}

impl BlobStorage {
    #[inline]
    pub fn new<T>() -> Self {
        Self {
            data: Default::default(),
            t_size: std::mem::size_of::<T>(),
            t_len: Default::default(),
        }
    }

    pub fn push<T>(&mut self, value: T) where T: bytemuck::Pod {
        let bytes = bytemuck::bytes_of(&value);
        self.data.extend_from_slice(bytes);
        self.t_len += 1;
    }

    pub fn swap_remove(&mut self, index: usize) -> Option<()> {
        if index >= self.t_len {
            return None;
        }
        let byte_index = index * self.t_size;
        let last_byte_index = (self.t_len - 1) * self.t_size;
        self.data.copy_within(last_byte_index..last_byte_index + self.t_size, byte_index);
        self.data.truncate(last_byte_index);
        self.t_len -= 1;
        Some(())
    }

    #[inline]
    pub fn get<T>(&self, index: usize) -> Option<&T> where T: bytemuck::Pod {
        let byte_index = index * self.t_size;
        let bytes = self.data.get(byte_index..byte_index + self.t_size)?;
        let value = bytemuck::from_bytes(bytes);
        Some(value)
    }

    #[inline]
    pub fn get_mut<T>(&mut self, index: usize) -> Option<&mut T> where T: bytemuck::Pod {
        let byte_index = index * self.t_size;
        let bytes = self.data.get_mut(byte_index..byte_index + self.t_size)?;
        let value = bytemuck::from_bytes_mut(bytes);
        Some(value)
    }

    #[inline]
    pub fn iter<T>(&self) -> impl Iterator<Item = &T> where T: bytemuck::Pod {
        let bytes = self.data.as_slice();
        bytemuck::cast_slice(bytes).iter()
    }

    #[inline]
    pub fn iter_mut<T>(&mut self) -> impl Iterator<Item = &mut T> where T: bytemuck::Pod {
        let bytes = self.data.as_mut_slice();
        bytemuck::cast_slice_mut(bytes).iter_mut()
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.t_len
    }
}

#[derive(Debug)]
pub struct IndexMap<K, V> {
    kv_map: ahash::AHashMap<K, usize>,
    dense: Vec<V>,
}

impl<K, V> Default for IndexMap<K, V> {
    #[inline]
    fn default() -> Self {
        Self {
            kv_map: Default::default(),
            dense: Default::default(),
        }
    }
}

impl<K, V> IndexMap<K, V> where K: std::hash::Hash + Eq {
    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        if let Some(index) = self.kv_map.get(&k) {
            let target = self.dense.get_mut(*index).unwrap();
            let old = std::mem::replace(target, v);
            return Some(old);
        }

        let index = self.dense.len();
        self.dense.push(v);
        self.kv_map.insert(k, index);
        None
    }

    #[inline]
    pub fn contains_key(&self, k: &K) -> bool {
        self.kv_map.contains_key(k)
    }

    #[inline]
    pub fn get(&self, k: &K) -> Option<&V> {
        let index = *self.kv_map.get(k)?;
        let v = self.dense.get(index).unwrap();
        Some(v)
    }

    #[inline]
    pub fn get_mut(&mut self, k: &K) -> Option<&mut V> {
        let index = *self.kv_map.get(k)?;
        let v = self.dense.get_mut(index).unwrap();
        Some(v)
    }

    #[inline]
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut V> {
        self.dense.iter_mut()
    }
}