17
17
#include " Firestore/core/src/model/object_value.h"
18
18
19
19
#include < algorithm>
20
- #include < map>
21
20
#include < set>
22
21
23
22
#include " Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
24
23
#include " Firestore/core/src/nanopb/fields_array.h"
25
24
#include " Firestore/core/src/nanopb/message.h"
26
25
#include " Firestore/core/src/nanopb/nanopb_util.h"
26
+ #include " absl/container/flat_hash_map.h"
27
+ #include " absl/container/flat_hash_set.h"
27
28
28
29
namespace firebase {
29
30
namespace firestore {
@@ -40,11 +41,11 @@ using nanopb::MakeStringView;
40
41
41
42
struct MapEntryKeyCompare {
42
43
bool operator ()(const google_firestore_v1_MapValue_FieldsEntry& entry,
43
- absl::string_view segment) {
44
+ absl::string_view segment) const {
44
45
return nanopb::MakeStringView (entry.key ) < segment;
45
46
}
46
47
bool operator ()(absl::string_view segment,
47
- const google_firestore_v1_MapValue_FieldsEntry& entry) {
48
+ const google_firestore_v1_MapValue_FieldsEntry& entry) const {
48
49
return segment < nanopb::MakeStringView (entry.key );
49
50
}
50
51
};
@@ -53,7 +54,7 @@ struct MapEntryKeyCompare {
53
54
* Finds an entry by key in the provided map value. Returns `nullptr` if the
54
55
* entry does not exist.
55
56
*/
56
- static google_firestore_v1_MapValue_FieldsEntry* FindEntry (
57
+ google_firestore_v1_MapValue_FieldsEntry* FindEntry (
57
58
const google_firestore_v1_Value& value, absl::string_view segment) {
58
59
if (value.which_value_type != google_firestore_v1_Value_map_value_tag) {
59
60
return nullptr ;
@@ -72,6 +73,97 @@ static google_firestore_v1_MapValue_FieldsEntry* FindEntry(
72
73
return found.first ;
73
74
}
74
75
76
+ size_t CalculateSizeOfUnion (
77
+ google_firestore_v1_MapValue* map_value,
78
+ const absl::flat_hash_map<std::string, google_firestore_v1_Value>& upserts,
79
+ const absl::flat_hash_set<std::string>& deletes) {
80
+ // Compute the size of the map after applying all mutations. The final size is
81
+ // the number of existing entries, plus the number of new entries
82
+ // minus the number of deleted entries.
83
+ return upserts.size () +
84
+ std::count_if (
85
+ map_value->fields , map_value->fields + map_value->fields_count ,
86
+ [&](const google_firestore_v1_MapValue_FieldsEntry& entry) {
87
+ absl::string_view field = MakeStringView (entry.key );
88
+ // Don't count if entry is deleted or if it is a replacement
89
+ // rather than an insert.
90
+ return deletes.find (field) == deletes.end () &&
91
+ upserts.find (field) == upserts.end ();
92
+ });
93
+ }
94
+
95
+ /* *
96
+ * Modifies `parent_map` by adding, replacing or deleting the specified
97
+ * entries.
98
+ */
99
+ void ApplyChanges (
100
+ google_firestore_v1_MapValue* parent,
101
+ const absl::flat_hash_map<std::string, google_firestore_v1_Value>& upserts,
102
+ const absl::flat_hash_set<std::string>& deletes) {
103
+ // TODO(mrschmidt): Consider using `absl::btree_map` and `absl::btree_set` for
104
+ // potentially better performance.
105
+ auto source_count = parent->fields_count ;
106
+ auto * source_fields = parent->fields ;
107
+
108
+ size_t target_count = CalculateSizeOfUnion (parent, upserts, deletes);
109
+ auto * target_fields =
110
+ MakeArray<google_firestore_v1_MapValue_FieldsEntry>(target_count);
111
+
112
+ auto delete_it = deletes.begin ();
113
+ auto upsert_it = upserts.begin ();
114
+
115
+ // Merge the existing data with the deletes and updates
116
+ for (pb_size_t source_index = 0 , target_index = 0 ;
117
+ target_index < target_count;) {
118
+ if (source_index < source_count) {
119
+ absl::string_view key = MakeString (source_fields[source_index].key );
120
+
121
+ // Check if the source key is deleted
122
+ if (delete_it != deletes.end () && *delete_it == key) {
123
+ FreeNanopbMessage (google_firestore_v1_MapValue_FieldsEntry_fields,
124
+ source_fields + source_index);
125
+
126
+ ++delete_it;
127
+ ++source_index;
128
+ continue ;
129
+ }
130
+
131
+ // Check if the source key is updated by the next upsert
132
+ if (upsert_it != upserts.end () && upsert_it->first == key) {
133
+ FreeNanopbMessage (google_firestore_v1_Value_fields,
134
+ &source_fields[source_index].value );
135
+ target_fields[target_index].key = source_fields[source_index].key ;
136
+ target_fields[target_index].value = DeepClone (upsert_it->second );
137
+
138
+ ++upsert_it;
139
+ ++source_index;
140
+ ++target_index;
141
+ continue ;
142
+ }
143
+
144
+ // Check if the source key comes before the next upsert
145
+ if (upsert_it == upserts.end () || upsert_it->first > key) {
146
+ target_fields[target_index] = source_fields[source_index];
147
+
148
+ ++source_index;
149
+ ++target_index;
150
+ continue ;
151
+ }
152
+ }
153
+
154
+ // Otherwise, insert the next upsert.
155
+ target_fields[target_index].key = MakeBytesArray (upsert_it->first );
156
+ target_fields[target_index].value = DeepClone (upsert_it->second );
157
+
158
+ ++upsert_it;
159
+ ++target_index;
160
+ }
161
+
162
+ free (parent->fields );
163
+ parent->fields = target_fields;
164
+ parent->fields_count = target_count;
165
+ }
166
+
75
167
} // namespace
76
168
77
169
MutableObjectValue::MutableObjectValue () {
@@ -80,29 +172,29 @@ MutableObjectValue::MutableObjectValue() {
80
172
value_->map_value .fields = nullptr ;
81
173
}
82
174
83
- model:: FieldMask MutableObjectValue::ToFieldMask () const {
175
+ FieldMask MutableObjectValue::ToFieldMask () const {
84
176
return ExtractFieldMask (value_->map_value );
85
177
}
86
178
87
- model:: FieldMask MutableObjectValue::ExtractFieldMask (
179
+ FieldMask MutableObjectValue::ExtractFieldMask (
88
180
const google_firestore_v1_MapValue& value) const {
89
181
std::set<FieldPath> fields;
90
182
91
183
for (size_t i = 0 ; i < value.fields_count ; ++i) {
92
- google_firestore_v1_MapValue_FieldsEntry& entry = value.fields [i];
184
+ const google_firestore_v1_MapValue_FieldsEntry& entry = value.fields [i];
93
185
FieldPath current_path ({MakeString (entry.key )});
94
186
95
187
if (entry.value .which_value_type !=
96
188
google_firestore_v1_Value_map_value_tag) {
97
- fields.insert (current_path);
189
+ fields.insert (std::move (( current_path)) );
98
190
continue ;
99
191
}
100
192
101
193
// Recursively extract the nested map
102
194
FieldMask nested_mask = ExtractFieldMask (entry.value .map_value );
103
195
if (nested_mask.begin () == nested_mask.end ()) {
104
- // Preserve the empty map by adding it to the FieldMask.
105
- fields.insert (current_path);
196
+ // Preserve the empty map by adding it to the FieldMask
197
+ fields.insert (std::move ( current_path) );
106
198
} else {
107
199
for (const FieldPath& nested_path : nested_mask) {
108
200
fields.insert (current_path.Append (nested_path));
@@ -136,7 +228,7 @@ void MutableObjectValue::Set(const FieldPath& path,
136
228
google_firestore_v1_MapValue* parent_map = ParentMap (path.PopLast ());
137
229
138
230
std::string last_segment = path.last_segment ();
139
- std::map <std::string, google_firestore_v1_Value> upserts{
231
+ absl::flat_hash_map <std::string, google_firestore_v1_Value> upserts{
140
232
{std::move (last_segment), value}};
141
233
142
234
ApplyChanges (parent_map, upserts, /* deletes=*/ {});
@@ -146,8 +238,8 @@ void MutableObjectValue::SetAll(const FieldMask& field_mask,
146
238
const MutableObjectValue& data) {
147
239
FieldPath parent;
148
240
149
- std::map <std::string, google_firestore_v1_Value> upserts;
150
- std::set <std::string> deletes;
241
+ absl::flat_hash_map <std::string, google_firestore_v1_Value> upserts;
242
+ absl::flat_hash_set <std::string> deletes;
151
243
152
244
for (const FieldPath& path : field_mask) {
153
245
if (!parent.IsImmediateParentOf (path)) {
@@ -188,7 +280,7 @@ void MutableObjectValue::Delete(const FieldPath& path) {
188
280
// We can only delete a leaf entry if its parent is a map.
189
281
if (nested_value->which_value_type ==
190
282
google_firestore_v1_Value_map_value_tag) {
191
- std::set <std::string> deletes{path.last_segment ()};
283
+ absl::flat_hash_set <std::string> deletes{path.last_segment ()};
192
284
ApplyChanges (&nested_value->map_value , /* upserts=*/ {}, deletes);
193
285
}
194
286
}
@@ -210,21 +302,21 @@ google_firestore_v1_MapValue* MutableObjectValue::ParentMap(
210
302
if (entry->value .which_value_type !=
211
303
google_firestore_v1_Value_map_value_tag) {
212
304
// Since the element is not a map value, free all existing data and
213
- // change it to a map type
305
+ // change it to a map type.
214
306
FreeNanopbMessage (FieldsArray<google_firestore_v1_Value>(),
215
307
&entry->value );
216
308
entry->value .which_value_type = google_firestore_v1_Value_map_value_tag;
217
309
}
218
310
219
311
parent = &entry->value ;
220
312
} else {
221
- // Create a new map value for the current segment
313
+ // Create a new map value for the current segment.
222
314
google_firestore_v1_Value new_entry{};
223
315
new_entry.which_value_type = google_firestore_v1_Value_map_value_tag;
224
316
225
- std::map <std::string, google_firestore_v1_Value> upserts{
317
+ absl::flat_hash_map <std::string, google_firestore_v1_Value> upserts{
226
318
{segment, new_entry}};
227
- ApplyChanges (&( parent->map_value ) , upserts, /* deletes=*/ {});
319
+ ApplyChanges (&parent->map_value , upserts, /* deletes=*/ {});
228
320
229
321
parent = &(FindEntry (*parent, segment)->value );
230
322
}
@@ -233,82 +325,6 @@ google_firestore_v1_MapValue* MutableObjectValue::ParentMap(
233
325
return &parent->map_value ;
234
326
}
235
327
236
- void MutableObjectValue::ApplyChanges (
237
- google_firestore_v1_MapValue* parent,
238
- const std::map<std::string, google_firestore_v1_Value>& upserts,
239
- const std::set<std::string>& deletes) const {
240
- auto source_count = parent->fields_count ;
241
- auto * source_fields = parent->fields ;
242
-
243
- // Compute the size of the map after applying all mutations. The final size is
244
- // the number of existing entries, plus the number of new entries
245
- // minus the number of deleted entries.
246
- size_t target_count =
247
- upserts.size () +
248
- std::count_if (source_fields, source_fields + source_count,
249
- [&](const google_firestore_v1_MapValue_FieldsEntry& entry) {
250
- std::string field = MakeString (entry.key );
251
- // Don't count if entry is deleted or if it is a
252
- // replacement rather than an insert.
253
- return deletes.find (field) == deletes.end () &&
254
- upserts.find (field) == upserts.end ();
255
- });
256
-
257
- auto * target_fields =
258
- MakeArray<google_firestore_v1_MapValue_FieldsEntry>(target_count);
259
-
260
- auto delete_it = deletes.begin ();
261
- auto upsert_it = upserts.begin ();
262
-
263
- // Merge the existing data with the deletes and updates.
264
- for (pb_size_t source_index = 0 , target_index = 0 ;
265
- target_index < target_count;) {
266
- if (source_index < source_count) {
267
- std::string key = MakeString (source_fields[source_index].key );
268
-
269
- // Check if the source key is deleted
270
- if (delete_it != deletes.end () && *delete_it == key) {
271
- FreeNanopbMessage (google_firestore_v1_MapValue_FieldsEntry_fields,
272
- source_fields + source_index);
273
- ++delete_it;
274
- ++source_index;
275
- continue ;
276
- }
277
-
278
- // Check if the source key is updated by the next upsert
279
- if (upsert_it != upserts.end () && upsert_it->first == key) {
280
- FreeNanopbMessage (google_firestore_v1_Value_fields,
281
- &source_fields[source_index].value );
282
- target_fields[target_index].key = source_fields[source_index].key ;
283
- target_fields[target_index].value = DeepClone (upsert_it->second );
284
- ++upsert_it;
285
- ++target_index;
286
- ++source_index;
287
- continue ;
288
- }
289
-
290
- // Check if the source key comes before the next upsert
291
- if (upsert_it == upserts.end () || upsert_it->first > key) {
292
- target_fields[target_index] = source_fields[source_index];
293
- ++target_index;
294
- ++source_index;
295
- continue ;
296
- }
297
- }
298
-
299
- // Otherwise, insert the next upsert.
300
- target_fields[target_index].key = MakeBytesArray (upsert_it->first );
301
- target_fields[target_index].value = DeepClone (upsert_it->second );
302
- ++upsert_it;
303
- ++target_index;
304
- }
305
-
306
- free (source_fields);
307
-
308
- parent->fields = target_fields;
309
- parent->fields_count = target_count;
310
- }
311
-
312
328
} // namespace model
313
329
} // namespace firestore
314
330
} // namespace firebase
0 commit comments