use std::hash::{Hash, Hasher};
use crate::engine::error::{RegistryError, RegistryResult};
use crate::engine::types::{ComponentID, COMPONENT_CAP, SIGNATURE_SIZE};
#[derive(Clone, Copy, Debug)]
#[must_use]
pub struct Signature {
pub(crate) components: [u64; SIGNATURE_SIZE],
}
impl Default for Signature {
fn default() -> Self {
Self {
components: [0u64; SIGNATURE_SIZE],
}
}
}
impl PartialEq for Signature {
fn eq(&self, other: &Self) -> bool {
self.components == other.components
}
}
impl Eq for Signature {}
impl Hash for Signature {
fn hash<H: Hasher>(&self, state: &mut H) {
self.components.hash(state);
}
}
impl Signature {
#[inline]
fn checked_index(component_id: ComponentID) -> RegistryResult<(usize, usize)> {
let component_index = component_id as usize;
if component_index >= COMPONENT_CAP {
return Err(RegistryError::InvalidComponentId {
component_id,
cap: COMPONENT_CAP,
});
}
Ok((component_index / 64, component_index % 64))
}
#[inline]
pub fn set(&mut self, component_id: ComponentID) {
let index = (component_id as usize) / 64;
let bits = (component_id as usize) % 64;
assert!(
index < SIGNATURE_SIZE,
"component_id {} exceeds COMPONENT_CAP",
component_id
);
self.components[index] |= 1u64 << bits;
}
#[inline]
pub fn try_set(&mut self, component_id: ComponentID) -> RegistryResult<()> {
let (index, bits) = Self::checked_index(component_id)?;
self.components[index] |= 1u64 << bits;
Ok(())
}
#[inline]
pub fn clear(&mut self, component_id: ComponentID) {
let index = (component_id as usize) / 64;
let bits = (component_id as usize) % 64;
assert!(
index < SIGNATURE_SIZE,
"component_id {} exceeds COMPONENT_CAP",
component_id
);
self.components[index] &= !(1u64 << bits);
}
#[inline]
pub fn try_clear(&mut self, component_id: ComponentID) -> RegistryResult<()> {
let (index, bits) = Self::checked_index(component_id)?;
self.components[index] &= !(1u64 << bits);
Ok(())
}
#[inline]
pub fn has(&self, component_id: ComponentID) -> bool {
let index = (component_id as usize) / 64;
let bits = (component_id as usize) % 64;
assert!(
index < SIGNATURE_SIZE,
"component_id {} exceeds COMPONENT_CAP",
component_id
);
(self.components[index] >> bits) & 1 == 1
}
#[inline]
pub fn try_has(&self, component_id: ComponentID) -> RegistryResult<bool> {
let (index, bits) = Self::checked_index(component_id)?;
Ok((self.components[index] >> bits) & 1 == 1)
}
#[inline]
pub fn contains_all(&self, signature: &Signature) -> bool {
for (component_a, component_b) in self.components.iter().zip(signature.components.iter()) {
if (component_a & component_b) != *component_b {
return false;
}
}
true
}
pub fn iterate_over_components(&self) -> impl Iterator<Item = ComponentID> + '_ {
iter_bits_from_words(&self.components)
}
#[inline]
pub fn as_words(&self) -> &[u64; SIGNATURE_SIZE] {
&self.components
}
}
#[inline]
pub fn iter_bits_from_words<'a>(
words: &'a [u64; SIGNATURE_SIZE],
) -> impl Iterator<Item = ComponentID> + 'a {
words.iter().enumerate().flat_map(|(word_index, &word)| {
let base = word_index * 64;
let mut bits = word;
std::iter::from_fn(move || {
if bits == 0 {
return None;
}
let tz = bits.trailing_zeros() as usize;
bits &= bits - 1;
Some((base + tz) as ComponentID)
})
})
}
#[inline]
pub(crate) fn or_signature_in_place(dst: &mut Signature, src: &Signature) {
for (d, s) in dst.components.iter_mut().zip(src.components.iter()) {
*d |= *s;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::error::RegistryError;
#[test]
fn fallible_helpers_reject_out_of_range_component_id() {
let invalid = COMPONENT_CAP as ComponentID;
let mut signature = Signature::default();
assert!(matches!(
signature.try_set(invalid),
Err(RegistryError::InvalidComponentId { .. })
));
assert!(matches!(
signature.try_clear(invalid),
Err(RegistryError::InvalidComponentId { .. })
));
assert!(matches!(
signature.try_has(invalid),
Err(RegistryError::InvalidComponentId { .. })
));
}
}