diff --git a/src/lib.rs b/src/lib.rs index 53c6646e..dd077861 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,8 +97,8 @@ pub use entity_builder::{BuiltEntity, BuiltEntityClone, EntityBuilder, EntityBui pub use entity_ref::{ComponentRef, ComponentRefShared, EntityRef, Ref, RefMut}; pub use query::{ Access, Batch, BatchedIter, Or, PreparedQuery, PreparedQueryBorrow, PreparedQueryIter, - PreparedView, Query, QueryBorrow, QueryIter, QueryMut, QueryShared, Satisfies, View, With, - Without, + PreparedView, Query, QueryBorrow, QueryIter, QueryMut, QueryShared, Satisfies, View, + ViewBorrow, With, Without, }; pub use query_one::QueryOne; pub use take::TakenEntity; diff --git a/src/query.rs b/src/query.rs index 1b470b8c..2f7d93f1 100644 --- a/src/query.rs +++ b/src/query.rs @@ -649,15 +649,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { if self.borrowed { return; } - for x in self.world.archetypes() { - if x.is_empty() { - continue; - } - // TODO: Release prior borrows on failure? - if let Some(state) = Q::Fetch::prepare(x) { - Q::Fetch::borrow(x, state); - } - } + start_borrow::(self.world.archetypes_inner()); self.borrowed = true; } @@ -728,14 +720,7 @@ unsafe impl<'w, Q: Query> Sync for QueryBorrow<'w, Q> where for<'a> Q::Item<'a>: impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> { fn drop(&mut self) { if self.borrowed { - for x in self.world.archetypes() { - if x.is_empty() { - continue; - } - if let Some(state) = Q::Fetch::prepare(x) { - Q::Fetch::release(x, state); - } - } + release_borrow::(self.world.archetypes_inner()); } } } @@ -1347,7 +1332,7 @@ impl<'q, Q: Query> View<'q, Q> { /// /// `'q` must be sufficient to guarantee that `Q` cannot violate borrow safety, either with /// dynamic borrow checks or by representing exclusive access to the `World`. - unsafe fn new(meta: &'q [EntityMeta], archetypes: &'q [Archetype]) -> Self { + pub(crate) unsafe fn new(meta: &'q [EntityMeta], archetypes: &'q [Archetype]) -> Self { let fetch = archetypes .iter() .map(|archetype| { @@ -1656,6 +1641,118 @@ impl<'a, 'q, Q: Query> IntoIterator for &'a mut PreparedView<'q, Q> { } } +/// A borrow of a [`World`](crate::World) sufficient to random-access the results of the query `Q`. +/// +/// Note that borrows are not released until this object is dropped. +/// +/// This struct is a thin wrapper around [`View`]. See it for more documentation. +pub struct ViewBorrow<'w, Q: Query> { + view: View<'w, Q>, +} + +impl<'w, Q: Query> ViewBorrow<'w, Q> { + pub(crate) fn new(world: &'w World) -> Self { + start_borrow::(world.archetypes_inner()); + let view = unsafe { View::::new(world.entities_meta(), world.archetypes_inner()) }; + + Self { view } + } + + /// Retrieve the query results corresponding to `entity` + /// + /// Will yield `None` if the entity does not exist or does not match the query. + /// + /// Does not require exclusive access to the map, but is defined only for queries yielding only shared references. + /// + /// See [`View::get``]. + pub fn get(&self, entity: Entity) -> Option> + where + Q: QueryShared, + { + self.view.get(entity) + } + + /// Retrieve the query results corresponding to `entity` + /// + /// Will yield `None` if the entity does not exist or does not match the query. + /// + /// See [`View::get_mut``]. + pub fn get_mut(&mut self, entity: Entity) -> Option> { + self.view.get_mut(entity) + } + + /// Equivalent to `get(entity).is_some()`, but does not require `Q: QueryShared` + /// + /// See [`View::contains``]. + pub fn contains(&self, entity: Entity) -> bool { + self.view.contains(entity) + } + + /// Like `get_mut`, but allows simultaneous access to multiple entities + /// + /// See [`View::get_unchecked``]. + /// + /// # Safety + /// + /// Must not be invoked while any unique borrow of the fetched components of `entity` is live. + pub unsafe fn get_unchecked(&self, entity: Entity) -> Option> { + self.view.get_unchecked(entity) + } + + /// Like `get_mut`, but allows checked simultaneous access to multiple entities + /// + /// For N > 3, the check for distinct entities will clone the array and take O(N log N) time. + /// + /// See [`View::get_many_mut``]. + /// + /// # Examples + /// + /// ``` + /// # use hecs::World; + /// let mut world = World::new(); + /// + /// let a = world.spawn((1, 1.0)); + /// let b = world.spawn((2, 4.0)); + /// let c = world.spawn((3, 9.0)); + /// + /// let mut view = world.view_mut::<&mut i32>(); + /// let [a, b, c] = view.get_mut_n([a, b, c]); + /// + /// assert_eq!(*a.unwrap(), 1); + /// assert_eq!(*b.unwrap(), 2); + /// assert_eq!(*c.unwrap(), 3); + /// ``` + pub fn get_many_mut( + &mut self, + entities: [Entity; N], + ) -> [Option>; N] { + self.view.get_many_mut(entities) + } + + /// Iterate over all entities satisfying `Q` + /// + /// See [`View::iter_mut``] + pub fn iter_mut(&mut self) -> ViewIter<'_, Q> { + self.view.iter_mut() + } +} + +impl<'w, Q: Query> Drop for ViewBorrow<'w, Q> { + fn drop(&mut self) { + release_borrow::(self.view.archetypes) + } +} + +impl<'a, 'q, Q: Query> IntoIterator for &'a mut ViewBorrow<'q, Q> { + type IntoIter = ViewIter<'a, Q>; + type Item = (Entity, Q::Item<'a>); + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + pub(crate) fn assert_distinct(entities: &[Entity; N]) { match N { 1 => (), @@ -1674,6 +1771,32 @@ pub(crate) fn assert_distinct(entities: &[Entity; N]) { } } } + +/// Start the borrow +fn start_borrow(archetypes: &[Archetype]) { + for x in archetypes { + if x.is_empty() { + continue; + } + // TODO: Release prior borrows on failure? + if let Some(state) = Q::Fetch::prepare(x) { + Q::Fetch::borrow(x, state); + } + } +} + +/// Releases the borrow +fn release_borrow(archetypes: &[Archetype]) { + for x in archetypes { + if x.is_empty() { + continue; + } + if let Some(state) = Q::Fetch::prepare(x) { + Q::Fetch::release(x, state); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/world.rs b/src/world.rs index 8118ab6a..d6e7bc67 100644 --- a/src/world.rs +++ b/src/world.rs @@ -25,7 +25,7 @@ use crate::entities::{Entities, EntityMeta, Location, ReserveEntitiesIterator}; use crate::query::{assert_borrow, assert_distinct}; use crate::{ Bundle, ColumnBatch, ComponentRef, DynamicBundle, Entity, EntityRef, Fetch, MissingComponent, - NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, + NoSuchEntity, Query, QueryBorrow, QueryMut, QueryOne, TakenEntity, View, ViewBorrow, }; /// An unordered collection of entities, each having any number of distinctly typed components @@ -399,6 +399,18 @@ impl World { QueryBorrow::new(self) } + /// Provide random access to any entity for a given Query. + pub fn view(&self) -> ViewBorrow<'_, Q> { + ViewBorrow::new(self) + } + + /// Provide random access to any entity for a given Query on a uniquely + /// borrowed world. Like [`view`](Self::view), but faster because dynamic borrow checks can be skipped. + pub fn view_mut(&mut self) -> View<'_, Q> { + assert_borrow::(); + unsafe { View::::new(self.entities_meta(), self.archetypes_inner()) } + } + /// Query a uniquely borrowed world /// /// Like [`query`](Self::query), but faster because dynamic borrow checks can be skipped. Note diff --git a/tests/tests.rs b/tests/tests.rs index bece118d..9183a746 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -258,6 +258,83 @@ fn random_access_via_view_mut() { assert!(!view.contains(f)); } +#[test] +fn view_borrow_on_world() { + let mut world = World::new(); + let e0 = world.spawn((3, "hello")); + let e1 = world.spawn((6.0, "world")); + let e2 = world.spawn((12,)); + + { + let str_view = world.view::<&&str>(); + + assert_eq!(*str_view.get(e0).unwrap(), "hello"); + assert_eq!(*str_view.get(e1).unwrap(), "world"); + assert_eq!(str_view.get(e2), None); + } + + { + let mut int_view = world.view::<&mut i32>(); + assert_eq!(*int_view.get_mut(e0).unwrap(), 3); + assert_eq!(int_view.get_mut(e1), None); + assert_eq!(*int_view.get_mut(e2).unwrap(), 12); + + // edit some value + *int_view.get_mut(e0).unwrap() = 100; + } + + { + let mut int_str_view = world.view::<(&&str, &mut i32)>(); + let (s, i) = int_str_view.get_mut(e0).unwrap(); + assert_eq!(*s, "hello"); + assert_eq!(*i, 100); + assert_eq!(int_str_view.get_mut(e1), None); + assert_eq!(int_str_view.get_mut(e2), None); + } +} + +#[test] +fn view_mut_on_world() { + let mut world = World::new(); + let e0 = world.spawn((3, "hello")); + let e1 = world.spawn((6.0, "world")); + let e2 = world.spawn((12,)); + + let str_view = world.view_mut::<&&str>(); + assert_eq!(*str_view.get(e0).unwrap(), "hello"); + assert_eq!(*str_view.get(e1).unwrap(), "world"); + assert_eq!(str_view.get(e2), None); + + let mut int_view = world.view_mut::<&mut i32>(); + assert_eq!(*int_view.get_mut(e0).unwrap(), 3); + assert_eq!(int_view.get_mut(e1), None); + assert_eq!(*int_view.get_mut(e2).unwrap(), 12); + + // edit some value + *int_view.get_mut(e0).unwrap() = 100; + + let mut int_str_view = world.view_mut::<(&&str, &mut i32)>(); + let (s, i) = int_str_view.get_mut(e0).unwrap(); + assert_eq!(*s, "hello"); + assert_eq!(*i, 100); + assert_eq!(int_str_view.get_mut(e1), None); + assert_eq!(int_str_view.get_mut(e2), None); +} + +#[should_panic] +#[test] +fn view_mut_panic() { + let mut world = World::new(); + let e = world.spawn(('a',)); + + // we should panic since we have two overlapping views: + let mut first_view = world.view::<&mut char>(); + let mut second_view = world.view::<&mut char>(); + + first_view.get_mut(e).unwrap(); + second_view.get_mut(e).unwrap(); +} + #[test] #[should_panic] fn simultaneous_access_must_be_non_overlapping() {