Skip to content

Commit cc4d0f1

Browse files
ValueUtil changes for FieldValue migration
1 parent 032be48 commit cc4d0f1

File tree

4 files changed

+177
-43
lines changed

4 files changed

+177
-43
lines changed

Firestore/core/src/model/server_timestamp_util.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const char kPreviousValueKey[] = "__previous_value__";
2929
const char kServerTimestampSentinel[] = "server_timestamp";
3030

3131
google_firestore_v1_Value EncodeServerTimestamp(
32-
google_protobuf_Timestamp local_write_time,
32+
const Timestamp& local_write_time,
3333
absl::optional<google_firestore_v1_Value> previous_value) {
3434
google_firestore_v1_Value result{};
3535
result.which_value_type = google_firestore_v1_Value_map_value_tag;

Firestore/core/src/model/server_timestamp_util.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define FIRESTORE_CORE_SRC_MODEL_SERVER_TIMESTAMP_UTIL_H_
1919

2020
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
21+
#include "Firestore/core/include/firebase/firestore/timestamp.h"
2122
#include "absl/types/optional.h"
2223

2324
namespace firebase {
@@ -27,11 +28,9 @@ namespace model {
2728
// Utility methods to handle ServerTimestamps, which are stored using special
2829
// sentinel fields in MapValues.
2930

30-
/**
31-
* Encodes the backing data for a server timestamp in a Value proto.
32-
*/
31+
/** Encodes the backing data for a server timestamp in a Value proto. */
3332
google_firestore_v1_Value EncodeServerTimestamp(
34-
google_protobuf_Timestamp local_write_time,
33+
const Timestamp& local_write_time,
3534
absl::optional<google_firestore_v1_Value> previous_value);
3635
/**
3736
* Returns whether the provided value is a field map that contains the

Firestore/core/src/model/value_util.cc

Lines changed: 102 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717
#include "Firestore/core/src/model/value_util.h"
1818

1919
#include <algorithm>
20+
#include <cmath>
21+
#include <limits>
2022
#include <memory>
2123
#include <vector>
2224

25+
#include "Firestore/core/src/model/database_id.h"
26+
#include "Firestore/core/src/model/document_key.h"
2327
#include "Firestore/core/src/model/server_timestamp_util.h"
2428
#include "Firestore/core/src/nanopb/nanopb_util.h"
2529
#include "Firestore/core/src/util/comparison.h"
@@ -123,9 +127,10 @@ ComparisonResult CompareBlobs(const google_firestore_v1_Value& left,
123127
? util::ComparisonResultFromInt(cmp)
124128
: util::Compare(left.bytes_value->size, right.bytes_value->size);
125129
} else {
126-
// An empty blob is represented by a nullptr
127-
return util::Compare(left.bytes_value != nullptr,
128-
right.bytes_value != nullptr);
130+
// An empty blob is represented by a nullptr (or an empty byte array)
131+
return util::Compare(
132+
!(left.bytes_value == nullptr || left.bytes_value->size == 0),
133+
!(right.bytes_value == nullptr || right.bytes_value->size == 0));
129134
}
130135
}
131136

@@ -247,8 +252,8 @@ ComparisonResult Compare(const google_firestore_v1_Value& left,
247252
}
248253
}
249254

250-
bool NumberEquals(const firebase::firestore::google_firestore_v1_Value& left,
251-
const firebase::firestore::google_firestore_v1_Value& right) {
255+
bool NumberEquals(const google_firestore_v1_Value& left,
256+
const google_firestore_v1_Value& right) {
252257
if (left.which_value_type == google_firestore_v1_Value_integer_value_tag &&
253258
right.which_value_type == google_firestore_v1_Value_integer_value_tag) {
254259
return left.integer_value == right.integer_value;
@@ -261,42 +266,36 @@ bool NumberEquals(const firebase::firestore::google_firestore_v1_Value& left,
261266
return false;
262267
}
263268

264-
bool ArrayEquals(const firebase::firestore::google_firestore_v1_Value& left,
265-
const firebase::firestore::google_firestore_v1_Value& right) {
266-
const google_firestore_v1_ArrayValue& left_array = left.array_value;
267-
const google_firestore_v1_ArrayValue& right_array = right.array_value;
268-
269-
if (left_array.values_count != right_array.values_count) {
269+
bool ArrayEquals(const google_firestore_v1_ArrayValue& left,
270+
const google_firestore_v1_ArrayValue& right) {
271+
if (left.values_count != right.values_count) {
270272
return false;
271273
}
272274

273-
for (size_t i = 0; i < left_array.values_count; ++i) {
274-
if (left_array.values[i] != right_array.values[i]) {
275+
for (size_t i = 0; i < left.values_count; ++i) {
276+
if (left.values[i] != right.values[i]) {
275277
return false;
276278
}
277279
}
278280

279281
return true;
280282
}
281283

282-
bool ObjectEquals(const firebase::firestore::google_firestore_v1_Value& left,
283-
const firebase::firestore::google_firestore_v1_Value& right) {
284-
google_firestore_v1_MapValue left_map = left.map_value;
285-
google_firestore_v1_MapValue right_map = right.map_value;
286-
287-
if (left_map.fields_count != right_map.fields_count) {
284+
bool ObjectEquals(const google_firestore_v1_MapValue& left,
285+
const google_firestore_v1_MapValue& right) {
286+
if (left.fields_count != right.fields_count) {
288287
return false;
289288
}
290289

291290
// Porting Note: MapValues in iOS are always kept in sorted order. We
292291
// therefore do no need to sort them before comparing.
293-
for (size_t i = 0; i < right_map.fields_count; ++i) {
294-
if (nanopb::MakeStringView(left_map.fields[i].key) !=
295-
nanopb::MakeStringView(right_map.fields[i].key)) {
292+
for (size_t i = 0; i < right.fields_count; ++i) {
293+
if (nanopb::MakeStringView(left.fields[i].key) !=
294+
nanopb::MakeStringView(right.fields[i].key)) {
296295
return false;
297296
}
298297

299-
if (left_map.fields[i].value != right_map.fields[i].value) {
298+
if (left.fields[i].value != right.fields[i].value) {
300299
return false;
301300
}
302301
}
@@ -349,16 +348,21 @@ bool Equals(const google_firestore_v1_Value& lhs,
349348
lhs.geo_point_value.longitude == rhs.geo_point_value.longitude;
350349

351350
case TypeOrder::kArray:
352-
return ArrayEquals(lhs, rhs);
351+
return ArrayEquals(lhs.array_value, rhs.array_value);
353352

354353
case TypeOrder::kMap:
355-
return ObjectEquals(lhs, rhs);
354+
return ObjectEquals(lhs.map_value, rhs.map_value);
356355

357356
default:
358357
HARD_FAIL("Invalid type value: %s", left_type);
359358
}
360359
}
361360

361+
bool Equals(const google_firestore_v1_ArrayValue& lhs,
362+
const google_firestore_v1_ArrayValue& rhs) {
363+
return ArrayEquals(lhs, rhs);
364+
}
365+
362366
std::string CanonifyTimestamp(const google_firestore_v1_Value& value) {
363367
return absl::StrFormat("time(%d,%d)", value.timestamp_value.seconds,
364368
value.timestamp_value.nanos);
@@ -381,13 +385,11 @@ std::string CanonifyGeoPoint(const google_firestore_v1_Value& value) {
381385
value.geo_point_value.longitude);
382386
}
383387

384-
std::string CanonifyArray(const google_firestore_v1_Value& value) {
385-
const auto& array = value.array_value;
386-
388+
std::string CanonifyArray(const google_firestore_v1_ArrayValue& array_value) {
387389
std::string result = "[";
388-
for (size_t i = 0; i < array.values_count; ++i) {
389-
absl::StrAppend(&result, CanonicalId(array.values[i]));
390-
if (i != array.values_count - 1) {
390+
for (size_t i = 0; i < array_value.values_count; ++i) {
391+
absl::StrAppend(&result, CanonicalId(array_value.values[i]));
392+
if (i != array_value.values_count - 1) {
391393
absl::StrAppend(&result, ",");
392394
}
393395
}
@@ -444,7 +446,7 @@ std::string CanonicalId(const google_firestore_v1_Value& value) {
444446
return CanonifyGeoPoint(value);
445447

446448
case google_firestore_v1_Value_array_value_tag:
447-
return CanonifyArray(value);
449+
return CanonifyArray(value.array_value);
448450

449451
case google_firestore_v1_Value_map_value_tag: {
450452
return CanonifyObject(value);
@@ -455,12 +457,61 @@ std::string CanonicalId(const google_firestore_v1_Value& value) {
455457
}
456458
}
457459

458-
google_firestore_v1_Value DeepClone(google_firestore_v1_Value source) {
460+
std::string CanonicalId(const google_firestore_v1_ArrayValue& value) {
461+
return CanonifyArray(value);
462+
}
463+
464+
bool Contains(google_firestore_v1_ArrayValue haystack,
465+
google_firestore_v1_Value needle) {
466+
for (pb_size_t i = 0; i < haystack.values_count; ++i) {
467+
if (Equals(haystack.values[i], needle)) {
468+
return true;
469+
}
470+
}
471+
return false;
472+
}
473+
474+
google_firestore_v1_Value NullValue() {
475+
google_firestore_v1_Value null_value{};
476+
null_value.which_value_type = google_firestore_v1_Value_null_value_tag;
477+
return null_value;
478+
}
479+
480+
bool IsNullValue(const google_firestore_v1_Value& value) {
481+
return value.which_value_type == google_firestore_v1_Value_null_value_tag;
482+
}
483+
484+
google_firestore_v1_Value NaNValue() {
485+
google_firestore_v1_Value nan_value{};
486+
nan_value.which_value_type = google_firestore_v1_Value_double_value_tag;
487+
nan_value.double_value = std::numeric_limits<double>::quiet_NaN();
488+
return nan_value;
489+
}
490+
491+
bool IsNaNValue(const google_firestore_v1_Value& value) {
492+
return value.which_value_type == google_firestore_v1_Value_double_value_tag &&
493+
isnan(value.double_value);
494+
}
495+
496+
google_firestore_v1_Value RefValue(const model::DatabaseId& database_id,
497+
const model::DocumentKey& document_key) {
498+
google_firestore_v1_Value result{};
499+
result.which_value_type = google_firestore_v1_Value_reference_value_tag;
500+
result.string_value = nanopb::MakeBytesArray(util::StringFormat(
501+
"projects/%s/databases/%s/documents/%s", database_id.project_id(),
502+
database_id.database_id(), document_key.ToString()));
503+
return result;
504+
}
505+
506+
google_firestore_v1_Value DeepClone(const google_firestore_v1_Value& source) {
459507
google_firestore_v1_Value target = source;
460508
switch (source.which_value_type) {
461509
case google_firestore_v1_Value_string_value_tag:
462-
target.string_value = nanopb::MakeBytesArray(source.string_value->bytes,
463-
source.string_value->size);
510+
target.string_value =
511+
source.string_value
512+
? nanopb::MakeBytesArray(source.string_value->bytes,
513+
source.string_value->size)
514+
: nullptr;
464515
break;
465516

466517
case google_firestore_v1_Value_reference_value_tag:
@@ -469,8 +520,10 @@ google_firestore_v1_Value DeepClone(google_firestore_v1_Value source) {
469520
break;
470521

471522
case google_firestore_v1_Value_bytes_value_tag:
472-
target.bytes_value = nanopb::MakeBytesArray(source.bytes_value->bytes,
473-
source.bytes_value->size);
523+
target.bytes_value =
524+
source.bytes_value ? nanopb::MakeBytesArray(source.bytes_value->bytes,
525+
source.bytes_value->size)
526+
: nullptr;
474527
break;
475528

476529
case google_firestore_v1_Value_array_value_tag:
@@ -499,6 +552,18 @@ google_firestore_v1_Value DeepClone(google_firestore_v1_Value source) {
499552
return target;
500553
}
501554

555+
google_firestore_v1_ArrayValue DeepClone(
556+
const google_firestore_v1_ArrayValue& source) {
557+
google_firestore_v1_ArrayValue target = source;
558+
target.values_count = source.values_count;
559+
target.values =
560+
nanopb::MakeArray<google_firestore_v1_Value>(source.values_count);
561+
for (pb_size_t i = 0; i < source.values_count; ++i) {
562+
target.values[i] = DeepClone(source.values[i]);
563+
}
564+
return target;
565+
}
566+
502567
} // namespace model
503568
} // namespace firestore
504569
} // namespace firebase

Firestore/core/src/model/value_util.h

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919

2020
#include <ostream>
2121
#include <string>
22+
#include <vector>
2223

2324
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
25+
#include "absl/types/optional.h"
2426

2527
namespace firebase {
2628
namespace firestore {
@@ -31,6 +33,9 @@ enum class ComparisonResult;
3133

3234
namespace model {
3335

36+
class DocumentKey;
37+
class DatabaseId;
38+
3439
/**
3540
* The order of types in Firestore. This order is based on the backend's
3641
* ordering, but modified to support server timestamps.
@@ -58,14 +63,69 @@ util::ComparisonResult Compare(const google_firestore_v1_Value& left,
5863
bool Equals(const google_firestore_v1_Value& left,
5964
const google_firestore_v1_Value& right);
6065

66+
bool Equals(const google_firestore_v1_ArrayValue& left,
67+
const google_firestore_v1_ArrayValue& right);
68+
6169
/**
6270
* Generate the canonical ID for the provided field value (as used in Target
6371
* serialization).
6472
*/
6573
std::string CanonicalId(const google_firestore_v1_Value& value);
6674

75+
/**
76+
* Generate the canonical ID for the provided array value (as used in Target
77+
* serialization).
78+
*/
79+
std::string CanonicalId(const google_firestore_v1_ArrayValue& value);
80+
81+
/** Returns true if the array value contains the specified element. */
82+
bool Contains(google_firestore_v1_ArrayValue haystack,
83+
google_firestore_v1_Value needle);
84+
85+
/** Returns `nullptr` in its Protobuf representation. */
86+
google_firestore_v1_Value NullValue();
87+
88+
/** Returns `true` if `value` is `nullptr` in its Protobuf representation. */
89+
bool IsNullValue(const google_firestore_v1_Value& value);
90+
91+
/** Returns `NaN` in its Protobuf representation. */
92+
google_firestore_v1_Value NaNValue();
93+
94+
/** Returns `true` if `value` is `NaN` in its Protobuf representation. */
95+
bool IsNaNValue(const google_firestore_v1_Value& value);
96+
97+
google_firestore_v1_Value RefValue(const DatabaseId& database_id,
98+
const DocumentKey& document_key);
99+
67100
/** Creates a copy of the contents of the Value proto. */
68-
google_firestore_v1_Value DeepClone(google_firestore_v1_Value source);
101+
google_firestore_v1_Value DeepClone(const google_firestore_v1_Value& source);
102+
103+
/** Creates a copy of the contents of the ArrayValue proto. */
104+
google_firestore_v1_ArrayValue DeepClone(
105+
const google_firestore_v1_ArrayValue& source);
106+
107+
/** Returns true if `value` is a INTEGER_VALUE. */
108+
inline bool IsInteger(const absl::optional<google_firestore_v1_Value>& value) {
109+
return value &&
110+
value->which_value_type == google_firestore_v1_Value_integer_value_tag;
111+
}
112+
113+
/** Returns true if `value` is a DOUBLE_VALUE. */
114+
inline bool IsDouble(const absl::optional<google_firestore_v1_Value>& value) {
115+
return value &&
116+
value->which_value_type == google_firestore_v1_Value_double_value_tag;
117+
}
118+
119+
/** Returns true if `value` is either a INTEGER_VALUE or a DOUBLE_VALUE. */
120+
inline bool IsNumber(const absl::optional<google_firestore_v1_Value>& value) {
121+
return IsInteger(value) || IsDouble(value);
122+
}
123+
124+
/** Returns true if `value` is an ARRAY_VALUE. */
125+
inline bool IsArray(const absl::optional<google_firestore_v1_Value>& value) {
126+
return value &&
127+
value->which_value_type == google_firestore_v1_Value_array_value_tag;
128+
}
69129

70130
} // namespace model
71131

@@ -79,6 +139,16 @@ inline bool operator!=(const google_firestore_v1_Value& lhs,
79139
return !model::Equals(lhs, rhs);
80140
}
81141

142+
inline bool operator==(const google_firestore_v1_ArrayValue& lhs,
143+
const google_firestore_v1_ArrayValue& rhs) {
144+
return model::Equals(lhs, rhs);
145+
}
146+
147+
inline bool operator!=(const google_firestore_v1_ArrayValue& lhs,
148+
const google_firestore_v1_ArrayValue& rhs) {
149+
return !model::Equals(lhs, rhs);
150+
}
151+
82152
inline std::ostream& operator<<(std::ostream& out,
83153
const google_firestore_v1_Value& value) {
84154
return out << model::CanonicalId(value);

0 commit comments

Comments
 (0)