diff --git a/packages/react-native/ReactCommon/react/utils/hash_combine.h b/packages/react-native/ReactCommon/react/utils/hash_combine.h new file mode 100644 index 00000000000..2cb16d84a9f --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/hash_combine.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::react { + +template < + typename T, + typename... Rest, + bool Enabled = !std::is_same::value, + typename = typename std::enable_if::type> +void hash_combine(std::size_t& seed, const T& v, const Rest&... rest) { + seed ^= std::hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + (hash_combine(seed, rest), ...); +} + +template +std::size_t hash_combine(const T& v, const Args&... args) { + std::size_t seed = 0; + hash_combine(seed, v, args...); + return seed; +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/utils/tests/hash_combineTests.cpp b/packages/react-native/ReactCommon/react/utils/tests/hash_combineTests.cpp new file mode 100644 index 00000000000..7b5fd583ff9 --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/tests/hash_combineTests.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +struct Person { + std::string firstName; + std::string lastName; +}; + +namespace std { +template <> +struct hash { + size_t operator()(const Person& person) const { + return facebook::react::hash_combine(person.firstName, person.lastName); + } +}; +} // namespace std + +namespace facebook::react { + +TEST(hash_combineTests, testIntegerTemplating) { + std::size_t seed = 0; + hash_combine(seed, 1); + + auto hashedValue = hash_combine(1); + EXPECT_EQ(hashedValue, seed); + + EXPECT_NE(hash_combine(1), hash_combine(2)); +} + +TEST(hash_combineTests, testIntegerCombinationsHashing) { + std::size_t seed = 0; + hash_combine(seed, 1, 2); + + auto hashedValue = hash_combine(1, 2); + EXPECT_EQ(hashedValue, seed); + + EXPECT_NE(hash_combine(1, 2), hash_combine(2, 1)); +} + +TEST(hash_combineTests, testContiniousIntegerHashing) { + std::size_t seed = 0; + + for (int i = 1; i <= 200; ++i) { + auto previousSeed = seed; + hash_combine(seed, i); + EXPECT_NE(seed, previousSeed); + } +} + +TEST(hash_combineTests, testStrings) { + std::size_t seed = 0; + hash_combine(seed, "react"); + + auto hashedValue = hash_combine("react"); + EXPECT_EQ(hashedValue, seed); + + EXPECT_NE( + hash_combine("react"), + hash_combine("react native")); +} + +TEST(hash_combineTests, testCustomTypes) { + auto person1 = Person{"John", "Doe"}; + auto person2 = Person{"Jane", "Doe"}; + + std::size_t seed = 0; + hash_combine(seed, person1); + + auto hashedValue = hash_combine(person1); + EXPECT_EQ(hashedValue, seed); + + EXPECT_NE(hash_combine(person1), hash_combine(person2)); +} + +} // namespace facebook::react