diff options
author | Gil <mcg@google.com> | 2018-01-19 12:27:11 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-19 12:27:11 -0800 |
commit | 39d8252300015c26f1932cff42032613fdb36a09 (patch) | |
tree | 93bb3ab7c476289c4afe6cf91773a60b6f53a94d /Firestore/core/src/firebase/firestore/util/comparison.cc | |
parent | 9f7c094f9f00a6efc0107071f109ef1bc4d7357d (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
Diffstat (limited to 'Firestore/core/src/firebase/firestore/util/comparison.cc')
-rw-r--r-- | Firestore/core/src/firebase/firestore/util/comparison.cc | 112 |
1 files changed, 112 insertions, 0 deletions
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 |