diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 7cfaff421103a..365f7ecc49438 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -7,24 +7,224 @@ use derive_more::From; use fixedbitset::{Difference, FixedBitSet, Intersection, IntoOnes, Ones, Union}; use thiserror::Error; +/// A set of bits that is either a finite set, or the set complement of a finite set. +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum InvertibleComponentIdSet { + /// A finite [`InvertibleComponentIdSet`] that includes all components in the inner set. + Included(ComponentIdSet), + /// An unbounded [`InvertibleComponentIdSet`] that includes all components *not* in the inner set. + Excluded(ComponentIdSet), +} + +impl Default for InvertibleComponentIdSet { + fn default() -> Self { + Self::new() + } +} + +impl InvertibleComponentIdSet { + /// Creates a new empty `InvertibleSet`. + #[inline] + pub const fn new() -> Self { + Self::Included(ComponentIdSet::new()) + } + + /// Creates a new `InvertibleSet` that includes all components. + #[inline] + pub const fn new_all() -> Self { + Self::Excluded(ComponentIdSet::new()) + } + + /// Adds a [`ComponentId`] to the set. + #[inline] + pub fn insert(&mut self, index: ComponentId) { + match self { + Self::Included(included) => included.insert(index), + Self::Excluded(excluded) => excluded.remove(index), + } + } + + /// Removes a [`ComponentId`] from the set. + #[inline] + pub fn remove(&mut self, index: ComponentId) { + match self { + Self::Included(included) => included.remove(index), + Self::Excluded(excluded) => excluded.insert(index), + } + } + + /// Removes all [`ComponentId`]s from the set. + #[inline] + pub fn clear(&mut self) { + *self = Self::new(); + } + + /// Adds all [`ComponentId`]s to the set. + #[inline] + pub fn all(&mut self) { + *self = Self::new_all(); + } + + /// Returns `true` if the [`ComponentId`] is in the set. + #[inline] + pub fn contains(&self, index: ComponentId) -> bool { + match self { + Self::Included(included) => included.contains(index), + Self::Excluded(excluded) => !excluded.contains(index), + } + } + + /// Returns `true` if the set is empty. + /// Note that an unbounded set is never clear. + #[inline] + pub fn is_clear(&self) -> bool { + match self { + Self::Included(included) => included.is_clear(), + Self::Excluded(_) => false, + } + } + + /// Returns `true` if the set contains all components. + /// Note that a finite set is never fully set. + #[inline] + pub fn is_all(&self) -> bool { + match self { + Self::Included(_) => false, + Self::Excluded(excluded) => excluded.is_clear(), + } + } + + /// Returns `true` if this is an unbounded set, or `false` if it is a finite set. + #[inline] + pub fn is_unbounded(&self) -> bool { + match self { + Self::Included(_) => false, + Self::Excluded(_) => true, + } + } + + /// If this is a finite set, returns the set. + /// If this is an unbounded set, returns `None`. + #[inline] + pub fn into_finite_set(self) -> Option { + match self { + Self::Included(included) => Some(included), + Self::Excluded(_) => None, + } + } + + /// If this is a finite set, returns the set. + /// If this is an unbounded set, returns `None`. + #[inline] + pub fn as_finite_set(&self) -> Option<&ComponentIdSet> { + match self { + Self::Included(included) => Some(included), + Self::Excluded(_) => None, + } + } + + /// If this is an unbounded set, returns the set of components + /// that are excluded from the set. + /// If this is a finite set, returns `None`. + #[inline] + pub fn as_exclusion_set(&self) -> Option<&ComponentIdSet> { + match self { + Self::Included(_) => None, + Self::Excluded(excluded) => Some(excluded), + } + } + + /// In-place union of two sets. + pub fn union_with(&mut self, other: &Self) { + match (&mut *self, other) { + (Self::Included(this), Self::Included(other)) => this.union_with(other), + (Self::Included(this), Self::Excluded(other)) => { + *self = Self::Excluded(other.difference(this).collect()); + } + (Self::Excluded(this), Self::Included(other)) => this.difference_with(other), + (Self::Excluded(this), Self::Excluded(other)) => this.intersect_with(other), + } + } + + /// Returns the union of two sets. + pub fn union(&self, other: &Self) -> Self { + let mut result = self.clone(); + result.union_with(other); + result + } + + /// In-place difference of two sets. + pub fn difference_with(&mut self, other: &Self) { + match (&mut *self, other) { + (Self::Included(this), Self::Included(other)) => this.difference_with(other), + (Self::Included(this), Self::Excluded(other)) => this.intersect_with(other), + (Self::Excluded(this), Self::Included(other)) => this.union_with(other), + (Self::Excluded(this), Self::Excluded(other)) => { + *self = Self::Included(other.difference(this).collect()); + } + } + } + + /// Returns the set difference of two sets. + pub fn difference(&self, other: &Self) -> Self { + let mut result = self.clone(); + result.difference_with(other); + result + } + + /// In-place intersection of two sets. + pub fn intersect_with(&mut self, other: &Self) { + match (&mut *self, other) { + (Self::Included(this), Self::Included(other)) => this.intersect_with(other), + (Self::Included(this), Self::Excluded(other)) => this.difference_with(other), + (Self::Excluded(this), Self::Included(other)) => { + *self = Self::Included(other.difference(this).collect()); + } + (Self::Excluded(this), Self::Excluded(other)) => this.union_with(other), + } + } + + /// Returns the intersection of two sets. + pub fn intersection(&self, other: &Self) -> Self { + let mut result = self.clone(); + result.intersect_with(other); + result + } + + /// Returns `true` if `self` has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + pub fn is_disjoint(&self, other: &Self) -> bool { + match (self, other) { + (Self::Included(this), Self::Included(other)) => this.is_disjoint(other), + // Two sets are disjoint if one is a subset of the other's complement. + (Self::Included(this), Self::Excluded(other)) => this.is_subset(other), + (Self::Excluded(this), Self::Included(other)) => other.is_subset(this), + (Self::Excluded(_), Self::Excluded(_)) => false, + } + } + + /// Returns `true` if the set is a subset of another, i.e. `other` contains at least all the values in `self`. + pub fn is_subset(&self, other: &Self) -> bool { + match (self, other) { + (Self::Included(this), Self::Included(other)) => this.is_subset(other), + // Two sets are disjoint if one is a subset of the other's complement. + (Self::Included(this), Self::Excluded(other)) => this.is_disjoint(other), + (Self::Excluded(_), Self::Included(_)) => false, + (Self::Excluded(this), Self::Excluded(other)) => other.is_subset(this), + } + } +} + /// Tracks read and write access to specific elements in a collection. /// /// Used internally to ensure soundness during system initialization and execution. /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions. -#[derive(Eq, PartialEq, Default, Hash, Debug)] +#[derive(Eq, PartialEq, Default, Debug)] pub struct Access { - /// All accessed components, or forbidden components if - /// `Self::component_read_and_writes_inverted` is set. - read_and_writes: ComponentIdSet, - /// All exclusively-accessed components, or components that may not be - /// exclusively accessed if `Self::component_writes_inverted` is set. - writes: ComponentIdSet, - /// Is `true` if this component can read all components *except* those - /// present in `Self::read_and_writes`. - read_and_writes_inverted: bool, - /// Is `true` if this component can write to all components *except* those - /// present in `Self::writes`. - writes_inverted: bool, + /// All accessed components. + read_and_writes: InvertibleComponentIdSet, + /// All exclusively-accessed components. + writes: InvertibleComponentIdSet, // Components that are not accessed, but whose presence in an archetype affect query results. archetypal: ComponentIdSet, } @@ -35,8 +235,6 @@ impl Clone for Access { Self { read_and_writes: self.read_and_writes.clone(), writes: self.writes.clone(), - read_and_writes_inverted: self.read_and_writes_inverted, - writes_inverted: self.writes_inverted, archetypal: self.archetypal.clone(), } } @@ -44,8 +242,6 @@ impl Clone for Access { fn clone_from(&mut self, source: &Self) { self.read_and_writes.clone_from(&source.read_and_writes); self.writes.clone_from(&source.writes); - self.read_and_writes_inverted = source.read_and_writes_inverted; - self.writes_inverted = source.writes_inverted; self.archetypal.clone_from(&source.archetypal); } } @@ -54,10 +250,8 @@ impl Access { /// Creates an empty [`Access`] collection. pub const fn new() -> Self { Self { - read_and_writes_inverted: false, - writes_inverted: false, - read_and_writes: ComponentIdSet::new(), - writes: ComponentIdSet::new(), + read_and_writes: InvertibleComponentIdSet::new(), + writes: InvertibleComponentIdSet::new(), archetypal: ComponentIdSet::new(), } } @@ -66,23 +260,22 @@ impl Access { /// This is equivalent to calling `read_all()` on `Access::new()`, /// but is available in a `const` context. pub(crate) const fn new_read_all() -> Self { - let mut access = Self::new(); - // Note that we cannot use `read_all()` - // because `FixedBitSet::clear()` is not `const`. - access.read_and_writes_inverted = true; - access + Self { + read_and_writes: InvertibleComponentIdSet::new_all(), + writes: InvertibleComponentIdSet::new(), + archetypal: ComponentIdSet::new(), + } } /// Creates an [`Access`] with read and write access to all components. /// This is equivalent to calling `write_all()` on `Access::new()`, /// but is available in a `const` context. pub(crate) const fn new_write_all() -> Self { - let mut access = Self::new(); - // Note that we cannot use `write_all()` - // because `FixedBitSet::clear()` is not `const`. - access.read_and_writes_inverted = true; - access.writes_inverted = true; - access + Self { + read_and_writes: InvertibleComponentIdSet::new_all(), + writes: InvertibleComponentIdSet::new_all(), + archetypal: ComponentIdSet::new(), + } } /// Adds access to the component given by `index`. @@ -93,11 +286,7 @@ impl Access { /// Adds access to the component given by `index`. pub fn add_read(&mut self, index: ComponentId) { - if !self.read_and_writes_inverted { - self.read_and_writes.insert(index); - } else { - self.read_and_writes.remove(index); - } + self.read_and_writes.insert(index); } /// Adds exclusive access to the component given by `index`. @@ -108,12 +297,8 @@ impl Access { /// Adds exclusive access to the component given by `index`. pub fn add_write(&mut self, index: ComponentId) { - self.add_read(index); - if !self.writes_inverted { - self.writes.insert(index); - } else { - self.writes.remove(index); - } + self.read_and_writes.insert(index); + self.writes.insert(index); } /// Adds access to the resource given by `index`. @@ -149,12 +334,8 @@ impl Access { /// `extend` with a call to `extend` followed by a call to /// `remove_read`. pub fn remove_read(&mut self, index: ComponentId) { - self.remove_write(index); - if self.read_and_writes_inverted { - self.read_and_writes.insert(index); - } else { - self.read_and_writes.remove(index); - } + self.writes.remove(index); + self.read_and_writes.remove(index); } /// Removes write access to the component given by `index`. @@ -172,11 +353,7 @@ impl Access { /// `extend` with a call to `extend` followed by a call to /// `remove_write`. pub fn remove_write(&mut self, index: ComponentId) { - if self.writes_inverted { - self.writes.insert(index); - } else { - self.writes.remove(index); - } + self.read_and_writes.remove(index); } /// Adds an archetypal (indirect) access to the component given by `index`. @@ -200,7 +377,7 @@ impl Access { /// Returns `true` if this can access the component given by `index`. pub fn has_read(&self, index: ComponentId) -> bool { - self.read_and_writes_inverted ^ self.read_and_writes.contains(index) + self.read_and_writes.contains(index) } /// Returns `true` if this can access any component. @@ -211,7 +388,7 @@ impl Access { /// Returns `true` if this can access any component. pub fn has_any_read(&self) -> bool { - self.read_and_writes_inverted || !self.read_and_writes.is_clear() + !self.read_and_writes.is_clear() } /// Returns `true` if this can exclusively access the component given by `index`. @@ -222,7 +399,7 @@ impl Access { /// Returns `true` if this can exclusively access the component given by `index`. pub fn has_write(&self, index: ComponentId) -> bool { - self.writes_inverted ^ self.writes.contains(index) + self.writes.contains(index) } /// Returns `true` if this accesses any component mutably. @@ -233,7 +410,7 @@ impl Access { /// Returns `true` if this accesses any component mutably. pub fn has_any_write(&self) -> bool { - self.writes_inverted || !self.writes.is_clear() + !self.writes.is_clear() } /// Returns `true` if this can access the resource given by `index`. @@ -281,8 +458,7 @@ impl Access { /// Sets this as having access to all components (i.e. `EntityRef` and `&World`). #[inline] pub fn read_all(&mut self) { - self.read_and_writes_inverted = true; - self.read_and_writes.clear(); + self.read_and_writes.all(); } /// Sets this as having mutable access to all components (i.e. `EntityMut` and `&mut World`). @@ -294,9 +470,8 @@ impl Access { /// Sets this as having mutable access to all components (i.e. `EntityMut` and `&mut World`). #[inline] pub fn write_all(&mut self) { - self.read_all(); - self.writes_inverted = true; - self.writes.clear(); + self.read_and_writes.all(); + self.writes.all(); } /// Returns `true` if this has access to all components (i.e. `EntityRef` and `&World`). @@ -308,7 +483,7 @@ impl Access { /// Returns `true` if this has access to all components (i.e. `EntityRef` and `&World`). #[inline] pub fn has_read_all(&self) -> bool { - self.read_and_writes_inverted && self.read_and_writes.is_clear() + self.read_and_writes.is_all() } /// Returns `true` if this has write access to all components (i.e. `EntityMut` and `&mut World`). @@ -320,37 +495,24 @@ impl Access { /// Returns `true` if this has write access to all components (i.e. `EntityMut` and `&mut World`). #[inline] pub fn has_write_all(&self) -> bool { - self.writes_inverted && self.writes.is_clear() + self.writes.is_all() } /// Removes all writes. pub fn clear_writes(&mut self) { - self.writes_inverted = false; self.writes.clear(); } /// Removes all accesses. pub fn clear(&mut self) { - self.read_and_writes_inverted = false; - self.writes_inverted = false; self.read_and_writes.clear(); self.writes.clear(); } /// Adds all access from `other`. pub fn extend(&mut self, other: &Access) { - invertible_union_with( - &mut self.read_and_writes, - &mut self.read_and_writes_inverted, - &other.read_and_writes, - other.read_and_writes_inverted, - ); - invertible_union_with( - &mut self.writes, - &mut self.writes_inverted, - &other.writes, - other.writes_inverted, - ); + self.read_and_writes.union_with(&other.read_and_writes); + self.writes.union_with(&other.writes); self.archetypal.union_with(&other.archetypal); } @@ -358,18 +520,8 @@ impl Access { /// This removes any reads and writes for any component written by `other`, /// and removes any writes for any component read by `other`. pub fn remove_conflicting_access(&mut self, other: &Access) { - invertible_difference_with( - &mut self.read_and_writes, - &mut self.read_and_writes_inverted, - &other.writes, - other.writes_inverted, - ); - invertible_difference_with( - &mut self.writes, - &mut self.writes_inverted, - &other.read_and_writes, - other.read_and_writes_inverted, - ); + self.read_and_writes.difference_with(&other.writes); + self.writes.difference_with(&other.read_and_writes); } /// Returns `true` if the access and `other` can be active at the same time. @@ -385,46 +537,8 @@ impl Access { pub fn is_compatible(&self, other: &Access) -> bool { // We have a conflict if we write and they read or write, or if they // write and we read or write. - for ( - lhs_writes, - rhs_reads_and_writes, - lhs_writes_inverted, - rhs_reads_and_writes_inverted, - ) in [ - ( - &self.writes, - &other.read_and_writes, - self.writes_inverted, - other.read_and_writes_inverted, - ), - ( - &other.writes, - &self.read_and_writes, - other.writes_inverted, - self.read_and_writes_inverted, - ), - ] { - match (lhs_writes_inverted, rhs_reads_and_writes_inverted) { - (true, true) => return false, - (false, true) => { - if !lhs_writes.is_subset(rhs_reads_and_writes) { - return false; - } - } - (true, false) => { - if !rhs_reads_and_writes.is_subset(lhs_writes) { - return false; - } - } - (false, false) => { - if !lhs_writes.is_disjoint(rhs_reads_and_writes) { - return false; - } - } - } - } - - true + self.writes.is_disjoint(&other.read_and_writes) + && other.writes.is_disjoint(&self.read_and_writes) } /// Returns `true` if the set is a subset of another, i.e. `other` contains @@ -437,89 +551,20 @@ impl Access { /// Returns `true` if the set is a subset of another, i.e. `other` contains /// at least all the values in `self`. pub fn is_subset(&self, other: &Access) -> bool { - for ( - our_components, - their_components, - our_components_inverted, - their_components_inverted, - ) in [ - ( - &self.read_and_writes, - &other.read_and_writes, - self.read_and_writes_inverted, - other.read_and_writes_inverted, - ), - ( - &self.writes, - &other.writes, - self.writes_inverted, - other.writes_inverted, - ), - ] { - match (our_components_inverted, their_components_inverted) { - (true, true) => { - if !their_components.is_subset(our_components) { - return false; - } - } - (true, false) => { - return false; - } - (false, true) => { - if !our_components.is_disjoint(their_components) { - return false; - } - } - (false, false) => { - if !our_components.is_subset(their_components) { - return false; - } - } - } - } - - true + self.read_and_writes.is_subset(&other.read_and_writes) + && self.writes.is_subset(&other.writes) } /// Returns a vector of elements that the access and `other` cannot access at the same time. #[inline] pub fn get_conflicts(&self, other: &Access) -> AccessConflicts { - let mut conflicts = ComponentIdSet::new(); - // We have a conflict if we write and they read or write, or if they // write and we read or write. - for ( - lhs_writes, - rhs_reads_and_writes, - lhs_writes_inverted, - rhs_reads_and_writes_inverted, - ) in [ - ( - &self.writes, - &other.read_and_writes, - self.writes_inverted, - other.read_and_writes_inverted, - ), - ( - &other.writes, - &self.read_and_writes, - other.writes_inverted, - self.read_and_writes_inverted, - ), - ] { - // There's no way that I can see to do this without a temporary. - // Neither CNF nor DNF allows us to avoid one. - let temp_conflicts: ComponentIdSet = - match (lhs_writes_inverted, rhs_reads_and_writes_inverted) { - (true, true) => return AccessConflicts::All, - (false, true) => lhs_writes.difference(rhs_reads_and_writes).collect(), - (true, false) => rhs_reads_and_writes.difference(lhs_writes).collect(), - (false, false) => lhs_writes.intersection(rhs_reads_and_writes).collect(), - }; - conflicts.union_with(&temp_conflicts); - } - - AccessConflicts::Individual(conflicts) + let mut conflicts = self.writes.intersection(&other.read_and_writes); + conflicts.union_with(&other.writes.intersection(&self.read_and_writes)); + conflicts + .into_finite_set() + .map_or(AccessConflicts::All, AccessConflicts::Individual) } /// Returns the indices of the components that this has an archetypal access to. @@ -534,30 +579,36 @@ impl Access { &self.archetypal } + /// Returns the set of components with read or write access. + pub fn reads_and_writes(&self) -> &InvertibleComponentIdSet { + &self.read_and_writes + } + /// Returns the set of components with read or write access, /// or an error if the access is unbounded. + #[deprecated(since = "0.20.0", note = "use `reads_and_writes().as_finite_set()")] pub fn try_reads_and_writes(&self) -> Result<&ComponentIdSet, UnboundedAccessError> { - // writes_inverted is only ever true when read_and_writes_inverted is - // also true. Therefore it is sufficient to check just read_and_writes_inverted. - if self.read_and_writes_inverted { - return Err(UnboundedAccessError { - writes_inverted: self.writes_inverted, - read_and_writes_inverted: self.read_and_writes_inverted, - }); - } - Ok(&self.read_and_writes) + self.read_and_writes + .as_finite_set() + .ok_or(UnboundedAccessError { + writes_inverted: self.writes.is_unbounded(), + read_and_writes_inverted: self.read_and_writes.is_unbounded(), + }) + } + + /// Returns the set of components with write access. + pub fn writes(&self) -> &InvertibleComponentIdSet { + &self.writes } /// Returns the set of components with write access, /// or an error if the access is unbounded. + #[deprecated(since = "0.20.0", note = "use `writes().as_finite_set()")] pub fn try_writes(&self) -> Result<&ComponentIdSet, UnboundedAccessError> { - if self.writes_inverted { - return Err(UnboundedAccessError { - writes_inverted: self.writes_inverted, - read_and_writes_inverted: self.read_and_writes_inverted, - }); - } - Ok(&self.writes) + self.writes.as_finite_set().ok_or(UnboundedAccessError { + writes_inverted: self.writes.is_unbounded(), + read_and_writes_inverted: self.read_and_writes.is_unbounded(), + }) } /// Returns an iterator over the component IDs and their [`ComponentAccessKind`]. @@ -601,7 +652,14 @@ impl Access { pub fn try_iter_access( &self, ) -> Result + '_, UnboundedAccessError> { - let reads_and_writes = self.try_reads_and_writes()?.iter().map(|index| { + let reads_and_writes_set = + self.read_and_writes + .as_finite_set() + .ok_or(UnboundedAccessError { + writes_inverted: self.writes.is_unbounded(), + read_and_writes_inverted: self.read_and_writes.is_unbounded(), + })?; + let reads_and_writes = reads_and_writes_set.iter().map(|index| { if self.writes.contains(index) { ComponentAccessKind::Exclusive(index) } else { @@ -611,59 +669,13 @@ impl Access { let archetypal = self .archetypal - .difference(&self.read_and_writes) + .difference(reads_and_writes_set) .map(ComponentAccessKind::Archetypal); Ok(reads_and_writes.chain(archetypal)) } } -/// Performs an in-place union of `other` into `self`, where either set may be inverted. -/// -/// Each set corresponds to a `FixedBitSet` if `inverted` is `false`, -/// or to the infinite (co-finite) complement of the `FixedBitSet` if `inverted` is `true`. -/// -/// This updates the `self` set to include any elements in the `other` set. -/// Note that this may change `self_inverted` to `true` if we add an infinite -/// set to a finite one, resulting in a new infinite set. -fn invertible_union_with( - self_set: &mut ComponentIdSet, - self_inverted: &mut bool, - other_set: &ComponentIdSet, - other_inverted: bool, -) { - match (*self_inverted, other_inverted) { - (true, true) => self_set.intersect_with(other_set), - (true, false) => self_set.difference_with(other_set), - (false, true) => { - *self_inverted = true; - self_set.difference_from(other_set); - } - (false, false) => self_set.union_with(other_set), - } -} - -/// Performs an in-place set difference of `other` from `self`, where either set may be inverted. -/// -/// Each set corresponds to a `FixedBitSet` if `inverted` is `false`, -/// or to the infinite (co-finite) complement of the `FixedBitSet` if `inverted` is `true`. -/// -/// This updates the `self` set to remove any elements in the `other` set. -/// Note that this may change `self_inverted` to `false` if we remove an -/// infinite set from another infinite one, resulting in a finite difference. -fn invertible_difference_with( - self_set: &mut ComponentIdSet, - self_inverted: &mut bool, - other_set: &ComponentIdSet, - other_inverted: bool, -) { - // We can share the implementation of `invertible_union_with` with some algebra: - // A - B = A & !B = !(!A | B) - *self_inverted = !*self_inverted; - invertible_union_with(self_set, self_inverted, other_set, other_inverted); - *self_inverted = !*self_inverted; -} - /// Error returned when attempting to iterate over items included in an [`Access`] /// if the access excludes items rather than including them. #[derive(Clone, Copy, PartialEq, Eq, Debug, Error)] @@ -757,7 +769,7 @@ impl From for FilteredAccessSet { } /// Records how two accesses conflict with each other -#[derive(Debug, PartialEq, From)] +#[derive(Debug, PartialEq, Eq, From)] pub enum AccessConflicts { /// Conflict is for all indices All, @@ -1282,10 +1294,18 @@ impl FilteredAccessSet { } /// A set of [`ComponentId`]s. -#[derive(Default, Eq, PartialEq, Hash)] +#[derive(Default, Eq)] #[repr(transparent)] pub struct ComponentIdSet(FixedBitSet); +impl PartialEq for ComponentIdSet { + fn eq(&self, other: &Self) -> bool { + // `FixedBitSet` requires equal lengths for equality, + // but we consider two sets equal if they have the same bits set + self.0.symmetric_difference(&other.0).next().is_none() + } +} + impl ComponentIdSet { /// Create a new empty `ComponentIdSet`. #[inline] @@ -1495,12 +1515,12 @@ impl> FusedIterator for ComponentIdIter {} #[cfg(test)] mod tests { - use super::{invertible_difference_with, invertible_union_with}; use crate::{ component::ComponentId, query::{ - access::AccessFilters, Access, AccessConflicts, ComponentAccessKind, ComponentIdSet, - FilteredAccess, FilteredAccessSet, UnboundedAccessError, + access::{AccessFilters, InvertibleComponentIdSet}, + Access, AccessConflicts, ComponentAccessKind, ComponentIdSet, FilteredAccess, + FilteredAccessSet, UnboundedAccessError, }, }; use alloc::{vec, vec::Vec}; @@ -1851,36 +1871,45 @@ mod tests { } #[test] - fn invertible_union_with_tests() { - let invertible_union = |mut self_inverted: bool, other_inverted: bool| { - // Check all four possible bit states: In both sets, the first, the second, or neither - let mut self_set = bit_set(4, [0, 1]); - let other_set = bit_set(4, [0, 2]); - invertible_union_with( - &mut self_set, - &mut self_inverted, - &other_set, - other_inverted, - ); - (self_set, self_inverted) - }; - - // Check each combination of `inverted` flags - let (s, i) = invertible_union(false, false); + fn invertible_union_tests() { + let set0 = ComponentIdSet::from_iter([ComponentId::new(0)]); + let set1 = ComponentIdSet::from_iter([ComponentId::new(1)]); + let set2 = ComponentIdSet::from_iter([ComponentId::new(2)]); + let set01 = ComponentIdSet::from_iter([ComponentId::new(0), ComponentId::new(1)]); + let set02 = ComponentIdSet::from_iter([ComponentId::new(0), ComponentId::new(2)]); + let set012 = ComponentIdSet::from_iter([ + ComponentId::new(0), + ComponentId::new(1), + ComponentId::new(2), + ]); + + // Check each combination of `Included` and `Excluded` // [0, 1] | [0, 2] = [0, 1, 2] - assert_eq!((s, i), (bit_set(4, [0, 1, 2]), false)); - - let (s, i) = invertible_union(false, true); + assert_eq!( + InvertibleComponentIdSet::Included(set01.clone()) + .union(&InvertibleComponentIdSet::Included(set02.clone())), + InvertibleComponentIdSet::Included(set012.clone()) + ); // [0, 1] | [1, 3, ...] = [0, 1, 3, ...] - assert_eq!((s, i), (bit_set(4, [2]), true)); + assert_eq!( + InvertibleComponentIdSet::Included(set01.clone()) + .union(&InvertibleComponentIdSet::Excluded(set02.clone())), + InvertibleComponentIdSet::Excluded(set2.clone()) + ); - let (s, i) = invertible_union(true, false); // [2, 3, ...] | [0, 2] = [0, 2, 3, ...] - assert_eq!((s, i), (bit_set(4, [1]), true)); + assert_eq!( + InvertibleComponentIdSet::Excluded(set01.clone()) + .union(&InvertibleComponentIdSet::Included(set02.clone())), + InvertibleComponentIdSet::Excluded(set1.clone()) + ); - let (s, i) = invertible_union(true, true); // [2, 3, ...] | [1, 3, ...] = [1, 2, 3, ...] - assert_eq!((s, i), (bit_set(4, [0]), true)); + assert_eq!( + InvertibleComponentIdSet::Excluded(set01.clone()) + .union(&InvertibleComponentIdSet::Excluded(set02.clone())), + InvertibleComponentIdSet::Excluded(set0.clone()) + ); } #[test] @@ -1889,52 +1918,99 @@ mod tests { // make sure we invert the bits beyond the original length. // Failing to call `grow` before `toggle_range` would cause bit 1 to be zero, // which would incorrectly treat it as included in the output set. - let mut self_set = bit_set(1, [0]); - let mut self_inverted = false; - let other_set = bit_set(3, [0, 1]); - let other_inverted = true; - invertible_union_with( - &mut self_set, - &mut self_inverted, - &other_set, - other_inverted, - ); + let mut self_set = InvertibleComponentIdSet::Included(bit_set(1, [0])); + let other_set = InvertibleComponentIdSet::Excluded(bit_set(3, [0, 1])); + self_set.union_with(&other_set); // [0] | [2, ...] = [0, 2, ...] - assert_eq!((self_set, self_inverted), (bit_set(3, [1]), true)); + assert_eq!( + self_set, + InvertibleComponentIdSet::Excluded(bit_set(3, [1])) + ); } #[test] - fn invertible_difference_with_tests() { - let invertible_difference = |mut self_inverted: bool, other_inverted: bool| { - // Check all four possible bit states: In both sets, the first, the second, or neither - let mut self_set = bit_set(4, [0, 1]); - let other_set = bit_set(4, [0, 2]); - invertible_difference_with( - &mut self_set, - &mut self_inverted, - &other_set, - other_inverted, - ); - (self_set, self_inverted) - }; - - // Check each combination of `inverted` flags - let (s, i) = invertible_difference(false, false); + fn invertible_difference_tests() { + let set0 = ComponentIdSet::from_iter([ComponentId::new(0)]); + let set1 = ComponentIdSet::from_iter([ComponentId::new(1)]); + let set2 = ComponentIdSet::from_iter([ComponentId::new(2)]); + let set01 = ComponentIdSet::from_iter([ComponentId::new(0), ComponentId::new(1)]); + let set02 = ComponentIdSet::from_iter([ComponentId::new(0), ComponentId::new(2)]); + let set012 = ComponentIdSet::from_iter([ + ComponentId::new(0), + ComponentId::new(1), + ComponentId::new(2), + ]); + + // Check each combination of `Included` and `Excluded` // [0, 1] - [0, 2] = [1] - assert_eq!((s, i), (bit_set(4, [1]), false)); - - let (s, i) = invertible_difference(false, true); + assert_eq!( + InvertibleComponentIdSet::Included(set01.clone()) + .difference(&InvertibleComponentIdSet::Included(set02.clone())), + InvertibleComponentIdSet::Included(set1.clone()) + ); // [0, 1] - [1, 3, ...] = [0] - assert_eq!((s, i), (bit_set(4, [0]), false)); + assert_eq!( + InvertibleComponentIdSet::Included(set01.clone()) + .difference(&InvertibleComponentIdSet::Excluded(set02.clone())), + InvertibleComponentIdSet::Included(set0.clone()) + ); - let (s, i) = invertible_difference(true, false); // [2, 3, ...] - [0, 2] = [3, ...] - assert_eq!((s, i), (bit_set(4, [0, 1, 2]), true)); + assert_eq!( + InvertibleComponentIdSet::Excluded(set01.clone()) + .difference(&InvertibleComponentIdSet::Included(set02.clone())), + InvertibleComponentIdSet::Excluded(set012.clone()) + ); - let (s, i) = invertible_difference(true, true); // [2, 3, ...] - [1, 3, ...] = [2] - assert_eq!((s, i), (bit_set(4, [2]), false)); + assert_eq!( + InvertibleComponentIdSet::Excluded(set01.clone()) + .difference(&InvertibleComponentIdSet::Excluded(set02.clone())), + InvertibleComponentIdSet::Included(set2.clone()) + ); + } + + #[test] + fn invertible_intersection_tests() { + let set0 = ComponentIdSet::from_iter([ComponentId::new(0)]); + let set1 = ComponentIdSet::from_iter([ComponentId::new(1)]); + let set2 = ComponentIdSet::from_iter([ComponentId::new(2)]); + let set01 = ComponentIdSet::from_iter([ComponentId::new(0), ComponentId::new(1)]); + let set02 = ComponentIdSet::from_iter([ComponentId::new(0), ComponentId::new(2)]); + let set012 = ComponentIdSet::from_iter([ + ComponentId::new(0), + ComponentId::new(1), + ComponentId::new(2), + ]); + + // Check each combination of `Included` and `Excluded` + // [0, 1] & [0, 2] = [0] + assert_eq!( + InvertibleComponentIdSet::Included(set01.clone()) + .intersection(&InvertibleComponentIdSet::Included(set02.clone())), + InvertibleComponentIdSet::Included(set0.clone()) + ); + // [0, 1] & [1, 3, ...] = [1] + assert_eq!( + InvertibleComponentIdSet::Included(set01.clone()) + .intersection(&InvertibleComponentIdSet::Excluded(set02.clone())), + InvertibleComponentIdSet::Included(set1.clone()) + ); + + // [2, 3, ...] & [0, 2] = [2] + assert_eq!( + InvertibleComponentIdSet::Excluded(set01.clone()) + .intersection(&InvertibleComponentIdSet::Included(set02.clone())), + InvertibleComponentIdSet::Included(set2.clone()) + ); + + // [2, 3, ...] & [1, 3, ...] = [3, ...] + assert_eq!( + InvertibleComponentIdSet::Excluded(set01.clone()) + .intersection(&InvertibleComponentIdSet::Excluded(set02.clone())), + InvertibleComponentIdSet::Excluded(set012.clone()) + ); } #[test] diff --git a/crates/bevy_ecs/src/query/access_iter.rs b/crates/bevy_ecs/src/query/access_iter.rs index 0e8979e648eef..35e2b7019dc56 100644 --- a/crates/bevy_ecs/src/query/access_iter.rs +++ b/crates/bevy_ecs/src/query/access_iter.rs @@ -108,7 +108,7 @@ fn has_conflicts_large<'a, Q: QueryData>( } /// The data storage type that is being accessed. -#[derive(Copy, Clone, Debug, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum EcsAccessType<'a> { /// Accesses [`Component`](crate::prelude::Component) data Component(EcsAccessLevel),