use core::fmt;
use smallvec::{SmallVec, smallvec};
use super::{EntityGroup, StorableEntity};
pub struct EntityStorage<T, const INLINE: usize = 1> {
items: SmallVec<[T; INLINE]>,
groups: SmallVec<[EntityGroup; 2]>,
}
impl<T: fmt::Debug, const INLINE: usize> fmt::Debug for EntityStorage<T, INLINE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(core::any::type_name::<Self>())
.field_with("groups", |f| {
let mut builder = f.debug_list();
for group in self.groups.iter() {
let range = group.as_range();
let items = &self.items[range.clone()];
builder.entry_with(|f| {
f.debug_map().entry(&"range", &range).entry(&"items", &items).finish()
});
}
builder.finish()
})
.finish()
}
}
impl<T, const INLINE: usize> Default for EntityStorage<T, INLINE> {
fn default() -> Self {
Self {
items: Default::default(),
groups: smallvec![EntityGroup::default()],
}
}
}
impl<T, const INLINE: usize> EntityStorage<T, INLINE> {
#[inline]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.items.len()
}
#[inline]
pub fn num_groups(&self) -> usize {
self.groups.len()
}
#[inline]
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.items.iter()
}
#[inline]
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
self.items.iter_mut()
}
pub fn groups(&self) -> impl Iterator<Item = EntityRange<'_, T>> + '_ {
self.groups.iter().map(|group| EntityRange {
range: group.as_range(),
items: self.items.as_slice(),
})
}
}
impl<T: StorableEntity, const INLINE: usize> EntityStorage<T, INLINE> {
pub fn empty_mut(&mut self) -> EntityRangeMut<'_, T, INLINE> {
EntityRangeMut {
group: 0,
range: 0..0,
groups: &mut self.groups,
items: &mut self.items,
}
}
pub fn push(&mut self, mut item: T) {
let index = self.items.len();
unsafe {
item.set_index(index);
}
self.items.push(item);
let group = self.groups.last_mut().unwrap();
group.grow(1);
}
pub fn extend<I>(&mut self, items: I)
where
I: IntoIterator<Item = T>,
{
let mut group = self.group_mut(self.groups.len() - 1);
group.extend(items);
}
#[inline]
pub fn push_group(&mut self, items: impl IntoIterator<Item = T>) -> usize {
let group = self.groups.len();
self.extend_group(group, items);
group
}
pub fn push_to_group(&mut self, group: usize, item: T) {
if self.groups.len() <= group {
let next_offset = self.groups.last().map(|group| group.as_range().end).unwrap_or(0);
self.groups.resize(group + 1, EntityGroup::new(next_offset, 0));
}
let mut group = self.group_mut(group);
group.push(item);
}
pub fn extend_group<I>(&mut self, group: usize, items: I)
where
I: IntoIterator<Item = T>,
{
if self.groups.len() <= group {
let next_offset = self.groups.last().map(|group| group.as_range().end).unwrap_or(0);
self.groups.resize(group + 1, EntityGroup::new(next_offset, 0));
}
let mut group = self.group_mut(group);
group.extend(items);
}
pub fn clear(&mut self) {
for mut item in self.items.drain(..) {
item.unlink();
}
self.groups.clear();
self.groups.push(EntityGroup::default());
}
pub fn all(&self) -> EntityRange<'_, T> {
EntityRange {
range: 0..self.items.len(),
items: self.items.as_slice(),
}
}
pub fn group(&self, group: usize) -> EntityRange<'_, T> {
EntityRange {
range: self.groups[group].as_range(),
items: self.items.as_slice(),
}
}
pub fn group_mut(&mut self, group: usize) -> EntityRangeMut<'_, T, INLINE> {
let range = self.groups[group].as_range();
EntityRangeMut {
group,
range,
groups: &mut self.groups,
items: &mut self.items,
}
}
}
impl<T, const INLINE: usize> core::ops::Index<usize> for EntityStorage<T, INLINE> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.items[index]
}
}
impl<T, const INLINE: usize> core::ops::IndexMut<usize> for EntityStorage<T, INLINE> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.items[index]
}
}
#[derive(Clone)]
pub struct EntityRange<'a, T> {
range: core::ops::Range<usize>,
items: &'a [T],
}
impl<'a, T> EntityRange<'a, T> {
pub fn new(range: core::ops::Range<usize>, items: &'a [T]) -> Self {
assert!(range.end <= items.len());
Self { range, items }
}
pub fn empty() -> Self {
Self {
range: 0..0,
items: &[],
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.as_slice().len()
}
#[inline(always)]
pub const fn range(&self) -> &core::ops::Range<usize> {
&self.range
}
#[inline]
pub fn as_slice(&self) -> &'a [T] {
if self.range.is_empty() {
&[]
} else {
&self.items[self.range.start..self.range.end]
}
}
#[inline]
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.as_slice().iter()
}
#[inline]
pub fn get(&self, index: usize) -> Option<&T> {
self.as_slice().get(index)
}
}
impl<T> core::ops::Index<usize> for EntityRange<'_, T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.as_slice()[index]
}
}
impl<'a, T> IntoIterator for EntityRange<'a, T> {
type IntoIter = core::slice::Iter<'a, T>;
type Item = &'a T;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.items[self.range.start..self.range.end].iter()
}
}
pub struct EntityRangeMut<'a, T, const INLINE: usize = 1> {
group: usize,
range: core::ops::Range<usize>,
groups: &'a mut [EntityGroup],
items: &'a mut SmallVec<[T; INLINE]>,
}
impl<T, const INLINE: usize> EntityRangeMut<'_, T, INLINE> {
#[inline]
pub fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.as_slice().len()
}
#[inline(always)]
pub const fn range(&self) -> &core::ops::Range<usize> {
&self.range
}
pub fn as_immutable(&self) -> EntityRange<'_, T> {
EntityRange {
range: self.range.clone(),
items: self.items.as_slice(),
}
}
#[inline]
pub fn as_slice(&self) -> &[T] {
if self.range.is_empty() {
&[]
} else {
&self.items[self.range.start..self.range.end]
}
}
#[inline]
pub fn as_slice_mut(&mut self) -> &mut [T] {
if self.range.is_empty() {
&mut []
} else {
&mut self.items[self.range.start..self.range.end]
}
}
#[inline]
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.as_slice().iter()
}
#[inline]
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
self.as_slice_mut().iter_mut()
}
#[inline]
pub fn get(&self, index: usize) -> Option<&T> {
self.as_slice().get(index)
}
#[inline]
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
self.as_slice_mut().get_mut(index)
}
}
impl<T: StorableEntity, const INLINE: usize> EntityRangeMut<'_, T, INLINE> {
#[inline]
pub fn push(&mut self, item: T) {
self.extend([item]);
}
pub fn extend<I>(&mut self, operands: I)
where
I: IntoIterator<Item = T>,
{
let is_last = self.groups.len() == self.group + 1;
if is_last {
self.extend_last(operands);
} else {
self.extend_within(operands);
}
}
fn extend_last<I>(&mut self, items: I)
where
I: IntoIterator<Item = T>,
{
let prev_len = self.items.len();
self.items.extend(items.into_iter().enumerate().map(|(i, mut item)| {
unsafe {
item.set_index(prev_len + i);
}
item
}));
let num_inserted = self.items.len().abs_diff(prev_len);
if num_inserted == 0 {
return;
}
self.groups[self.group].grow(num_inserted);
self.range = self.groups[self.group].as_range();
}
fn extend_within<I>(&mut self, items: I)
where
I: IntoIterator<Item = T>,
{
let prev_len = self.items.len();
let start = self.range.end;
self.items.insert_many(
start,
items.into_iter().enumerate().map(|(i, mut item)| {
unsafe {
item.set_index(start + i);
}
item
}),
);
let num_inserted = self.items.len().abs_diff(prev_len);
if num_inserted == 0 {
return;
}
self.groups[self.group].grow(num_inserted);
self.range = self.groups[self.group].as_range();
for group in self.groups[(self.group + 1)..].iter_mut() {
group.shift_start(num_inserted as isize);
}
let shifted = self.range.end;
for (offset, item) in self.items[shifted..].iter_mut().enumerate() {
unsafe {
item.set_index(shifted + offset);
}
}
}
pub fn erase(&mut self, index: usize) -> T {
assert!(self.range.len() > index, "index out of bounds");
self.range.end -= 1;
self.groups[self.group].shrink(1);
let mut removed = self.items.remove(self.range.start + index);
{
removed.unlink();
}
let next_group = self.group + 1;
if next_group < self.groups.len() {
for group in self.groups[next_group..].iter_mut() {
group.shift_start(-1);
}
}
let next_item = index;
if next_item < self.items.len() {
for (offset, item) in self.items[next_item..].iter_mut().enumerate() {
unsafe {
item.set_index(next_item + offset);
}
}
}
removed
}
pub fn pop(&mut self) -> Option<T> {
if self.range.is_empty() {
return None;
}
let index = self.range.end - 1;
self.range.end = index;
self.groups[self.group].shrink(1);
let mut removed = self.items.remove(index);
{
removed.unlink();
}
let next_group = self.group + 1;
if next_group < self.groups.len() {
for group in self.groups[next_group..].iter_mut() {
group.shift_start(-1);
}
}
let next_item = index;
if next_item < self.items.len() {
for (offset, item) in self.items[next_item..].iter_mut().enumerate() {
unsafe {
item.set_index(next_item + offset);
}
}
}
Some(removed)
}
}
impl<T: StorableEntity + Copy, const INLINE: usize> EntityRangeMut<'_, T, INLINE> {
pub fn clear(&mut self) {
if self.range.is_empty() {
return;
}
let total_len = self.items.len();
let len = self.range.len();
let end = self.range.end;
self.range.end = self.range.start;
self.groups[self.group].shrink(len);
for item in self.items[self.range.start..end].iter_mut() {
item.unlink();
}
let trailing_items = total_len - end;
if trailing_items > 0 {
self.items.copy_within(end.., self.range.start);
self.items.truncate(self.range.start + trailing_items);
} else {
self.items.truncate(self.range.start);
}
let next_group = self.group + 1;
if next_group < self.groups.len() {
let shift = -(len as isize);
for group in self.groups[next_group..].iter_mut() {
group.shift_start(shift);
}
}
if trailing_items > 0 {
for (offset, item) in self.items[self.range.start..(self.range.start + trailing_items)]
.iter_mut()
.enumerate()
{
unsafe {
item.set_index(self.range.start + offset);
}
}
}
}
pub fn take(&mut self) -> SmallVec<[T; INLINE]> {
let mut taken = SmallVec::<[T; INLINE]>::with_capacity(self.len());
if self.range.is_empty() {
return taken;
}
let total_len = self.items.len();
let len = self.range.len();
let end = self.range.end;
self.range.end = self.range.start;
self.groups[self.group].shrink(len);
taken.extend_from_slice(&self.items[self.range.start..end]);
let trailing_items = total_len - end;
if trailing_items > 0 {
self.items.copy_within(end.., self.range.start);
self.items.truncate(self.range.start + trailing_items);
} else {
self.items.truncate(self.range.start);
}
let next_group = self.group + 1;
if next_group < self.groups.len() {
let shift = -(len as isize);
for group in self.groups[next_group..].iter_mut() {
group.shift_start(shift);
}
}
if trailing_items > 0 {
for (offset, item) in self.items[self.range.start..(self.range.start + trailing_items)]
.iter_mut()
.enumerate()
{
unsafe {
item.set_index(self.range.start + offset);
}
}
}
taken
}
}
impl<T, const INLINE: usize> core::ops::Index<usize> for EntityRangeMut<'_, T, INLINE> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.as_slice()[index]
}
}
impl<T, const INLINE: usize> core::ops::IndexMut<usize> for EntityRangeMut<'_, T, INLINE> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.as_slice_mut()[index]
}
}
impl<'a, T> IntoIterator for EntityRangeMut<'a, T> {
type IntoIter = core::slice::IterMut<'a, T>;
type Item = &'a mut T;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.items[self.range.start..self.range.end].iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Item {
index: usize,
value: usize,
linked: bool,
}
impl Item {
pub fn new(value: usize) -> Self {
Self {
index: 0,
value,
linked: true,
}
}
}
impl StorableEntity for Item {
fn index(&self) -> usize {
self.index
}
unsafe fn set_index(&mut self, index: usize) {
self.index = index;
}
fn unlink(&mut self) {
self.linked = false;
}
}
type ItemStorage = EntityStorage<Item, 1>;
#[allow(unused)]
type ItemRange<'a> = EntityRange<'a, Item>;
#[allow(unused)]
type ItemRangeMut<'a> = EntityRangeMut<'a, Item, 1>;
#[test]
fn entity_storage_empty_operations() {
let mut storage = ItemStorage::default();
assert_eq!(storage.len(), 0);
assert!(storage.is_empty());
assert_eq!(storage.num_groups(), 1);
{
let range = storage.all();
assert_eq!(range.len(), 0);
assert!(range.is_empty());
assert_eq!(range.as_slice(), &[]);
assert_eq!(range.iter().next(), None);
}
let group = storage.push_group(None);
assert_eq!(group, 1);
assert_eq!(storage.num_groups(), 2);
assert_eq!(storage.len(), 0);
assert!(storage.is_empty());
{
let range = storage.group(0);
assert_eq!(range.len(), 0);
assert!(range.is_empty());
assert_eq!(range.as_slice(), &[]);
assert_eq!(range.iter().next(), None);
}
}
#[test]
fn entity_storage_push_to_empty_group_entity_range() {
let mut storage = ItemStorage::default();
let mut group_range = storage.group_mut(0);
assert_eq!(group_range.len(), 0);
assert!(group_range.is_empty());
assert_eq!(group_range.as_slice(), &[]);
assert_eq!(group_range.iter().next(), None);
group_range.push(Item::new(0));
group_range.push(Item::new(1));
assert_eq!(group_range[0].value, 0);
assert!(group_range[0].linked);
assert_eq!(group_range[1].value, 1);
assert!(group_range[1].linked);
assert_eq!(group_range.len(), 2);
assert!(!group_range.is_empty());
assert_eq!(
group_range.as_slice(),
&[
Item {
index: 0,
value: 0,
linked: true
},
Item {
index: 1,
value: 1,
linked: true
}
]
);
assert_eq!(
group_range.iter().next(),
Some(&Item {
index: 0,
value: 0,
linked: true
})
);
}
#[test]
fn entity_storage_extend_empty_group_entity_range() {
let mut storage = ItemStorage::default();
storage.push_to_group(0, Item::new(0));
let group_id = storage.push_group(None);
let mut group_range = storage.group_mut(group_id);
group_range.extend([Item::new(1), Item::new(2)]);
assert_eq!(group_range.len(), 2);
assert_eq!(group_range.range().start, 1);
assert_eq!(group_range.range().end, 3);
assert_eq!(
group_range.as_slice(),
&[
Item {
index: 1,
value: 1,
linked: true
},
Item {
index: 2,
value: 2,
linked: true
}
]
);
assert_eq!(
group_range.iter().next(),
Some(&Item {
index: 1,
value: 1,
linked: true
})
);
assert_eq!(group_range[0].value, 1);
assert!(group_range[0].linked);
assert_eq!(group_range[1].value, 2);
assert!(group_range[1].linked);
}
#[test]
fn entity_storage_pop_from_non_empty_group_entity_range() {
let mut storage = ItemStorage::default();
assert_eq!(storage.num_groups(), 1);
storage.push_to_group(0, Item::new(0));
assert_eq!(storage.len(), 1);
assert!(!storage.is_empty());
let mut group_range = storage.group_mut(0);
assert_eq!(group_range.len(), 1);
assert!(!group_range.is_empty());
assert_eq!(
group_range.as_slice(),
&[Item {
index: 0,
value: 0,
linked: true
}]
);
assert_eq!(
group_range.iter().next(),
Some(&Item {
index: 0,
value: 0,
linked: true
})
);
let item = group_range.pop();
assert_eq!(
item,
Some(Item {
index: 0,
value: 0,
linked: false
})
);
assert_eq!(group_range.len(), 0);
assert!(group_range.is_empty());
assert_eq!(group_range.as_slice(), &[]);
assert_eq!(group_range.iter().next(), None);
assert_eq!(group_range.range.clone(), 0..0);
let item = group_range.pop();
assert_eq!(item, None);
assert_eq!(group_range.len(), 0);
assert!(group_range.is_empty());
assert_eq!(group_range.as_slice(), &[]);
assert_eq!(group_range.iter().next(), None);
assert_eq!(group_range.range.clone(), 0..0);
}
#[test]
fn entity_storage_push_to_empty_group_entity_range_before_other_groups() {
let mut storage = ItemStorage::default();
storage.extend_group(0, [Item::new(0), Item::new(1)]);
let group1 = storage.push_group(None);
let group2 = storage.push_group(None);
let group3 = storage.push_group([Item::new(4), Item::new(5)]);
assert!(!storage.is_empty());
assert_eq!(storage.len(), 4);
assert_eq!(storage.num_groups(), 4);
assert_eq!(storage.group(0).range.clone(), 0..2);
assert_eq!(storage.group(1).range.clone(), 2..2);
assert_eq!(storage.group(2).range.clone(), 2..2);
assert_eq!(storage.group(3).range.clone(), 2..4);
{
let mut group_range = storage.group_mut(group1);
assert_eq!(group_range.len(), 0);
assert!(group_range.is_empty());
assert_eq!(group_range.as_slice(), &[]);
assert_eq!(group_range.iter().next(), None);
group_range.push(Item::new(2));
group_range.push(Item::new(3));
assert_eq!(group_range.len(), 2);
assert!(!group_range.is_empty());
assert_eq!(
group_range.as_slice(),
&[
Item {
index: 2,
value: 2,
linked: true
},
Item {
index: 3,
value: 3,
linked: true
}
]
);
assert_eq!(
group_range.iter().next(),
Some(&Item {
index: 2,
value: 2,
linked: true
})
);
}
let group_range = storage.group(group2);
assert_eq!(group_range.range.clone(), 4..4);
assert_eq!(group_range.len(), 0);
assert!(group_range.is_empty());
assert_eq!(group_range.as_slice(), &[]);
assert_eq!(group_range.iter().next(), None);
let group_range = storage.group(group3);
assert_eq!(group_range.range.clone(), 4..6);
assert_eq!(group_range.len(), 2);
assert!(!group_range.is_empty());
assert_eq!(
group_range.as_slice(),
&[
Item {
index: 4,
value: 4,
linked: true
},
Item {
index: 5,
value: 5,
linked: true
}
]
);
assert_eq!(
group_range.iter().next(),
Some(&Item {
index: 4,
value: 4,
linked: true
})
);
}
#[test]
fn entity_storage_pop_from_non_empty_group_entity_range_before_other_groups() {
let mut storage = ItemStorage::default();
storage.extend_group(0, [Item::new(0), Item::new(1)]);
let group1 = storage.push_group(None);
let group2 = storage.push_group(None);
let group3 = storage.push_group([Item::new(4), Item::new(5)]);
assert!(!storage.is_empty());
assert_eq!(storage.len(), 4);
assert_eq!(storage.num_groups(), 4);
assert_eq!(storage.group(0).range.clone(), 0..2);
assert_eq!(storage.group(1).range.clone(), 2..2);
assert_eq!(storage.group(2).range.clone(), 2..2);
assert_eq!(storage.group(3).range.clone(), 2..4);
{
let mut group_range = storage.group_mut(0);
let item = group_range.pop();
assert_eq!(
item,
Some(Item {
index: 1,
value: 1,
linked: false
})
);
assert_eq!(group_range.len(), 1);
assert!(!group_range.is_empty());
assert_eq!(
group_range.as_slice(),
&[Item {
index: 0,
value: 0,
linked: true
}]
);
}
for group_index in [group1, group2] {
let group_range = storage.group(group_index);
assert_eq!(group_range.range.clone(), 1..1);
assert_eq!(group_range.len(), 0);
assert!(group_range.is_empty());
assert_eq!(group_range.as_slice(), &[]);
assert_eq!(group_range.iter().next(), None);
}
let group_range = storage.group(group3);
assert_eq!(group_range.range.clone(), 1..3);
assert_eq!(group_range.len(), 2);
assert!(!group_range.is_empty());
assert_eq!(
group_range.as_slice(),
&[
Item {
index: 1,
value: 4,
linked: true
},
Item {
index: 2,
value: 5,
linked: true
}
]
);
assert_eq!(
group_range.iter().next(),
Some(&Item {
index: 1,
value: 4,
linked: true
})
);
}
}