Skip to content

Commit

Permalink
add len, is_empty, iter method on SparseSets, make SparseSets inspect… (
Browse files Browse the repository at this point in the history
#7638)

…able like Table. Rename clear to clear_entities to clarify that metadata keeps, only value cleared

# Objective

- Provide some inspectability for SparseSets. 

## Solution

- `Tables` has these three methods, len, is_empty and iter too. Add these methods to `SparseSets`, so user can print the shape of storage.

---

## Changelog

> This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section.

- Add `len`, `is_empty`, `iter` methods on SparseSets.
- Rename `clear` to `clear_entities` to clarify its purpose.
- Add `new_for_test` on `ComponentInfo` to make test code easy.
- Add test case covering new methods.

## Migration Guide

> This section is optional. If there are no breaking changes, you can delete this section.

- Simply adding new functionality is not a breaking change.
  • Loading branch information
shuoli84 committed Feb 15, 2023
1 parent e392e99 commit 1dc713b
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 10 deletions.
3 changes: 2 additions & 1 deletion crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ impl ComponentInfo {
self.descriptor.is_send_and_sync
}

fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
/// Create a new [`ComponentInfo`].
pub(crate) fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
ComponentInfo { id, descriptor }
}
}
Expand Down
86 changes: 78 additions & 8 deletions crates/bevy_ecs/src/storage/sparse_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,35 @@ pub struct SparseSets {
}

impl SparseSets {
pub fn get_or_insert(&mut self, component_info: &ComponentInfo) -> &mut ComponentSparseSet {
/// Returns the number of [`ComponentSparseSet`]s this collection contains.
#[inline]
pub fn len(&self) -> usize {
self.sets.len()
}

/// Returns true if this collection contains no [`ComponentSparseSet`]s.
#[inline]
pub fn is_empty(&self) -> bool {
self.sets.is_empty()
}

/// An Iterator visiting all ([`ComponentId`], [`ComponentSparseSet`]) pairs.
/// NOTE: Order is not guaranteed.
pub fn iter(&self) -> impl Iterator<Item = (ComponentId, &ComponentSparseSet)> {
self.sets.iter().map(|(id, data)| (*id, data))
}

/// Gets a reference to the [`ComponentSparseSet`] of a [`ComponentId`].
pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
self.sets.get(component_id)
}

/// Gets a mutable reference of [`ComponentSparseSet`] of a [`ComponentInfo`].
/// Create a new [`ComponentSparseSet`] if not exists.
pub(crate) fn get_or_insert(
&mut self,
component_info: &ComponentInfo,
) -> &mut ComponentSparseSet {
if !self.sets.contains(component_info.id()) {
self.sets.insert(
component_info.id(),
Expand All @@ -508,15 +536,13 @@ impl SparseSets {
self.sets.get_mut(component_info.id()).unwrap()
}

pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
self.sets.get(component_id)
}

pub fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ComponentSparseSet> {
/// Gets a mutable reference to the [`ComponentSparseSet`] of a [`ComponentId`].
pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ComponentSparseSet> {
self.sets.get_mut(component_id)
}

pub fn clear(&mut self) {
/// Clear entities stored in each [`ComponentSparseSet`]
pub(crate) fn clear_entities(&mut self) {
for set in self.sets.values_mut() {
set.clear();
}
Expand All @@ -531,7 +557,13 @@ impl SparseSets {

#[cfg(test)]
mod tests {
use crate::{entity::Entity, storage::SparseSet};
use super::SparseSets;
use crate::{
self as bevy_ecs,
component::{Component, ComponentDescriptor, ComponentId, ComponentInfo},
entity::Entity,
storage::SparseSet,
};

#[derive(Debug, Eq, PartialEq)]
struct Foo(usize);
Expand Down Expand Up @@ -584,4 +616,42 @@ mod tests {
*set.get_mut(e1).unwrap() = Foo(11);
assert_eq!(set.get(e1), Some(&Foo(11)));
}

#[test]
fn sparse_sets() {
let mut sets = SparseSets::default();

#[derive(Component, Default, Debug)]
struct TestComponent1;

#[derive(Component, Default, Debug)]
struct TestComponent2;

assert_eq!(sets.len(), 0);
assert!(sets.is_empty());

init_component::<TestComponent1>(&mut sets, 1);
assert_eq!(sets.len(), 1);

init_component::<TestComponent2>(&mut sets, 2);
assert_eq!(sets.len(), 2);

// check its shape by iter
let mut collected_sets = sets
.iter()
.map(|(id, set)| (id, set.len()))
.collect::<Vec<_>>();
collected_sets.sort();
assert_eq!(
collected_sets,
vec![(ComponentId::new(1), 0), (ComponentId::new(2), 0),]
);

fn init_component<T: Component>(sets: &mut SparseSets, id: usize) {
let descriptor = ComponentDescriptor::new::<TestComponent1>();
let id = ComponentId::new(id);
let info = ComponentInfo::new(id, descriptor);
sets.get_or_insert(&info);
}
}
}
2 changes: 2 additions & 0 deletions crates/bevy_ecs/src/storage/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,11 +654,13 @@ pub(crate) struct TableMoveResult {
}

impl Tables {
/// Returns the number of [`Table`]s this collection contains
#[inline]
pub fn len(&self) -> usize {
self.tables.len()
}

/// Returns true if this collection contains no [`Table`]s
#[inline]
pub fn is_empty(&self) -> bool {
self.tables.is_empty()
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1538,7 +1538,7 @@ impl World {
/// Despawns all entities in this [`World`].
pub fn clear_entities(&mut self) {
self.storages.tables.clear();
self.storages.sparse_sets.clear();
self.storages.sparse_sets.clear_entities();
self.archetypes.clear_entities();
self.entities.clear();
}
Expand Down

0 comments on commit 1dc713b

Please sign in to comment.