Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - bevy_reflect: Fix deserialization with readers #6894

Closed
wants to merge 4 commits into from

Conversation

MrGVSV
Copy link
Member

@MrGVSV MrGVSV commented Dec 9, 2022

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 I received in a previous PR.


Changelog

  • Fix bug where scene deserialization using certain readers could fail (e.g. BufReader, File, etc.)

@MrGVSV MrGVSV added C-Bug An unexpected or incorrect behavior A-Reflection Runtime information about types A-Scenes Serialized ECS data stored on the disk labels Dec 9, 2022
crates/bevy_reflect/src/serde/de.rs Outdated Show resolved Hide resolved
crates/bevy_scene/src/serde.rs Outdated Show resolved Hide resolved
@MrGVSV MrGVSV force-pushed the reflect-deserialize-reader branch 2 times, most recently from 2200596 to 4ff041c Compare December 10, 2022 00:16
@@ -273,7 +274,7 @@ impl<'a, 'de> Visitor<'de> for UntypedReflectDeserializerVisitor<'a> {
A: MapAccess<'de>,
{
let type_name = map
.next_key::<String>()?
.next_key::<Cow<'de, str>>()?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK Deserialize for Cow is implemented to always deserialize the owned variant (thus String in our case), see serde-rs/serde#1852
The issue also contains an example for how to make this work, although I think this is simplier:

#[derive(Deserialize)]
#[serde(transparent)]
struct BorrowableCowStr<'a>(
    #[serde(borrow)]
    Cow<'a, str>,
);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh interesting. Maybe I'll give this a shot then? @james7132 any thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unfortunate we have to do that, but if that's what it takes to avoid the allocation, that's OK.

Copy link
Contributor

@SkiFire13 SkiFire13 Dec 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, it only avoids the allocation when deserializing a &'de str is possible.

From a quick look I think it would be possible to remove the allocation even when a shorter-lived &str is given, but that probably requires using next_key_seed and a custom DeserializeSeed struct to directly deserialize a &TypeRegistration (that borrows from the registry ofc) instead of a &str.

But I could try this in a follow up PR, so no need to block this one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think a follow up PR would be good if there's still room to optimize 🙂

/// 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>);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is used in two different crates, should this construct be moved to something like bevy_utils? Or is it fine to keep this duplication since it's only a few lines?

Copy link
Contributor

@soqb soqb Dec 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the implementation is short and niche enough to leave as is in this pr, but we should probably keep an eye on it. three uses is where i'd probably stick it into a shared crate.

edit: although it could be exported from bevy_reflect::de since bevy_scene depends on it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, should this be something that is owned by bevy_reflect::de and depended on by bevy_scene? I feel like bevy_reflect shouldn't really have that responsibility, right?

I can export it if that's preferred.

Copy link
Contributor

@soqb soqb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@MrGVSV MrGVSV added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Dec 27, 2022
@james7132 james7132 added this to the 0.10 milestone Jan 4, 2023
@cart
Copy link
Member

cart commented Jan 4, 2023

bors r+

bors bot pushed a commit that referenced this pull request Jan 4, 2023
# 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.)
@bors
Copy link
Contributor

bors bot commented Jan 4, 2023

Build failed:

@MrGVSV
Copy link
Member Author

MrGVSV commented Jan 4, 2023

Just added the derive feature to the serde dependency. I'm guessing this is needed so we can use the helper attributes with --no-default-features enabled.

@cart
Copy link
Member

cart commented Jan 4, 2023

bors r+

bors bot pushed a commit that referenced this pull request Jan 4, 2023
# 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.)
@bors bors bot changed the title bevy_reflect: Fix deserialization with readers [Merged by Bors] - bevy_reflect: Fix deserialization with readers Jan 4, 2023
@bors bors bot closed this Jan 4, 2023
bors bot pushed a commit that referenced this pull request Jan 9, 2023
# Objective

This a follow-up to #6894, see #6894 (comment)

The goal is to avoid cloning any string when getting a `&TypeRegistration` corresponding to a string which is being deserialized. As a bonus code duplication is also reduced.

## Solution

The manual deserialization of a string and lookup into the type registry has been moved into a separate `TypeRegistrationDeserializer` type, which implements `DeserializeSeed` with a `Visitor` that accepts any string with `visit_str`, even ones that may not live longer than that function call.
`BorrowedStr` has been removed since it's no longer used.

---

## Changelog

- The type `TypeRegistrationDeserializer` has been added, which simplifies getting a `&TypeRegistration` while deserializing a string.
bors bot pushed a commit that referenced this pull request Jan 9, 2023
# Objective

This a follow-up to #6894, see #6894 (comment)

The goal is to avoid cloning any string when getting a `&TypeRegistration` corresponding to a string which is being deserialized. As a bonus code duplication is also reduced.

## Solution

The manual deserialization of a string and lookup into the type registry has been moved into a separate `TypeRegistrationDeserializer` type, which implements `DeserializeSeed` with a `Visitor` that accepts any string with `visit_str`, even ones that may not live longer than that function call.
`BorrowedStr` has been removed since it's no longer used.

---

## Changelog

- The type `TypeRegistrationDeserializer` has been added, which simplifies getting a `&TypeRegistration` while deserializing a string.
bors bot pushed a commit that referenced this pull request Jan 9, 2023
# Objective

This a follow-up to #6894, see #6894 (comment)

The goal is to avoid cloning any string when getting a `&TypeRegistration` corresponding to a string which is being deserialized. As a bonus code duplication is also reduced.

## Solution

The manual deserialization of a string and lookup into the type registry has been moved into a separate `TypeRegistrationDeserializer` type, which implements `DeserializeSeed` with a `Visitor` that accepts any string with `visit_str`, even ones that may not live longer than that function call.
`BorrowedStr` has been removed since it's no longer used.

---

## Changelog

- The type `TypeRegistrationDeserializer` has been added, which simplifies getting a `&TypeRegistration` while deserializing a string.
james7132 pushed a commit to james7132/bevy that referenced this pull request Jan 21, 2023
…ne#7094)

# Objective

This a follow-up to bevyengine#6894, see bevyengine#6894 (comment)

The goal is to avoid cloning any string when getting a `&TypeRegistration` corresponding to a string which is being deserialized. As a bonus code duplication is also reduced.

## Solution

The manual deserialization of a string and lookup into the type registry has been moved into a separate `TypeRegistrationDeserializer` type, which implements `DeserializeSeed` with a `Visitor` that accepts any string with `visit_str`, even ones that may not live longer than that function call.
`BorrowedStr` has been removed since it's no longer used.

---

## Changelog

- The type `TypeRegistrationDeserializer` has been added, which simplifies getting a `&TypeRegistration` while deserializing a string.
alradish pushed a commit to alradish/bevy that referenced this pull request Jan 22, 2023
# Objective

Fixes bevyengine#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](bevyengine#4561 (comment)) I received in a previous PR.

---

## Changelog

- Fix bug where scene deserialization using certain readers could fail (e.g. `BufReader`, `File`, etc.)
alradish pushed a commit to alradish/bevy that referenced this pull request Jan 22, 2023
…ne#7094)

# Objective

This a follow-up to bevyengine#6894, see bevyengine#6894 (comment)

The goal is to avoid cloning any string when getting a `&TypeRegistration` corresponding to a string which is being deserialized. As a bonus code duplication is also reduced.

## Solution

The manual deserialization of a string and lookup into the type registry has been moved into a separate `TypeRegistrationDeserializer` type, which implements `DeserializeSeed` with a `Visitor` that accepts any string with `visit_str`, even ones that may not live longer than that function call.
`BorrowedStr` has been removed since it's no longer used.

---

## Changelog

- The type `TypeRegistrationDeserializer` has been added, which simplifies getting a `&TypeRegistration` while deserializing a string.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

Fixes bevyengine#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](bevyengine#4561 (comment)) I received in a previous PR.

---

## Changelog

- Fix bug where scene deserialization using certain readers could fail (e.g. `BufReader`, `File`, etc.)
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
…ne#7094)

# Objective

This a follow-up to bevyengine#6894, see bevyengine#6894 (comment)

The goal is to avoid cloning any string when getting a `&TypeRegistration` corresponding to a string which is being deserialized. As a bonus code duplication is also reduced.

## Solution

The manual deserialization of a string and lookup into the type registry has been moved into a separate `TypeRegistrationDeserializer` type, which implements `DeserializeSeed` with a `Visitor` that accepts any string with `visit_str`, even ones that may not live longer than that function call.
`BorrowedStr` has been removed since it's no longer used.

---

## Changelog

- The type `TypeRegistrationDeserializer` has been added, which simplifies getting a `&TypeRegistration` while deserializing a string.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Reflection Runtime information about types A-Scenes Serialized ECS data stored on the disk C-Bug An unexpected or incorrect behavior S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Serializing and Deserializing a DynamicScene with a custom serializer fails
5 participants