-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Reflect Diff-ing #944
Reflect Diff-ing #944
Conversation
(2) feels "better" to me, but it does also significantly complicate the serializer / deserializer situation because they need to know how to deserialize diffs (and distinguish them from other types, while still encoding the original type) |
In (1) you could also distinguish two modes of operation like Option (2) is more powerful but also harder (esp. for lists). enum ElementDiff<T> {
Added(T),
Removed,
Updated(T),
// NoDifference, // maybe for completeness
} I think diffing lists generally has a hard computational complexity. The naive algorithm would be to treat lists as You can also represent the ListDiff as a sequence of update instructions. I think that is what serde-diff does for lists and other types as well. (Approach B) As a combination of the two approaches, I'm imagining a greedy algorithm that finds maximal sub-lists where it can use Approach A and cuts them out into nodes of a Rope-like tree. On the parent-level Rope it would use Approach B. These two parts would work in mutual recursion. The resulting ListDiff is a tree. This idea is probably not novel and there is literature on generic binary diffing that you can use for reference. As for tuples, it depends on the size of the data whether treating them as maps or opaquely (or hybrid) is more efficient. I don't know enough rust but I imagine if "sizeof" is available in the deriving macro you could even do optimized code generation. |
I'm going to hold off on wrapping this up / merging it until I start working on nested scenes (which was the primary motivator for this feature). That was what I was planning on doing next, but I think my focus is better spent elsewhere. Nested scenes really start getting valuable / desirable when we have an editor, so I'd rather build features like this within the context of a real bevy editor / let that inform the design. |
closing this as its not particularly novel, the changes are small, and the conflicts are big enough that adapting this would be hard. If we decide we need this, starting from scratch is probably the move. |
This enables "diff-ing"
Reflect
values. The general idea is that you can do:A "rule" that should always hold true for diffs is: for a given
let diff = a.diff(&b)
, callinga.apply(&diff)
should produce a value equivalent tob
Because of this, this implementation currently has one big caveat: we only return a real "diff" for
Struct
types. Every other type is treated as a "value type". They will return the "full" value if there is any mismatch.Map
: we can't storeMap
diffs in DynamicMap because it doesn't encode "removed values". If there is any difference, the diff will contain the "full"Map
value.apply
.List
: we can't storeList
diffs in DynamicList because it doesn't encode "removed values" or allow for sparse indexing. The diff will contain the "full"List
value.apply
.TupleStruct
: DynamicTupleStruct doesn't support sparse indexing so we must include every field.I'm considering a couple of options for the cases above: