diff options
Diffstat (limited to 'Firestore/Source/Util/FSTComparison.m')
-rw-r--r-- | Firestore/Source/Util/FSTComparison.m | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/Firestore/Source/Util/FSTComparison.m b/Firestore/Source/Util/FSTComparison.m new file mode 100644 index 0000000..e4f4ccb --- /dev/null +++ b/Firestore/Source/Util/FSTComparison.m @@ -0,0 +1,175 @@ +/* + * 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 "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 |