Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion include/tsl/robin_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,18 @@ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy {
return insert_impl(KeySelect()(value), std::forward<P>(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 <typename P>
std::pair<iterator, bool> insert_with_hash(std::size_t precalculated_hash,
P&& value) {
return insert_impl_with_hash(precalculated_hash, KeySelect()(value),
std::forward<P>(value));
}

template <typename P>
iterator insert_hint(const_iterator hint, P&& value) {
if (hint != cend() &&
Expand Down Expand Up @@ -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 <class K, class M>
std::pair<iterator, bool> insert_or_assign_with_hash(
std::size_t precalculated_hash, K&& key, M&& obj) {
auto it = try_emplace_with_hash(precalculated_hash, std::forward<K>(key),
std::forward<M>(obj));
if (!it.second) {
it.first.value() = std::forward<M>(obj);
}

return it;
}

template <class K, class M>
iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) {
if (hint != cend() && compare_keys(KeySelect()(*hint), key)) {
Expand Down Expand Up @@ -755,6 +784,20 @@ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy {
std::forward_as_tuple(std::forward<Args>(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 <class K, class... Args>
std::pair<iterator, bool> 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<K>(key)),
std::forward_as_tuple(std::forward<Args>(args)...));
}

template <class K, class... Args>
iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) {
if (hint != cend() && compare_keys(KeySelect()(*hint), key)) {
Expand Down Expand Up @@ -1161,7 +1204,14 @@ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy {
template <class K, class... Args>
std::pair<iterator, bool> 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<Args>(value_type_args)...);
}

template <class K, class... Args>
std::pair<iterator, bool> 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;
Expand Down
62 changes: 62 additions & 0 deletions include/tsl/robin_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<iterator, bool> 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<iterator, bool> 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);
}
Expand Down Expand Up @@ -287,6 +305,28 @@ class robin_map {
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(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 <class M>
std::pair<iterator, bool> 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<M>(obj));
}

/**
* @copydoc insert_or_assign_with_hash(std::size_t precalculated_hash, const key_type& k, M&& obj)
*/
template <class M>
std::pair<iterator, bool> 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<M>(obj));
}

/**
* Due to the way elements are stored, emplace will need to move or copy the
* key-value once. The method is equivalent to
Expand Down Expand Up @@ -332,6 +372,28 @@ class robin_map {
std::forward<Args>(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 <class... Args>
std::pair<iterator, bool> 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>(args)...);
}

/**
* @copydoc try_emplace_with_hash(std::size_t precalculated_hash, const key_type& k, Args&&... args)
*/
template <class... Args>
std::pair<iterator, bool> 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>(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) {
Expand Down
18 changes: 18 additions & 0 deletions include/tsl/robin_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<iterator, bool> 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<iterator, bool> 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);
}
Expand Down
39 changes: 39 additions & 0 deletions tests/robin_map_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
49 changes: 49 additions & 0 deletions tests/robin_set_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, identity_hash<int>> set = {1, 2, 3, 4, 5, 6};
const tsl::robin_set<int, identity_hash<int>> 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()
Loading