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