1 /*
2  * Copyright (c) 2024 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 #ifndef EXPIRE_LRU_CACHE_H
16 #define EXPIRE_LRU_CACHE_H
17 
18 #include <map>
19 #include <mutex>
20 #include <memory>
21 #include <list>
22 #include <algorithm>
23 #include "datetime_ex.h"
24 
25 namespace OHOS {
26 namespace {
27 constexpr int64_t DEFAULT_EXPIRE_TIME = 1000;
28 }
29 
30 template <typename TKey, typename TValue>
31 class ExpireLruCache {
32 public:
33     ExpireLruCache(size_t cacheSize = 8, int64_t expireTimeSec = 1000) : size_(cacheSize),
34         expireTimeMilliSec_(expireTimeSec)
35     {
36         size_ = (size_ > 0) ? size_ : 1;
37         expireTimeMilliSec_ = (expireTimeMilliSec_ < 0) ? DEFAULT_EXPIRE_TIME : expireTimeMilliSec_;
38     }
~ExpireLruCache()39     ~ExpireLruCache() {}
40 
Add(const TKey & key,const TValue & val)41     void Add(const TKey& key, const TValue& val)
42     {
43         std::lock_guard<std::mutex> lock(lock_);
44         DoAdd(key, val);
45         return;
46     }
47 
Get(const TKey & key)48     std::shared_ptr<TValue> Get(const TKey& key)
49     {
50         std::lock_guard<std::mutex> lock(lock_);
51         return DoGet(key);
52     }
53 
Remove(const TKey & key)54     void Remove(const TKey& key)
55     {
56         std::lock_guard<std::mutex> lock(lock_);
57         return DoRemove(key);
58     }
59 
Clear()60     void Clear()
61     {
62         std::lock_guard<std::mutex> lock(lock_);
63         DoClear();
64         return;
65     }
66 private:
67     class Timestamp {
68     public:
Timestamp()69         Timestamp()
70         {
71             ts_ = GetTickCount();
72         }
~Timestamp()73         ~Timestamp() {}
74 
75         int64_t operator-(const Timestamp& ts)
76         {
77             return ts_ - ts.ts_;
78         }
79 
IsExpired(int64_t interval)80         bool IsExpired(int64_t interval) const
81         {
82             Timestamp now;
83             int64_t diff = now - *this;
84             return diff > interval;
85         }
86 
Raw()87         int64_t Raw() const
88         {
89             return ts_;
90         }
91     private:
92         /* unit:ms */
93         int64_t ts_;
94     };
95     size_t size_;
96     int64_t expireTimeMilliSec_;
97     std::mutex lock_;
98     std::map<TKey, std::shared_ptr<TValue>> data_;
99     std::map<TKey, Timestamp> timestamp_;
100     std::list<TKey> keys_;
101 
DoAdd(const TKey & key,const TValue & value)102     void DoAdd(const TKey& key, const TValue& value)
103     {
104         Timestamp now;
105         size_t curSize = data_.size();
106         auto iter = data_.find(key);
107         auto timestampIter = timestamp_.find(key);
108 
109         if (iter != data_.end()) {
110             data_.erase(iter);
111             timestamp_.erase(timestampIter);
112             data_.insert(std::make_pair(key, std::make_shared<TValue>(value)));
113             timestamp_.insert(std::make_pair(key, now));
114             auto keyiter = std::find(keys_.begin(), keys_.end(), key);
115             keys_.splice(keys_.begin(), keys_, keyiter);
116 
117             return;
118         }
119 
120         if (curSize >= size_) {
121             bool ifClearExpiredCache = false;
122             if (expireTimeMilliSec_ > 0) {
123                 ifClearExpiredCache = DoClearExpiredCache();
124             }
125             if (ifClearExpiredCache == false) {
126                 auto lruKey = keys_.back();
127                 data_.erase(lruKey);
128                 timestamp_.erase(lruKey);
129                 keys_.pop_back();
130             }
131         }
132 
133         data_.insert(std::make_pair(key, std::make_shared<TValue>(value)));
134         timestamp_.insert(std::make_pair(key, now));
135         keys_.push_front(key);
136 
137         return;
138     }
139 
DoGet(const TKey & key)140     std::shared_ptr<TValue> DoGet(const TKey& key)
141     {
142         auto dataIter = data_.find(key);
143         if (dataIter == data_.end()) {
144             return nullptr;
145         }
146 
147         auto timestampIter = timestamp_.find(key);
148         if ((expireTimeMilliSec_ > 0) && (timestampIter->second.IsExpired(expireTimeMilliSec_))) {
149             data_.erase(dataIter);
150             timestamp_.erase(timestampIter);
151             keys_.remove(key);
152 
153             return nullptr;
154         } else {
155             auto keyiter = std::find(keys_.begin(), keys_.end(), key);
156             keys_.splice(keys_.begin(), keys_, keyiter) ;
157 
158             return dataIter->second;
159         }
160     }
161 
DoRemove(const TKey & key)162     void DoRemove(const TKey& key)
163     {
164         keys_.remove(key);
165         data_.erase(key);
166         timestamp_.erase(key);
167     }
168 
DoClear()169     void DoClear()
170     {
171         data_.clear();
172         timestamp_.clear();
173         keys_.clear();
174     }
175 
DoClearExpiredCache()176     bool DoClearExpiredCache()
177     {
178         bool ifClear = false;
179         for (auto iter = timestamp_.begin(); iter != timestamp_.end();) {
180             if (iter->second.IsExpired(expireTimeMilliSec_)) {
181                 ifClear = true;
182                 keys_.remove(iter->first);
183                 data_.erase(iter->first);
184                 iter = timestamp_.erase(iter);
185             } else {
186                 iter++;
187             }
188         }
189         return ifClear;
190     }
191 };
192 }
193 
194 #endif
195