bevy_reflect: Fix deserialization with readers (#6894)
# Objective Fixes #6891 ## Solution Replaces deserializing map keys as `&str` with deserializing them as `String`. This bug seems to occur when using something like `File` or `BufReader` rather than bytes or a string directly (I only tested `File` and `BufReader` for `rmp-serde` and `serde_json`). This might be an issue with other `Read` impls as well (except `&[u8]` it seems). We already had passing tests for Message Pack but none that use a `File` or `BufReader`. This PR also adds or modifies tests to check for this in the future. This change was also based on [feedback](https://github.com/bevyengine/bevy/pull/4561#discussion_r957385136) I received in a previous PR. --- ## Changelog - Fix bug where scene deserialization using certain readers could fail (e.g. `BufReader`, `File`, etc.)
This commit is contained in:
parent
8d19045d2f
commit
717def2ccf
@ -29,7 +29,7 @@ downcast-rs = "1.2"
|
|||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
once_cell = "1.11"
|
once_cell = "1.11"
|
||||||
serde = "1"
|
serde = { version = "1", features = ["derive"] }
|
||||||
smallvec = { version = "1.6", features = ["serde", "union", "const_generics"], optional = true }
|
smallvec = { version = "1.6", features = ["serde", "union", "const_generics"], optional = true }
|
||||||
glam = { version = "0.22", features = ["serde"], optional = true }
|
glam = { version = "0.22", features = ["serde"], optional = true }
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ use serde::de::{
|
|||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
@ -210,6 +211,13 @@ impl<'de> Visitor<'de> for U32Visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper struct for deserializing strings without allocating (when possible).
|
||||||
|
///
|
||||||
|
/// Based on [this comment](https://github.com/bevyengine/bevy/pull/6894#discussion_r1045069010).
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct BorrowableCowStr<'a>(#[serde(borrow)] Cow<'a, str>);
|
||||||
|
|
||||||
/// A general purpose deserializer for reflected types.
|
/// A general purpose deserializer for reflected types.
|
||||||
///
|
///
|
||||||
/// This will return a [`Box<dyn Reflect>`] containing the deserialized data.
|
/// This will return a [`Box<dyn Reflect>`] containing the deserialized data.
|
||||||
@ -273,8 +281,9 @@ impl<'a, 'de> Visitor<'de> for UntypedReflectDeserializerVisitor<'a> {
|
|||||||
A: MapAccess<'de>,
|
A: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let type_name = map
|
let type_name = map
|
||||||
.next_key::<String>()?
|
.next_key::<BorrowableCowStr>()?
|
||||||
.ok_or_else(|| Error::invalid_length(0, &"at least one entry"))?;
|
.ok_or_else(|| Error::invalid_length(0, &"at least one entry"))?
|
||||||
|
.0;
|
||||||
|
|
||||||
let registration = self.registry.get_with_name(&type_name).ok_or_else(|| {
|
let registration = self.registry.get_with_name(&type_name).ok_or_else(|| {
|
||||||
Error::custom(format_args!("No registration found for `{type_name}`"))
|
Error::custom(format_args!("No registration found for `{type_name}`"))
|
||||||
@ -1536,10 +1545,11 @@ mod tests {
|
|||||||
108, 101, 144, 146, 100, 145, 101,
|
108, 101, 144, 146, 100, 145, 101,
|
||||||
];
|
];
|
||||||
|
|
||||||
let deserializer = UntypedReflectDeserializer::new(®istry);
|
let mut reader = std::io::BufReader::new(input.as_slice());
|
||||||
|
|
||||||
|
let deserializer = UntypedReflectDeserializer::new(®istry);
|
||||||
let dynamic_output = deserializer
|
let dynamic_output = deserializer
|
||||||
.deserialize(&mut rmp_serde::Deserializer::new(input.as_slice()))
|
.deserialize(&mut rmp_serde::Deserializer::new(&mut reader))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let output = <MyStruct as FromReflect>::from_reflect(dynamic_output.as_ref()).unwrap();
|
let output = <MyStruct as FromReflect>::from_reflect(dynamic_output.as_ref()).unwrap();
|
||||||
|
@ -34,3 +34,4 @@ thiserror = "1.0"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
postcard = { version = "1.0", features = ["alloc"] }
|
postcard = { version = "1.0", features = ["alloc"] }
|
||||||
bincode = "1.3"
|
bincode = "1.3"
|
||||||
|
rmp-serde = "1.1"
|
||||||
|
@ -9,6 +9,7 @@ use serde::{
|
|||||||
ser::SerializeStruct,
|
ser::SerializeStruct,
|
||||||
Deserialize, Deserializer, Serialize, Serializer,
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
};
|
};
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
pub const SCENE_STRUCT: &str = "Scene";
|
pub const SCENE_STRUCT: &str = "Scene";
|
||||||
@ -352,14 +353,14 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
|
|||||||
{
|
{
|
||||||
let mut added = HashSet::new();
|
let mut added = HashSet::new();
|
||||||
let mut components = Vec::new();
|
let mut components = Vec::new();
|
||||||
while let Some(key) = map.next_key::<&str>()? {
|
while let Some(BorrowableCowStr(key)) = map.next_key()? {
|
||||||
if !added.insert(key) {
|
if !added.insert(key.clone()) {
|
||||||
return Err(Error::custom(format!("duplicate component: `{key}`")));
|
return Err(Error::custom(format!("duplicate component: `{key}`")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let registration = self
|
let registration = self
|
||||||
.registry
|
.registry
|
||||||
.get_with_name(key)
|
.get_with_name(&key)
|
||||||
.ok_or_else(|| Error::custom(format!("no registration found for `{key}`")))?;
|
.ok_or_else(|| Error::custom(format!("no registration found for `{key}`")))?;
|
||||||
components.push(
|
components.push(
|
||||||
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?,
|
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?,
|
||||||
@ -384,6 +385,13 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper struct for deserializing strings without allocating (when possible).
|
||||||
|
///
|
||||||
|
/// Based on [this comment](https://github.com/bevyengine/bevy/pull/6894#discussion_r1045069010).
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct BorrowableCowStr<'a>(#[serde(borrow)] Cow<'a, str>);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::serde::{SceneDeserializer, SceneSerializer};
|
use crate::serde::{SceneDeserializer, SceneSerializer};
|
||||||
@ -394,6 +402,8 @@ mod tests {
|
|||||||
use bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
|
use bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
|
||||||
use bincode::Options;
|
use bincode::Options;
|
||||||
use serde::de::DeserializeSeed;
|
use serde::de::DeserializeSeed;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default)]
|
#[derive(Component, Reflect, Default)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
@ -567,6 +577,49 @@ mod tests {
|
|||||||
assert_scene_eq(&scene, &deserialized_scene);
|
assert_scene_eq(&scene, &deserialized_scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_roundtrip_messagepack() {
|
||||||
|
let mut world = create_world();
|
||||||
|
|
||||||
|
world.spawn(MyComponent {
|
||||||
|
foo: [1, 2, 3],
|
||||||
|
bar: (1.3, 3.7),
|
||||||
|
baz: MyEnum::Tuple("Hello World!".to_string()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let registry = world.resource::<AppTypeRegistry>();
|
||||||
|
|
||||||
|
let scene = DynamicScene::from_world(&world, registry);
|
||||||
|
|
||||||
|
let scene_serializer = SceneSerializer::new(&scene, ®istry.0);
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut ser = rmp_serde::Serializer::new(&mut buf);
|
||||||
|
scene_serializer.serialize(&mut ser).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
145, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58,
|
||||||
|
58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67,
|
||||||
|
111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166, 102,
|
||||||
|
102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101, 108,
|
||||||
|
108, 111, 32, 87, 111, 114, 108, 100, 33
|
||||||
|
],
|
||||||
|
buf
|
||||||
|
);
|
||||||
|
|
||||||
|
let scene_deserializer = SceneDeserializer {
|
||||||
|
type_registry: ®istry.0.read(),
|
||||||
|
};
|
||||||
|
let mut reader = BufReader::new(buf.as_slice());
|
||||||
|
|
||||||
|
let deserialized_scene = scene_deserializer
|
||||||
|
.deserialize(&mut rmp_serde::Deserializer::new(&mut reader))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(1, deserialized_scene.entities.len());
|
||||||
|
assert_scene_eq(&scene, &deserialized_scene);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_roundtrip_bincode() {
|
fn should_roundtrip_bincode() {
|
||||||
let mut world = create_world();
|
let mut world = create_world();
|
||||||
|
Loading…
Reference in New Issue
Block a user