1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #pragma once |
4 | |
5 | #include "Prerequisites/BsPrerequisitesUtil.h" |
6 | |
7 | namespace bs |
8 | { |
9 | struct SerializationContext; |
10 | |
11 | /** @addtogroup Internal-Utility |
12 | * @{ |
13 | */ |
14 | |
15 | /** @addtogroup Serialization-Internal |
16 | * @{ |
17 | */ |
18 | |
19 | /** |
20 | * Represents an interface RTTI objects need to implement if they want to provide custom "diff" generation and applying. |
21 | */ |
22 | class BS_UTILITY_EXPORT IDiff |
23 | { |
24 | public: |
25 | virtual ~IDiff() = default; |
26 | |
27 | /** |
28 | * Generates per-field differences between the provided original and new object. Any field or array entry that is |
29 | * different in the new object compared to the original will be output in the resulting object, with a full |
30 | * hierarchy of that field. |
31 | * |
32 | * Will return null if there is no difference. |
33 | */ |
34 | SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, const SPtr<SerializedObject>& newObj); |
35 | |
36 | /** |
37 | * Applies a previously generated per-field differences to the provided object. This will essentially transform the |
38 | * original object the differences were generated for into the modified version. |
39 | */ |
40 | void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, SerializationContext* context); |
41 | |
42 | protected: |
43 | typedef UnorderedMap<SPtr<SerializedObject>, SPtr<SerializedObject>> ObjectMap; |
44 | typedef UnorderedMap<SPtr<SerializedObject>, SPtr<IReflectable>> DiffObjectMap; |
45 | |
46 | /** Types of commands that are used when applying difference field values. */ |
47 | enum DiffCommandType |
48 | { |
49 | Diff_Plain = 0x01, |
50 | Diff_Reflectable = 0x02, |
51 | Diff_ReflectablePtr = 0x03, |
52 | Diff_DataBlock = 0x04, |
53 | Diff_ArraySize = 0x05, |
54 | Diff_ObjectStart = 0x06, |
55 | Diff_ObjectEnd = 0x07, |
56 | Diff_SubObjectStart = 0x08, |
57 | Diff_ArrayFlag = 0x10 |
58 | }; |
59 | |
60 | /** |
61 | * A command that is used for delaying writing to an object, it contains all necessary information for setting RTTI |
62 | * field values on an object. |
63 | */ |
64 | struct DiffCommand |
65 | { |
66 | RTTIField* field; |
67 | UINT32 type; |
68 | SPtr<IReflectable> object; |
69 | UINT8* value; |
70 | SPtr<DataStream> streamValue; |
71 | UINT32 size; |
72 | |
73 | union |
74 | { |
75 | UINT32 arrayIdx; |
76 | UINT32 arraySize; |
77 | RTTITypeBase* rttiType; |
78 | }; |
79 | }; |
80 | |
81 | /** |
82 | * Recursive version of generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&). |
83 | * |
84 | * @see generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&) |
85 | */ |
86 | virtual SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, |
87 | const SPtr<SerializedObject>& newObj, ObjectMap& objectMap) = 0; |
88 | |
89 | /** |
90 | * Generates a difference between data of a specific field type indiscriminately of the specific field type. |
91 | * |
92 | * @see generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&) |
93 | */ |
94 | SPtr<SerializedInstance> generateDiff(RTTITypeBase* rtti, UINT32 fieldType, const SPtr<SerializedInstance>& orgData, |
95 | const SPtr<SerializedInstance>& newData, ObjectMap& objectMap); |
96 | |
97 | /** |
98 | * Recursive version of applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff). Outputs a |
99 | * set of commands that then must be executed in order to actually apply the difference to the provided object. |
100 | * |
101 | * @see applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff) |
102 | */ |
103 | virtual void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, FrameAlloc& alloc, |
104 | DiffObjectMap& objectMap, FrameVector<DiffCommand>& diffCommands, SerializationContext* context) = 0; |
105 | |
106 | /** |
107 | * Applies diff according to the diff handler retrieved from the provided RTTI object. |
108 | * |
109 | * @see applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff) |
110 | */ |
111 | void applyDiff(RTTITypeBase* rtti, const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, |
112 | FrameAlloc& alloc, DiffObjectMap& objectMap, FrameVector<DiffCommand>& diffCommands, |
113 | SerializationContext* context); |
114 | }; |
115 | |
116 | /** |
117 | * Generates and applies "diffs". Diffs contain per-field differences between an original and new object. These |
118 | * differences can be saved and then applied to an original object to transform it to the new version. |
119 | * |
120 | * @note Objects must be in intermediate serialized format generated by IntermediateSerializer. |
121 | */ |
122 | class BS_UTILITY_EXPORT BinaryDiff : public IDiff |
123 | { |
124 | public: |
125 | using IDiff::generateDiff; |
126 | |
127 | private: |
128 | /** @copydoc IDiff::generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&, ObjectMap&) */ |
129 | SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, const SPtr<SerializedObject>& newObj, |
130 | ObjectMap& objectMap) override; |
131 | |
132 | |
133 | /** |
134 | * Generates a diff between two values of a single field. |
135 | * |
136 | * @param[in] field Field the values belong to. |
137 | * @param[in] rtti RTTI type owning the field. |
138 | * @param[in] oldData Old value of the field. |
139 | * @param[in] newData New value of the field. |
140 | * @param[in] objectMap A map that can be used for determining if an object way previously referenced so we |
141 | * don't generate a separate diff. |
142 | * @param[out] modification Object containing the diff value, if there is a difference. |
143 | * @return True if a difference is detected, false otherwise. |
144 | */ |
145 | bool generateDiff(const RTTIField* field, RTTITypeBase* rtti, const SPtr<SerializedInstance>& oldData, |
146 | const SPtr<SerializedInstance>& newData, ObjectMap& objectMap, |
147 | SPtr<SerializedInstance>& modification); |
148 | |
149 | /** @copydoc IDiff::applyDiff(const SPtr<IReflectable>&, const SPtr<SerializedObject>&, FrameAlloc&, DiffObjectMap&, FrameVector<DiffCommand>&) */ |
150 | void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, FrameAlloc& alloc, |
151 | DiffObjectMap& objectMap, FrameVector<DiffCommand>& diffCommands, SerializationContext* context) override; |
152 | }; |
153 | |
154 | /** @} */ |
155 | /** @} */ |
156 | } |
157 | |