support most extended APIs for sets as we do for maps.

This commit is contained in:
greg
2022-01-22 21:15:55 -05:00
parent 6893d3c259
commit 43c4c86479
3 changed files with 320 additions and 204 deletions
+102 -85
View File
@@ -2929,7 +2929,7 @@ public:
};
// --------------------------------------------------------------------
// phmap expension: emplace_with_hash
// phmap extension: emplace_with_hash
// ----------------------------------
// same as emplace, but hashval is provided
// --------------------------------------------------------------------
@@ -3081,6 +3081,8 @@ public:
return {iterator(inner, &sets_[0] + num_tables, res.first), res.second};
}
// lazy_emplace
// ------------
template <class K = key_type, class F>
iterator lazy_emplace(const key_arg<K>& key, F&& f) {
auto hashval = this->hash(key);
@@ -3090,15 +3092,109 @@ public:
return make_iterator(&inner, set.lazy_emplace_with_hash(key, hashval, std::forward<F>(f)));
}
// emplace_single
// --------------
template <class K = key_type, class F>
void emplace_single(const key_arg<K>& key, F&& f) {
auto hashval = this->hash(key);
void emplace_single_with_hash(const key_arg<K>& key, size_t &hashval, F&& f) {
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
typename Lockable::UniqueLock m(inner);
set.emplace_single_with_hash(key, hashval, std::forward<F>(f));
}
template <class K = key_type, class F>
void emplace_single(const key_arg<K>& key, F&& f) {
auto hashval = this->hash(key);
emplace_single_with_hash<K, F>(key, hashval, std::forward<F>(f));
}
// if set contains key, lambda is called with the value_type (under read lock protection),
// and if_contains returns true. This is a const API and lambda should not modify the value
// -----------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool if_contains(const key_arg<K>& key, F&& f) const {
return const_cast<parallel_hash_set*>(this)->template
modify_if_impl<K, F, typename Lockable::SharedLock>(key, std::forward<F>(f));
}
// if set contains key, lambda is called with the value_type without read lock protection,
// and if_contains_unsafe returns true. This is a const API and lambda should not modify the value
// This should be used only if we know that no other thread may be mutating the set at the time.
// -----------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool if_contains_unsafe(const key_arg<K>& key, F&& f) const {
return const_cast<parallel_hash_set*>(this)->template
modify_if_impl<K, F, LockableBaseImpl<phmap::NullMutex>::DoNothing>(key, std::forward<F>(f));
}
// if map contains key, lambda is called with the value_type (under write lock protection),
// and modify_if returns true. This is a non-const API and lambda is allowed to modify the mapped value
// ----------------------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool modify_if(const key_arg<K>& key, F&& f) {
return modify_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
}
// -----------------------------------------------------------------------------------------
template <class K = key_type, class F, class L>
bool modify_if_impl(const key_arg<K>& key, F&& f) {
#if __cplusplus >= 201703L
static_assert(std::is_invocable<F, value_type&>::value);
#endif
L m;
auto ptr = this->template find_ptr<K, L>(key, this->hash(key), m);
if (ptr == nullptr)
return false;
std::forward<F>(f)(*ptr);
return true;
}
// if map contains key, lambda is called with the mapped value (under write lock protection).
// If the lambda returns true, the key is subsequently erased from the map (the write lock
// is only released after erase).
// returns true if key was erased, false otherwise.
// ----------------------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool erase_if(const key_arg<K>& key, F&& f) {
return erase_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
}
template <class K = key_type, class F, class L>
bool erase_if_impl(const key_arg<K>& key, F&& f) {
#if __cplusplus >= 201703L
static_assert(std::is_invocable<F, value_type&>::value);
#endif
L m;
auto it = this->template find<K, L>(key, this->hash(key), m);
if (it == this->end()) return false;
if (std::forward<F>(f)(const_cast<value_type &>(*it)))
{
this->erase(it);
return true;
}
return false;
}
// if map does not contains key, it is inserted and the mapped value is value-constructed
// with the provided arguments (if any), as with try_emplace.
// if map already contains key, then the lambda is called with the mapped value (under
// write lock protection) and can update the mapped value.
// returns true if key was not already present, false otherwise.
// ---------------------------------------------------------------------------------------
template <class K = key_type, class FExists, class FEmplace>
bool lazy_emplace_l(const key_arg<K>& key, FExists&& fExists, FEmplace&& fEmplace) {
typename Lockable::UniqueLock m;
auto res = this->find_or_prepare_insert(key, m);
Inner* inner = std::get<0>(res);
if (std::get<2>(res))
inner->set_.lazy_emplace_at(std::get<1>(res), std::forward<FEmplace>(fEmplace));
else {
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
std::forward<FExists>(fExists)(const_cast<value_type &>(*it)); // in case of the set, non "key" part of value_type can be changed
}
return std::get<2>(res);
}
// Extension API: support iterating over all values
//
// flat_hash_set<std::string> s;
@@ -3554,8 +3650,9 @@ class parallel_hash_map : public parallel_hash_set<N, RefSet, Mtx_, Policy, Hash
using Lockable = phmap::LockableImpl<Mtx_>;
public:
using key_type = typename Policy::key_type;
using key_type = typename Policy::key_type;
using mapped_type = typename Policy::mapped_type;
using value_type = typename Base::value_type;
template <class K>
using key_arg = typename KeyArgImpl::template type<K, key_type>;
@@ -3692,43 +3789,6 @@ public:
return try_emplace_with_hash(hashval, k, std::forward<Args>(args)...).first;
}
// if map contains key, lambda is called with the mapped value (under read lock protection),
// and if_contains returns true. This is a const API and lambda should not modify the value
// -----------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool if_contains(const key_arg<K>& key, F&& f) const {
return const_cast<parallel_hash_map*>(this)->template
modify_if_impl<K, F, typename Lockable::SharedLock>(key, std::forward<F>(f));
}
// if map contains key, lambda is called with the mapped value without read lock protection,
// and if_contains_unsafe returns true. This is a const API and lambda should not modify the value
// This should be used only if we know that no other thread may be mutating the map at the time.
// -----------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool if_contains_unsafe(const key_arg<K>& key, F&& f) const {
return const_cast<parallel_hash_map*>(this)->template
modify_if_impl<K, F, LockableBaseImpl<phmap::NullMutex>::DoNothing>(key, std::forward<F>(f));
}
// if map contains key, lambda is called with the mapped value (under write lock protection),
// and modify_if returns true. This is a non-const API and lambda is allowed to modify the mapped value
// ----------------------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool modify_if(const key_arg<K>& key, F&& f) {
return modify_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
}
// if map contains key, lambda is called with the mapped value (under write lock protection).
// If the lambda returns true, the key is subsequently erased from the map (the write lock
// is only released after erase).
// returns true if key was erased, false otherwise.
// ----------------------------------------------------------------------------------------------------
template <class K = key_type, class F>
bool erase_if(const key_arg<K>& key, F&& f) {
return erase_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
}
// if map does not contains key, it is inserted and the mapped value is value-constructed
// with the provided arguments (if any), as with try_emplace.
// if map already contains key, then the lambda is called with the mapped value (under
@@ -3746,21 +3806,7 @@ public:
std::forward_as_tuple(std::forward<Args>(args)...));
else {
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
std::forward<F>(f)(Policy::value(&*it));
}
return std::get<2>(res);
}
template <class K = key_type, class FExists, class FEmplace>
bool lazy_emplace_l(const key_arg<K>& key, FExists&& fExists, FEmplace&& fEmplace) {
typename Lockable::UniqueLock m;
auto res = this->find_or_prepare_insert(key, m);
typename Base::Inner* inner = std::get<0>(res);
if (std::get<2>(res))
inner->set_.lazy_emplace_at(std::get<1>(res), std::forward<FEmplace>(fEmplace));
else {
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
std::forward<FExists>(fExists)(Policy::value(&*it));
std::forward<F>(f)(const_cast<value_type &>(*it)); // in case of the set, non "key" part of value_type can be changed
}
return std::get<2>(res);
}
@@ -3778,35 +3824,6 @@ public:
}
private:
template <class K = key_type, class F, class L>
bool modify_if_impl(const key_arg<K>& key, F&& f) {
#if __cplusplus >= 201703L
static_assert(std::is_invocable<F, mapped_type&>::value);
#endif
L m;
auto ptr = this->template find_ptr<K, L>(key, this->hash(key), m);
if (ptr == nullptr)
return false;
std::forward<F>(f)(Policy::value(ptr));
return true;
}
template <class K = key_type, class F, class L>
bool erase_if_impl(const key_arg<K>& key, F&& f) {
#if __cplusplus >= 201703L
static_assert(std::is_invocable<F, mapped_type&>::value);
#endif
L m;
auto it = this->template find<K, L>(key, this->hash(key), m);
if (it == this->end()) return false;
if (std::forward<F>(f)(Policy::value(&*it)))
{
this->erase(it);
return true;
}
return false;
}
template <class K, class V>
std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {