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()