1/*
2 * Copyright (c) 2023 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_INPUTMETHOD_IMF_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
17#define OHOS_INPUTMETHOD_IMF_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
18#include <functional>
19#include <map>
20#include <mutex>
21namespace OHOS {
22template<typename _Key, typename _Tp>
23class ConcurrentMap {
24    template<typename _First, typename... _Rest>
25    static _First First();
26
27public:
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;
38    ~ConcurrentMap()
39    {
40        Clear();
41    }
42
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
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
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 Emplace(
84        _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,
93        _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
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
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>
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
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
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
141    void Clear() noexcept
142    {
143        std::lock_guard<decltype(mutex_)> lock(mutex_);
144        return entries_.clear();
145    }
146
147    bool Empty() const noexcept
148    {
149        std::lock_guard<decltype(mutex_)> lock(mutex_);
150        return entries_.empty();
151    }
152
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
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
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
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.
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.
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
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    bool ComputeIfAbsent(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action)
263    {
264        if (action == nullptr) {
265            return false;
266        }
267        std::lock_guard<decltype(mutex_)> lock(mutex_);
268        auto it = entries_.find(key);
269        if (it != entries_.end()) {
270            return false;
271        }
272        auto result = entries_.emplace(key, mapped_type());
273        it = result.second ? result.first : entries_.end();
274        if (it == entries_.end()) {
275            return false;
276        }
277        if (!action(it->first, it->second)) {
278            entries_.erase(key);
279            return false;
280        }
281        return true;
282    }
283
284private:
285    std::map<_Key, _Tp> Steal() noexcept
286    {
287        std::lock_guard<decltype(mutex_)> lock(mutex_);
288        return std::move(entries_);
289    }
290
291    std::map<_Key, _Tp> Clone() const noexcept
292    {
293        std::lock_guard<decltype(mutex_)> lock(mutex_);
294        return entries_;
295    }
296
297private:
298    mutable std::recursive_mutex mutex_;
299    std::map<_Key, _Tp> entries_;
300};
301} // namespace OHOS
302#endif // OHOS_INPUTMETHOD_IMF_FRAMEWORKS_COMMON_CONCURRENT_MAP_H
303