RenderCore/Utilities/RCEqualityHelpers.h (155 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import <RenderCore/CKDefines.h> #if CK_NOT_SWIFT #import <Foundation/Foundation.h> #import <string> #import <vector> // From folly: // This is the Hash128to64 function from Google's cityhash (available // under the MIT License). We use it to reduce multiple 64 bit hashes // into a single hash. inline uint64_t RCHashCombine(const uint64_t upper, const uint64_t lower) { // Murmur-inspired hashing. const uint64_t kMul = 0x9ddfea08eb382d69ULL; uint64_t a = (lower ^ upper) * kMul; a ^= (a >> 47); uint64_t b = (upper ^ a) * kMul; b ^= (b >> 47); b *= kMul; return b; } // fnv-1 hash function inline uint64_t RCHashCString(const char *str) { uint64_t retval = 0; unsigned char *s = (unsigned char *)str; while (*s) { retval *= (uint64_t)0x100000001b3ULL; retval ^= (uint64_t)*s++; } return retval; } #if defined(__LP64__) && __LP64__ inline size_t RCHash64ToNative(uint64_t key) { return key; } #else // Thomas Wang downscaling hash function inline size_t RCHash64ToNative(uint64_t key) { key = (~key) + (key << 18); key = key ^ (key >> 31); key = key * 21; key = key ^ (key >> 11); key = key + (key << 6); key = key ^ (key >> 22); return (uint32_t) key; } #endif NSUInteger RCIntegerArrayHash(const NSUInteger *subhashes, NSUInteger count); namespace RC { // Default is not an ObjC class template<typename T, typename V = bool> struct is_objc_class : std::false_type { }; // Conditionally enable this template specialization on whether T is convertible to id, makes the is_objc_class a true_type template<typename T> struct is_objc_class<T, typename std::enable_if<std::is_convertible<T, id>::value, bool>::type> : std::true_type { }; // CKUtils::hash<T>()(value) -> either std::hash<T> if c++ or [o hash] if ObjC object. template <typename T, typename Enable = void> struct hash; // For non-objc types, defer to std::hash template <typename T> struct hash<T, typename std::enable_if<!is_objc_class<T>::value>::type> { size_t operator ()(const T& a) const { return std::hash<T>()(a); } }; // For objc types, call [o hash] template <typename T> struct hash<T, typename std::enable_if<is_objc_class<T>::value>::type> { size_t operator ()(id o) const { return [o hash]; } }; template <typename T, typename Enable = void> struct is_equal; // For non-objc types use == operator template <typename T> struct is_equal<T, typename std::enable_if<!is_objc_class<T>::value>::type> { bool operator ()(const T& a, const T& b) const { return a == b; } }; // For objc types, check pointer equality, then use -isEqual: template <typename T> struct is_equal<T, typename std::enable_if<is_objc_class<T>::value>::type> { bool operator ()(id a, id b) const { return a == b || [a isEqual:b]; } }; }; namespace RCTupleOperations { // Recursive case (hash up to Index) template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1> struct _hash_helper { static size_t hash(Tuple const& tuple) { size_t prev = _hash_helper<Tuple, Index-1>::hash(tuple); using TypeForIndex = typename std::tuple_element<Index,Tuple>::type; size_t thisHash = RC::hash<TypeForIndex>()(std::get<Index>(tuple)); return RCHash64ToNative(RCHashCombine(prev, thisHash)); } }; // Base case (hash 0th element) template <class Tuple> struct _hash_helper<Tuple, 0> { static size_t hash(Tuple const& tuple) { using TypeForIndex = typename std::tuple_element<0,Tuple>::type; return RC::hash<TypeForIndex>()(std::get<0>(tuple)); } }; // Recursive case (elements equal up to Index) template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1> struct _eq_helper { static bool equal(Tuple const& a, Tuple const& b) { bool prev = _eq_helper<Tuple, Index-1>::equal(a, b); using TypeForIndex = typename std::tuple_element<Index,Tuple>::type; auto aValue = std::get<Index>(a); auto bValue = std::get<Index>(b); return prev && RC::is_equal<TypeForIndex>()(aValue, bValue); } }; // Base case (0th elements equal) template <class Tuple> struct _eq_helper<Tuple, 0> { static bool equal(Tuple const& a, Tuple const& b) { using TypeForIndex = typename std::tuple_element<0,Tuple>::type; auto& aValue = std::get<0>(a); auto& bValue = std::get<0>(b); return RC::is_equal<TypeForIndex>()(aValue, bValue); } }; template <typename ... TT> struct hash; template <typename ... TT> struct hash<std::tuple<TT...>> { size_t operator()(std::tuple<TT...> const& tt) const { return _hash_helper<std::tuple<TT...>>::hash(tt); } }; template <typename ... TT> struct equal_to; template <typename ... TT> struct equal_to<std::tuple<TT...>> { bool operator()(std::tuple<TT...> const& a, std::tuple<TT...> const& b) const { return _eq_helper<std::tuple<TT...>>::equal(a, b); } }; } /** Correctly equates two objects, including cases where both objects are nil (where `isEqual:` would return NO). */ inline BOOL RCObjectIsEqual(id<NSObject> obj, id<NSObject> otherObj) { return obj == otherObj || [obj isEqual:otherObj]; } typedef BOOL (^RCEqualityComparisonBlock)(id object, id comparisonObject); /** * Correctly executes the comparisonBlock for two objects, including cases one of the objects is nil or * of a different type (where `isEqual:` would return NO). */ inline BOOL RCCompareObjectEquality(id object, id comparisonObject, RCEqualityComparisonBlock comparisonBlock) { if (object == comparisonObject) { return YES; } else if (!object || !comparisonObject || ![comparisonObject isKindOfClass:[object class]]) { return NO; } return comparisonBlock(object, comparisonObject); } /** Correctly equates two vectors of keys (scope state key) */ inline bool RCKeyVectorsEqual(const std::vector<id<NSObject>> &a, const std::vector<id<NSObject>> &b) { if (a.size() != b.size()) { return false; } return std::equal(a.begin(), a.end(), b.begin(), [](id<NSObject> x, id<NSObject> y){ return RCObjectIsEqual(x, y); // be pedantic and use a lambda here becuase BOOL != bool }); } #endif