Skip to content

Commit

Permalink
bevy_reflect: Reflect arrays (bevyengine#4701)
Browse files Browse the repository at this point in the history
# Objective

> ℹ️ **Note**: This is a rebased version of bevyengine#2383. A large portion of it has not been touched (only a few minor changes) so that any additional discussion may happen here. All credit should go to @NathanSWard for their work on the original PR.

- Currently reflection is not supported for arrays.
- Fixes bevyengine#1213

## Solution

* Implement reflection for arrays via the `Array` trait.
* Note, `Array` is different from `List` in the way that you cannot push elements onto an array as they are statically sized.
* Now `List` is defined as a sub-trait of `Array`.

---

## Changelog

* Added the `Array` reflection trait
* Allows arrays up to length 32 to be reflected via the `Array` trait

## Migration Guide

* The `List` trait now has the `Array` supertrait. This means that `clone_dynamic` will need to specify which version to use:
  ```rust
  // Before
  let cloned = my_list.clone_dynamic();
  // After
  let cloned = List::clone_dynamic(&my_list);
  ```
* All implementers of `List` will now need to implement `Array` (this mostly involves moving the existing methods to the `Array` impl)

Co-authored-by: NathanW <nathansward@comcast.net>
Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
  • Loading branch information
3 people authored and ItsDoot committed Feb 1, 2023
1 parent 255e5a5 commit 92d2d3e
Show file tree
Hide file tree
Showing 10 changed files with 650 additions and 84 deletions.
300 changes: 300 additions & 0 deletions crates/bevy_reflect/src/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef};
use serde::ser::SerializeSeq;
use std::{
any::Any,
hash::{Hash, Hasher},
};

/// A static-sized array of [`Reflect`] items.
///
/// This corresponds to types like `[T; N]` (arrays).
///
/// Currently, this only supports arrays of up to 32 items. It can technically
/// contain more than 32, but the blanket [`GetTypeRegistration`] is only
/// implemented up to the 32 item limit due to a [limitation] on `Deserialize`.
///
/// [`GetTypeRegistration`]: crate::GetTypeRegistration
/// [limitation]: https://github.com/serde-rs/serde/issues/1937
pub trait Array: Reflect {
/// Returns a reference to the element at `index`, or `None` if out of bounds.
fn get(&self, index: usize) -> Option<&dyn Reflect>;
/// Returns a mutable reference to the element at `index`, or `None` if out of bounds.
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>;
/// Returns the number of elements in the collection.
fn len(&self) -> usize;
/// Returns `true` if the collection contains no elements.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns an iterator over the collection.
fn iter(&self) -> ArrayIter;

fn clone_dynamic(&self) -> DynamicArray {
DynamicArray {
name: self.type_name().to_string(),
values: self.iter().map(|value| value.clone_value()).collect(),
}
}
}

/// A fixed-size list of reflected values.
///
/// This differs from [`DynamicList`] in that the size of the [`DynamicArray`]
/// is constant, whereas a [`DynamicList`] can have items added and removed.
///
/// This isn't to say that a [`DynamicArray`] is immutable— its items
/// can be mutated— just that the _number_ of items cannot change.
///
/// [`DynamicList`]: crate::DynamicList
pub struct DynamicArray {
pub(crate) name: String,
pub(crate) values: Box<[Box<dyn Reflect>]>,
}

impl DynamicArray {
#[inline]
pub fn new(values: Box<[Box<dyn Reflect>]>) -> Self {
Self {
name: String::default(),
values,
}
}

pub fn from_vec<T: Reflect>(values: Vec<T>) -> Self {
Self {
name: String::default(),
values: values
.into_iter()
.map(|field| Box::new(field) as Box<dyn Reflect>)
.collect::<Vec<_>>()
.into_boxed_slice(),
}
}

#[inline]
pub fn name(&self) -> &str {
&self.name
}

#[inline]
pub fn set_name(&mut self, name: String) {
self.name = name;
}
}

// SAFE: any and any_mut both return self
unsafe impl Reflect for DynamicArray {
#[inline]
fn type_name(&self) -> &str {
self.name.as_str()
}

#[inline]
fn any(&self) -> &dyn Any {
self
}

#[inline]
fn any_mut(&mut self) -> &mut dyn Any {
self
}

#[inline]
fn as_reflect(&self) -> &dyn Reflect {
self
}

#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}

fn apply(&mut self, value: &dyn Reflect) {
array_apply(self, value);
}

#[inline]
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}

#[inline]
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Array(self)
}

#[inline]
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Array(self)
}

#[inline]
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone_dynamic())
}

#[inline]
fn reflect_hash(&self) -> Option<u64> {
array_hash(self)
}

fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
array_partial_eq(self, value)
}

fn serializable(&self) -> Option<Serializable> {
Some(Serializable::Borrowed(self))
}
}

impl Array for DynamicArray {
#[inline]
fn get(&self, index: usize) -> Option<&dyn Reflect> {
self.values.get(index).map(|value| &**value)
}

#[inline]
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
self.values.get_mut(index).map(|value| &mut **value)
}

#[inline]
fn len(&self) -> usize {
self.values.len()
}

#[inline]
fn iter(&self) -> ArrayIter {
ArrayIter {
array: self,
index: 0,
}
}

#[inline]
fn clone_dynamic(&self) -> DynamicArray {
DynamicArray {
name: self.name.clone(),
values: self
.values
.iter()
.map(|value| value.clone_value())
.collect(),
}
}
}

/// An iterator over an [`Array`].
pub struct ArrayIter<'a> {
pub(crate) array: &'a dyn Array,
pub(crate) index: usize,
}

impl<'a> Iterator for ArrayIter<'a> {
type Item = &'a dyn Reflect;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
let value = self.array.get(self.index);
self.index += 1;
value
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.array.len();
(size, Some(size))
}
}

impl<'a> ExactSizeIterator for ArrayIter<'a> {}

impl<'a> serde::Serialize for dyn Array {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
array_serialize(self, serializer)
}
}

impl serde::Serialize for DynamicArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
array_serialize(self, serializer)
}
}

/// Serializes the given [array](Array).
#[inline]
pub fn array_serialize<A: Array + ?Sized, S>(array: &A, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(Some(array.len()))?;
for element in array.iter() {
let serializable = element.serializable().ok_or_else(|| {
serde::ser::Error::custom(format!(
"Type '{}' does not support `Reflect` serialization",
element.type_name()
))
})?;
seq.serialize_element(serializable.borrow())?;
}
seq.end()
}

/// Returns the `u64` hash of the given [array](Array).
#[inline]
pub fn array_hash<A: Array>(array: &A) -> Option<u64> {
let mut hasher = crate::ReflectHasher::default();
std::any::Any::type_id(array).hash(&mut hasher);
array.len().hash(&mut hasher);
for value in array.iter() {
hasher.write_u64(value.reflect_hash()?)
}
Some(hasher.finish())
}

/// Applies the reflected [array](Array) data to the given [array](Array).
///
/// # Panics
///
/// * Panics if the two arrays have differing lengths.
/// * Panics if the reflected value is not a [valid array](ReflectRef::Array).
///
#[inline]
pub fn array_apply<A: Array>(array: &mut A, reflect: &dyn Reflect) {
if let ReflectRef::Array(reflect_array) = reflect.reflect_ref() {
if array.len() != reflect_array.len() {
panic!("Attempted to apply different sized `Array` types.");
}
for (i, value) in reflect_array.iter().enumerate() {
let v = array.get_mut(i).unwrap();
v.apply(value);
}
} else {
panic!("Attempted to apply a non-`Array` type to an `Array` type.");
}
}

/// Compares two [arrays](Array) (one concrete and one reflected) to see if they
/// are equal.
#[inline]
pub fn array_partial_eq<A: Array>(array: &A, reflect: &dyn Reflect) -> Option<bool> {
match reflect.reflect_ref() {
ReflectRef::Array(reflect_array) if reflect_array.len() == array.len() => {
for (a, b) in array.iter().zip(reflect_array.iter()) {
if let Some(false) | None = a.reflect_partial_eq(b) {
return Some(false);
}
}
}
_ => return Some(false),
}

Some(true)
}
37 changes: 22 additions & 15 deletions crates/bevy_reflect/src/impls/smallvec.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use smallvec::{Array, SmallVec};
use smallvec::SmallVec;
use std::any::Any;

use crate::{serde::Serializable, FromReflect, List, ListIter, Reflect, ReflectMut, ReflectRef};
use crate::{
serde::Serializable, Array, ArrayIter, FromReflect, List, Reflect, ReflectMut, ReflectRef,
};

impl<T: Array + Send + Sync + 'static> List for SmallVec<T>
impl<T: smallvec::Array + Send + Sync + 'static> Array for SmallVec<T>
where
T::Item: FromReflect + Clone,
{
Expand All @@ -27,9 +29,21 @@ where
<SmallVec<T>>::len(self)
}

fn iter(&self) -> ArrayIter {
ArrayIter {
array: self,
index: 0,
}
}
}

impl<T: smallvec::Array + Send + Sync + 'static> List for SmallVec<T>
where
T::Item: FromReflect + Clone,
{
fn push(&mut self, value: Box<dyn Reflect>) {
let value = value.take::<T::Item>().unwrap_or_else(|value| {
<T as Array>::Item::from_reflect(&*value).unwrap_or_else(|| {
<T as smallvec::Array>::Item::from_reflect(&*value).unwrap_or_else(|| {
panic!(
"Attempted to push invalid value of type {}.",
value.type_name()
Expand All @@ -38,17 +52,10 @@ where
});
SmallVec::push(self, value);
}

fn iter(&self) -> ListIter {
ListIter {
list: self,
index: 0,
}
}
}

// SAFE: any and any_mut both return self
unsafe impl<T: Array + Send + Sync + 'static> Reflect for SmallVec<T>
unsafe impl<T: smallvec::Array + Send + Sync + 'static> Reflect for SmallVec<T>
where
T::Item: FromReflect + Clone,
{
Expand Down Expand Up @@ -90,7 +97,7 @@ where
}

fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone_dynamic())
Box::new(List::clone_dynamic(self))
}

fn reflect_hash(&self) -> Option<u64> {
Expand All @@ -106,15 +113,15 @@ where
}
}

impl<T: Array + Send + Sync + 'static> FromReflect for SmallVec<T>
impl<T: smallvec::Array + Send + Sync + 'static> FromReflect for SmallVec<T>
where
T::Item: FromReflect + Clone,
{
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
let mut new_list = Self::with_capacity(ref_list.len());
for field in ref_list.iter() {
new_list.push(<T as Array>::Item::from_reflect(field)?);
new_list.push(<T as smallvec::Array>::Item::from_reflect(field)?);
}
Some(new_list)
} else {
Expand Down
Loading

0 comments on commit 92d2d3e

Please sign in to comment.