1/*
2 * Copyright (c) 2021 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 UTILS_BASE_SAFE_MAP_H
17#define UTILS_BASE_SAFE_MAP_H
18
19#include <map>
20#include <mutex>
21
22namespace OHOS {
23
24/**
25 * @brief Provides interfaces for thread-safe map operations.
26 */
27template <typename K, typename V>
28class SafeMap {
29public:
30    SafeMap() {}
31
32    ~SafeMap() {}
33
34    SafeMap(const SafeMap& rhs)
35    {
36        operator=(rhs);
37    }
38
39    SafeMap& operator=(const SafeMap& rhs)
40    {
41        if (this == &rhs) {
42            return *this;
43        }
44        auto tmp = rhs.Clone();
45        std::lock_guard<std::mutex> lock(mutex_);
46        map_ = std::move(tmp);
47
48        return *this;
49    }
50
51    V ReadVal(const K& key)
52    {
53        std::lock_guard<std::mutex> lock(mutex_);
54        return map_[key];
55    }
56
57    template<typename LambdaCallback>
58    void ChangeValueByLambda(const K& key, LambdaCallback callback)
59    {
60        std::lock_guard<std::mutex> lock(mutex_);
61        callback(map_[key]);
62    }
63
64    /**
65     * @brief Obtains the map size.
66     *
67     * In the multithread scenario, the map size returned is a tmp status,
68     * because elements may be inserted or removed by other threads after
69     * <b>Size()</b> is called.
70     */
71    int Size()
72    {
73        std::lock_guard<std::mutex> lock(mutex_);
74        return map_.size();
75    }
76
77    /**
78     * @brief Checks whether the map is empty.
79     *
80     * In the multithread scenario, the value returned by <b>Empty()</b> is a
81     * tmp status, because elements may be inserted or removed by other threads
82     * after <b>Empty()</b> is called.
83     *
84     * @return Returns <b>true</b> if the map is empty;
85     * returns <b>false</b> otherwise.
86     */
87    bool IsEmpty()
88    {
89        std::lock_guard<std::mutex> lock(mutex_);
90        return map_.empty();
91    }
92
93    /**
94     * @brief Inserts an element to the map.
95     *
96     * @param key Indicates the key of the key-value (KV) pair to insert.
97     * @param value Indicates the value of the KV pair to insert.
98     * @return Returns <b>true</b> if the KV pair is inserted; returns
99     * <b>false</b> otherwise.
100     */
101    bool Insert(const K& key, const V& value)
102    {
103        std::lock_guard<std::mutex> lock(mutex_);
104        auto ret = map_.insert(std::pair<K, V>(key, value));
105        return ret.second;
106    }
107
108    /**
109     * @brief Forcibly inserts an element to the map.
110     *
111     * @param key Indicates the key of the KV pair to insert.
112     * @param value Indicates the value of the KV pair to insert.
113     * @note If the key to insert already exists, delete and then insert
114     * the KV pair to ensure that the value is inserted.
115     */
116    void EnsureInsert(const K& key, const V& value)
117    {
118        std::lock_guard<std::mutex> lock(mutex_);
119        auto ret = map_.insert(std::pair<K, V>(key, value));
120        // find key and cannot insert
121        if (!ret.second) {
122            map_.erase(ret.first);
123            map_.insert(std::pair<K, V>(key, value));
124            return;
125        }
126        return;
127    }
128
129    /**
130     * @brief Searches for an element in the map.
131     *
132     * @param Key Indicates the key to search.
133     * @param value Indicates the value of the KV pair to search.
134     * @return Returns <b>true</b> if the KV pair is found;
135     * returns <b>false</b> otherwise.
136     */
137    bool Find(const K& key, V& value)
138    {
139        bool ret = false;
140        std::lock_guard<std::mutex> lock(mutex_);
141
142        auto iter = map_.find(key);
143        if (iter != map_.end()) {
144            value = iter->second;
145            ret = true;
146        }
147
148        return ret;
149    }
150
151    /**
152     * @brief Replaces the value of a KV pair.
153     *
154     * @param Key Indicates the key of the KV pair.
155     * @param oldValue Indicates the value to be replaced.
156     * @param newValue Indicates the new value of the KV pair.
157     * @return Returns <b>true</b> if the key is replaced;
158     * returns <b>false</b> otherwise.
159     */
160    bool FindOldAndSetNew(const K& key, V& oldValue, const V& newValue)
161    {
162        bool ret = false;
163        std::lock_guard<std::mutex> lock(mutex_);
164        if (map_.size() > 0) {
165            auto iter = map_.find(key);
166            if (iter != map_.end()) {
167                oldValue = iter->second;
168                map_.erase(iter);
169                map_.insert(std::pair<K, V>(key, newValue));
170                ret = true;
171            }
172        }
173
174        return ret;
175    }
176
177    /**
178     * @brief Erases a KV pair.
179     *
180     * @param Key Indicates the key of the KV pair to erase.
181     */
182    void Erase(const K& key)
183    {
184        std::lock_guard<std::mutex> lock(mutex_);
185        map_.erase(key);
186    }
187
188    /**
189     * @brief Deletes all KV pairs from the map.
190     */
191    void Clear()
192    {
193        std::lock_guard<std::mutex> lock(mutex_);
194        map_.clear();
195        return;
196    }
197
198    using SafeMapCallBack = std::function<void(const K, V&)>;
199
200    /**
201     * @brief Iterates over the elements of the map.
202     *
203     * @param callback Called to perform the custom operations on
204     * each KV pair.
205     */
206    void Iterate(const SafeMapCallBack& callback)
207    {
208        std::lock_guard<std::mutex> lock(mutex_);
209        if (!map_.empty()) {
210            for (auto it = map_.begin(); it != map_.end(); it++) {
211                callback(it -> first, it -> second);
212            }
213        }
214    }
215
216private:
217    mutable std::mutex mutex_;
218    std::map<K, V> map_;
219
220    std::map<K, V> Clone() const noexcept
221    {
222        std::lock_guard<std::mutex> lock(mutex_);
223        return map_;
224    }
225};
226
227} // namespace OHOS
228#endif
229