diff --git a/include/tsl/robin_hash.h b/include/tsl/robin_hash.h index 163e0e2..51662be 100644 --- a/include/tsl/robin_hash.h +++ b/include/tsl/robin_hash.h @@ -686,6 +686,18 @@ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy { return insert_impl(KeySelect()(value), std::forward

(value)); } + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(KeySelect()(value)), + * otherwise the behaviour is undefined. + */ + template + std::pair insert_with_hash(std::size_t precalculated_hash, + P&& value) { + return insert_impl_with_hash(precalculated_hash, KeySelect()(value), + std::forward

(value)); + } + template iterator insert_hint(const_iterator hint, P&& value) { if (hint != cend() && @@ -726,6 +738,23 @@ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy { return it; } + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key), otherwise the + * behaviour is undefined. + */ + template + std::pair insert_or_assign_with_hash( + std::size_t precalculated_hash, K&& key, M&& obj) { + auto it = try_emplace_with_hash(precalculated_hash, std::forward(key), + std::forward(obj)); + if (!it.second) { + it.first.value() = std::forward(obj); + } + + return it; + } + template iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) { if (hint != cend() && compare_keys(KeySelect()(*hint), key)) { @@ -755,6 +784,20 @@ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy { std::forward_as_tuple(std::forward(args)...)); } + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key), otherwise the + * behaviour is undefined. + */ + template + std::pair try_emplace_with_hash( + std::size_t precalculated_hash, K&& key, Args&&... args) { + return insert_impl_with_hash( + precalculated_hash, key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)); + } + template iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) { if (hint != cend() && compare_keys(KeySelect()(*hint), key)) { @@ -1161,7 +1204,14 @@ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy { template std::pair insert_impl(const K& key, Args&&... value_type_args) { - const std::size_t hash = hash_key(key); + return insert_impl_with_hash(hash_key(key), key, + std::forward(value_type_args)...); + } + + template + std::pair insert_impl_with_hash( + std::size_t hash, const K& key, Args&&... value_type_args) { + tsl_rh_assert(hash == hash_key(key)); std::size_t ibucket = bucket_for_hash(hash); distance_type dist_from_ideal_bucket = 0; diff --git a/include/tsl/robin_map.h b/include/tsl/robin_map.h index b594810..daec9e6 100644 --- a/include/tsl/robin_map.h +++ b/include/tsl/robin_map.h @@ -244,6 +244,24 @@ class robin_map { return m_ht.insert(std::move(value)); } + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(value.first). Useful to + * speed-up the insertion if you already have the hash. + */ + std::pair insert_with_hash(std::size_t precalculated_hash, + const value_type& value) { + return m_ht.insert_with_hash(precalculated_hash, value); + } + + /** + * @copydoc insert_with_hash(std::size_t precalculated_hash, const value_type& value) + */ + std::pair insert_with_hash(std::size_t precalculated_hash, + value_type&& value) { + return m_ht.insert_with_hash(precalculated_hash, std::move(value)); + } + iterator insert(const_iterator hint, const value_type& value) { return m_ht.insert_hint(hint, value); } @@ -287,6 +305,28 @@ class robin_map { return m_ht.insert_or_assign(hint, std::move(k), std::forward(obj)); } + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(k). Useful to speed-up + * the insertion if you already have the hash. + */ + template + std::pair insert_or_assign_with_hash( + std::size_t precalculated_hash, const key_type& k, M&& obj) { + return m_ht.insert_or_assign_with_hash(precalculated_hash, k, + std::forward(obj)); + } + + /** + * @copydoc insert_or_assign_with_hash(std::size_t precalculated_hash, const key_type& k, M&& obj) + */ + template + std::pair insert_or_assign_with_hash( + std::size_t precalculated_hash, key_type&& k, M&& obj) { + return m_ht.insert_or_assign_with_hash(precalculated_hash, std::move(k), + std::forward(obj)); + } + /** * Due to the way elements are stored, emplace will need to move or copy the * key-value once. The method is equivalent to @@ -332,6 +372,28 @@ class robin_map { std::forward(args)...); } + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(k). Useful to speed-up + * the insertion if you already have the hash. + */ + template + std::pair try_emplace_with_hash( + std::size_t precalculated_hash, const key_type& k, Args&&... args) { + return m_ht.try_emplace_with_hash(precalculated_hash, k, + std::forward(args)...); + } + + /** + * @copydoc try_emplace_with_hash(std::size_t precalculated_hash, const key_type& k, Args&&... args) + */ + template + std::pair try_emplace_with_hash( + std::size_t precalculated_hash, key_type&& k, Args&&... args) { + return m_ht.try_emplace_with_hash(precalculated_hash, std::move(k), + std::forward(args)...); + } + iterator erase(iterator pos) { return m_ht.erase(pos); } iterator erase(const_iterator pos) { return m_ht.erase(pos); } iterator erase(const_iterator first, const_iterator last) { diff --git a/include/tsl/robin_set.h b/include/tsl/robin_set.h index e115007..c48bb5c 100644 --- a/include/tsl/robin_set.h +++ b/include/tsl/robin_set.h @@ -215,6 +215,24 @@ class robin_set { return m_ht.insert(std::move(value)); } + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(value). Useful to speed-up + * the insertion if you already have the hash. + */ + std::pair insert_with_hash(std::size_t precalculated_hash, + const value_type& value) { + return m_ht.insert_with_hash(precalculated_hash, value); + } + + /** + * @copydoc insert_with_hash(std::size_t precalculated_hash, const value_type& value) + */ + std::pair insert_with_hash(std::size_t precalculated_hash, + value_type&& value) { + return m_ht.insert_with_hash(precalculated_hash, std::move(value)); + } + iterator insert(const_iterator hint, const value_type& value) { return m_ht.insert_hint(hint, value); } diff --git a/tests/robin_map_tests.cpp b/tests/robin_map_tests.cpp index 68398e2..3facbd7 100644 --- a/tests/robin_map_tests.cpp +++ b/tests/robin_map_tests.cpp @@ -1439,6 +1439,45 @@ BOOST_AUTO_TEST_CASE(test_precalculated_hash) { BOOST_REQUIRE_NE(map.hash_function()(2), map.hash_function()(3)); BOOST_CHECK_EQUAL(std::distance(it_range.first, it_range.second), 0); + /** + * try_emplace_with_hash + */ + auto it_te = map.try_emplace_with_hash(map.hash_function()(7), 7, -7); + BOOST_CHECK(it_te.second); + BOOST_CHECK_EQUAL(it_te.first->first, 7); + BOOST_CHECK_EQUAL(it_te.first->second, -7); + + // Key already present: no insert, value left untouched. + it_te = map.try_emplace_with_hash(map.hash_function()(7), 7, -70); + BOOST_CHECK(!it_te.second); + BOOST_CHECK_EQUAL(it_te.first->second, -7); + + /** + * insert_or_assign_with_hash + */ + auto it_ioa = map.insert_or_assign_with_hash(map.hash_function()(8), 8, -8); + BOOST_CHECK(it_ioa.second); + BOOST_CHECK_EQUAL(it_ioa.first->first, 8); + BOOST_CHECK_EQUAL(it_ioa.first->second, -8); + + // Key already present: no insert, but value is overwritten. + it_ioa = map.insert_or_assign_with_hash(map.hash_function()(8), 8, -80); + BOOST_CHECK(!it_ioa.second); + BOOST_CHECK_EQUAL(it_ioa.first->second, -80); + + /** + * insert_with_hash + */ + auto it_ins = map.insert_with_hash(map.hash_function()(9), {9, -9}); + BOOST_CHECK(it_ins.second); + BOOST_CHECK_EQUAL(it_ins.first->first, 9); + BOOST_CHECK_EQUAL(it_ins.first->second, -9); + + // Key already present: no insert, value left untouched. + it_ins = map.insert_with_hash(map.hash_function()(9), {9, -90}); + BOOST_CHECK(!it_ins.second); + BOOST_CHECK_EQUAL(it_ins.first->second, -9); + /** * erase */ diff --git a/tests/robin_set_tests.cpp b/tests/robin_set_tests.cpp index e68b514..79b5693 100644 --- a/tests/robin_set_tests.cpp +++ b/tests/robin_set_tests.cpp @@ -171,4 +171,53 @@ BOOST_AUTO_TEST_CASE(test_erase_fast) { BOOST_CHECK(set.size() == 0); } +BOOST_AUTO_TEST_CASE(test_precalculated_hash) { + tsl::robin_set> set = {1, 2, 3, 4, 5, 6}; + const tsl::robin_set> set_const = set; + + /** + * find + */ + BOOST_REQUIRE(set.find(3, set.hash_function()(3)) != set.end()); + BOOST_CHECK_EQUAL(*set.find(3, set.hash_function()(3)), 3); + + BOOST_REQUIRE(set_const.find(3, set_const.hash_function()(3)) != + set_const.end()); + + BOOST_REQUIRE_NE(set.hash_function()(2), set.hash_function()(3)); + BOOST_CHECK(set.find(3, set.hash_function()(2)) == set.end()); + + /** + * contains + */ + BOOST_CHECK(set.contains(3, set.hash_function()(3))); + BOOST_CHECK(!set.contains(3, set.hash_function()(2))); + + /** + * count + */ + BOOST_CHECK_EQUAL(set.count(3, set.hash_function()(3)), 1); + BOOST_CHECK_EQUAL(set.count(3, set.hash_function()(2)), 0); + + /** + * insert_with_hash + */ + auto it_ins = set.insert_with_hash(set.hash_function()(7), 7); + BOOST_CHECK(it_ins.second); + BOOST_CHECK_EQUAL(*it_ins.first, 7); + + // Value already present: no insert. + it_ins = set.insert_with_hash(set.hash_function()(7), 7); + BOOST_CHECK(!it_ins.second); + BOOST_CHECK_EQUAL(*it_ins.first, 7); + + /** + * erase + */ + BOOST_CHECK_EQUAL(set.erase(3, set.hash_function()(3)), 1); + + BOOST_REQUIRE_NE(set.hash_function()(2), set.hash_function()(4)); + BOOST_CHECK_EQUAL(set.erase(4, set.hash_function()(2)), 0); +} + BOOST_AUTO_TEST_SUITE_END()