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 
Contains(const key_type & key)114     bool Contains(const key_type &key) const noexcept
115     {
116         std::lock_guard<decltype(mutex_)> lock(mutex_);
117         return (entries_.find(key) != entries_.end());
118     }
119 
120     template <typename _Obj>
InsertOrAssign(const key_type & key,_Obj && obj)121     bool InsertOrAssign(const key_type &key, _Obj &&obj) noexcept
122     {
123         std::lock_guard<decltype(mutex_)> lock(mutex_);
124         auto it = entries_.insert_or_assign(key, std::forward<_Obj>(obj));
125         return it.second;
126     }
127 
Insert(const key_type & key,const mapped_type & value)128     bool Insert(const key_type &key, const mapped_type &value) noexcept
129     {
130         std::lock_guard<decltype(mutex_)> lock(mutex_);
131         auto it = entries_.insert(value_type { key, value });
132         return it.second;
133     }
134 
Erase(const key_type & key)135     size_type Erase(const key_type &key) noexcept
136     {
137         std::lock_guard<decltype(mutex_)> lock(mutex_);
138         return entries_.erase(key);
139     }
140 
Clear()141     void Clear() noexcept
142     {
143         std::lock_guard<decltype(mutex_)> lock(mutex_);
144         return entries_.clear();
145     }
146 
Empty()147     bool Empty() const noexcept
148     {
149         std::lock_guard<decltype(mutex_)> lock(mutex_);
150         return entries_.empty();
151     }
152 
Size()153     size_type Size() const noexcept
154     {
155         std::lock_guard<decltype(mutex_)> lock(mutex_);
156         return entries_.size();
157     }
158 
159     // The action`s return true means meeting the erase condition
160     // The action`s return false means not meeting the erase condition
EraseIf(const std::function<bool (const key_type & key,mapped_type & value)> & action)161     size_type EraseIf(const std::function<bool(const key_type &key, mapped_type &value)> &action) noexcept
162     {
163         if (action == nullptr) {
164             return 0;
165         }
166         std::lock_guard<decltype(mutex_)> lock(mutex_);
167 #if __cplusplus > 201703L
168         auto count = std::erase_if(entries_,
169             [&action](value_type &value) -> bool { return action(value.first, value.second); });
170 #else
171         auto count = entries_.size();
172         for (auto it = entries_.begin(); it != entries_.end();) {
173             if (action((*it).first, (*it).second)) {
174                 it = entries_.erase(it);
175             } else {
176                 ++it;
177             }
178         }
179         count -= entries_.size();
180 #endif
181         return count;
182     }
183 
ForEach(const std::function<bool (const key_type &,mapped_type &)> & action)184     void ForEach(const std::function<bool(const key_type &, mapped_type &)> &action)
185     {
186         if (action == nullptr) {
187             return;
188         }
189         std::lock_guard<decltype(mutex_)> lock(mutex_);
190         for (auto &[key, value] : entries_) {
191             if (action(key, value)) {
192                 break;
193             }
194         }
195     }
196 
ForEachCopies(const std::function<bool (const key_type &,mapped_type &)> & action)197     void ForEachCopies(const std::function<bool(const key_type &, mapped_type &)> &action)
198     {
199         if (action == nullptr) {
200             return;
201         }
202         auto entries = Clone();
203         for (auto &[key, value] : entries) {
204             if (action(key, value)) {
205                 break;
206             }
207         }
208     }
209 
210     // 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)211     bool Compute(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action)
212     {
213         if (action == nullptr) {
214             return false;
215         }
216         std::lock_guard<decltype(mutex_)> lock(mutex_);
217         auto it = entries_.find(key);
218         if (it == entries_.end()) {
219             auto result = entries_.emplace(key, mapped_type());
220             it = result.second ? result.first : entries_.end();
221         }
222         if (it == entries_.end()) {
223             return false;
224         }
225         if (!action(it->first, it->second)) {
226             entries_.erase(key);
227         }
228         return true;
229     }
230 
231     // 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)232     bool ComputeIfPresent(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action)
233     {
234         if (action == nullptr) {
235             return false;
236         }
237         std::lock_guard<decltype(mutex_)> lock(mutex_);
238         auto it = entries_.find(key);
239         if (it == entries_.end()) {
240             return false;
241         }
242         if (!action(key, it->second)) {
243             entries_.erase(key);
244         }
245         return true;
246     }
247 
ComputeIfAbsent(const key_type & key,const std::function<mapped_type (const key_type &)> & action)248     bool ComputeIfAbsent(const key_type &key, const std::function<mapped_type(const key_type &)> &action)
249     {
250         if (action == nullptr) {
251             return false;
252         }
253         std::lock_guard<decltype(mutex_)> lock(mutex_);
254         auto it = entries_.find(key);
255         if (it != entries_.end()) {
256             return false;
257         }
258         entries_.emplace(key, action(key));
259         return true;
260     }
261 
262 private:
Steal()263     std::map<_Key, _Tp> Steal() noexcept
264     {
265         std::lock_guard<decltype(mutex_)> lock(mutex_);
266         return std::move(entries_);
267     }
268 
Clone()269     std::map<_Key, _Tp> Clone() const noexcept
270     {
271         std::lock_guard<decltype(mutex_)> lock(mutex_);
272         return entries_;
273     }
274 
275 private:
276     mutable std::recursive_mutex mutex_;
277     std::map<_Key, _Tp> entries_;
278 };
279 } // namespace OHOS
280 #endif // OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
281