1 /*
2  * Copyright (c) 2022 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 <mutex>
21 namespace OHOS {
22 template<typename _Key, typename _Tp>
23 class ConcurrentMap {
24     template<typename _First, typename... _Rest>
25     static _First First();
26 
27 public:
28     using map_type = typename std::map<_Key, _Tp>;
29     using filter_type = typename std::function<bool(map_type &)>;
30     using key_type = typename std::map<_Key, _Tp>::key_type;
31     using mapped_type = typename std::map<_Key, _Tp>::mapped_type;
32     using value_type = typename std::map<_Key, _Tp>::value_type;
33     using size_type = typename std::map<_Key, _Tp>::size_type;
34     using reference = typename std::map<_Key, _Tp>::reference;
35     using const_reference = typename std::map<_Key, _Tp>::const_reference;
36 
37     ConcurrentMap() = default;
~ConcurrentMap()38     ~ConcurrentMap()
39     {
40         Clear();
41     }
42 
ConcurrentMap(const ConcurrentMap & other)43     ConcurrentMap(const ConcurrentMap &other)
44     {
45         operator=(std::move(other));
46     }
47 
48     ConcurrentMap &operator=(const ConcurrentMap &other) noexcept
49     {
50         if (this == &other) {
51             return *this;
52         }
53         auto tmp = other.Clone();
54         std::lock_guard<decltype(mutex_)> lock(mutex_);
55         entries_ = std::move(tmp);
56         return *this;
57     }
58 
ConcurrentMap(ConcurrentMap && other)59     ConcurrentMap(ConcurrentMap &&other) noexcept
60     {
61         operator=(std::move(other));
62     }
63 
64     ConcurrentMap &operator=(ConcurrentMap &&other) noexcept
65     {
66         if (this == &other) {
67             return *this;
68         }
69         auto tmp = other.Steal();
70         std::lock_guard<decltype(mutex_)> lock(mutex_);
71         entries_ = std::move(tmp);
72         return *this;
73     }
74 
Emplace()75     bool Emplace() noexcept
76     {
77         std::lock_guard<decltype(mutex_)> lock(mutex_);
78         auto it = entries_.emplace();
79         return it.second;
80     }
81 
82     template<typename... _Args>
83     typename std::enable_if<!std::is_convertible_v<decltype(First<_Args...>()), filter_type>, bool>::type
84     Emplace(_Args &&...args) noexcept
85     {
86         std::lock_guard<decltype(mutex_)> lock(mutex_);
87         auto it = entries_.emplace(std::forward<_Args>(args)...);
88         return it.second;
89     }
90 
91     template<typename _Filter, typename... _Args>
92     typename std::enable_if<std::is_convertible_v<_Filter, filter_type>, bool>::type
Emplace(const _Filter & filter,_Args &&...args)93     Emplace(const _Filter &filter, _Args &&...args) noexcept
94     {
95         std::lock_guard<decltype(mutex_)> lock(mutex_);
96         if (!filter(entries_)) {
97             return false;
98         }
99         auto it = entries_.emplace(std::forward<_Args>(args)...);
100         return it.second;
101     }
102 
Find(const key_type & key)103     std::pair<bool, mapped_type> Find(const key_type &key) const noexcept
104     {
105         std::lock_guard<decltype(mutex_)> lock(mutex_);
106         auto it = entries_.find(key);
107         if (it == entries_.end()) {
108             return std::pair { false, mapped_type() };
109         }
110 
111         return std::pair { true, it->second };
112     }
113 
ContainIf(const key_type & key,const std::function<bool (const mapped_type & value)> & action)114     bool ContainIf(const key_type &key, const std::function<bool(const mapped_type &value)> &action) const noexcept
115     {
116         std::lock_guard<decltype(mutex_)> lock(mutex_);
117         auto it = entries_.find(key);
118         if (it == entries_.end()) {
119             return false;
120         }
121         if (action) {
122             return action(it->second);
123         }
124         return true;
125     }
126 
Contains(const key_type & key)127     bool Contains(const key_type &key) const noexcept
128     {
129         std::lock_guard<decltype(mutex_)> lock(mutex_);
130         return (entries_.find(key) != entries_.end());
131     }
132 
133     template <typename _Obj>
InsertOrAssign(const key_type & key,_Obj && obj)134     bool InsertOrAssign(const key_type &key, _Obj &&obj) noexcept
135     {
136         std::lock_guard<decltype(mutex_)> lock(mutex_);
137         auto it = entries_.insert_or_assign(key, std::forward<_Obj>(obj));
138         return it.second;
139     }
140 
Insert(const key_type & key,const mapped_type & value)141     bool Insert(const key_type &key, const mapped_type &value) noexcept
142     {
143         std::lock_guard<decltype(mutex_)> lock(mutex_);
144         auto it = entries_.insert(value_type { key, value });
145         return it.second;
146     }
147 
Erase(const key_type & key)148     size_type Erase(const key_type &key) noexcept
149     {
150         std::lock_guard<decltype(mutex_)> lock(mutex_);
151         return entries_.erase(key);
152     }
153 
Clear()154     void Clear() noexcept
155     {
156         std::lock_guard<decltype(mutex_)> lock(mutex_);
157         return entries_.clear();
158     }
159 
Empty()160     bool Empty() const noexcept
161     {
162         std::lock_guard<decltype(mutex_)> lock(mutex_);
163         return entries_.empty();
164     }
165 
Size()166     size_type Size() const noexcept
167     {
168         std::lock_guard<decltype(mutex_)> lock(mutex_);
169         return entries_.size();
170     }
171 
172     // The action`s return true means meeting the erase condition
173     // The action`s return false means not meeting the erase condition
EraseIf(const std::function<bool (const key_type & key,mapped_type & value)> & action)174     size_type EraseIf(const std::function<bool(const key_type &key, mapped_type &value)> &action) noexcept
175     {
176         if (action == nullptr) {
177             return 0;
178         }
179         std::lock_guard<decltype(mutex_)> lock(mutex_);
180 #if __cplusplus > 201703L
181         auto count = std::erase_if(entries_,
182             [&action](value_type &value) -> bool { return action(value.first, value.second); });
183 #else
184         auto count = entries_.size();
185         for (auto it = entries_.begin(); it != entries_.end();) {
186             if (action((*it).first, (*it).second)) {
187                 it = entries_.erase(it);
188             } else {
189                 ++it;
190             }
191         }
192         count -= entries_.size();
193 #endif
194         return count;
195     }
196 
ForEach(const std::function<bool (const key_type &,mapped_type &)> & action)197     void ForEach(const std::function<bool(const key_type &, mapped_type &)> &action)
198     {
199         if (action == nullptr) {
200             return;
201         }
202         std::lock_guard<decltype(mutex_)> lock(mutex_);
203         for (auto &[key, value] : entries_) {
204             if (action(key, value)) {
205                 break;
206             }
207         }
208     }
209 
ForEachCopies(const std::function<bool (const key_type &,mapped_type &)> & action)210     void ForEachCopies(const std::function<bool(const key_type &, mapped_type &)> &action)
211     {
212         if (action == nullptr) {
213             return;
214         }
215         auto entries = Clone();
216         for (auto &[key, value] : entries) {
217             if (action(key, value)) {
218                 break;
219             }
220         }
221     }
222 
223     // 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)224     bool Compute(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action)
225     {
226         if (action == nullptr) {
227             return false;
228         }
229         std::lock_guard<decltype(mutex_)> lock(mutex_);
230         auto it = entries_.find(key);
231         if (it == entries_.end()) {
232             auto result = entries_.emplace(key, mapped_type());
233             it = result.second ? result.first : entries_.end();
234         }
235         if (it == entries_.end()) {
236             return false;
237         }
238         if (!action(it->first, it->second)) {
239             entries_.erase(key);
240         }
241         return true;
242     }
243 
244     // 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)245     bool ComputeIfPresent(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action)
246     {
247         if (action == nullptr) {
248             return false;
249         }
250         std::lock_guard<decltype(mutex_)> lock(mutex_);
251         auto it = entries_.find(key);
252         if (it == entries_.end()) {
253             return false;
254         }
255         if (!action(key, it->second)) {
256             entries_.erase(key);
257         }
258         return true;
259     }
260 
ComputeIfAbsent(const key_type & key,const std::function<mapped_type (const key_type &)> & action)261     bool ComputeIfAbsent(const key_type &key, const std::function<mapped_type(const key_type &)> &action)
262     {
263         if (action == nullptr) {
264             return false;
265         }
266         std::lock_guard<decltype(mutex_)> lock(mutex_);
267         auto it = entries_.find(key);
268         if (it != entries_.end()) {
269             return false;
270         }
271         entries_.emplace(key, action(key));
272         return true;
273     }
274 
DoActionIfEmpty(const std::function<void (void)> & action)275     void DoActionIfEmpty(const std::function<void(void)> &action)
276     {
277         if (action == nullptr) {
278             return;
279         }
280         std::lock_guard<decltype(mutex_)> lock(mutex_);
281         if (entries_.empty()) {
282             action();
283         }
284         return;
285     }
286 
287 private:
Steal()288     std::map<_Key, _Tp> Steal() noexcept
289     {
290         std::lock_guard<decltype(mutex_)> lock(mutex_);
291         return std::move(entries_);
292     }
293 
Clone()294     std::map<_Key, _Tp> Clone() const noexcept
295     {
296         std::lock_guard<decltype(mutex_)> lock(mutex_);
297         return entries_;
298     }
299 
300 private:
301     mutable std::recursive_mutex mutex_;
302     std::map<_Key, _Tp> entries_;
303 };
304 } // namespace OHOS
305 #endif // OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
306