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_LRU_BUCKET_H
17 #define OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_LRU_BUCKET_H
18 
19 #include <map>
20 #include <mutex>
21 #include <list>
22 
23 namespace OHOS {
24 template<typename _Key, typename _Tp>
25 class LRUBucket {
26 public:
LRUBucket(size_t capacity)27     LRUBucket(size_t capacity)
28         : size_(0), capacity_(capacity) {}
29 
30     LRUBucket(LRUBucket &&bucket) noexcept = delete;
31     LRUBucket(const LRUBucket &bucket) = delete;
32     LRUBucket &operator=(LRUBucket &&bucket) noexcept = delete;
33     LRUBucket &operator=(const LRUBucket &bucket) = delete;
34 
~LRUBucket()35     ~LRUBucket()
36     {
37         std::lock_guard<decltype(mutex_)> lock(mutex_);
38         while (size_ > 0) {
39             PopBack();
40         }
41     }
42 
Size()43     size_t Size() const
44     {
45         return size_;
46     }
47 
Capacity()48     size_t Capacity() const
49     {
50         return capacity_;
51     }
52 
ResetCapacity(size_t capacity)53     bool ResetCapacity(size_t capacity)
54     {
55         std::lock_guard<decltype(mutex_)> lock(mutex_);
56         capacity_ = capacity;
57         while (capacity_ < size_) {
58             PopBack();
59         }
60         return capacity_ == capacity;
61     }
62 
63     /**
64      * The time complexity is O(log(index size))
65      **/
66     bool Get(const _Key &key, _Tp &value, bool isLRU = true)
67     {
68         std::lock_guard<decltype(mutex_)> lock(mutex_);
69         auto it = indexes_.find(key);
70         if (it != indexes_.end()) {
71             if (isLRU) {
72                 // move node from the list;
73                 Remove(it->second);
74                 // insert node to the head
75                 Insert(&head_, it->second);
76             }
77             value = it->second->value_;
78             return true;
79         }
80         return false;
81     }
82 
83     /**
84      * The time complexity is O(log(index size))
85      **/
Set(const _Key & key,const _Tp & value)86     bool Set(const _Key &key, const _Tp &value)
87     {
88         std::lock_guard<decltype(mutex_)> lock(mutex_);
89         if (capacity_ == 0) {
90             return false;
91         }
92 
93         auto it = indexes_.find(key);
94         if (it != indexes_.end()) {
95             Update(it->second, value);
96             Remove(it->second);
97             Insert(&head_, it->second);
98             return true;
99         }
100 
101         while (capacity_ <= size_) {
102             PopBack();
103         }
104 
105         auto *node = new(std::nothrow) Node(value);
106         if (node == nullptr) {
107             return false;
108         }
109 
110         Insert(&head_, node);
111         auto pair = indexes_.emplace(key, node);
112         node->iterator_ = pair.first;
113         return true;
114     }
115 
116     /**
117      * Just update the values, not change the lru
118      **/
Update(const _Key & key,const _Tp & value)119     bool Update(const _Key &key, const _Tp &value)
120     {
121         std::lock_guard<decltype(mutex_)> lock(mutex_);
122         auto it = indexes_.find(key);
123         if (it != indexes_.end()) {
124             Update(it->second, value);
125             return true;
126         }
127         return false;
128     }
129 
130     /**
131      * The time complexity is O(min(indexes, values))
132      * Just update the values, not change the lru chain
133      */
Update(const std::map<_Key,_Tp> & values)134     bool Update(const std::map<_Key, _Tp> &values)
135     {
136         std::lock_guard<decltype(mutex_)> lock(mutex_);
137         auto idx = indexes_.begin();
138         auto val = values.begin();
139         bool updated = false;
140         auto comp = indexes_.key_comp();
141         while (idx != indexes_.end() && val != values.end()) {
142             if (comp(idx->first, val->first)) {
143                 ++idx;
144                 continue;
145             }
146             if (comp(val->first, idx->first)) {
147                 ++val;
148                 continue;
149             }
150             updated = true;
151             Update(idx->second, val->second);
152             ++idx;
153             ++val;
154         }
155         return updated;
156     }
157 
158     /**
159      * The time complexity is O(log(index size))
160      * */
Delete(const _Key & key)161     bool Delete(const _Key &key)
162     {
163         std::lock_guard<decltype(mutex_)> lock(mutex_);
164         auto it = indexes_.find(key);
165         if (it != indexes_.end()) {
166             Remove(it->second);
167             Delete(it->second);
168             return true;
169         }
170         return false;
171     }
172 
173 private:
174     struct Node final {
175         using iterator = typename std::map<_Key, Node *>::iterator;
Nodefinal176         Node(const _Tp &value) : value_(value) {}
Nodefinal177         Node() : value_() {}
178         ~Node() = default;
179         _Tp value_;
180         iterator iterator_;
181         Node *prev_ = this;
182         Node *next_ = this;
183     };
184 
PopBack()185     void PopBack()
186     {
187         auto *node = head_.prev_;
188         Remove(node);
189         Delete(node);
190     }
191 
Update(Node * node,const _Tp & value)192     void Update(Node *node, const _Tp &value)
193     {
194         node->value_ = value;
195     }
196 
Remove(Node * node)197     void Remove(Node *node)
198     {
199         node->prev_->next_ = node->next_;
200         node->next_->prev_ = node->prev_;
201         size_--;
202     }
203 
Insert(Node * prev,Node * node)204     void Insert(Node *prev, Node *node)
205     {
206         prev->next_->prev_ = node;
207         node->next_ = prev->next_;
208         prev->next_ = node;
209         node->prev_ = prev;
210         size_++;
211     }
212 
Delete(Node * node)213     void Delete(Node *node)
214     {
215         indexes_.erase(node->iterator_);
216         delete node;
217     }
218 
219     mutable std::recursive_mutex mutex_;
220     std::map<_Key, Node *> indexes_;
221     Node head_;
222     size_t size_;
223     size_t capacity_;
224 };
225 } // namespace OHOS
226 #endif // OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_LRU_BUCKET_H
227