use crate::component::ComponentId;
use crate::world::World;
use alloc::{format, string::String, vec, vec::Vec};
use core::{fmt, fmt::Debug};
use derive_more::From;
use fixedbitset::FixedBitSet;
use thiserror::Error;
struct FormattedBitSet<'a> {
bit_set: &'a FixedBitSet,
}
impl<'a> FormattedBitSet<'a> {
fn new(bit_set: &'a FixedBitSet) -> Self {
Self { bit_set }
}
}
impl<'a> Debug for FormattedBitSet<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.bit_set.ones()).finish()
}
}
#[derive(Eq, PartialEq, Default)]
pub struct Access {
component_read_and_writes: FixedBitSet,
component_writes: FixedBitSet,
resource_read_and_writes: FixedBitSet,
resource_writes: FixedBitSet,
component_read_and_writes_inverted: bool,
component_writes_inverted: bool,
reads_all_resources: bool,
writes_all_resources: bool,
archetypal: FixedBitSet,
}
impl Clone for Access {
fn clone(&self) -> Self {
Self {
component_read_and_writes: self.component_read_and_writes.clone(),
component_writes: self.component_writes.clone(),
resource_read_and_writes: self.resource_read_and_writes.clone(),
resource_writes: self.resource_writes.clone(),
component_read_and_writes_inverted: self.component_read_and_writes_inverted,
component_writes_inverted: self.component_writes_inverted,
reads_all_resources: self.reads_all_resources,
writes_all_resources: self.writes_all_resources,
archetypal: self.archetypal.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.component_read_and_writes
.clone_from(&source.component_read_and_writes);
self.component_writes.clone_from(&source.component_writes);
self.resource_read_and_writes
.clone_from(&source.resource_read_and_writes);
self.resource_writes.clone_from(&source.resource_writes);
self.component_read_and_writes_inverted = source.component_read_and_writes_inverted;
self.component_writes_inverted = source.component_writes_inverted;
self.reads_all_resources = source.reads_all_resources;
self.writes_all_resources = source.writes_all_resources;
self.archetypal.clone_from(&source.archetypal);
}
}
impl Debug for Access {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Access")
.field(
"component_read_and_writes",
&FormattedBitSet::new(&self.component_read_and_writes),
)
.field(
"component_writes",
&FormattedBitSet::new(&self.component_writes),
)
.field(
"resource_read_and_writes",
&FormattedBitSet::new(&self.resource_read_and_writes),
)
.field(
"resource_writes",
&FormattedBitSet::new(&self.resource_writes),
)
.field(
"component_read_and_writes_inverted",
&self.component_read_and_writes_inverted,
)
.field("component_writes_inverted", &self.component_writes_inverted)
.field("reads_all_resources", &self.reads_all_resources)
.field("writes_all_resources", &self.writes_all_resources)
.field("archetypal", &FormattedBitSet::new(&self.archetypal))
.finish()
}
}
impl Access {
pub const fn new() -> Self {
Self {
reads_all_resources: false,
writes_all_resources: false,
component_read_and_writes_inverted: false,
component_writes_inverted: false,
component_read_and_writes: FixedBitSet::new(),
component_writes: FixedBitSet::new(),
resource_read_and_writes: FixedBitSet::new(),
resource_writes: FixedBitSet::new(),
archetypal: FixedBitSet::new(),
}
}
pub(crate) const fn new_read_all() -> Self {
let mut access = Self::new();
access.reads_all_resources = true;
access.component_read_and_writes_inverted = true;
access
}
pub(crate) const fn new_write_all() -> Self {
let mut access = Self::new();
access.reads_all_resources = true;
access.writes_all_resources = true;
access.component_read_and_writes_inverted = true;
access.component_writes_inverted = true;
access
}
fn add_component_sparse_set_index_read(&mut self, index: usize) {
if !self.component_read_and_writes_inverted {
self.component_read_and_writes.grow_and_insert(index);
} else if index < self.component_read_and_writes.len() {
self.component_read_and_writes.remove(index);
}
}
fn add_component_sparse_set_index_write(&mut self, index: usize) {
if !self.component_writes_inverted {
self.component_writes.grow_and_insert(index);
} else if index < self.component_writes.len() {
self.component_writes.remove(index);
}
}
pub fn add_component_read(&mut self, index: ComponentId) {
let sparse_set_index = index.index();
self.add_component_sparse_set_index_read(sparse_set_index);
}
pub fn add_component_write(&mut self, index: ComponentId) {
let sparse_set_index = index.index();
self.add_component_sparse_set_index_read(sparse_set_index);
self.add_component_sparse_set_index_write(sparse_set_index);
}
pub fn add_resource_read(&mut self, index: ComponentId) {
self.resource_read_and_writes.grow_and_insert(index.index());
}
pub fn add_resource_write(&mut self, index: ComponentId) {
self.resource_read_and_writes.grow_and_insert(index.index());
self.resource_writes.grow_and_insert(index.index());
}
fn remove_component_sparse_set_index_read(&mut self, index: usize) {
if self.component_read_and_writes_inverted {
self.component_read_and_writes.grow_and_insert(index);
} else if index < self.component_read_and_writes.len() {
self.component_read_and_writes.remove(index);
}
}
fn remove_component_sparse_set_index_write(&mut self, index: usize) {
if self.component_writes_inverted {
self.component_writes.grow_and_insert(index);
} else if index < self.component_writes.len() {
self.component_writes.remove(index);
}
}
pub fn remove_component_read(&mut self, index: ComponentId) {
let sparse_set_index = index.index();
self.remove_component_sparse_set_index_write(sparse_set_index);
self.remove_component_sparse_set_index_read(sparse_set_index);
}
pub fn remove_component_write(&mut self, index: ComponentId) {
let sparse_set_index = index.index();
self.remove_component_sparse_set_index_write(sparse_set_index);
}
pub fn add_archetypal(&mut self, index: ComponentId) {
self.archetypal.grow_and_insert(index.index());
}
pub fn has_component_read(&self, index: ComponentId) -> bool {
self.component_read_and_writes_inverted
^ self.component_read_and_writes.contains(index.index())
}
pub fn has_any_component_read(&self) -> bool {
self.component_read_and_writes_inverted || !self.component_read_and_writes.is_clear()
}
pub fn has_component_write(&self, index: ComponentId) -> bool {
self.component_writes_inverted ^ self.component_writes.contains(index.index())
}
pub fn has_any_component_write(&self) -> bool {
self.component_writes_inverted || !self.component_writes.is_clear()
}
pub fn has_resource_read(&self, index: ComponentId) -> bool {
self.reads_all_resources || self.resource_read_and_writes.contains(index.index())
}
pub fn has_any_resource_read(&self) -> bool {
self.reads_all_resources || !self.resource_read_and_writes.is_clear()
}
pub fn has_resource_write(&self, index: ComponentId) -> bool {
self.writes_all_resources || self.resource_writes.contains(index.index())
}
pub fn has_any_resource_write(&self) -> bool {
self.writes_all_resources || !self.resource_writes.is_clear()
}
pub fn has_any_read(&self) -> bool {
self.has_any_component_read() || self.has_any_resource_read()
}
pub fn has_any_write(&self) -> bool {
self.has_any_component_write() || self.has_any_resource_write()
}
pub fn has_archetypal(&self, index: ComponentId) -> bool {
self.archetypal.contains(index.index())
}
#[inline]
pub fn read_all_components(&mut self) {
self.component_read_and_writes_inverted = true;
self.component_read_and_writes.clear();
}
#[inline]
pub fn write_all_components(&mut self) {
self.read_all_components();
self.component_writes_inverted = true;
self.component_writes.clear();
}
#[inline]
pub const fn read_all_resources(&mut self) {
self.reads_all_resources = true;
}
#[inline]
pub const fn write_all_resources(&mut self) {
self.reads_all_resources = true;
self.writes_all_resources = true;
}
#[inline]
pub fn read_all(&mut self) {
self.read_all_components();
self.read_all_resources();
}
#[inline]
pub fn write_all(&mut self) {
self.write_all_components();
self.write_all_resources();
}
#[inline]
pub fn has_read_all_components(&self) -> bool {
self.component_read_and_writes_inverted && self.component_read_and_writes.is_clear()
}
#[inline]
pub fn has_write_all_components(&self) -> bool {
self.component_writes_inverted && self.component_writes.is_clear()
}
#[inline]
pub fn has_read_all_resources(&self) -> bool {
self.reads_all_resources
}
#[inline]
pub fn has_write_all_resources(&self) -> bool {
self.writes_all_resources
}
pub fn has_read_all(&self) -> bool {
self.has_read_all_components() && self.has_read_all_resources()
}
pub fn has_write_all(&self) -> bool {
self.has_write_all_components() && self.has_write_all_resources()
}
pub fn clear_writes(&mut self) {
self.writes_all_resources = false;
self.component_writes_inverted = false;
self.component_writes.clear();
self.resource_writes.clear();
}
pub fn clear(&mut self) {
self.reads_all_resources = false;
self.writes_all_resources = false;
self.component_read_and_writes_inverted = false;
self.component_writes_inverted = false;
self.component_read_and_writes.clear();
self.component_writes.clear();
self.resource_read_and_writes.clear();
self.resource_writes.clear();
}
pub fn extend(&mut self, other: &Access) {
invertible_union_with(
&mut self.component_read_and_writes,
&mut self.component_read_and_writes_inverted,
&other.component_read_and_writes,
other.component_read_and_writes_inverted,
);
invertible_union_with(
&mut self.component_writes,
&mut self.component_writes_inverted,
&other.component_writes,
other.component_writes_inverted,
);
self.reads_all_resources = self.reads_all_resources || other.reads_all_resources;
self.writes_all_resources = self.writes_all_resources || other.writes_all_resources;
self.resource_read_and_writes
.union_with(&other.resource_read_and_writes);
self.resource_writes.union_with(&other.resource_writes);
self.archetypal.union_with(&other.archetypal);
}
pub fn remove_conflicting_access(&mut self, other: &Access) {
invertible_difference_with(
&mut self.component_read_and_writes,
&mut self.component_read_and_writes_inverted,
&other.component_writes,
other.component_writes_inverted,
);
invertible_difference_with(
&mut self.component_writes,
&mut self.component_writes_inverted,
&other.component_read_and_writes,
other.component_read_and_writes_inverted,
);
if other.reads_all_resources {
self.writes_all_resources = false;
self.resource_writes.clear();
}
if other.writes_all_resources {
self.reads_all_resources = false;
self.resource_read_and_writes.clear();
}
self.resource_read_and_writes
.difference_with(&other.resource_writes);
self.resource_writes
.difference_with(&other.resource_read_and_writes);
}
pub fn is_components_compatible(&self, other: &Access) -> bool {
for (
lhs_writes,
rhs_reads_and_writes,
lhs_writes_inverted,
rhs_reads_and_writes_inverted,
) in [
(
&self.component_writes,
&other.component_read_and_writes,
self.component_writes_inverted,
other.component_read_and_writes_inverted,
),
(
&other.component_writes,
&self.component_read_and_writes,
other.component_writes_inverted,
self.component_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
}
pub fn is_resources_compatible(&self, other: &Access) -> bool {
if self.writes_all_resources {
return !other.has_any_resource_read();
}
if other.writes_all_resources {
return !self.has_any_resource_read();
}
if self.reads_all_resources {
return !other.has_any_resource_write();
}
if other.reads_all_resources {
return !self.has_any_resource_write();
}
self.resource_writes
.is_disjoint(&other.resource_read_and_writes)
&& other
.resource_writes
.is_disjoint(&self.resource_read_and_writes)
}
pub fn is_compatible(&self, other: &Access) -> bool {
self.is_components_compatible(other) && self.is_resources_compatible(other)
}
pub fn is_subset_components(&self, other: &Access) -> bool {
for (
our_components,
their_components,
our_components_inverted,
their_components_inverted,
) in [
(
&self.component_read_and_writes,
&other.component_read_and_writes,
self.component_read_and_writes_inverted,
other.component_read_and_writes_inverted,
),
(
&self.component_writes,
&other.component_writes,
self.component_writes_inverted,
other.component_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
}
pub fn is_subset_resources(&self, other: &Access) -> bool {
if self.writes_all_resources {
return other.writes_all_resources;
}
if other.writes_all_resources {
return true;
}
if self.reads_all_resources {
return other.reads_all_resources;
}
if other.reads_all_resources {
return self.resource_writes.is_subset(&other.resource_writes);
}
self.resource_read_and_writes
.is_subset(&other.resource_read_and_writes)
&& self.resource_writes.is_subset(&other.resource_writes)
}
pub fn is_subset(&self, other: &Access) -> bool {
self.is_subset_components(other) && self.is_subset_resources(other)
}
fn get_component_conflicts(&self, other: &Access) -> AccessConflicts {
let mut conflicts = FixedBitSet::new();
for (
lhs_writes,
rhs_reads_and_writes,
lhs_writes_inverted,
rhs_reads_and_writes_inverted,
) in [
(
&self.component_writes,
&other.component_read_and_writes,
self.component_writes_inverted,
other.component_read_and_writes_inverted,
),
(
&other.component_writes,
&self.component_read_and_writes,
other.component_writes_inverted,
self.component_read_and_writes_inverted,
),
] {
let temp_conflicts: FixedBitSet =
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)
}
pub fn get_conflicts(&self, other: &Access) -> AccessConflicts {
let mut conflicts = match self.get_component_conflicts(other) {
AccessConflicts::All => return AccessConflicts::All,
AccessConflicts::Individual(conflicts) => conflicts,
};
if self.reads_all_resources {
if other.writes_all_resources {
return AccessConflicts::All;
}
conflicts.extend(other.resource_writes.ones());
}
if other.reads_all_resources {
if self.writes_all_resources {
return AccessConflicts::All;
}
conflicts.extend(self.resource_writes.ones());
}
if self.writes_all_resources {
conflicts.extend(other.resource_read_and_writes.ones());
}
if other.writes_all_resources {
conflicts.extend(self.resource_read_and_writes.ones());
}
conflicts.extend(
self.resource_writes
.intersection(&other.resource_read_and_writes),
);
conflicts.extend(
self.resource_read_and_writes
.intersection(&other.resource_writes),
);
AccessConflicts::Individual(conflicts)
}
pub fn resource_reads_and_writes(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.resource_read_and_writes.ones().map(ComponentId::new)
}
pub fn resource_reads(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.resource_read_and_writes
.difference(&self.resource_writes)
.map(ComponentId::new)
}
pub fn resource_writes(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.resource_writes.ones().map(ComponentId::new)
}
pub fn archetypal(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.archetypal.ones().map(ComponentId::new)
}
pub fn try_iter_component_access(
&self,
) -> Result<impl Iterator<Item = ComponentAccessKind> + '_, UnboundedAccessError> {
if self.component_read_and_writes_inverted {
return Err(UnboundedAccessError {
writes_inverted: self.component_writes_inverted,
read_and_writes_inverted: self.component_read_and_writes_inverted,
});
}
let reads_and_writes = self.component_read_and_writes.ones().map(|index| {
let sparse_index = ComponentId::new(index);
if self.component_writes.contains(index) {
ComponentAccessKind::Exclusive(sparse_index)
} else {
ComponentAccessKind::Shared(sparse_index)
}
});
let archetypal = self
.archetypal
.ones()
.filter(|&index| {
!self.component_writes.contains(index)
&& !self.component_read_and_writes.contains(index)
})
.map(|index| ComponentAccessKind::Archetypal(ComponentId::new(index)));
Ok(reads_and_writes.chain(archetypal))
}
}
fn invertible_union_with(
self_set: &mut FixedBitSet,
self_inverted: &mut bool,
other_set: &FixedBitSet,
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.grow(other_set.len());
self_set.toggle_range(..);
self_set.intersect_with(other_set);
}
(false, false) => self_set.union_with(other_set),
}
}
fn invertible_difference_with(
self_set: &mut FixedBitSet,
self_inverted: &mut bool,
other_set: &FixedBitSet,
other_inverted: bool,
) {
*self_inverted = !*self_inverted;
invertible_union_with(self_set, self_inverted, other_set, other_inverted);
*self_inverted = !*self_inverted;
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
#[error("Access is unbounded")]
pub struct UnboundedAccessError {
pub writes_inverted: bool,
pub read_and_writes_inverted: bool,
}
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
pub enum ComponentAccessKind {
Archetypal(ComponentId),
Shared(ComponentId),
Exclusive(ComponentId),
}
impl ComponentAccessKind {
pub fn index(&self) -> &ComponentId {
let (Self::Archetypal(value) | Self::Shared(value) | Self::Exclusive(value)) = self;
value
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct FilteredAccess {
pub(crate) access: Access,
pub(crate) required: FixedBitSet,
pub(crate) filter_sets: Vec<AccessFilters>,
}
impl Clone for FilteredAccess {
fn clone(&self) -> Self {
Self {
access: self.access.clone(),
required: self.required.clone(),
filter_sets: self.filter_sets.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.access.clone_from(&source.access);
self.required.clone_from(&source.required);
self.filter_sets.clone_from(&source.filter_sets);
}
}
impl Default for FilteredAccess {
fn default() -> Self {
Self::matches_everything()
}
}
impl From<FilteredAccess> for FilteredAccessSet {
fn from(filtered_access: FilteredAccess) -> Self {
let mut base = FilteredAccessSet::default();
base.add(filtered_access);
base
}
}
#[derive(Debug, PartialEq, From)]
pub enum AccessConflicts {
All,
Individual(FixedBitSet),
}
impl AccessConflicts {
fn add(&mut self, other: &Self) {
match (self, other) {
(s, AccessConflicts::All) => {
*s = AccessConflicts::All;
}
(AccessConflicts::Individual(this), AccessConflicts::Individual(other)) => {
this.extend(other.ones());
}
_ => {}
}
}
pub fn is_empty(&self) -> bool {
match self {
Self::All => false,
Self::Individual(set) => set.is_empty(),
}
}
pub(crate) fn format_conflict_list(&self, world: &World) -> String {
match self {
AccessConflicts::All => String::new(),
AccessConflicts::Individual(indices) => indices
.ones()
.map(|index| {
format!(
"{}",
world
.components
.get_name(ComponentId::new(index))
.unwrap()
.shortname()
)
})
.collect::<Vec<_>>()
.join(", "),
}
}
pub(crate) fn empty() -> Self {
Self::Individual(FixedBitSet::new())
}
}
impl From<Vec<ComponentId>> for AccessConflicts {
fn from(value: Vec<ComponentId>) -> Self {
Self::Individual(value.iter().map(|c| c.index()).collect())
}
}
impl FilteredAccess {
pub fn matches_everything() -> Self {
Self {
access: Access::default(),
required: FixedBitSet::default(),
filter_sets: vec![AccessFilters::default()],
}
}
pub fn matches_nothing() -> Self {
Self {
access: Access::default(),
required: FixedBitSet::default(),
filter_sets: Vec::new(),
}
}
#[inline]
pub fn access(&self) -> &Access {
&self.access
}
#[inline]
pub fn access_mut(&mut self) -> &mut Access {
&mut self.access
}
pub fn add_component_read(&mut self, index: ComponentId) {
self.access.add_component_read(index);
self.add_required(index);
self.and_with(index);
}
pub fn add_component_write(&mut self, index: ComponentId) {
self.access.add_component_write(index);
self.add_required(index);
self.and_with(index);
}
pub fn add_resource_read(&mut self, index: ComponentId) {
self.access.add_resource_read(index);
}
pub fn add_resource_write(&mut self, index: ComponentId) {
self.access.add_resource_write(index);
}
fn add_required(&mut self, index: ComponentId) {
self.required.grow_and_insert(index.index());
}
pub fn and_with(&mut self, index: ComponentId) {
for filter in &mut self.filter_sets {
filter.with.grow_and_insert(index.index());
}
}
pub fn and_without(&mut self, index: ComponentId) {
for filter in &mut self.filter_sets {
filter.without.grow_and_insert(index.index());
}
}
pub fn append_or(&mut self, other: &FilteredAccess) {
self.filter_sets.append(&mut other.filter_sets.clone());
}
pub fn extend_access(&mut self, other: &FilteredAccess) {
self.access.extend(&other.access);
}
pub fn is_compatible(&self, other: &FilteredAccess) -> bool {
if !self.access.is_resources_compatible(&other.access) {
return false;
}
if self.access.is_components_compatible(&other.access) {
return true;
}
self.filter_sets.iter().all(|filter| {
other
.filter_sets
.iter()
.all(|other_filter| filter.is_ruled_out_by(other_filter))
})
}
pub fn get_conflicts(&self, other: &FilteredAccess) -> AccessConflicts {
if !self.is_compatible(other) {
return self.access.get_conflicts(&other.access);
}
AccessConflicts::empty()
}
pub fn extend(&mut self, other: &FilteredAccess) {
self.access.extend(&other.access);
self.required.union_with(&other.required);
if other.filter_sets.len() == 1 {
for filter in &mut self.filter_sets {
filter.with.union_with(&other.filter_sets[0].with);
filter.without.union_with(&other.filter_sets[0].without);
}
return;
}
let mut new_filters = Vec::with_capacity(self.filter_sets.len() * other.filter_sets.len());
for filter in &self.filter_sets {
for other_filter in &other.filter_sets {
let mut new_filter = filter.clone();
new_filter.with.union_with(&other_filter.with);
new_filter.without.union_with(&other_filter.without);
new_filters.push(new_filter);
}
}
self.filter_sets = new_filters;
}
pub fn read_all(&mut self) {
self.access.read_all();
}
pub fn write_all(&mut self) {
self.access.write_all();
}
pub fn read_all_components(&mut self) {
self.access.read_all_components();
}
pub fn write_all_components(&mut self) {
self.access.write_all_components();
}
pub fn is_subset(&self, other: &FilteredAccess) -> bool {
self.required.is_subset(&other.required) && self.access().is_subset(other.access())
}
pub fn with_filters(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.filter_sets
.iter()
.flat_map(|f| f.with.ones().map(ComponentId::new))
}
pub fn without_filters(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.filter_sets
.iter()
.flat_map(|f| f.without.ones().map(ComponentId::new))
}
pub fn contains(&self, index: ComponentId) -> bool {
self.access().has_archetypal(index)
|| self
.filter_sets
.iter()
.any(|f| f.with.contains(index.index()) || f.without.contains(index.index()))
}
}
#[derive(Eq, PartialEq, Default)]
pub(crate) struct AccessFilters {
pub(crate) with: FixedBitSet,
pub(crate) without: FixedBitSet,
}
impl Clone for AccessFilters {
fn clone(&self) -> Self {
Self {
with: self.with.clone(),
without: self.without.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.with.clone_from(&source.with);
self.without.clone_from(&source.without);
}
}
impl Debug for AccessFilters {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AccessFilters")
.field("with", &FormattedBitSet::new(&self.with))
.field("without", &FormattedBitSet::new(&self.without))
.finish()
}
}
impl AccessFilters {
fn is_ruled_out_by(&self, other: &Self) -> bool {
!self.with.is_disjoint(&other.without) || !self.without.is_disjoint(&other.with)
}
}
#[derive(Debug, PartialEq, Eq, Default)]
pub struct FilteredAccessSet {
combined_access: Access,
filtered_accesses: Vec<FilteredAccess>,
}
impl Clone for FilteredAccessSet {
fn clone(&self) -> Self {
Self {
combined_access: self.combined_access.clone(),
filtered_accesses: self.filtered_accesses.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.combined_access.clone_from(&source.combined_access);
self.filtered_accesses.clone_from(&source.filtered_accesses);
}
}
impl FilteredAccessSet {
pub const fn new() -> Self {
Self {
combined_access: Access::new(),
filtered_accesses: Vec::new(),
}
}
#[inline]
pub fn combined_access(&self) -> &Access {
&self.combined_access
}
pub fn is_compatible(&self, other: &FilteredAccessSet) -> bool {
if self.combined_access.is_compatible(other.combined_access()) {
return true;
}
for filtered in &self.filtered_accesses {
for other_filtered in &other.filtered_accesses {
if !filtered.is_compatible(other_filtered) {
return false;
}
}
}
true
}
pub fn get_conflicts(&self, other: &FilteredAccessSet) -> AccessConflicts {
let mut conflicts = AccessConflicts::empty();
if !self.combined_access.is_compatible(other.combined_access()) {
for filtered in &self.filtered_accesses {
for other_filtered in &other.filtered_accesses {
conflicts.add(&filtered.get_conflicts(other_filtered));
}
}
}
conflicts
}
pub fn get_conflicts_single(&self, filtered_access: &FilteredAccess) -> AccessConflicts {
let mut conflicts = AccessConflicts::empty();
if !self.combined_access.is_compatible(filtered_access.access()) {
for filtered in &self.filtered_accesses {
conflicts.add(&filtered.get_conflicts(filtered_access));
}
}
conflicts
}
pub fn add(&mut self, filtered_access: FilteredAccess) {
self.combined_access.extend(&filtered_access.access);
self.filtered_accesses.push(filtered_access);
}
pub fn add_unfiltered_resource_read(&mut self, index: ComponentId) {
let mut filter = FilteredAccess::default();
filter.add_resource_read(index);
self.add(filter);
}
pub fn add_unfiltered_resource_write(&mut self, index: ComponentId) {
let mut filter = FilteredAccess::default();
filter.add_resource_write(index);
self.add(filter);
}
pub fn add_unfiltered_read_all_resources(&mut self) {
let mut filter = FilteredAccess::default();
filter.access.read_all_resources();
self.add(filter);
}
pub fn add_unfiltered_write_all_resources(&mut self) {
let mut filter = FilteredAccess::default();
filter.access.write_all_resources();
self.add(filter);
}
pub fn extend(&mut self, filtered_access_set: FilteredAccessSet) {
self.combined_access
.extend(&filtered_access_set.combined_access);
self.filtered_accesses
.extend(filtered_access_set.filtered_accesses);
}
pub fn read_all(&mut self) {
let mut filter = FilteredAccess::matches_everything();
filter.read_all();
self.add(filter);
}
pub fn write_all(&mut self) {
let mut filter = FilteredAccess::matches_everything();
filter.write_all();
self.add(filter);
}
pub fn clear(&mut self) {
self.combined_access.clear();
self.filtered_accesses.clear();
}
}
#[cfg(test)]
mod tests {
use super::{invertible_difference_with, invertible_union_with};
use crate::{
component::ComponentId,
query::{
access::AccessFilters, Access, AccessConflicts, ComponentAccessKind, FilteredAccess,
FilteredAccessSet, UnboundedAccessError,
},
};
use alloc::{vec, vec::Vec};
use fixedbitset::FixedBitSet;
fn create_sample_access() -> Access {
let mut access = Access::default();
access.add_component_read(ComponentId::new(1));
access.add_component_read(ComponentId::new(2));
access.add_component_write(ComponentId::new(3));
access.add_archetypal(ComponentId::new(5));
access.read_all();
access
}
fn create_sample_filtered_access() -> FilteredAccess {
let mut filtered_access = FilteredAccess::default();
filtered_access.add_component_write(ComponentId::new(1));
filtered_access.add_component_read(ComponentId::new(2));
filtered_access.add_required(ComponentId::new(3));
filtered_access.and_with(ComponentId::new(4));
filtered_access
}
fn create_sample_access_filters() -> AccessFilters {
let mut access_filters = AccessFilters::default();
access_filters.with.grow_and_insert(3);
access_filters.without.grow_and_insert(5);
access_filters
}
fn create_sample_filtered_access_set() -> FilteredAccessSet {
let mut filtered_access_set = FilteredAccessSet::default();
filtered_access_set.add_unfiltered_resource_read(ComponentId::new(2));
filtered_access_set.add_unfiltered_resource_write(ComponentId::new(4));
filtered_access_set.read_all();
filtered_access_set
}
#[test]
fn test_access_clone() {
let original = create_sample_access();
let cloned = original.clone();
assert_eq!(original, cloned);
}
#[test]
fn test_access_clone_from() {
let original = create_sample_access();
let mut cloned = Access::default();
cloned.add_component_write(ComponentId::new(7));
cloned.add_component_read(ComponentId::new(4));
cloned.add_archetypal(ComponentId::new(8));
cloned.write_all();
cloned.clone_from(&original);
assert_eq!(original, cloned);
}
#[test]
fn test_filtered_access_clone() {
let original = create_sample_filtered_access();
let cloned = original.clone();
assert_eq!(original, cloned);
}
#[test]
fn test_filtered_access_clone_from() {
let original = create_sample_filtered_access();
let mut cloned = FilteredAccess::default();
cloned.add_component_write(ComponentId::new(7));
cloned.add_component_read(ComponentId::new(4));
cloned.append_or(&FilteredAccess::default());
cloned.clone_from(&original);
assert_eq!(original, cloned);
}
#[test]
fn test_access_filters_clone() {
let original = create_sample_access_filters();
let cloned = original.clone();
assert_eq!(original, cloned);
}
#[test]
fn test_access_filters_clone_from() {
let original = create_sample_access_filters();
let mut cloned = AccessFilters::default();
cloned.with.grow_and_insert(1);
cloned.without.grow_and_insert(2);
cloned.clone_from(&original);
assert_eq!(original, cloned);
}
#[test]
fn test_filtered_access_set_clone() {
let original = create_sample_filtered_access_set();
let cloned = original.clone();
assert_eq!(original, cloned);
}
#[test]
fn test_filtered_access_set_from() {
let original = create_sample_filtered_access_set();
let mut cloned = FilteredAccessSet::default();
cloned.add_unfiltered_resource_read(ComponentId::new(7));
cloned.add_unfiltered_resource_write(ComponentId::new(9));
cloned.write_all();
cloned.clone_from(&original);
assert_eq!(original, cloned);
}
#[test]
fn read_all_access_conflicts() {
let mut access_a = Access::default();
access_a.add_component_write(ComponentId::new(0));
let mut access_b = Access::default();
access_b.read_all();
assert!(!access_b.is_compatible(&access_a));
let mut access_a = Access::default();
access_a.read_all();
let mut access_b = Access::default();
access_b.read_all();
assert!(access_b.is_compatible(&access_a));
}
#[test]
fn access_get_conflicts() {
let mut access_a = Access::default();
access_a.add_component_read(ComponentId::new(0));
access_a.add_component_read(ComponentId::new(1));
let mut access_b = Access::default();
access_b.add_component_read(ComponentId::new(0));
access_b.add_component_write(ComponentId::new(1));
assert_eq!(
access_a.get_conflicts(&access_b),
vec![ComponentId::new(1)].into()
);
let mut access_c = Access::default();
access_c.add_component_write(ComponentId::new(0));
access_c.add_component_write(ComponentId::new(1));
assert_eq!(
access_a.get_conflicts(&access_c),
vec![ComponentId::new(0), ComponentId::new(1)].into()
);
assert_eq!(
access_b.get_conflicts(&access_c),
vec![ComponentId::new(0), ComponentId::new(1)].into()
);
let mut access_d = Access::default();
access_d.add_component_read(ComponentId::new(0));
assert_eq!(access_d.get_conflicts(&access_a), AccessConflicts::empty());
assert_eq!(access_d.get_conflicts(&access_b), AccessConflicts::empty());
assert_eq!(
access_d.get_conflicts(&access_c),
vec![ComponentId::new(0)].into()
);
}
#[test]
fn filtered_combined_access() {
let mut access_a = FilteredAccessSet::default();
access_a.add_unfiltered_resource_read(ComponentId::new(1));
let mut filter_b = FilteredAccess::default();
filter_b.add_resource_write(ComponentId::new(1));
let conflicts = access_a.get_conflicts_single(&filter_b);
assert_eq!(
&conflicts,
&AccessConflicts::from(vec![ComponentId::new(1)]),
"access_a: {access_a:?}, filter_b: {filter_b:?}"
);
}
#[test]
fn filtered_access_extend() {
let mut access_a = FilteredAccess::default();
access_a.add_component_read(ComponentId::new(0));
access_a.add_component_read(ComponentId::new(1));
access_a.and_with(ComponentId::new(2));
let mut access_b = FilteredAccess::default();
access_b.add_component_read(ComponentId::new(0));
access_b.add_component_write(ComponentId::new(3));
access_b.and_without(ComponentId::new(4));
access_a.extend(&access_b);
let mut expected = FilteredAccess::default();
expected.add_component_read(ComponentId::new(0));
expected.add_component_read(ComponentId::new(1));
expected.and_with(ComponentId::new(2));
expected.add_component_write(ComponentId::new(3));
expected.and_without(ComponentId::new(4));
assert!(access_a.eq(&expected));
}
#[test]
fn filtered_access_extend_or() {
let mut access_a = FilteredAccess::default();
access_a.add_component_write(ComponentId::new(0));
access_a.add_component_write(ComponentId::new(1));
let mut access_b = FilteredAccess::default();
access_b.and_with(ComponentId::new(2));
let mut access_c = FilteredAccess::default();
access_c.and_with(ComponentId::new(3));
access_c.and_without(ComponentId::new(4));
access_b.append_or(&access_c);
access_a.extend(&access_b);
let mut expected = FilteredAccess::default();
expected.add_component_write(ComponentId::new(0));
expected.add_component_write(ComponentId::new(1));
expected.filter_sets = vec![
AccessFilters {
with: FixedBitSet::with_capacity_and_blocks(3, [0b111]),
without: FixedBitSet::default(),
},
AccessFilters {
with: FixedBitSet::with_capacity_and_blocks(4, [0b1011]),
without: FixedBitSet::with_capacity_and_blocks(5, [0b10000]),
},
];
assert_eq!(access_a, expected);
}
#[test]
fn try_iter_component_access_simple() {
let mut access = Access::default();
access.add_component_read(ComponentId::new(1));
access.add_component_read(ComponentId::new(2));
access.add_component_write(ComponentId::new(3));
access.add_archetypal(ComponentId::new(5));
let result = access
.try_iter_component_access()
.map(Iterator::collect::<Vec<_>>);
assert_eq!(
result,
Ok(vec![
ComponentAccessKind::Shared(ComponentId::new(1)),
ComponentAccessKind::Shared(ComponentId::new(2)),
ComponentAccessKind::Exclusive(ComponentId::new(3)),
ComponentAccessKind::Archetypal(ComponentId::new(5)),
]),
);
}
#[test]
fn try_iter_component_access_unbounded_write_all() {
let mut access = Access::default();
access.add_component_read(ComponentId::new(1));
access.add_component_read(ComponentId::new(2));
access.write_all();
let result = access
.try_iter_component_access()
.map(Iterator::collect::<Vec<_>>);
assert_eq!(
result,
Err(UnboundedAccessError {
writes_inverted: true,
read_and_writes_inverted: true
}),
);
}
#[test]
fn try_iter_component_access_unbounded_read_all() {
let mut access = Access::default();
access.add_component_read(ComponentId::new(1));
access.add_component_read(ComponentId::new(2));
access.read_all();
let result = access
.try_iter_component_access()
.map(Iterator::collect::<Vec<_>>);
assert_eq!(
result,
Err(UnboundedAccessError {
writes_inverted: false,
read_and_writes_inverted: true
}),
);
}
fn bit_set(bits: usize, iter: impl IntoIterator<Item = usize>) -> FixedBitSet {
let mut result = FixedBitSet::with_capacity(bits);
result.extend(iter);
result
}
#[test]
fn invertible_union_with_tests() {
let invertible_union = |mut self_inverted: bool, other_inverted: bool| {
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)
};
let (s, i) = invertible_union(false, false);
assert_eq!((s, i), (bit_set(4, [0, 1, 2]), false));
let (s, i) = invertible_union(false, true);
assert_eq!((s, i), (bit_set(4, [2]), true));
let (s, i) = invertible_union(true, false);
assert_eq!((s, i), (bit_set(4, [1]), true));
let (s, i) = invertible_union(true, true);
assert_eq!((s, i), (bit_set(4, [0]), true));
}
#[test]
fn invertible_union_with_different_lengths() {
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,
);
assert_eq!((self_set, self_inverted), (bit_set(3, [1]), true));
}
#[test]
fn invertible_difference_with_tests() {
let invertible_difference = |mut self_inverted: bool, other_inverted: bool| {
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)
};
let (s, i) = invertible_difference(false, false);
assert_eq!((s, i), (bit_set(4, [1]), false));
let (s, i) = invertible_difference(false, true);
assert_eq!((s, i), (bit_set(4, [0]), false));
let (s, i) = invertible_difference(true, false);
assert_eq!((s, i), (bit_set(4, [0, 1, 2]), true));
let (s, i) = invertible_difference(true, true);
assert_eq!((s, i), (bit_set(4, [2]), false));
}
}