use std::marker::PhantomData;
use super::source_iterator::{IndexSetIterator, SourceIterator};
use crate::entity::index::IndexSetResult;
use crate::entity::property_value_store_core::RawPropertyValueVec;
use crate::entity::{ContextEntitiesExt, Entity, EntityId, PopulationIterator};
use crate::hashing::{HashValueType, IndexSet};
use crate::prelude::Property;
use crate::Context;
pub(super) type BxPropertySource<'a, E> = Box<dyn AbstractPropertySource<'a, E> + 'a>;
#[derive(Copy, Clone, Eq, PartialEq)]
pub(crate) struct PropertySourceId {
pub property_id: usize,
pub value_hash: HashValueType,
}
pub(crate) trait AbstractPropertySource<'a, E: Entity>:
Iterator<Item = EntityId<E>>
{
fn id(&self) -> PropertySourceId;
fn clone_box(&self) -> BxPropertySource<'a, E>;
fn contains(&self, entity_id: EntityId<E>) -> bool;
fn sort_key(&self) -> (usize, u8);
}
pub(super) struct DerivedPropertySource<'a, E: Entity, P: Property<E>> {
context: &'a Context,
value: P,
next_index: usize,
population_size: usize,
_phantom: PhantomData<E>,
}
impl<'a, E: Entity, P: Property<E>> DerivedPropertySource<'a, E, P> {
pub fn new(context: &'a Context, value: P) -> Self {
let population_size = context.get_entity_count::<E>();
DerivedPropertySource {
context,
value,
next_index: 0,
population_size,
_phantom: PhantomData,
}
}
}
impl<'a, E: Entity, P: Property<E>> Clone for DerivedPropertySource<'a, E, P> {
fn clone(&self) -> Self {
Self {
context: self.context,
value: self.value,
next_index: self.next_index,
population_size: self.population_size,
_phantom: PhantomData,
}
}
}
impl<'a, E: Entity, P: Property<E>> AbstractPropertySource<'a, E>
for DerivedPropertySource<'a, E, P>
{
fn id(&self) -> PropertySourceId {
PropertySourceId {
property_id: P::index_id(),
value_hash: P::hash_property_value(&self.value.make_canonical()),
}
}
fn contains(&self, entity_id: EntityId<E>) -> bool {
P::compute_derived(self.context, entity_id) == self.value
}
fn clone_box(&self) -> BxPropertySource<'a, E> {
Box::new((*self).clone())
}
fn sort_key(&self) -> (usize, u8) {
(self.context.get_entity_count::<E>(), 6)
}
}
impl<'a, E: Entity, P: Property<E>> Iterator for DerivedPropertySource<'a, E, P> {
type Item = EntityId<E>;
fn next(&mut self) -> Option<Self::Item> {
while self.next_index < self.population_size {
let entity_id = EntityId::<E>::new(self.next_index);
self.next_index += 1;
if P::compute_derived(self.context, entity_id) == self.value {
return Some(entity_id);
}
}
None
}
}
pub(super) struct ConcretePropertySource<'a, E: Entity, P: Property<E>> {
values: &'a RawPropertyValueVec<P>,
value: P,
next_index: usize,
is_default_value: bool,
population_size: usize,
_phantom: PhantomData<E>,
}
impl<'a, E: Entity, P: Property<E>> ConcretePropertySource<'a, E, P> {
pub fn new(values: &'a RawPropertyValueVec<P>, value: P, population_size: usize) -> Self {
let is_default_value = !P::is_required() && P::default_const() == value;
ConcretePropertySource {
values,
value,
next_index: 0,
is_default_value,
population_size,
_phantom: PhantomData,
}
}
}
impl<'a, E: Entity, P: Property<E>> Clone for ConcretePropertySource<'a, E, P> {
fn clone(&self) -> Self {
Self {
values: self.values,
value: self.value,
next_index: self.next_index,
is_default_value: self.is_default_value,
population_size: self.population_size,
_phantom: PhantomData,
}
}
}
impl<'a, E: Entity, P: Property<E>> AbstractPropertySource<'a, E>
for ConcretePropertySource<'a, E, P>
{
fn id(&self) -> PropertySourceId {
PropertySourceId {
property_id: P::index_id(),
value_hash: P::hash_property_value(&self.value.make_canonical()),
}
}
fn contains(&self, person_id: EntityId<E>) -> bool {
if let Some(found_value) = self.values.get(person_id.0) {
found_value == self.value
} else {
self.is_default_value
}
}
fn clone_box(&self) -> BxPropertySource<'a, E> {
Box::new((*self).clone())
}
fn sort_key(&self) -> (usize, u8) {
let upper = if !self.is_default_value {
self.values.len()
} else {
self.population_size
};
(upper, 5)
}
}
impl<'a, E: Entity, P: Property<E>> Iterator for ConcretePropertySource<'a, E, P> {
type Item = EntityId<E>;
fn next(&mut self) -> Option<Self::Item> {
while self.next_index < self.population_size {
self.next_index += 1;
if let Some(found_value) = self.values.get(self.next_index - 1) {
if found_value == self.value {
return Some(EntityId::new(self.next_index - 1));
}
} else {
if self.is_default_value {
return Some(EntityId::new(self.next_index - 1));
} else {
self.next_index = self.population_size;
}
}
}
None
}
}
pub(crate) enum SourceSet<'a, E: Entity> {
Empty,
Population(usize),
#[allow(dead_code)]
Entity(EntityId<E>),
IndexSet(&'a IndexSet<EntityId<E>>),
PropertySet(BxPropertySource<'a, E>),
}
impl<'a, E: Entity> Clone for SourceSet<'a, E> {
fn clone(&self) -> Self {
match self {
Self::Empty => Self::Empty,
Self::Population(population) => Self::Population(*population),
Self::Entity(entity_id) => Self::Entity(*entity_id),
Self::IndexSet(index_set) => Self::IndexSet(index_set),
Self::PropertySet(source) => Self::PropertySet(source.clone_box()),
}
}
}
impl<'a, E: Entity> PartialEq for SourceSet<'a, E> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Empty, Self::Empty) => true,
(Self::Population(left), Self::Population(right)) => left == right,
(Self::Entity(left), Self::Entity(right)) => left == right,
(Self::IndexSet(left), Self::IndexSet(right)) => std::ptr::eq(&**left, &**right),
(Self::PropertySet(left), Self::PropertySet(right)) => left.id() == right.id(),
_ => false,
}
}
}
impl<'a, E: Entity> Eq for SourceSet<'a, E> {}
impl<'a, E: Entity> SourceSet<'a, E> {
pub(super) fn try_len(&self) -> Option<usize> {
match self {
SourceSet::Empty => Some(0),
SourceSet::Population(population) => Some(*population),
SourceSet::Entity(_) => Some(1),
SourceSet::IndexSet(source) => Some(source.len()),
SourceSet::PropertySet(_) => None,
}
}
pub(super) fn sort_key(&self) -> (usize, u8) {
match self {
SourceSet::Empty => (0, 0),
SourceSet::Entity(_) => (1, 1),
SourceSet::Population(population) => (*population, 2),
SourceSet::IndexSet(source) => (source.len(), 3),
SourceSet::PropertySet(source) => source.sort_key(),
}
}
pub(crate) fn new<P: Property<E>>(value: P, context: &'a Context) -> Option<Self> {
let property_store = context.entity_store.get_property_store::<E>();
{
match property_store.get_index_set_with_hash_for_property_id(
P::index_id(),
P::hash_property_value(&value.make_canonical()),
) {
IndexSetResult::Set(entity_set) => {
return Some(SourceSet::IndexSet(entity_set));
}
IndexSetResult::Empty => {
return None;
}
IndexSetResult::Unsupported => {}
}
}
if P::is_derived() {
Some(SourceSet::PropertySet(Box::new(DerivedPropertySource::<
E,
P,
>::new(
context, value
))))
} else {
let property_value_store = property_store.get::<P>();
let values: &'a RawPropertyValueVec<P> = &property_value_store.data;
Some(SourceSet::<'a>::PropertySet(Box::<
ConcretePropertySource<'a, E, P>,
>::new(
ConcretePropertySource::<'a, E, P>::new(
values,
value,
context.get_entity_count::<E>(),
),
)))
}
}
pub(super) fn contains(&self, id: EntityId<E>) -> bool {
match self {
SourceSet::Empty => false,
SourceSet::Population(population) => id.0 < *population,
SourceSet::Entity(entity_id) => *entity_id == id,
SourceSet::IndexSet(source) => source.contains(&id),
SourceSet::PropertySet(source) => source.contains(id),
}
}
pub(super) fn into_iter(self) -> SourceIterator<'a, E> {
match self {
SourceSet::Empty => SourceIterator::Empty,
SourceSet::Population(population) => {
SourceIterator::Population(PopulationIterator::new(population))
}
SourceSet::Entity(entity_id) => SourceIterator::Entity {
id: entity_id,
exhausted: false,
},
SourceSet::IndexSet(ids) => {
SourceIterator::IndexIter(IndexSetIterator::from_index_set(ids))
}
SourceSet::PropertySet(property_vec) => SourceIterator::PropertyVecIter(property_vec),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{define_derived_property, define_entity, define_property};
define_entity!(Person);
define_property!(struct Age(u8), Person, default_const = Age(0));
define_property!(struct Flag(bool), Person, default_const = Flag(false));
define_derived_property!(struct IsAdult(bool), Person, [Age], |age| IsAdult(age.0 >= 18));
#[test]
fn source_set_variants_basic_behavior() {
let empty = SourceSet::<Person>::Empty;
assert_eq!(empty.sort_key(), (0, 0));
assert!(!empty.contains(EntityId::new(0)));
assert_eq!(empty.into_iter().count(), 0);
let population = SourceSet::<Person>::Population(3);
assert_eq!(population.sort_key(), (3, 2));
assert!(population.contains(EntityId::new(0)));
assert!(!population.contains(EntityId::new(3)));
let population_ids = SourceSet::<Person>::Population(3)
.into_iter()
.collect::<Vec<_>>();
assert_eq!(
population_ids,
vec![EntityId::new(0), EntityId::new(1), EntityId::new(2)]
);
let singleton = SourceSet::<Person>::Entity(EntityId::new(9));
assert_eq!(singleton.sort_key(), (1, 1));
assert!(singleton.contains(EntityId::new(9)));
assert!(!singleton.contains(EntityId::new(8)));
let singleton_ids = SourceSet::<Person>::Entity(EntityId::new(9))
.into_iter()
.collect::<Vec<_>>();
assert_eq!(singleton_ids, vec![EntityId::new(9)]);
let ids = [EntityId::new(1), EntityId::new(4), EntityId::new(8)]
.into_iter()
.collect::<IndexSet<_>>();
let ids_ref = &ids;
let indexed = SourceSet::<Person>::IndexSet(ids_ref);
assert_eq!(indexed.sort_key(), (3, 3));
assert!(indexed.contains(EntityId::new(4)));
assert!(!indexed.contains(EntityId::new(2)));
}
#[test]
fn source_set_new_uses_indexed_or_unindexed_backing() {
let mut context = Context::new();
for age in [1u8, 2, 2, 3] {
context.add_entity((Age(age), Flag(true))).unwrap();
}
assert!(matches!(
SourceSet::<Person>::new::<Age>(Age(2), &context).unwrap(),
SourceSet::PropertySet(_)
));
let unindexed_ids = SourceSet::<Person>::new::<Age>(Age(2), &context)
.unwrap()
.into_iter()
.collect::<Vec<_>>();
context.index_property::<Person, Age>();
assert!(matches!(
SourceSet::<Person>::new::<Age>(Age(2), &context).unwrap(),
SourceSet::IndexSet(_)
));
let indexed_ids = SourceSet::<Person>::new::<Age>(Age(2), &context)
.unwrap()
.into_iter()
.collect::<Vec<_>>();
assert_eq!(unindexed_ids, indexed_ids);
}
#[test]
fn source_set_new_derived_vs_nonderived_backing() {
let mut context = Context::new();
context.add_entity((Age(12), Flag(true))).unwrap();
context.add_entity((Age(20), Flag(true))).unwrap();
context.add_entity((Age(44), Flag(false))).unwrap();
let nonderived = SourceSet::<Person>::new::<Age>(Age(20), &context).unwrap();
assert!(matches!(nonderived, SourceSet::PropertySet(_)));
let derived = SourceSet::<Person>::new::<IsAdult>(IsAdult(true), &context).unwrap();
assert!(matches!(derived, SourceSet::PropertySet(_)));
let derived_ids = SourceSet::<Person>::new::<IsAdult>(IsAdult(true), &context)
.unwrap()
.into_iter()
.collect::<Vec<_>>();
assert_eq!(derived_ids, vec![EntityId::new(1), EntityId::new(2)]);
}
}