29
29
#include " Firestore/core/src/firebase/firestore/util/hard_assert.h"
30
30
#include " Firestore/core/src/firebase/firestore/util/hashing.h"
31
31
#include " Firestore/core/src/firebase/firestore/util/to_string.h"
32
+ #include " absl/algorithm/container.h"
33
+ #include " absl/base/casts.h"
32
34
#include " absl/memory/memory.h"
33
35
#include " absl/strings/escaping.h"
34
36
@@ -42,6 +44,7 @@ using BaseValue = FieldValue::BaseValue;
42
44
using Type = FieldValue::Type;
43
45
44
46
using util::Compare;
47
+ using util::CompareContainer;
45
48
using util::ComparisonResult;
46
49
47
50
template <typename T>
@@ -59,6 +62,13 @@ class NullValue : public FieldValue::BaseValue {
59
62
return util::ToString (nullptr );
60
63
}
61
64
65
+ bool Equals (const BaseValue& other) const override {
66
+ if (type () != other.type ()) return false ;
67
+
68
+ // NullValue is the only instance of itself
69
+ return true ;
70
+ }
71
+
62
72
ComparisonResult CompareTo (const BaseValue& other) const override {
63
73
ComparisonResult cmp = CompareTypes (other);
64
74
if (!util::Same (cmp)) return cmp;
@@ -95,11 +105,19 @@ class SimpleFieldValue : public FieldValue::BaseValue {
95
105
return util::ToString (value_);
96
106
}
97
107
108
+ bool Equals (const BaseValue& other) const override {
109
+ if (type () != other.type ()) return false ;
110
+
111
+ auto & other_value = Cast<SimpleFieldValue>(other);
112
+ return value_ == other_value.value ();
113
+ }
114
+
98
115
ComparisonResult CompareTo (const BaseValue& other) const override {
99
116
ComparisonResult cmp = CompareTypes (other);
100
117
if (!util::Same (cmp)) return cmp;
101
118
102
- return Compare (value_, Cast<SimpleFieldValue>(other).value ());
119
+ auto & other_value = Cast<SimpleFieldValue>(other);
120
+ return Compare (value_, other_value.value ());
103
121
}
104
122
105
123
size_t Hash () const override {
@@ -139,6 +157,17 @@ int64_t Integer(const BaseValue& rep) {
139
157
class DoubleValue : public NumberValue <Type::Double, double > {
140
158
public:
141
159
using NumberValue<Type::Double, double >::NumberValue;
160
+
161
+ bool Equals (const BaseValue& other) const override {
162
+ if (type () != other.type ()) return false ;
163
+
164
+ auto & other_value = Cast<DoubleValue>(other);
165
+ return util::DoubleBitwiseEquals (value (), other_value.value ());
166
+ }
167
+
168
+ size_t Hash () const override {
169
+ return util::DoubleBitwiseHash (value ());
170
+ }
142
171
};
143
172
144
173
double Double (const BaseValue& rep) {
@@ -173,6 +202,12 @@ ComparisonResult NumberValue<type_enum, ValueType>::CompareTo(
173
202
}
174
203
}
175
204
205
+ // TODO(wilhuff): Use SimpleFieldValue as a base once we migrate to absl::Hash.
206
+ //
207
+ // This can't extend SimpleFieldValue because `util::Hash` is undefined for
208
+ // Timestamp (and you can't override a compile-time error in a base class out
209
+ // of existence). absl::Hash allows us to implement hashing in a way that
210
+ // requires no public declaration of conformance.
176
211
class TimestampValue : public BaseValue {
177
212
public:
178
213
explicit TimestampValue (Timestamp value) : value_(value) {
@@ -186,6 +221,13 @@ class TimestampValue : public BaseValue {
186
221
return util::ToString (value_);
187
222
}
188
223
224
+ bool Equals (const BaseValue& other) const override {
225
+ if (type () != other.type ()) return false ;
226
+
227
+ auto & other_value = Cast<TimestampValue>(other);
228
+ return value_ == other_value.value_ ;
229
+ }
230
+
189
231
ComparisonResult CompareTo (const BaseValue& other) const override {
190
232
ComparisonResult cmp = CompareTypes (other);
191
233
if (!util::Same (cmp)) return cmp;
@@ -230,6 +272,13 @@ class ServerTimestampValue : public FieldValue::BaseValue {
230
272
return absl::StrCat (" ServerTimestamp(local_write_time=" , time, " )" );
231
273
}
232
274
275
+ bool Equals (const BaseValue& other) const override {
276
+ if (type () != other.type ()) return false ;
277
+
278
+ auto & other_value = Cast<ServerTimestampValue>(other);
279
+ return local_write_time_ == other_value.local_write_time_ ;
280
+ }
281
+
233
282
ComparisonResult CompareTo (const BaseValue& other) const override {
234
283
ComparisonResult cmp = CompareTypes (other);
235
284
if (!util::Same (cmp)) return cmp;
@@ -291,6 +340,13 @@ class ReferenceValue : public FieldValue::BaseValue {
291
340
return Type::Reference;
292
341
}
293
342
343
+ bool Equals (const BaseValue& other) const override {
344
+ if (type () != other.type ()) return false ;
345
+
346
+ auto & other_value = Cast<ReferenceValue>(other);
347
+ return database_id_ == other_value.database_id_ && key_ == other_value.key_ ;
348
+ }
349
+
294
350
ComparisonResult CompareTo (const BaseValue& other) const override {
295
351
ComparisonResult cmp = CompareTypes (other);
296
352
if (!util::Same (cmp)) return cmp;
@@ -328,6 +384,13 @@ class GeoPointValue : public BaseValue {
328
384
return util::ToString (value_);
329
385
}
330
386
387
+ bool Equals (const BaseValue& other) const override {
388
+ if (type () != other.type ()) return false ;
389
+
390
+ auto & other_value = Cast<GeoPointValue>(other);
391
+ return value_ == other_value.value_ ;
392
+ }
393
+
331
394
ComparisonResult CompareTo (const BaseValue& other) const override {
332
395
ComparisonResult cmp = CompareTypes (other);
333
396
if (!util::Same (cmp)) return cmp;
@@ -357,6 +420,13 @@ class ArrayContents : public FieldValue::BaseValue {
357
420
return Type::Array;
358
421
}
359
422
423
+ bool Equals (const BaseValue& other) const override {
424
+ if (type () != other.type ()) return false ;
425
+
426
+ auto & other_value = Cast<ArrayContents>(other);
427
+ return absl::c_equal (value_, other_value.value_ );
428
+ }
429
+
360
430
ComparisonResult CompareTo (const BaseValue& other) const override {
361
431
ComparisonResult cmp = CompareTypes (other);
362
432
if (!util::Same (cmp)) return cmp;
@@ -390,6 +460,13 @@ class MapContents : public FieldValue::BaseValue {
390
460
return Type::Object;
391
461
}
392
462
463
+ bool Equals (const BaseValue& other) const override {
464
+ if (type () != other.type ()) return false ;
465
+
466
+ auto & other_value = Cast<MapContents>(other);
467
+ return absl::c_equal (value_, other_value.value_ );
468
+ }
469
+
393
470
ComparisonResult CompareTo (const BaseValue& other) const override {
394
471
ComparisonResult cmp = CompareTypes (other);
395
472
if (!util::Same (cmp)) return cmp;
@@ -576,7 +653,25 @@ FieldValue FieldValue::FromInteger(int64_t value) {
576
653
return FieldValue (std::make_shared<IntegerValue>(value));
577
654
}
578
655
656
+ // We use a canonical NaN bit pattern that's common for both Objective-C and
657
+ // Java. Specifically:
658
+ //
659
+ // - sign: 0
660
+ // - exponent: 11 bits, all 1
661
+ // - significand: 52 bits, MSB=1, rest=0
662
+ //
663
+ // This matches the Firestore backend which uses Double.doubleToLongBits from
664
+ // the JDK (which is defined to normalize all NaNs to this value). This also
665
+ // happens to be a common value for NAN in C++, but C++ does not require this
666
+ // specific NaN value to be used, so we normalize.
667
+ const uint64_t kCanonicalNanBits = 0x7ff8000000000000ULL ;
668
+
579
669
FieldValue FieldValue::FromDouble (double value) {
670
+ static double canonical_nan = absl::bit_cast<double >(kCanonicalNanBits );
671
+ if (std::isnan (value)) {
672
+ value = canonical_nan;
673
+ }
674
+
580
675
return FieldValue (std::make_shared<DoubleValue>(value));
581
676
}
582
677
@@ -636,6 +731,10 @@ FieldValue FieldValue::FromMap(FieldValue::Map&& value) {
636
731
return FieldValue (std::make_shared<MapContents>(std::move (value)));
637
732
}
638
733
734
+ bool operator ==(const FieldValue& lhs, const FieldValue& rhs) {
735
+ return lhs.rep_ ->Equals (*rhs.rep_ );
736
+ }
737
+
639
738
std::ostream& operator <<(std::ostream& os, const FieldValue& value) {
640
739
return os << value.ToString ();
641
740
}
0 commit comments