• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "lru_cache.h"
17 
18 #include <algorithm>
19 #include <mutex>
20 
21 #include "netstack_log.h"
22 
23 static constexpr const char *LRU_INDEX = "LRUIndex";
24 static constexpr const int DECIMAL_BASE = 10;
25 static constexpr const int MAX_SIZE = 1024 * 1024;
26 static constexpr const size_t INVALID_SIZE = SIZE_MAX;
27 
28 namespace OHOS::NetStack {
GetMapValueSize(const std::unordered_map<std::string,std::string> & m)29 static size_t GetMapValueSize(const std::unordered_map<std::string, std::string> &m)
30 {
31     size_t size = 0;
32     for (const auto &p : m) {
33         if (p.second.size() > MAX_SIZE) {
34             return INVALID_SIZE;
35         }
36         if (size + p.second.size() > MAX_SIZE) {
37             return INVALID_SIZE;
38         }
39         size += p.second.size();
40     }
41     if (size > MAX_SIZE || size == 0) {
42         return INVALID_SIZE;
43     }
44     return size;
45 }
46 
Node(std::string key,std::unordered_map<std::string,std::string> value)47 LRUCache::Node::Node(std::string key, std::unordered_map<std::string, std::string> value)
48     : key(std::move(key)), value(std::move(value))
49 {
50 }
51 
LRUCache()52 LRUCache::LRUCache() : capacity_(MAX_SIZE), size_(0) {}
53 
LRUCache(size_t capacity)54 LRUCache::LRUCache(size_t capacity) : capacity_(std::min<size_t>(MAX_SIZE, capacity)), size_(0) {}
55 
AddNode(const Node & node)56 void LRUCache::AddNode(const Node &node)
57 {
58     nodeList_.emplace_front(node);
59     cache_[node.key] = nodeList_.begin();
60     size_ += GetMapValueSize(node.value);
61 }
62 
MoveNodeToHead(const std::list<Node>::iterator & it)63 void LRUCache::MoveNodeToHead(const std::list<Node>::iterator &it)
64 {
65     std::string key = it->key;
66     std::unordered_map<std::string, std::string> value = it->value;
67     nodeList_.erase(it);
68     nodeList_.emplace_front(key, value);
69     cache_[key] = nodeList_.begin();
70 }
71 
EraseTailNode()72 void LRUCache::EraseTailNode()
73 {
74     if (nodeList_.empty()) {
75         return;
76     }
77     Node node = nodeList_.back();
78     nodeList_.pop_back();
79     cache_.erase(node.key);
80     size_ -= GetMapValueSize(node.value);
81 }
82 
Get(const std::string & key)83 std::unordered_map<std::string, std::string> LRUCache::Get(const std::string &key)
84 {
85     std::lock_guard<std::mutex> guard(mutex_);
86 
87     if (cache_.find(key) == cache_.end()) {
88         return {};
89     }
90     auto it = cache_[key];
91     auto value = it->value;
92     MoveNodeToHead(it);
93     return value;
94 }
95 
Put(const std::string & key,const std::unordered_map<std::string,std::string> & value)96 void LRUCache::Put(const std::string &key, const std::unordered_map<std::string, std::string> &value)
97 {
98     std::lock_guard<std::mutex> guard(mutex_);
99 
100     if (GetMapValueSize(value) == INVALID_SIZE) {
101         NETSTACK_LOGE("value is invalid(0 or too long) can not insert to cache");
102         return;
103     }
104 
105     if (cache_.find(key) == cache_.end()) {
106         AddNode(Node(key, value));
107         while (size_ > capacity_) {
108             EraseTailNode();
109         }
110         return;
111     }
112 
113     auto it = cache_[key];
114 
115     size_ -= GetMapValueSize(it->value);
116     it->value = value;
117     size_ += GetMapValueSize(it->value);
118 
119     MoveNodeToHead(it);
120     while (size_ > capacity_) {
121         EraseTailNode();
122     }
123 }
124 
MergeOtherCache(const LRUCache & other)125 void LRUCache::MergeOtherCache(const LRUCache &other)
126 {
127     std::list<Node> reverseList;
128     {
129         // set mutex in min scope
130         std::lock_guard<std::mutex> guard(mutex_);
131         if (other.nodeList_.empty()) {
132             return;
133         }
134         reverseList = other.nodeList_;
135     }
136     reverseList.reverse();
137     for (const auto &node : reverseList) {
138         Put(node.key, node.value);
139     }
140 }
141 
WriteCacheToJsonValue()142 Json::Value LRUCache::WriteCacheToJsonValue()
143 {
144     Json::Value root;
145 
146     int index = 0;
147     {
148         // set mutex in min scope
149         std::lock_guard<std::mutex> guard(mutex_);
150         for (const auto &node : nodeList_) {
151             root[node.key] = Json::Value();
152             for (const auto &p : node.value) {
153                 root[node.key][p.first] = p.second;
154             }
155             root[node.key][LRU_INDEX] = std::to_string(index);
156             ++index;
157         }
158     }
159     return root;
160 }
161 
ReadCacheFromJsonValue(const Json::Value & root)162 void LRUCache::ReadCacheFromJsonValue(const Json::Value &root)
163 {
164     std::vector<Node> nodeVec;
165     for (auto it = root.begin(); it != root.end(); ++it) {
166         if (!it.key().isString()) {
167             continue;
168         }
169         Json::Value value = root[it.key().asString()];
170         if (!value.isObject()) {
171             continue;
172         }
173 
174         std::unordered_map<std::string, std::string> m;
175         for (auto innerIt = value.begin(); innerIt != value.end(); ++innerIt) {
176             if (!innerIt.key().isString()) {
177                 continue;
178             }
179             Json::Value innerValue = root[it.key().asString()][innerIt.key().asString()];
180             if (!innerValue.isString()) {
181                 continue;
182             }
183 
184             m[innerIt.key().asString()] = innerValue.asString();
185         }
186 
187         if (m.find(LRU_INDEX) != m.end()) {
188             nodeVec.emplace_back(it.key().asString(), m);
189         }
190     }
191     std::sort(nodeVec.begin(), nodeVec.end(), [](Node &a, Node &b) {
192         return std::strtol(a.value[LRU_INDEX].c_str(), nullptr, DECIMAL_BASE) >
193                std::strtol(b.value[LRU_INDEX].c_str(), nullptr, DECIMAL_BASE);
194     });
195     for (auto &node : nodeVec) {
196         node.value.erase(LRU_INDEX);
197         if (!node.value.empty()) {
198             Put(node.key, node.value);
199         }
200     }
201 }
202 
Clear()203 void LRUCache::Clear()
204 {
205     std::lock_guard<std::mutex> guard(mutex_);
206     cache_.clear();
207     nodeList_.clear();
208 }
209 } // namespace OHOS::NetStack
210