1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
17 #define OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
18 #include <functional>
19 #include <map>
20 #include <shared_mutex>
21 
22 namespace OHOS {
23 template<typename _Key, typename _Tp>
24 class ConcurrentMap {
25     template<typename _First, typename... _Rest>
26     static _First First();
27 
28 public:
29     using map_type = typename std::map<_Key, _Tp>;
30     using filter_type = typename std::function<bool(map_type &)>;
31     using key_type = typename std::map<_Key, _Tp>::key_type;
32     using mapped_type = typename std::map<_Key, _Tp>::mapped_type;
33     using value_type = typename std::map<_Key, _Tp>::value_type;
34     using size_type = typename std::map<_Key, _Tp>::size_type;
35     using reference = typename std::map<_Key, _Tp>::reference;
36     using const_reference = typename std::map<_Key, _Tp>::const_reference;
37 
38     ConcurrentMap() = default;
~ConcurrentMap()39     ~ConcurrentMap()
40     {
41         Clear();
42     }
43 
ConcurrentMap(const ConcurrentMap & other)44     ConcurrentMap(const ConcurrentMap &other)
45     {
46         operator=(std::move(other));
47     }
48 
ConcurrentMap(std::map<_Key,_Tp> && other)49     ConcurrentMap(std::map<_Key, _Tp> &&other) noexcept
50     {
51         operator=(std::move(other));
52     }
53 
54     ConcurrentMap &operator=(std::map<_Key, _Tp> &&other) noexcept
55     {
56         if (&entries_ == &other) {
57             return *this;
58         }
59         std::unique_lock<decltype(mutex_)> lock(mutex_);
60         entries_ = std::move(other);
61         return *this;
62     }
63 
64     ConcurrentMap &operator=(const ConcurrentMap &other) noexcept
65     {
66         if (this == &other) {
67             return *this;
68         }
69         auto tmp = other.Clone();
70         std::unique_lock<decltype(mutex_)> lock(mutex_);
71         entries_ = std::move(tmp);
72         return *this;
73     }
74 
ConcurrentMap(ConcurrentMap && other)75     ConcurrentMap(ConcurrentMap &&other) noexcept
76     {
77         operator=(std::move(other));
78     }
79 
80     ConcurrentMap &operator=(ConcurrentMap &&other) noexcept
81     {
82         if (this == &other) {
83             return *this;
84         }
85         auto tmp = other.Steal();
86         std::unique_lock<decltype(mutex_)> lock(mutex_);
87         entries_ = std::move(tmp);
88         return *this;
89     }
90 
Emplace()91     bool Emplace() noexcept
92     {
93         std::unique_lock<decltype(mutex_)> lock(mutex_);
94         auto it = entries_.emplace();
95         return it.second;
96     }
97 
98     template<typename... _Args>
99     typename std::enable_if<!std::is_convertible_v<decltype(First<_Args...>()), filter_type>, bool>::type
100     Emplace(_Args &&...args) noexcept
101     {
102         std::unique_lock<decltype(mutex_)> lock(mutex_);
103         auto it = entries_.emplace(std::forward<_Args>(args)...);
104         return it.second;
105     }
106 
107     template<typename _Filter, typename... _Args>
108     typename std::enable_if<std::is_convertible_v<_Filter, filter_type>, bool>::type
Emplace(const _Filter & filter,_Args &&...args)109     Emplace(const _Filter &filter, _Args &&...args) noexcept
110     {
111         std::unique_lock<decltype(mutex_)> lock(mutex_);
112         if (!filter(entries_)) {
113             return false;
114         }
115         auto it = entries_.emplace(std::forward<_Args>(args)...);
116         return it.second;
117     }
118 
Find(const key_type & key)119     std::pair<bool, mapped_type> Find(const key_type &key) const noexcept
120     {
121         std::shared_lock<decltype(mutex_)> lock(mutex_);
122         auto it = entries_.find(key);
123         if (it == entries_.end()) {
124             return std::pair { false, mapped_type() };
125         }
126 
127         return std::pair { true, it->second };
128     }
129 
ContainIf(const key_type & key,const std::function<bool (const mapped_type & value)> & action)130     bool ContainIf(const key_type &key, const std::function<bool(const mapped_type &value)> &action) const noexcept
131     {
132         std::shared_lock<decltype(mutex_)> lock(mutex_);
133         auto it = entries_.find(key);
134         if (it == entries_.end()) {
135             return false;
136         }
137         if (action) {
138             return action(it->second);
139         }
140         return true;
141     }
142 
Contains(const key_type & key)143     bool Contains(const key_type &key) const noexcept
144     {
145         std::shared_lock<decltype(mutex_)> lock(mutex_);
146         return (entries_.find(key) != entries_.end());
147     }
148 
149     template <typename _Obj>
InsertOrAssign(const key_type & key,_Obj && obj)150     bool InsertOrAssign(const key_type &key, _Obj &&obj) noexcept
151     {
152         std::unique_lock<decltype(mutex_)> lock(mutex_);
153         auto it = entries_.insert_or_assign(key, std::forward<_Obj>(obj));
154         return it.second;
155     }
156 
Insert(const key_type & key,const mapped_type & value)157     bool Insert(const key_type &key, const mapped_type &value) noexcept
158     {
159         std::unique_lock<decltype(mutex_)> lock(mutex_);
160         auto it = entries_.insert(value_type { key, value });
161         return it.second;
162     }
163 
Erase(const key_type & key)164     size_type Erase(const key_type &key) noexcept
165     {
166         std::unique_lock<decltype(mutex_)> lock(mutex_);
167         return entries_.erase(key);
168     }
169 
EraseIf(const key_type & key,const std::function<bool (const key_type & key,mapped_type & value)> & action)170     size_type EraseIf(const key_type &key,
171         const std::function<bool(const key_type &key, mapped_type &value)> &action) noexcept
172     {
173         if (action == nullptr) {
174             return 0;
175         }
176         std::unique_lock<decltype(mutex_)> lock(mutex_);
177         auto it = entries_.find(key);
178         if (it == entries_.end() || !action((*it).first, (*it).second)) {
179             return 0;
180         }
181         return entries_.erase(key);
182     }
183 
Clear()184     void Clear() noexcept
185     {
186         std::unique_lock<decltype(mutex_)> lock(mutex_);
187         return entries_.clear();
188     }
189 
Empty()190     bool Empty() const noexcept
191     {
192         std::shared_lock<decltype(mutex_)> lock(mutex_);
193         return entries_.empty();
194     }
195 
Size()196     size_type Size() const noexcept
197     {
198         std::shared_lock<decltype(mutex_)> lock(mutex_);
199         return entries_.size();
200     }
201 
202     // The action`s return true means meeting the erase condition
203     // The action`s return false means not meeting the erase condition
EraseIf(const std::function<bool (const key_type & key,mapped_type & value)> & action)204     size_type EraseIf(const std::function<bool(const key_type &key, mapped_type &value)> &action) noexcept
205     {
206         if (action == nullptr) {
207             return 0;
208         }
209         std::unique_lock<decltype(mutex_)> lock(mutex_);
210 #if __cplusplus > 201703L
211         auto count = std::erase_if(entries_,
212             [&action](value_type &value) -> bool { return action(value.first, value.second); });
213 #else
214         auto count = entries_.size();
215         for (auto it = entries_.begin(); it != entries_.end();) {
216             if (action((*it).first, (*it).second)) {
217                 it = entries_.erase(it);
218             } else {
219                 ++it;
220             }
221         }
222         count -= entries_.size();
223 #endif
224         return count;
225     }
226 
ForEach(const std::function<bool (const key_type &,mapped_type &)> & action)227     void ForEach(const std::function<bool(const key_type &, mapped_type &)> &action)
228     {
229         if (action == nullptr) {
230             return;
231         }
232         std::shared_lock<decltype(mutex_)> lock(mutex_);
233         for (auto &[key, value] : entries_) {
234             if (action(key, value)) {
235                 break;
236             }
237         }
238     }
239 
ForEachCopies(const std::function<bool (const key_type &,mapped_type &)> & action)240     void ForEachCopies(const std::function<bool(const key_type &, mapped_type &)> &action)
241     {
242         if (action == nullptr) {
243             return;
244         }
245         auto entries = Clone();
246         for (auto &[key, value] : entries) {
247             if (action(key, value)) {
248                 break;
249             }
250         }
251     }
252 
253     // The action's return value means that the element is keep in map or not; true means keeping, false means removing.
Compute(const key_type & key,const std::function<bool (const key_type &,mapped_type &)> & action)254     bool Compute(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action)
255     {
256         if (action == nullptr) {
257             return false;
258         }
259         std::unique_lock<decltype(mutex_)> lock(mutex_);
260         auto it = entries_.find(key);
261         if (it == entries_.end()) {
262             auto result = entries_.emplace(key, mapped_type());
263             it = result.second ? result.first : entries_.end();
264         }
265         if (it == entries_.end()) {
266             return false;
267         }
268         if (!action(it->first, it->second)) {
269             entries_.erase(key);
270         }
271         return true;
272     }
273 
274     // The action's return value means that the element is keep in map or not; true means keeping, false means removing.
ComputeIfPresent(const key_type & key,const std::function<bool (const key_type &,mapped_type &)> & action)275     bool ComputeIfPresent(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action)
276     {
277         if (action == nullptr) {
278             return false;
279         }
280         std::unique_lock<decltype(mutex_)> lock(mutex_);
281         auto it = entries_.find(key);
282         if (it == entries_.end()) {
283             return false;
284         }
285         if (!action(key, it->second)) {
286             entries_.erase(key);
287         }
288         return true;
289     }
290 
ComputeIfAbsent(const key_type & key,const std::function<mapped_type (const key_type &)> & action)291     bool ComputeIfAbsent(const key_type &key, const std::function<mapped_type(const key_type &)> &action)
292     {
293         if (action == nullptr) {
294             return false;
295         }
296         std::unique_lock<decltype(mutex_)> lock(mutex_);
297         auto it = entries_.find(key);
298         if (it != entries_.end()) {
299             return false;
300         }
301         entries_.emplace(key, action(key));
302         return true;
303     }
304 
DoActionIfEmpty(const std::function<void (void)> & action)305     void DoActionIfEmpty(const std::function<void(void)> &action)
306     {
307         if (action == nullptr) {
308             return;
309         }
310         std::shared_lock<decltype(mutex_)> lock(mutex_);
311         if (entries_.empty()) {
312             action();
313         }
314         return;
315     }
316 
DoActionWhenClone(const std::function<void (const std::map<_Key,_Tp> &)> & action)317     void DoActionWhenClone(const std::function<void(const std::map<_Key, _Tp> &)> &action)
318     {
319         if (action == nullptr) {
320             return;
321         }
322         auto tmp = Clone();
323         std::shared_lock<decltype(mutex_)> lock(mutex_);
324         action(tmp);
325         return;
326     }
327 
Clone()328     std::map<_Key, _Tp> Clone() const noexcept
329     {
330         std::shared_lock<decltype(mutex_)> lock(mutex_);
331         return entries_;
332     }
333 
334 private:
Steal()335     std::map<_Key, _Tp> Steal() noexcept
336     {
337         std::lock_guard<decltype(mutex_)> lock(mutex_);
338         return std::move(entries_);
339     }
340 
341 private:
342     mutable std::shared_mutex mutex_;
343     std::map<_Key, _Tp> entries_;
344 };
345 } // namespace OHOS
346 #endif // OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
347