Skip to content

Commit 717def2

Browse files
committed
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](#4561 (comment)) I received in a previous PR. --- ## Changelog - Fix bug where scene deserialization using certain readers could fail (e.g. `BufReader`, `File`, etc.)
1 parent 8d19045 commit 717def2

File tree

4 files changed

+72
-8
lines changed

4 files changed

+72
-8
lines changed

crates/bevy_reflect/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ downcast-rs = "1.2"
2929
parking_lot = "0.12.1"
3030
thiserror = "1.0"
3131
once_cell = "1.11"
32-
serde = "1"
32+
serde = { version = "1", features = ["derive"] }
3333
smallvec = { version = "1.6", features = ["serde", "union", "const_generics"], optional = true }
3434
glam = { version = "0.22", features = ["serde"], optional = true }
3535

crates/bevy_reflect/src/serde/de.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use serde::de::{
1212
};
1313
use serde::Deserialize;
1414
use std::any::TypeId;
15+
use std::borrow::Cow;
1516
use std::fmt;
1617
use std::fmt::{Debug, Display, Formatter};
1718
use std::slice::Iter;
@@ -210,6 +211,13 @@ impl<'de> Visitor<'de> for U32Visitor {
210211
}
211212
}
212213

214+
/// Helper struct for deserializing strings without allocating (when possible).
215+
///
216+
/// Based on [this comment](https://github.com/bevyengine/bevy/pull/6894#discussion_r1045069010).
217+
#[derive(Deserialize)]
218+
#[serde(transparent)]
219+
struct BorrowableCowStr<'a>(#[serde(borrow)] Cow<'a, str>);
220+
213221
/// A general purpose deserializer for reflected types.
214222
///
215223
/// This will return a [`Box<dyn Reflect>`] containing the deserialized data.
@@ -273,8 +281,9 @@ impl<'a, 'de> Visitor<'de> for UntypedReflectDeserializerVisitor<'a> {
273281
A: MapAccess<'de>,
274282
{
275283
let type_name = map
276-
.next_key::<String>()?
277-
.ok_or_else(|| Error::invalid_length(0, &"at least one entry"))?;
284+
.next_key::<BorrowableCowStr>()?
285+
.ok_or_else(|| Error::invalid_length(0, &"at least one entry"))?
286+
.0;
278287

279288
let registration = self.registry.get_with_name(&type_name).ok_or_else(|| {
280289
Error::custom(format_args!("No registration found for `{type_name}`"))
@@ -1536,10 +1545,11 @@ mod tests {
15361545
108, 101, 144, 146, 100, 145, 101,
15371546
];
15381547

1539-
let deserializer = UntypedReflectDeserializer::new(&registry);
1548+
let mut reader = std::io::BufReader::new(input.as_slice());
15401549

1550+
let deserializer = UntypedReflectDeserializer::new(&registry);
15411551
let dynamic_output = deserializer
1542-
.deserialize(&mut rmp_serde::Deserializer::new(input.as_slice()))
1552+
.deserialize(&mut rmp_serde::Deserializer::new(&mut reader))
15431553
.unwrap();
15441554

15451555
let output = <MyStruct as FromReflect>::from_reflect(dynamic_output.as_ref()).unwrap();

crates/bevy_scene/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ thiserror = "1.0"
3434
[dev-dependencies]
3535
postcard = { version = "1.0", features = ["alloc"] }
3636
bincode = "1.3"
37+
rmp-serde = "1.1"

crates/bevy_scene/src/serde.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use serde::{
99
ser::SerializeStruct,
1010
Deserialize, Deserializer, Serialize, Serializer,
1111
};
12+
use std::borrow::Cow;
1213
use std::fmt::Formatter;
1314

1415
pub const SCENE_STRUCT: &str = "Scene";
@@ -352,14 +353,14 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
352353
{
353354
let mut added = HashSet::new();
354355
let mut components = Vec::new();
355-
while let Some(key) = map.next_key::<&str>()? {
356-
if !added.insert(key) {
356+
while let Some(BorrowableCowStr(key)) = map.next_key()? {
357+
if !added.insert(key.clone()) {
357358
return Err(Error::custom(format!("duplicate component: `{key}`")));
358359
}
359360

360361
let registration = self
361362
.registry
362-
.get_with_name(key)
363+
.get_with_name(&key)
363364
.ok_or_else(|| Error::custom(format!("no registration found for `{key}`")))?;
364365
components.push(
365366
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?,
@@ -384,6 +385,13 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
384385
}
385386
}
386387

388+
/// Helper struct for deserializing strings without allocating (when possible).
389+
///
390+
/// Based on [this comment](https://github.com/bevyengine/bevy/pull/6894#discussion_r1045069010).
391+
#[derive(Deserialize)]
392+
#[serde(transparent)]
393+
struct BorrowableCowStr<'a>(#[serde(borrow)] Cow<'a, str>);
394+
387395
#[cfg(test)]
388396
mod tests {
389397
use crate::serde::{SceneDeserializer, SceneSerializer};
@@ -394,6 +402,8 @@ mod tests {
394402
use bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
395403
use bincode::Options;
396404
use serde::de::DeserializeSeed;
405+
use serde::Serialize;
406+
use std::io::BufReader;
397407

398408
#[derive(Component, Reflect, Default)]
399409
#[reflect(Component)]
@@ -567,6 +577,49 @@ mod tests {
567577
assert_scene_eq(&scene, &deserialized_scene);
568578
}
569579

580+
#[test]
581+
fn should_roundtrip_messagepack() {
582+
let mut world = create_world();
583+
584+
world.spawn(MyComponent {
585+
foo: [1, 2, 3],
586+
bar: (1.3, 3.7),
587+
baz: MyEnum::Tuple("Hello World!".to_string()),
588+
});
589+
590+
let registry = world.resource::<AppTypeRegistry>();
591+
592+
let scene = DynamicScene::from_world(&world, registry);
593+
594+
let scene_serializer = SceneSerializer::new(&scene, &registry.0);
595+
let mut buf = Vec::new();
596+
let mut ser = rmp_serde::Serializer::new(&mut buf);
597+
scene_serializer.serialize(&mut ser).unwrap();
598+
599+
assert_eq!(
600+
vec![
601+
145, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58,
602+
58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67,
603+
111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166, 102,
604+
102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101, 108,
605+
108, 111, 32, 87, 111, 114, 108, 100, 33
606+
],
607+
buf
608+
);
609+
610+
let scene_deserializer = SceneDeserializer {
611+
type_registry: &registry.0.read(),
612+
};
613+
let mut reader = BufReader::new(buf.as_slice());
614+
615+
let deserialized_scene = scene_deserializer
616+
.deserialize(&mut rmp_serde::Deserializer::new(&mut reader))
617+
.unwrap();
618+
619+
assert_eq!(1, deserialized_scene.entities.len());
620+
assert_scene_eq(&scene, &deserialized_scene);
621+
}
622+
570623
#[test]
571624
fn should_roundtrip_bincode() {
572625
let mut world = create_world();

0 commit comments

Comments
 (0)