mirror of
https://github.com/greg7mdp/parallel-hashmap.git
synced 2026-08-29 08:34:39 +08:00
support most extended APIs for sets as we do for maps.
This commit is contained in:
Vendored
+102
-85
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user