2424#include " Firestore/core/src/nanopb/fields_array.h"
2525#include " Firestore/core/src/nanopb/message.h"
2626#include " Firestore/core/src/nanopb/nanopb_util.h"
27+ #include " Firestore/core/src/util/hashing.h"
28+ #include " absl/types/span.h"
2729
2830namespace firebase {
2931namespace firestore {
3032namespace model {
3133
3234namespace {
3335
34- using nanopb::FieldsArray ;
36+ using nanopb::CheckedSize ;
3537using nanopb::FreeFieldsArray;
38+ using nanopb::FreeNanopbMessage;
3639using nanopb::MakeArray;
3740using nanopb::MakeBytesArray;
3841using nanopb::MakeString;
3942using nanopb::MakeStringView;
43+ using nanopb::ReleaseFieldOwnership;
44+ using nanopb::SetRepeatedField;
4045
4146struct MapEntryKeyCompare {
4247 bool operator ()(const google_firestore_v1_MapValue_FieldsEntry& entry,
@@ -49,6 +54,28 @@ struct MapEntryKeyCompare {
4954 }
5055};
5156
57+ /* * Traverses a Value proto and sorts all MapValues by key. */
58+ void SortFields (google_firestore_v1_Value& value) {
59+ if (value.which_value_type == google_firestore_v1_Value_map_value_tag) {
60+ google_firestore_v1_MapValue& map_value = value.map_value ;
61+ std::sort (map_value.fields , map_value.fields + map_value.fields_count ,
62+ [](const google_firestore_v1_MapValue_FieldsEntry& lhs,
63+ const google_firestore_v1_MapValue_FieldsEntry& rhs) {
64+ return nanopb::MakeStringView (lhs.key ) <
65+ nanopb::MakeStringView (rhs.key );
66+ });
67+
68+ for (pb_size_t i = 0 ; i < map_value.fields_count ; ++i) {
69+ SortFields (map_value.fields [i].value );
70+ }
71+ } else if (value.which_value_type ==
72+ google_firestore_v1_Value_array_value_tag) {
73+ for (pb_size_t i = 0 ; i < value.array_value .values_count ; ++i) {
74+ SortFields (value.array_value .values [i]);
75+ }
76+ }
77+ }
78+
5279/* *
5380 * Finds an entry by key in the provided map value. Returns `nullptr` if the
5481 * entry does not exist.
@@ -135,6 +162,7 @@ void ApplyChanges(
135162
136163 target_entry.key = source_entry.key ;
137164 target_entry.value = DeepClone (upsert_it->second );
165+ SortFields (target_entry.value );
138166
139167 ++upsert_it;
140168 ++source_index;
@@ -162,22 +190,57 @@ void ApplyChanges(
162190
163191 free (parent->fields );
164192 parent->fields = target_fields;
165- parent->fields_count = target_count;
193+ parent->fields_count = CheckedSize ( target_count) ;
166194}
167195
168196} // namespace
169197
170- MutableObjectValue::MutableObjectValue () {
198+ ObjectValue::ObjectValue () {
171199 value_->which_value_type = google_firestore_v1_Value_map_value_tag;
172200 value_->map_value .fields_count = 0 ;
173201 value_->map_value .fields = nullptr ;
174202}
175203
176- FieldMask MutableObjectValue::ToFieldMask () const {
204+ ObjectValue::ObjectValue (const google_firestore_v1_Value& value)
205+ : value_(value) {
206+ HARD_ASSERT (value.which_value_type == google_firestore_v1_Value_map_value_tag,
207+ " ObjectValues should be backed by a MapValue" );
208+ SortFields (*value_);
209+ }
210+
211+ ObjectValue::ObjectValue (const ObjectValue& other)
212+ : value_(DeepClone(*other.value_)) {
213+ }
214+
215+ ObjectValue ObjectValue::FromMapValue (google_firestore_v1_MapValue map_value) {
216+ google_firestore_v1_Value value{};
217+ value.which_value_type = google_firestore_v1_Value_map_value_tag;
218+ value.map_value = map_value;
219+ return ObjectValue{value};
220+ }
221+
222+ ObjectValue ObjectValue::FromFieldsEntry (
223+ google_firestore_v1_Document_FieldsEntry* fields_entry, pb_size_t count) {
224+ google_firestore_v1_Value value{};
225+ value.which_value_type = google_firestore_v1_Value_map_value_tag;
226+ SetRepeatedField (
227+ &value.map_value .fields , &value.map_value .fields_count ,
228+ absl::Span<google_firestore_v1_Document_FieldsEntry>(fields_entry, count),
229+ [](const google_firestore_v1_Document_FieldsEntry& entry) {
230+ google_firestore_v1_MapValue_FieldsEntry result{};
231+ result.key = entry.key ;
232+ result.value = entry.value ;
233+ return result;
234+ });
235+ ReleaseFieldOwnership (fields_entry, count);
236+ return ObjectValue{value};
237+ }
238+
239+ FieldMask ObjectValue::ToFieldMask () const {
177240 return ExtractFieldMask (value_->map_value );
178241}
179242
180- FieldMask MutableObjectValue ::ExtractFieldMask (
243+ FieldMask ObjectValue ::ExtractFieldMask (
181244 const google_firestore_v1_MapValue& value) const {
182245 std::set<FieldPath> fields;
183246
@@ -206,7 +269,7 @@ FieldMask MutableObjectValue::ExtractFieldMask(
206269 return FieldMask (std::move (fields));
207270}
208271
209- absl::optional<google_firestore_v1_Value> MutableObjectValue ::Get (
272+ absl::optional<google_firestore_v1_Value> ObjectValue ::Get (
210273 const FieldPath& path) const {
211274 if (path.empty ()) {
212275 return *value_;
@@ -222,8 +285,12 @@ absl::optional<google_firestore_v1_Value> MutableObjectValue::Get(
222285 return nested_value;
223286}
224287
225- void MutableObjectValue::Set (const FieldPath& path,
226- const google_firestore_v1_Value& value) {
288+ google_firestore_v1_Value ObjectValue::Get () const {
289+ return *value_;
290+ }
291+
292+ void ObjectValue::Set (const FieldPath& path,
293+ const google_firestore_v1_Value& value) {
227294 HARD_ASSERT (!path.empty (), " Cannot set field for empty path on ObjectValue" );
228295
229296 google_firestore_v1_MapValue* parent_map = ParentMap (path.PopLast ());
@@ -235,8 +302,7 @@ void MutableObjectValue::Set(const FieldPath& path,
235302 ApplyChanges (parent_map, upserts, /* deletes=*/ {});
236303}
237304
238- void MutableObjectValue::SetAll (const FieldMask& field_mask,
239- const MutableObjectValue& data) {
305+ void ObjectValue::SetAll (const FieldMask& field_mask, const ObjectValue& data) {
240306 FieldPath parent;
241307
242308 std::map<std::string, google_firestore_v1_Value> upserts;
@@ -264,7 +330,7 @@ void MutableObjectValue::SetAll(const FieldMask& field_mask,
264330 ApplyChanges (parent_map, upserts, deletes);
265331}
266332
267- void MutableObjectValue ::Delete (const FieldPath& path) {
333+ void ObjectValue ::Delete (const FieldPath& path) {
268334 HARD_ASSERT (!path.empty (), " Cannot delete field with empty path" );
269335
270336 google_firestore_v1_Value* nested_value = value_.get ();
@@ -286,12 +352,19 @@ void MutableObjectValue::Delete(const FieldPath& path) {
286352 }
287353}
288354
355+ std::string ObjectValue::ToString () const {
356+ return CanonicalId (*value_);
357+ }
358+
359+ size_t ObjectValue::Hash () const {
360+ return util::Hash (CanonicalId (*value_));
361+ }
362+
289363/* *
290364 * Returns the map that contains the leaf element of `path`. If the parent
291365 * entry does not yet exist, or if it is not a map, a new map will be created.
292366 */
293- google_firestore_v1_MapValue* MutableObjectValue::ParentMap (
294- const FieldPath& path) {
367+ google_firestore_v1_MapValue* ObjectValue::ParentMap (const FieldPath& path) {
295368 google_firestore_v1_Value* parent = value_.get ();
296369
297370 // Find a or create a parent map entry for `path`.
0 commit comments