aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Gil <mcg@google.com>2018-01-19 12:27:11 -0800
committerGravatar GitHub <noreply@github.com>2018-01-19 12:27:11 -0800
commit39d8252300015c26f1932cff42032613fdb36a09 (patch)
tree93bb3ab7c476289c4afe6cf91773a60b6f53a94d
parent9f7c094f9f00a6efc0107071f109ef1bc4d7357d (diff)
Port comparison to C++ (#678)
This reimplements our comparison functions as C++ Comparators and then provides compatibility shims for interoperating with existing Objective-C usage. A few specialized comparators aren't suitable for porting but only have a single usage (e.g. CompareBytes for comparing NSData * instances). In these cases I've moved them into the caller. * Use int32_t for typeof(ID) in FSTDocumentReference * Migrate callers of FSTComparison.h to Objective-C++ * Port comparison to C++ * Migrate usages of FSTComparison.h to C++ equivalents * Remove FSTComparison
-rw-r--r--Firestore/Example/Firestore.xcodeproj/project.pbxproj8
-rw-r--r--Firestore/Example/Tests/Util/FSTComparisonTests.m143
-rw-r--r--Firestore/Source/API/FIRGeoPoint.mm (renamed from Firestore/Source/API/FIRGeoPoint.m)17
-rw-r--r--Firestore/Source/Core/FSTTimestamp.mm (renamed from Firestore/Source/Core/FSTTimestamp.m)9
-rw-r--r--Firestore/Source/Local/FSTDocumentReference.h4
-rw-r--r--Firestore/Source/Local/FSTDocumentReference.mm (renamed from Firestore/Source/Local/FSTDocumentReference.m)11
-rw-r--r--Firestore/Source/Local/FSTMemoryMutationQueue.mm (renamed from Firestore/Source/Local/FSTMemoryMutationQueue.m)7
-rw-r--r--Firestore/Source/Model/FSTFieldValue.mm (renamed from Firestore/Source/Model/FSTFieldValue.m)72
-rw-r--r--Firestore/Source/Util/FSTComparison.h66
-rw-r--r--Firestore/Source/Util/FSTComparison.m175
-rw-r--r--Firestore/core/src/firebase/firestore/util/CMakeLists.txt3
-rw-r--r--Firestore/core/src/firebase/firestore/util/comparison.cc112
-rw-r--r--Firestore/core/src/firebase/firestore/util/comparison.h181
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_apple.h13
-rw-r--r--Firestore/core/test/firebase/firestore/util/CMakeLists.txt1
-rw-r--r--Firestore/core/test/firebase/firestore/util/comparison_test.cc210
-rw-r--r--Firestore/third_party/abseil-cpp/absl/base/macros.h2
17 files changed, 614 insertions, 420 deletions
diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
index 538758d..7e922b6 100644
--- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj
+++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
@@ -30,6 +30,7 @@
54764FAB1FAA0C320085E60A /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAA1FAA0C320085E60A /* string_util_test.cc */; };
54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; };
548DB927200D590300E00ABC /* assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB926200D590300E00ABC /* assert_test.cc */; };
+ 548DB929200D59F600E00ABC /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; };
5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; };
5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; };
54C2294F1FECABAE007D065B /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; };
@@ -148,7 +149,6 @@
DE51B1FD1F0D492C0013853F /* FSTSpecTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1991F0D48AC0013853F /* FSTSpecTests.m */; };
DE51B1FE1F0D492C0013853F /* FSTSyncEngineTestDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B19B1F0D48AC0013853F /* FSTSyncEngineTestDriver.m */; };
DE51B1FF1F0D493A0013853F /* FSTAssertTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1861F0D48AC0013853F /* FSTAssertTests.m */; };
- DE51B2001F0D493A0013853F /* FSTComparisonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */; };
DE51B2011F0D493E0013853F /* FSTHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1891F0D48AC0013853F /* FSTHelpers.m */; };
F104BBD69BC3F0796E3A77C1 /* Pods_Firestore_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */; };
/* End PBXBuildFile section */
@@ -204,6 +204,7 @@
54764FAA1FAA0C320085E60A /* string_util_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_util_test.cc; path = ../../Port/string_util_test.cc; sourceTree = "<group>"; };
54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FSTGoogleTestTests.mm; path = GoogleTest/FSTGoogleTestTests.mm; sourceTree = "<group>"; };
548DB926200D590300E00ABC /* assert_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = assert_test.cc; path = ../../core/test/firebase/firestore/util/assert_test.cc; sourceTree = "<group>"; };
+ 548DB928200D59F600E00ABC /* comparison_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = comparison_test.cc; path = ../../core/test/firebase/firestore/util/comparison_test.cc; sourceTree = "<group>"; };
5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTIntegrationTestCase.mm; sourceTree = "<group>"; };
54C2294E1FECABAE007D065B /* log_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = log_test.cc; path = ../../core/test/firebase/firestore/util/log_test.cc; sourceTree = "<group>"; };
54DA129C1F315EE100DD57A1 /* collection_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = collection_spec_test.json; sourceTree = "<group>"; };
@@ -308,7 +309,6 @@
DE51B1821F0D48AC0013853F /* FSTPathTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTPathTests.m; sourceTree = "<group>"; };
DE51B1841F0D48AC0013853F /* FIRGeoPointTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRGeoPointTests.m; sourceTree = "<group>"; };
DE51B1861F0D48AC0013853F /* FSTAssertTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTAssertTests.m; sourceTree = "<group>"; };
- DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTComparisonTests.m; sourceTree = "<group>"; };
DE51B1881F0D48AC0013853F /* FSTHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTHelpers.h; sourceTree = "<group>"; };
DE51B1891F0D48AC0013853F /* FSTHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTHelpers.m; sourceTree = "<group>"; };
DE51B1941F0D48AC0013853F /* FSTLevelDBSpecTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLevelDBSpecTests.m; sourceTree = "<group>"; };
@@ -400,6 +400,7 @@
children = (
548DB926200D590300E00ABC /* assert_test.cc */,
54740A521FC913E500713A1A /* autoid_test.cc */,
+ 548DB928200D59F600E00ABC /* comparison_test.cc */,
54C2294E1FECABAE007D065B /* log_test.cc */,
54740A531FC913E500713A1A /* secure_random_test.cc */,
5436F32320008FAD006E51E3 /* string_printf_test.cc */,
@@ -646,7 +647,6 @@
54E9281E1F33950B00C1953E /* FSTIntegrationTestCase.h */,
5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */,
DE51B1861F0D48AC0013853F /* FSTAssertTests.m */,
- DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */,
DE51B1881F0D48AC0013853F /* FSTHelpers.h */,
DE51B1891F0D48AC0013853F /* FSTHelpers.m */,
54E9282A1F339CAD00C1953E /* XCTestCase+Await.h */,
@@ -1210,7 +1210,6 @@
DE2EF0881F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m in Sources */,
DE51B1FD1F0D492C0013853F /* FSTSpecTests.m in Sources */,
ABAEEF4F1FD5F8B100C966CB /* FIRQueryTests.m in Sources */,
- DE51B2001F0D493A0013853F /* FSTComparisonTests.m in Sources */,
ABF341051FE860CA00C48322 /* FSTAPIHelpers.m in Sources */,
DE51B1CC1F0D48C00013853F /* FIRGeoPointTests.m in Sources */,
DE51B1E11F0D490D0013853F /* FSTMemoryRemoteDocumentCacheTests.m in Sources */,
@@ -1276,6 +1275,7 @@
DE51B1E41F0D490D0013853F /* FSTQueryCacheTests.m in Sources */,
DE51B1CD1F0D48CD0013853F /* FSTDatabaseInfoTests.m in Sources */,
AB382F7E1FE03059007CA955 /* FIRFieldPathTests.m in Sources */,
+ 548DB929200D59F600E00ABC /* comparison_test.cc in Sources */,
DE51B1F21F0D49140013853F /* FSTPathTests.m in Sources */,
AB99452C1FE3018D00DFC1E6 /* FIRQuerySnapshotTests.m in Sources */,
54740A571FC914BA00713A1A /* secure_random_test.cc in Sources */,
diff --git a/Firestore/Example/Tests/Util/FSTComparisonTests.m b/Firestore/Example/Tests/Util/FSTComparisonTests.m
deleted file mode 100644
index 5632e64..0000000
--- a/Firestore/Example/Tests/Util/FSTComparisonTests.m
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "Firestore/Source/Util/FSTComparison.h"
-
-#import <XCTest/XCTest.h>
-
-union DoubleBits {
- double d;
- uint64_t bits;
-};
-
-#define ASSERT_BIT_EQUALS(expected, actual) \
- do { \
- union DoubleBits expectedBits = {.d = expected}; \
- union DoubleBits actualBits = {.d = expected}; \
- if (expectedBits.bits != actualBits.bits) { \
- XCTFail(@"Expected <%f> to compare equal to <%f> with bits <%llX> equal to <%llX>", actual, \
- expected, actualBits.bits, expectedBits.bits); \
- } \
- } while (0);
-
-#define ASSERT_ORDERED_SAME(doubleValue, longValue) \
- do { \
- NSComparisonResult result = FSTCompareMixed(doubleValue, longValue); \
- if (result != NSOrderedSame) { \
- XCTFail(@"Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \
- } \
- } while (0);
-
-#define ASSERT_ORDERED_DESCENDING(doubleValue, longValue) \
- do { \
- NSComparisonResult result = FSTCompareMixed(doubleValue, longValue); \
- if (result != NSOrderedDescending) { \
- XCTFail(@"Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \
- } \
- } while (0);
-
-#define ASSERT_ORDERED_ASCENDING(doubleValue, longValue) \
- do { \
- NSComparisonResult result = FSTCompareMixed(doubleValue, longValue); \
- if (result != NSOrderedAscending) { \
- XCTFail(@"Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \
- } \
- } while (0);
-
-@interface FSTComparisonTests : XCTestCase
-@end
-
-@implementation FSTComparisonTests
-
-- (void)testMixedComparison {
- // Infinities
- ASSERT_ORDERED_ASCENDING(-INFINITY, LLONG_MIN);
- ASSERT_ORDERED_ASCENDING(-INFINITY, LLONG_MAX);
- ASSERT_ORDERED_ASCENDING(-INFINITY, 0LL);
-
- ASSERT_ORDERED_DESCENDING(INFINITY, LLONG_MIN);
- ASSERT_ORDERED_DESCENDING(INFINITY, LLONG_MAX);
- ASSERT_ORDERED_DESCENDING(INFINITY, 0LL);
-
- // NaN
- ASSERT_ORDERED_ASCENDING(NAN, LLONG_MIN);
- ASSERT_ORDERED_ASCENDING(NAN, LLONG_MAX);
- ASSERT_ORDERED_ASCENDING(NAN, 0LL);
-
- // Large values (note DBL_MIN is positive and near zero).
- ASSERT_ORDERED_ASCENDING(-DBL_MAX, LLONG_MIN);
-
- // Tests around LLONG_MIN
- ASSERT_BIT_EQUALS((double)LLONG_MIN, -0x1.0p63);
- ASSERT_ORDERED_SAME(-0x1.0p63, LLONG_MIN);
- ASSERT_ORDERED_ASCENDING(-0x1.0p63, LLONG_MIN + 1);
-
- XCTAssertLessThan(-0x1.0000000000001p63, -0x1.0p63);
- ASSERT_ORDERED_ASCENDING(-0x1.0000000000001p63, LLONG_MIN);
- ASSERT_ORDERED_DESCENDING(-0x1.FFFFFFFFFFFFFp62, LLONG_MIN);
-
- // Tests around LLONG_MAX
- // Note LLONG_MAX cannot be exactly represented by a double, so the system rounds it to the
- // nearest double, which is 2^63. This number, in turn is larger than the maximum representable
- // as a long.
- ASSERT_BIT_EQUALS(0x1.0p63, (double)LLONG_MAX);
- ASSERT_ORDERED_DESCENDING(0x1.0p63, LLONG_MAX);
-
- // The largest value with an exactly long representation
- XCTAssertEqual((long)0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL);
- ASSERT_ORDERED_SAME(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL);
-
- ASSERT_ORDERED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFB00LL);
- ASSERT_ORDERED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFBFFLL);
- ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC01LL);
- ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFD00LL);
-
- ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFEp62, 0x7FFFFFFFFFFFFC00LL);
-
- // Tests around MAX_SAFE_INTEGER
- ASSERT_ORDERED_SAME(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFFLL);
- ASSERT_ORDERED_DESCENDING(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFELL);
- ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFEp52, 0x1FFFFFFFFFFFFFLL);
- ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFFp52, 0x20000000000000LL);
-
- // Tests around MIN_SAFE_INTEGER
- ASSERT_ORDERED_SAME(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFFLL);
- ASSERT_ORDERED_ASCENDING(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFELL);
- ASSERT_ORDERED_DESCENDING(-0x1.FFFFFFFFFFFFEp52, -0x1FFFFFFFFFFFFFLL);
- ASSERT_ORDERED_DESCENDING(-0x1.FFFFFFFFFFFFFp52, -0x20000000000000LL);
-
- // Tests around zero.
- ASSERT_ORDERED_SAME(-0.0, 0LL);
- ASSERT_ORDERED_SAME(0.0, 0LL);
-
- // The smallest representable positive value should be greater than zero
- ASSERT_ORDERED_DESCENDING(DBL_MIN, 0LL);
- ASSERT_ORDERED_ASCENDING(-DBL_MIN, 0LL);
-
- // Note that 0x1.0p-1074 is a hex floating point literal representing the minimum subnormal
- // number: <https://en.wikipedia.org/wiki/Denormal_number>.
- double minSubNormal = 0x1.0p-1074;
- ASSERT_ORDERED_DESCENDING(minSubNormal, 0LL);
- ASSERT_ORDERED_ASCENDING(-minSubNormal, 0LL);
-
- // Other sanity checks
- ASSERT_ORDERED_ASCENDING(0.5, 1LL);
- ASSERT_ORDERED_DESCENDING(0.5, 0LL);
- ASSERT_ORDERED_ASCENDING(1.5, 2LL);
- ASSERT_ORDERED_DESCENDING(1.5, 1LL);
-}
-
-@end
diff --git a/Firestore/Source/API/FIRGeoPoint.m b/Firestore/Source/API/FIRGeoPoint.mm
index 72e9e7d..8d89633 100644
--- a/Firestore/Source/API/FIRGeoPoint.m
+++ b/Firestore/Source/API/FIRGeoPoint.mm
@@ -16,9 +16,14 @@
#import "Firestore/Source/API/FIRGeoPoint+Internal.h"
-#import "Firestore/Source/Util/FSTComparison.h"
+#import "Firestore/core/src/firebase/firestore/util/comparison.h"
+
#import "Firestore/Source/Util/FSTUsageValidation.h"
+using firebase::firestore::util::DoubleBitwiseEquals;
+using firebase::firestore::util::DoubleBitwiseHash;
+using firebase::firestore::util::WrapCompare;
+
NS_ASSUME_NONNULL_BEGIN
@implementation FIRGeoPoint
@@ -45,11 +50,11 @@ NS_ASSUME_NONNULL_BEGIN
}
- (NSComparisonResult)compare:(FIRGeoPoint *)other {
- NSComparisonResult result = FSTCompareDoubles(self.latitude, other.latitude);
+ NSComparisonResult result = WrapCompare<double>(self.latitude, other.latitude);
if (result != NSOrderedSame) {
return result;
} else {
- return FSTCompareDoubles(self.longitude, other.longitude);
+ return WrapCompare<double>(self.longitude, other.longitude);
}
}
@@ -67,12 +72,12 @@ NS_ASSUME_NONNULL_BEGIN
return NO;
}
FIRGeoPoint *otherGeoPoint = (FIRGeoPoint *)other;
- return FSTDoubleBitwiseEquals(self.latitude, otherGeoPoint.latitude) &&
- FSTDoubleBitwiseEquals(self.longitude, otherGeoPoint.longitude);
+ return DoubleBitwiseEquals(self.latitude, otherGeoPoint.latitude) &&
+ DoubleBitwiseEquals(self.longitude, otherGeoPoint.longitude);
}
- (NSUInteger)hash {
- return 31 * FSTDoubleBitwiseHash(self.latitude) + FSTDoubleBitwiseHash(self.longitude);
+ return 31 * DoubleBitwiseHash(self.latitude) + DoubleBitwiseHash(self.longitude);
}
/** Implements NSCopying without actually copying because geopoints are immutable. */
diff --git a/Firestore/Source/Core/FSTTimestamp.m b/Firestore/Source/Core/FSTTimestamp.mm
index 6d9e314..d2b492a 100644
--- a/Firestore/Source/Core/FSTTimestamp.m
+++ b/Firestore/Source/Core/FSTTimestamp.mm
@@ -16,8 +16,11 @@
#import "Firestore/Source/Core/FSTTimestamp.h"
+#include "Firestore/core/src/firebase/firestore/util/comparison.h"
+
#import "Firestore/Source/Util/FSTAssert.h"
-#import "Firestore/Source/Util/FSTComparison.h"
+
+using firebase::firestore::util::WrapCompare;
NS_ASSUME_NONNULL_BEGIN
@@ -110,11 +113,11 @@ static const int kNanosPerSecond = 1000000000;
}
- (NSComparisonResult)compare:(FSTTimestamp *)other {
- NSComparisonResult result = FSTCompareInt64s(self.seconds, other.seconds);
+ NSComparisonResult result = WrapCompare<int64_t>(self.seconds, other.seconds);
if (result != NSOrderedSame) {
return result;
}
- return FSTCompareInt32s(self.nanos, other.nanos);
+ return WrapCompare<int32_t>(self.nanos, other.nanos);
}
@end
diff --git a/Firestore/Source/Local/FSTDocumentReference.h b/Firestore/Source/Local/FSTDocumentReference.h
index eff60e4..04b8416 100644
--- a/Firestore/Source/Local/FSTDocumentReference.h
+++ b/Firestore/Source/Local/FSTDocumentReference.h
@@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTDocumentReference : NSObject <NSCopying>
/** Initializes the document reference with the given key and ID. */
-- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int)ID NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int32_t)ID NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@@ -43,7 +43,7 @@ NS_ASSUME_NONNULL_BEGIN
* The targetID of a referring target or the batchID of a referring mutation batch. (Which this
* is depends upon which FSTReferenceSet this reference is a part of.)
*/
-@property(nonatomic, assign, readonly) int ID;
+@property(nonatomic, assign, readonly) int32_t ID;
@end
diff --git a/Firestore/Source/Local/FSTDocumentReference.m b/Firestore/Source/Local/FSTDocumentReference.mm
index 25a5935..4310baa 100644
--- a/Firestore/Source/Local/FSTDocumentReference.m
+++ b/Firestore/Source/Local/FSTDocumentReference.mm
@@ -16,14 +16,17 @@
#import "Firestore/Source/Local/FSTDocumentReference.h"
+#include "Firestore/core/src/firebase/firestore/util/comparison.h"
+
#import "Firestore/Source/Model/FSTDocumentKey.h"
-#import "Firestore/Source/Util/FSTComparison.h"
+
+using firebase::firestore::util::WrapCompare;
NS_ASSUME_NONNULL_BEGIN
@implementation FSTDocumentReference
-- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int)ID {
+- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int32_t)ID {
self = [super init];
if (self) {
_key = key;
@@ -67,13 +70,13 @@ const NSComparator FSTDocumentReferenceComparatorByKey =
if (result != NSOrderedSame) {
return result;
}
- return FSTCompareInts(left.ID, right.ID);
+ return WrapCompare<int32_t>(left.ID, right.ID);
};
/** Sorts document references by ID then key. */
const NSComparator FSTDocumentReferenceComparatorByID =
^NSComparisonResult(FSTDocumentReference *left, FSTDocumentReference *right) {
- NSComparisonResult result = FSTCompareInts(left.ID, right.ID);
+ NSComparisonResult result = WrapCompare<int32_t>(left.ID, right.ID);
if (result != NSOrderedSame) {
return result;
}
diff --git a/Firestore/Source/Local/FSTMemoryMutationQueue.m b/Firestore/Source/Local/FSTMemoryMutationQueue.mm
index b155264..702f614 100644
--- a/Firestore/Source/Local/FSTMemoryMutationQueue.m
+++ b/Firestore/Source/Local/FSTMemoryMutationQueue.mm
@@ -23,10 +23,13 @@
#import "Firestore/Source/Model/FSTMutationBatch.h"
#import "Firestore/Source/Model/FSTPath.h"
#import "Firestore/Source/Util/FSTAssert.h"
-#import "Firestore/Source/Util/FSTComparison.h"
NS_ASSUME_NONNULL_BEGIN
+static const NSComparator NumberComparator = ^NSComparisonResult(NSNumber *left, NSNumber *right) {
+ return [left compare:right];
+};
+
@interface FSTMemoryMutationQueue ()
/**
@@ -260,7 +263,7 @@ NS_ASSUME_NONNULL_BEGIN
// Find unique batchIDs referenced by all documents potentially matching the query.
__block FSTImmutableSortedSet<NSNumber *> *uniqueBatchIDs =
- [FSTImmutableSortedSet setWithComparator:FSTNumberComparator];
+ [FSTImmutableSortedSet setWithComparator:NumberComparator];
FSTDocumentReferenceBlock block = ^(FSTDocumentReference *reference, BOOL *stop) {
FSTResourcePath *rowKeyPath = reference.key.path;
if (![prefix isPrefixOfPath:rowKeyPath]) {
diff --git a/Firestore/Source/Model/FSTFieldValue.m b/Firestore/Source/Model/FSTFieldValue.mm
index a6326a7..8ffc98e 100644
--- a/Firestore/Source/Model/FSTFieldValue.m
+++ b/Firestore/Source/Model/FSTFieldValue.mm
@@ -16,6 +16,9 @@
#import "Firestore/Source/Model/FSTFieldValue.h"
+#include "Firestore/core/src/firebase/firestore/util/comparison.h"
+#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
+
#import "Firestore/Source/API/FIRGeoPoint+Internal.h"
#import "Firestore/Source/API/FIRSnapshotOptions+Internal.h"
#import "Firestore/Source/Core/FSTTimestamp.h"
@@ -24,7 +27,14 @@
#import "Firestore/Source/Model/FSTPath.h"
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTClasses.h"
-#import "Firestore/Source/Util/FSTComparison.h"
+
+using firebase::firestore::util::Comparator;
+using firebase::firestore::util::CompareMixedNumber;
+using firebase::firestore::util::DoubleBitwiseEquals;
+using firebase::firestore::util::DoubleBitwiseHash;
+using firebase::firestore::util::MakeStringView;
+using firebase::firestore::util::ReverseOrder;
+using firebase::firestore::util::WrapCompare;
NS_ASSUME_NONNULL_BEGIN
@@ -208,7 +218,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSComparisonResult)compare:(FSTFieldValue *)other {
if ([other isKindOfClass:[FSTBooleanValue class]]) {
- return FSTCompareBools(self.internalValue, ((FSTBooleanValue *)other).internalValue);
+ return WrapCompare<bool>(self.internalValue, ((FSTBooleanValue *)other).internalValue);
} else {
return [self defaultCompare:other];
}
@@ -231,19 +241,22 @@ NS_ASSUME_NONNULL_BEGIN
if ([self isKindOfClass:[FSTDoubleValue class]]) {
double thisDouble = ((FSTDoubleValue *)self).internalValue;
if ([other isKindOfClass:[FSTDoubleValue class]]) {
- return FSTCompareDoubles(thisDouble, ((FSTDoubleValue *)other).internalValue);
+ return WrapCompare(thisDouble, ((FSTDoubleValue *)other).internalValue);
} else {
FSTAssert([other isKindOfClass:[FSTIntegerValue class]], @"Unknown number value: %@",
other);
- return FSTCompareMixed(thisDouble, ((FSTIntegerValue *)other).internalValue);
+ auto result = CompareMixedNumber(thisDouble, ((FSTIntegerValue *)other).internalValue);
+ return static_cast<NSComparisonResult>(result);
}
} else {
int64_t thisInt = ((FSTIntegerValue *)self).internalValue;
if ([other isKindOfClass:[FSTIntegerValue class]]) {
- return FSTCompareInt64s(thisInt, ((FSTIntegerValue *)other).internalValue);
+ return WrapCompare(thisInt, ((FSTIntegerValue *)other).internalValue);
} else {
FSTAssert([other isKindOfClass:[FSTDoubleValue class]], @"Unknown number value: %@", other);
- return -1 * FSTCompareMixed(((FSTDoubleValue *)other).internalValue, thisInt);
+ double otherDouble = ((FSTDoubleValue *)other).internalValue;
+ auto result = ReverseOrder(CompareMixedNumber(otherDouble, thisInt));
+ return static_cast<NSComparisonResult>(result);
}
}
}
@@ -334,11 +347,11 @@ NS_ASSUME_NONNULL_BEGIN
// NOTE: isEqual: should compare NaN equal to itself and -0.0 not equal to 0.0.
return [other isKindOfClass:[FSTDoubleValue class]] &&
- FSTDoubleBitwiseEquals(self.internalValue, ((FSTDoubleValue *)other).internalValue);
+ DoubleBitwiseEquals(self.internalValue, ((FSTDoubleValue *)other).internalValue);
}
- (NSUInteger)hash {
- return FSTDoubleBitwiseHash(self.internalValue);
+ return DoubleBitwiseHash(self.internalValue);
}
// NOTE: compare: is implemented in NumberValue.
@@ -347,6 +360,17 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTStringValue
+/**
+ * Specialization of Comparator for NSStrings.
+ */
+template <>
+struct Comparator<NSString *> {
+ bool operator()(NSString *left, NSString *right) const {
+ Comparator<absl::string_view> lessThan;
+ return lessThan(MakeStringView(left), MakeStringView(right));
+ }
+};
+
@interface FSTStringValue ()
@property(nonatomic, copy, readonly) NSString *internalValue;
@end
@@ -385,7 +409,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSComparisonResult)compare:(FSTFieldValue *)other {
if ([other isKindOfClass:[FSTStringValue class]]) {
- return FSTCompareStrings(self.internalValue, ((FSTStringValue *)other).internalValue);
+ return WrapCompare(self.internalValue, ((FSTStringValue *)other).internalValue);
} else {
return [self defaultCompare:other];
}
@@ -556,6 +580,22 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTBlobValue
+static NSComparisonResult CompareBytes(NSData *left, NSData *right) {
+ NSUInteger minLength = MIN(left.length, right.length);
+ int result = memcmp(left.bytes, right.bytes, minLength);
+ if (result < 0) {
+ return NSOrderedAscending;
+ } else if (result > 0) {
+ return NSOrderedDescending;
+ } else if (left.length < right.length) {
+ return NSOrderedAscending;
+ } else if (left.length > right.length) {
+ return NSOrderedDescending;
+ } else {
+ return NSOrderedSame;
+ }
+}
+
@interface FSTBlobValue ()
@property(nonatomic, copy, readonly) NSData *internalValue;
@end
@@ -594,7 +634,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSComparisonResult)compare:(FSTFieldValue *)other {
if ([other isKindOfClass:[FSTBlobValue class]]) {
- return FSTCompareBytes(self.internalValue, ((FSTBlobValue *)other).internalValue);
+ return CompareBytes(self.internalValue, ((FSTBlobValue *)other).internalValue);
} else {
return [self defaultCompare:other];
}
@@ -664,6 +704,10 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTObjectValue
+static const NSComparator StringComparator = ^NSComparisonResult(NSString *left, NSString *right) {
+ return WrapCompare(left, right);
+};
+
@interface FSTObjectValue ()
@property(nonatomic, strong, readonly)
FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *internalValue;
@@ -677,7 +721,7 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_once(&onceToken, ^{
FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *empty =
- [FSTImmutableSortedDictionary dictionaryWithComparator:FSTStringComparator];
+ [FSTImmutableSortedDictionary dictionaryWithComparator:StringComparator];
sharedEmptyInstance = [[FSTObjectValue alloc] initWithImmutableDictionary:empty];
});
return sharedEmptyInstance;
@@ -694,7 +738,7 @@ NS_ASSUME_NONNULL_BEGIN
- (id)initWithDictionary:(NSDictionary<NSString *, FSTFieldValue *> *)value {
FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *dictionary =
- [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:FSTStringComparator];
+ [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:StringComparator];
return [self initWithImmutableDictionary:dictionary];
}
@@ -748,7 +792,7 @@ NS_ASSUME_NONNULL_BEGIN
key2 = [enumerator2 nextObject];
}
// Only equal if both enumerators are exhausted.
- return FSTCompareBools(key1 != nil, key2 != nil);
+ return WrapCompare(key1 != nil, key2 != nil);
} else {
return [self defaultCompare:other];
}
@@ -876,7 +920,7 @@ NS_ASSUME_NONNULL_BEGIN
return cmp;
}
}
- return FSTCompareUIntegers(selfArray.count, otherArray.count);
+ return WrapCompare<int64_t>(selfArray.count, otherArray.count);
} else {
return [self defaultCompare:other];
}
diff --git a/Firestore/Source/Util/FSTComparison.h b/Firestore/Source/Util/FSTComparison.h
deleted file mode 100644
index e6e57e6..0000000
--- a/Firestore/Source/Util/FSTComparison.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import <Foundation/Foundation.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Compares two NSStrings. */
-NSComparisonResult FSTCompareStrings(NSString *left, NSString *right);
-
-/** Compares two BOOLs. */
-NSComparisonResult FSTCompareBools(BOOL left, BOOL right);
-
-/** Compares two integers. */
-NSComparisonResult FSTCompareInts(int left, int right);
-
-/** Compares two int32_t. */
-NSComparisonResult FSTCompareInt32s(int32_t left, int32_t right);
-
-/** Compares two int64_t. */
-NSComparisonResult FSTCompareInt64s(int64_t left, int64_t right);
-
-/** Compares two NSUIntegers. */
-NSComparisonResult FSTCompareUIntegers(NSUInteger left, NSUInteger right);
-
-/** Compares two doubles (using Firestore semantics for NaN). */
-NSComparisonResult FSTCompareDoubles(double left, double right);
-
-/** Compares a double and an int64_t. */
-NSComparisonResult FSTCompareMixed(double doubleValue, int64_t longValue);
-
-/** Compare two NSData byte sequences. */
-NSComparisonResult FSTCompareBytes(NSData *left, NSData *right);
-
-/** A simple NSComparator for comparing NSNumber instances. */
-extern const NSComparator FSTNumberComparator;
-
-/** A simple NSComparator for comparing NSString instances. */
-extern const NSComparator FSTStringComparator;
-
-/**
- * Compares the bitwise representation of two doubles, but normalizes NaN values. This is
- * similar to what the backend and android clients do, including comparing -0.0 as not equal to 0.0.
- */
-BOOL FSTDoubleBitwiseEquals(double left, double right);
-
-/**
- * Computes a bitwise hash of a double, but normalizes NaN values, suitable for use when using
- * FSTDoublesAreBitwiseEqual for equality.
- */
-NSUInteger FSTDoubleBitwiseHash(double d);
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Util/FSTComparison.m b/Firestore/Source/Util/FSTComparison.m
deleted file mode 100644
index 9c5c3eb..0000000
--- a/Firestore/Source/Util/FSTComparison.m
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#import "Firestore/Source/Util/FSTComparison.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-union DoubleBits {
- double d;
- uint64_t bits;
-};
-
-const NSComparator FSTNumberComparator = ^NSComparisonResult(NSNumber *left, NSNumber *right) {
- return [left compare:right];
-};
-
-const NSComparator FSTStringComparator = ^NSComparisonResult(NSString *left, NSString *right) {
- return FSTCompareStrings(left, right);
-};
-
-NSComparisonResult FSTCompareStrings(NSString *left, NSString *right) {
- // NOTE: NSLiteralSearch is necessary to compare the raw character codes. By default,
- // precomposed characters are considered equivalent to their decomposed equivalents.
- return [left compare:right options:NSLiteralSearch];
-}
-
-NSComparisonResult FSTCompareBools(BOOL left, BOOL right) {
- if (!left) {
- return right ? NSOrderedAscending : NSOrderedSame;
- } else {
- return right ? NSOrderedSame : NSOrderedDescending;
- }
-}
-
-NSComparisonResult FSTCompareInts(int left, int right) {
- if (left > right) {
- return NSOrderedDescending;
- }
- if (right > left) {
- return NSOrderedAscending;
- }
- return NSOrderedSame;
-}
-
-NSComparisonResult FSTCompareInt32s(int32_t left, int32_t right) {
- if (left > right) {
- return NSOrderedDescending;
- }
- if (right > left) {
- return NSOrderedAscending;
- }
- return NSOrderedSame;
-}
-
-NSComparisonResult FSTCompareInt64s(int64_t left, int64_t right) {
- if (left > right) {
- return NSOrderedDescending;
- }
- if (right > left) {
- return NSOrderedAscending;
- }
- return NSOrderedSame;
-}
-
-NSComparisonResult FSTCompareUIntegers(NSUInteger left, NSUInteger right) {
- if (left > right) {
- return NSOrderedDescending;
- }
- if (right > left) {
- return NSOrderedAscending;
- }
- return NSOrderedSame;
-}
-
-NSComparisonResult FSTCompareDoubles(double left, double right) {
- // NaN sorts equal to itself and before any other number.
- if (left < right) {
- return NSOrderedAscending;
- } else if (left > right) {
- return NSOrderedDescending;
- } else if (left == right) {
- return NSOrderedSame;
- } else {
- // One or both left and right is NaN.
- if (isnan(left)) {
- return isnan(right) ? NSOrderedSame : NSOrderedAscending;
- } else {
- return NSOrderedDescending;
- }
- }
-}
-
-static const double LONG_MIN_VALUE_AS_DOUBLE = (double)LLONG_MIN;
-static const double LONG_MAX_VALUE_AS_DOUBLE = (double)LLONG_MAX;
-
-NSComparisonResult FSTCompareMixed(double doubleValue, int64_t longValue) {
- // LLONG_MIN has an exact representation as double, so to check for a value outside the range
- // representable by long, we have to check for strictly less than LLONG_MIN. Note that this also
- // handles negative infinity.
- if (doubleValue < LONG_MIN_VALUE_AS_DOUBLE) {
- return NSOrderedAscending;
- }
-
- // LLONG_MAX has no exact representation as double (casting as we've done makes 2^63, which is
- // larger than LLONG_MAX), so consider any value greater than or equal to the threshold to be out
- // of range. This also handles positive infinity.
- if (doubleValue >= LONG_MAX_VALUE_AS_DOUBLE) {
- return NSOrderedDescending;
- }
-
- // In Firestore NaN is defined to compare before all other numbers.
- if (isnan(doubleValue)) {
- return NSOrderedAscending;
- }
-
- int64_t doubleAsLong = (int64_t)doubleValue;
- NSComparisonResult cmp = FSTCompareInt64s(doubleAsLong, longValue);
- if (cmp != NSOrderedSame) {
- return cmp;
- }
-
- // At this point the long representations are equal but this could be due to rounding.
- double longAsDouble = (double)longValue;
- return FSTCompareDoubles(doubleValue, longAsDouble);
-}
-
-NSComparisonResult FSTCompareBytes(NSData *left, NSData *right) {
- NSUInteger minLength = MIN(left.length, right.length);
- int result = memcmp(left.bytes, right.bytes, minLength);
- if (result < 0) {
- return NSOrderedAscending;
- } else if (result > 0) {
- return NSOrderedDescending;
- } else if (left.length < right.length) {
- return NSOrderedAscending;
- } else if (left.length > right.length) {
- return NSOrderedDescending;
- } else {
- return NSOrderedSame;
- }
-}
-
-/** Helper to normalize a double and then return the raw bits as a uint64_t. */
-uint64_t FSTDoubleBits(double d) {
- if (isnan(d)) {
- d = NAN;
- }
- union DoubleBits converter = {.d = d};
- return converter.bits;
-}
-
-BOOL FSTDoubleBitwiseEquals(double left, double right) {
- return FSTDoubleBits(left) == FSTDoubleBits(right);
-}
-
-NSUInteger FSTDoubleBitwiseHash(double d) {
- uint64_t bits = FSTDoubleBits(d);
- // Note that x ^ (x >> 32) works fine for both 32 and 64 bit definitions of NSUInteger
- return (((NSUInteger)bits) ^ (NSUInteger)(bits >> 32));
-}
-
-NS_ASSUME_NONNULL_END
diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
index 737173b..09db164 100644
--- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
+++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
@@ -49,6 +49,7 @@ cc_library(
string_apple.h
DEPENDS
FirebaseCore
+ absl_strings
EXCLUDE_FROM_ALL
)
@@ -106,6 +107,8 @@ cc_library(
SOURCES
autoid.cc
autoid.h
+ comparison.cc
+ comparison.h
config.h
firebase_assert.h
log.h
diff --git a/Firestore/core/src/firebase/firestore/util/comparison.cc b/Firestore/core/src/firebase/firestore/util/comparison.cc
new file mode 100644
index 0000000..4bef843
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/comparison.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Firestore/core/src/firebase/firestore/util/comparison.h"
+
+#include <math.h>
+
+#include <limits>
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+bool Comparator<absl::string_view>::operator()(
+ const absl::string_view& left, const absl::string_view& right) const {
+ // TODO(wilhuff): truncation aware comparison
+ return left < right;
+}
+
+bool Comparator<double>::operator()(double left, double right) const {
+ // NaN sorts equal to itself and before any other number.
+ if (left < right) {
+ return true;
+ } else if (left >= right) {
+ return false;
+ } else {
+ // One or both left and right is NaN.
+ return isnan(left) && !isnan(right);
+ }
+}
+
+static constexpr double INT64_MIN_VALUE_AS_DOUBLE =
+ static_cast<double>(std::numeric_limits<int64_t>::min());
+
+static constexpr double INT64_MAX_VALUE_AS_DOUBLE =
+ static_cast<double>(std::numeric_limits<int64_t>::max());
+
+ComparisonResult CompareMixedNumber(double double_value, int64_t int64_value) {
+ // LLONG_MIN has an exact representation as double, so to check for a value
+ // outside the range representable by long, we have to check for strictly less
+ // than LLONG_MIN. Note that this also handles negative infinity.
+ if (double_value < INT64_MIN_VALUE_AS_DOUBLE) {
+ return ComparisonResult::Ascending;
+ }
+
+ // LLONG_MAX has no exact representation as double (casting as we've done
+ // makes 2^63, which is larger than LLONG_MAX), so consider any value greater
+ // than or equal to the threshold to be out of range. This also handles
+ // positive infinity.
+ if (double_value >= INT64_MAX_VALUE_AS_DOUBLE) {
+ return ComparisonResult::Descending;
+ }
+
+ // In Firestore NaN is defined to compare before all other numbers.
+ if (isnan(double_value)) {
+ return ComparisonResult::Ascending;
+ }
+
+ auto double_as_int64 = static_cast<int64_t>(double_value);
+ ComparisonResult cmp = Compare<int64_t>(double_as_int64, int64_value);
+ if (cmp != ComparisonResult::Same) {
+ return cmp;
+ }
+
+ // At this point the long representations are equal but this could be due to
+ // rounding.
+ double int64_as_double = static_cast<double>(int64_value);
+ return Compare<double>(double_value, int64_as_double);
+}
+
+/** Helper to normalize a double and then return the raw bits as a uint64_t. */
+uint64_t DoubleBits(double d) {
+ if (isnan(d)) {
+ d = NAN;
+ }
+
+ // Unlike C, C++ does not define type punning through a union type.
+
+ // TODO(wilhuff): replace with absl::bit_cast
+ static_assert(sizeof(double) == sizeof(uint64_t), "doubles must be 8 bytes");
+ uint64_t bits;
+ memcpy(&bits, &d, sizeof(bits));
+ return bits;
+}
+
+bool DoubleBitwiseEquals(double left, double right) {
+ return DoubleBits(left) == DoubleBits(right);
+}
+
+size_t DoubleBitwiseHash(double d) {
+ uint64_t bits = DoubleBits(d);
+ // Note that x ^ (x >> 32) works fine for both 32 and 64 bit definitions of
+ // size_t
+ return static_cast<size_t>(bits) ^ static_cast<size_t>(bits >> 32);
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/util/comparison.h b/Firestore/core/src/firebase/firestore/util/comparison.h
new file mode 100644
index 0000000..6fd1e2b
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/comparison.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_COMPARISON_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_COMPARISON_H_
+
+#if __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
+#include "absl/strings/string_view.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+/**
+ * An enumeration describing the result of a three-way comparison among
+ * strongly-ordered values (i.e. where comparison between values always yields
+ * less-than, equal-to, or greater-than).
+ *
+ * This is equivalent to:
+ *
+ * * NSComparisonResult from the iOS/macOS Foundation framework.
+ * * std::strong_ordering from C++20
+ *
+ * The values of the constants are specifically chosen so as to make casting
+ * between this type and NSComparisonResult possible.
+ */
+enum class ComparisonResult {
+ /** The left hand side was less than the right. */
+ Ascending = -1,
+
+ /** The left hand side was equal to the right. */
+ Same = 0,
+
+ /** The left hand side was greater than the right. */
+ Descending = 1
+};
+
+/**
+ * Returns the reverse order (i.e. Ascending => Descending) etc.
+ */
+constexpr ComparisonResult ReverseOrder(ComparisonResult result) {
+ return static_cast<ComparisonResult>(-static_cast<int>(result));
+}
+
+/**
+ * A generalized comparator for types in Firestore, with ordering defined
+ * according to Firestore's semantics. This is useful as argument to e.g.
+ * std::sort.
+ *
+ * Comparators are only defined for the limited set of types for which
+ * Firestore defines an ordering.
+ */
+template <typename T>
+struct Comparator {
+ // By default comparison is not defined
+};
+
+/** Compares two strings. */
+template <>
+struct Comparator<absl::string_view> {
+ bool operator()(const absl::string_view& left,
+ const absl::string_view& right) const;
+};
+
+/** Compares two bools: false < true. */
+template <>
+struct Comparator<bool> : public std::less<bool> {};
+
+/** Compares two int32_t. */
+template <>
+struct Comparator<int32_t> : public std::less<int32_t> {};
+
+/** Compares two int64_t. */
+template <>
+struct Comparator<int64_t> : public std::less<int64_t> {};
+
+/** Compares two doubles (using Firestore semantics for NaN). */
+template <>
+struct Comparator<double> {
+ bool operator()(double left, double right) const;
+};
+
+/** Compare two byte sequences. */
+// TODO(wilhuff): perhaps absl::Span<uint8_t> would be better?
+template <>
+struct Comparator<std::vector<uint8_t>>
+ : public std::less<std::vector<uint8_t>> {};
+
+/**
+ * Perform a three-way comparison between the left and right values using
+ * the appropriate Comparator for the values based on their type.
+ */
+template <typename T>
+ComparisonResult Compare(const T& left, const T& right) {
+ Comparator<T> less_than;
+ if (less_than(left, right)) {
+ return ComparisonResult::Ascending;
+ } else if (less_than(right, left)) {
+ return ComparisonResult::Descending;
+ } else {
+ return ComparisonResult::Same;
+ }
+}
+
+#if __OBJC__
+/**
+ * Returns true if the given ComparisonResult and NSComparisonResult have the
+ * same integer values (at compile time).
+ */
+constexpr bool EqualValue(ComparisonResult lhs, NSComparisonResult rhs) {
+ return static_cast<int>(lhs) == static_cast<int>(rhs);
+}
+
+/**
+ * Performs a three-way comparison, identically to Compare, but converts the
+ * result to an NSComparisonResult.
+ *
+ * This function exists for interoperation with Objective-C++ and should
+ * eventually be removed.
+ */
+template <typename T>
+inline NSComparisonResult WrapCompare(const T& left, const T& right) {
+ static_assert(EqualValue(ComparisonResult::Ascending, NSOrderedAscending),
+ "Ascending invalid");
+ static_assert(EqualValue(ComparisonResult::Same, NSOrderedSame),
+ "Same invalid");
+ static_assert(EqualValue(ComparisonResult::Descending, NSOrderedDescending),
+ "Descending invalid");
+
+ return static_cast<NSComparisonResult>(Compare<T>(left, right));
+}
+#endif
+
+/** Compares a double and an int64_t. */
+ComparisonResult CompareMixedNumber(double doubleValue, int64_t longValue);
+
+/** Normalizes a double and then return the raw bits as a uint64_t. */
+uint64_t DoubleBits(double d);
+
+/**
+ * Compares the bitwise representation of two doubles, but normalizes NaN
+ * values. This is similar to what the backend and android clients do, including
+ * comparing -0.0 as not equal to 0.0.
+ */
+bool DoubleBitwiseEquals(double left, double right);
+
+/**
+ * Computes a bitwise hash of a double, but normalizes NaN values, suitable for
+ * use when using FSTDoublesAreBitwiseEqual for equality.
+ */
+size_t DoubleBitwiseHash(double d);
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_COMPARISON_H_
diff --git a/Firestore/core/src/firebase/firestore/util/string_apple.h b/Firestore/core/src/firebase/firestore/util/string_apple.h
index e1be8c3..108ade7 100644
--- a/Firestore/core/src/firebase/firestore/util/string_apple.h
+++ b/Firestore/core/src/firebase/firestore/util/string_apple.h
@@ -17,8 +17,13 @@
#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_APPLE_H_
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_APPLE_H_
+// Everything in this header exists for compatibility with Objective-C.
+#if __OBJC__
+
#import <Foundation/Foundation.h>
+#include "absl/strings/string_view.h"
+
namespace firebase {
namespace firestore {
namespace util {
@@ -32,8 +37,16 @@ inline NSString* WrapNSStringNoCopy(const char* c_str) {
freeWhenDone:NO];
}
+// Creates an absl::string_view wrapper for the contents of the given NSString.
+inline absl::string_view MakeStringView(NSString* str) {
+ return absl::string_view(
+ [str UTF8String], [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
+}
+
} // namespace util
} // namespace firestore
} // namespace firebase
+#endif // __OBJC__
+
#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_APPLE_H_
diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
index 468c62e..5e7612c 100644
--- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
+++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
@@ -36,6 +36,7 @@ cc_test(
firebase_firestore_util_test
SOURCES
autoid_test.cc
+ comparison_test.cc
string_printf_test.cc
DEPENDS
firebase_firestore_util
diff --git a/Firestore/core/test/firebase/firestore/util/comparison_test.cc b/Firestore/core/test/firebase/firestore/util/comparison_test.cc
new file mode 100644
index 0000000..ecbed4a
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/util/comparison_test.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Firestore/core/src/firebase/firestore/util/comparison.h"
+
+#include <math.h>
+
+#include <limits>
+
+#include "Firestore/core/src/firebase/firestore/util/string_printf.h"
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+#define ASSERT_SAME(comparison) \
+ do { \
+ ASSERT_EQ(ComparisonResult::Same, comparison); \
+ } while (0)
+
+#define ASSERT_ASCENDING(comparison) \
+ do { \
+ ASSERT_EQ(ComparisonResult::Ascending, comparison); \
+ } while (0)
+
+#define ASSERT_DESCENDING(comparison) \
+ do { \
+ ASSERT_EQ(ComparisonResult::Descending, comparison); \
+ } while (0)
+
+TEST(Comparison, ReverseOrder) {
+ ASSERT_ASCENDING(ReverseOrder(ComparisonResult::Descending));
+ ASSERT_DESCENDING(ReverseOrder(ComparisonResult::Ascending));
+ ASSERT_SAME(ReverseOrder(ComparisonResult::Same));
+}
+
+TEST(Comparison, StringCompare) {
+ ASSERT_ASCENDING(Compare<absl::string_view>("", "a"));
+ ASSERT_ASCENDING(Compare<absl::string_view>("a", "b"));
+ ASSERT_ASCENDING(Compare<absl::string_view>("a", "aa"));
+
+ ASSERT_DESCENDING(Compare<absl::string_view>("a", ""));
+ ASSERT_DESCENDING(Compare<absl::string_view>("b", "a"));
+ ASSERT_DESCENDING(Compare<absl::string_view>("aa", "a"));
+
+ ASSERT_SAME(Compare<absl::string_view>("", ""));
+ ASSERT_SAME(Compare<absl::string_view>("", std::string()));
+ ASSERT_SAME(Compare<absl::string_view>("a", "a"));
+}
+
+TEST(Comparison, BooleanCompare) {
+ ASSERT_SAME(Compare<bool>(false, false));
+ ASSERT_SAME(Compare<bool>(true, true));
+ ASSERT_ASCENDING(Compare<bool>(false, true));
+ ASSERT_DESCENDING(Compare<bool>(true, false));
+}
+
+TEST(Comparison, DoubleCompare) {
+ ASSERT_SAME(Compare<double>(NAN, NAN));
+ ASSERT_ASCENDING(Compare<double>(NAN, 0));
+ ASSERT_DESCENDING(Compare<double>(0, NAN));
+
+ ASSERT_SAME(Compare<double>(-INFINITY, -INFINITY));
+ ASSERT_SAME(Compare<double>(INFINITY, INFINITY));
+ ASSERT_ASCENDING(Compare<double>(-INFINITY, INFINITY));
+ ASSERT_DESCENDING(Compare<double>(INFINITY, -INFINITY));
+
+ ASSERT_SAME(Compare<double>(0, 0));
+ ASSERT_SAME(Compare<double>(-0, -0));
+ ASSERT_SAME(Compare<double>(-0, 0));
+}
+
+#define ASSERT_BIT_EQUALS(expected, actual) \
+ do { \
+ uint64_t expectedBits = DoubleBits(expected); \
+ uint64_t actualBits = DoubleBits(actual); \
+ if (expectedBits != actualBits) { \
+ std::string message = StringPrintf( \
+ "Expected <%f> to compare equal to <%f> " \
+ "with bits <%llX> equal to <%llX>", \
+ actual, expected, actualBits, expectedBits); \
+ FAIL() << message; \
+ } \
+ } while (0);
+
+#define ASSERT_MIXED_SAME(doubleValue, longValue) \
+ do { \
+ ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \
+ if (result != ComparisonResult::Same) { \
+ std::string message = StringPrintf( \
+ "Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \
+ FAIL() << message; \
+ } \
+ } while (0);
+
+#define ASSERT_MIXED_DESCENDING(doubleValue, longValue) \
+ do { \
+ ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \
+ if (result != ComparisonResult::Descending) { \
+ std::string message = StringPrintf( \
+ "Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \
+ FAIL() << message; \
+ } \
+ } while (0);
+
+#define ASSERT_MIXED_ASCENDING(doubleValue, longValue) \
+ do { \
+ ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \
+ if (result != ComparisonResult::Ascending) { \
+ std::string message = StringPrintf( \
+ "Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \
+ FAIL() << message; \
+ } \
+ } while (0);
+
+TEST(Comparison, MixedNumberCompare) {
+ // Infinities
+ ASSERT_MIXED_ASCENDING(-INFINITY, LLONG_MIN);
+ ASSERT_MIXED_ASCENDING(-INFINITY, LLONG_MAX);
+ ASSERT_MIXED_ASCENDING(-INFINITY, 0LL);
+
+ ASSERT_MIXED_DESCENDING(INFINITY, LLONG_MIN);
+ ASSERT_MIXED_DESCENDING(INFINITY, LLONG_MAX);
+ ASSERT_MIXED_DESCENDING(INFINITY, 0LL);
+
+ // NaN
+ ASSERT_MIXED_ASCENDING(NAN, LLONG_MIN);
+ ASSERT_MIXED_ASCENDING(NAN, LLONG_MAX);
+ ASSERT_MIXED_ASCENDING(NAN, 0LL);
+
+ // Large values (note DBL_MIN is positive and near zero).
+ ASSERT_MIXED_ASCENDING(-DBL_MAX, LLONG_MIN);
+
+ // Tests around LLONG_MIN
+ ASSERT_BIT_EQUALS((double)LLONG_MIN, -0x1.0p63);
+ ASSERT_MIXED_SAME(-0x1.0p63, LLONG_MIN);
+ ASSERT_MIXED_ASCENDING(-0x1.0p63, LLONG_MIN + 1);
+
+ ASSERT_LT(-0x1.0000000000001p63, -0x1.0p63);
+ ASSERT_MIXED_ASCENDING(-0x1.0000000000001p63, LLONG_MIN);
+ ASSERT_MIXED_DESCENDING(-0x1.FFFFFFFFFFFFFp62, LLONG_MIN);
+
+ // Tests around LLONG_MAX
+ // Note LLONG_MAX cannot be exactly represented by a double, so the system
+ // rounds it to the nearest double, which is 2^63. This number, in turn is
+ // larger than the maximum representable as a long.
+ ASSERT_BIT_EQUALS(0x1.0p63, (double)LLONG_MAX);
+ ASSERT_MIXED_DESCENDING(0x1.0p63, LLONG_MAX);
+
+ // The largest value with an exactly long representation
+ ASSERT_EQ((int64_t)0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL);
+ ASSERT_MIXED_SAME(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL);
+
+ ASSERT_MIXED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFB00LL);
+ ASSERT_MIXED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFBFFLL);
+ ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC01LL);
+ ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFD00LL);
+
+ ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFEp62, 0x7FFFFFFFFFFFFC00LL);
+
+ // Tests around MAX_SAFE_INTEGER
+ ASSERT_MIXED_SAME(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFFLL);
+ ASSERT_MIXED_DESCENDING(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFELL);
+ ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFEp52, 0x1FFFFFFFFFFFFFLL);
+ ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFFp52, 0x20000000000000LL);
+
+ // Tests around MIN_SAFE_INTEGER
+ ASSERT_MIXED_SAME(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFFLL);
+ ASSERT_MIXED_ASCENDING(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFELL);
+ ASSERT_MIXED_DESCENDING(-0x1.FFFFFFFFFFFFEp52, -0x1FFFFFFFFFFFFFLL);
+ ASSERT_MIXED_DESCENDING(-0x1.FFFFFFFFFFFFFp52, -0x20000000000000LL);
+
+ // Tests around zero.
+ ASSERT_MIXED_SAME(-0.0, 0LL);
+ ASSERT_MIXED_SAME(0.0, 0LL);
+
+ // The smallest representable positive value should be greater than zero
+ ASSERT_MIXED_DESCENDING(DBL_MIN, 0LL);
+ ASSERT_MIXED_ASCENDING(-DBL_MIN, 0LL);
+
+ // Note that 0x1.0p-1074 is a hex floating point literal representing the
+ // minimum subnormal number: <https://en.wikipedia.org/wiki/Denormal_number>.
+ double minSubNormal = 0x1.0p-1074;
+ ASSERT_MIXED_DESCENDING(minSubNormal, 0LL);
+ ASSERT_MIXED_ASCENDING(-minSubNormal, 0LL);
+
+ // Other sanity checks
+ ASSERT_MIXED_ASCENDING(0.5, 1LL);
+ ASSERT_MIXED_DESCENDING(0.5, 0LL);
+ ASSERT_MIXED_ASCENDING(1.5, 2LL);
+ ASSERT_MIXED_DESCENDING(1.5, 1LL);
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/third_party/abseil-cpp/absl/base/macros.h b/Firestore/third_party/abseil-cpp/absl/base/macros.h
index d414087..07c5cf7 100644
--- a/Firestore/third_party/abseil-cpp/absl/base/macros.h
+++ b/Firestore/third_party/abseil-cpp/absl/base/macros.h
@@ -196,7 +196,7 @@ enum LinkerInitialized {
#define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0)
#else
#define ABSL_ASSERT(expr) \
- (ABSL_PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }())
+ (void) (ABSL_PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }())
#endif
#endif // ABSL_BASE_MACROS_H_