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