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
16 #include "lru_cache_disk_handler.h"
17
18 #include <fstream>
19 #include <sstream>
20 #include <algorithm>
21 #include "netstack_log.h"
22
23 namespace OHOS::NetStack::Http {
24
GetMapValueSize(const std::unordered_map<std::string,std::string> & m)25 static size_t GetMapValueSize(const std::unordered_map<std::string, std::string> &m)
26 {
27 size_t size = 0;
28 for (const auto &p : m) {
29 if (p.second.size() > MAX_SIZE) {
30 return INVALID_SIZE;
31 }
32 if (size + p.second.size() > MAX_SIZE) {
33 return INVALID_SIZE;
34 }
35 size += p.second.size();
36 }
37 if (size > MAX_SIZE || size == 0) {
38 return INVALID_SIZE;
39 }
40 return size;
41 }
42
Node(std::string key,std::unordered_map<std::string,std::string> value)43 LRUCache::Node::Node(std::string key, std::unordered_map<std::string, std::string> value)
44 : key(std::move(key)), value(std::move(value))
45 {
46 }
47
LRUCache()48 LRUCache::LRUCache() : capacity_(MAX_SIZE), size_(0) {}
49
LRUCache(size_t capacity)50 LRUCache::LRUCache(size_t capacity) : capacity_(std::min<size_t>(MAX_SIZE, capacity)), size_(0) {}
51
AddNode(const Node & node)52 void LRUCache::AddNode(const Node &node)
53 {
54 nodeList_.emplace_front(node);
55 cache_[node.key] = nodeList_.begin();
56 size_ += GetMapValueSize(node.value);
57 }
58
MoveNodeToHead(const std::list<Node>::iterator & it)59 void LRUCache::MoveNodeToHead(const std::list<Node>::iterator &it)
60 {
61 std::string key = it->key;
62 std::unordered_map<std::string, std::string> value = it->value;
63 nodeList_.erase(it);
64 nodeList_.emplace_front(key, value);
65 cache_[key] = nodeList_.begin();
66 }
67
EraseTailNode()68 void LRUCache::EraseTailNode()
69 {
70 if (nodeList_.empty()) {
71 return;
72 }
73 Node node = nodeList_.back();
74 nodeList_.pop_back();
75 cache_.erase(node.key);
76 size_ -= GetMapValueSize(node.value);
77 }
78
Get(const std::string & key)79 std::unordered_map<std::string, std::string> LRUCache::Get(const std::string &key)
80 {
81 std::lock_guard<std::mutex> guard(mutex_);
82
83 if (cache_.find(key) == cache_.end()) {
84 return {};
85 }
86 auto it = cache_[key];
87 auto value = it->value;
88 MoveNodeToHead(it);
89 return value;
90 }
91
Put(const std::string & key,const std::unordered_map<std::string,std::string> & value)92 void LRUCache::Put(const std::string &key, const std::unordered_map<std::string, std::string> &value)
93 {
94 std::lock_guard<std::mutex> guard(mutex_);
95
96 if (GetMapValueSize(value) == INVALID_SIZE) {
97 NETSTACK_LOGE("value is invalid(0 or too long) can not insert to cache");
98 return;
99 }
100
101 if (cache_.find(key) == cache_.end()) {
102 AddNode(Node(key, value));
103 while (size_ > capacity_) {
104 EraseTailNode();
105 }
106 return;
107 }
108
109 auto it = cache_[key];
110
111 size_ -= GetMapValueSize(it->value);
112 it->value = value;
113 size_ += GetMapValueSize(it->value);
114
115 MoveNodeToHead(it);
116 while (size_ > capacity_) {
117 EraseTailNode();
118 }
119 }
120
MergeOtherCache(const LRUCache & other)121 void LRUCache::MergeOtherCache(const LRUCache &other)
122 {
123 std::list<Node> reverseList;
124 {
125 // set mutex in min scope
126 std::lock_guard<std::mutex> guard(mutex_);
127 if (other.nodeList_.empty()) {
128 return;
129 }
130 reverseList = other.nodeList_;
131 }
132 reverseList.reverse();
133 for (const auto &node : reverseList) {
134 Put(node.key, node.value);
135 }
136 }
137
WriteCacheToJsonValue()138 cJSON* LRUCache::WriteCacheToJsonValue()
139 {
140 cJSON* root = cJSON_CreateObject();
141
142 int index = 0;
143 {
144 // set mutex in min scope
145 std::lock_guard<std::mutex> guard(mutex_);
146 for (const auto &node : nodeList_) {
147 cJSON *nodeKey = cJSON_CreateObject();
148 for (const auto &p : node.value) {
149 cJSON_AddItemToObject(nodeKey, p.first.c_str(), cJSON_CreateString(p.second.c_str()));
150 }
151 cJSON_AddItemToObject(nodeKey, LRU_INDEX, cJSON_CreateString(std::to_string(index).c_str()));
152 ++index;
153 cJSON_AddItemToObject(root, node.key.c_str(), nodeKey);
154 }
155 }
156 return root;
157 }
158
ReadCacheFromJsonValue(const cJSON * root)159 void LRUCache::ReadCacheFromJsonValue(const cJSON* root)
160 {
161 std::vector<Node> nodeVec;
162 for (int32_t i = 0; i < cJSON_GetArraySize(root); i++) {
163 cJSON *keyItem = cJSON_GetArrayItem(root, i);
164 if (keyItem == nullptr || !cJSON_IsObject(keyItem)) {
165 continue;
166 }
167 std::string key = keyItem->string;
168 std::unordered_map<std::string, std::string> m;
169 for (int32_t j = 0; j < cJSON_GetArraySize(keyItem); j++) {
170 cJSON *valueItem = cJSON_GetArrayItem(keyItem, j);
171 if (valueItem == nullptr) {
172 NETSTACK_LOGD("valueItem is null");
173 continue;
174 }
175 std::string valueKey = valueItem->string;
176 m[valueKey] = cJSON_GetStringValue(valueItem);
177 }
178
179 if (m.find(LRU_INDEX) != m.end()) {
180 nodeVec.emplace_back(key, m);
181 }
182 }
183 std::sort(nodeVec.begin(), nodeVec.end(), [](Node &a, Node &b) {
184 return std::strtol(a.value[LRU_INDEX].c_str(), nullptr, DECIMAL_BASE) >
185 std::strtol(b.value[LRU_INDEX].c_str(), nullptr, DECIMAL_BASE);
186 });
187 for (auto &node : nodeVec) {
188 node.value.erase(LRU_INDEX);
189 if (!node.value.empty()) {
190 Put(node.key, node.value);
191 }
192 }
193 }
Clear()194 void LRUCache::Clear()
195 {
196 std::lock_guard<std::mutex> guard(mutex_);
197 cache_.clear();
198 nodeList_.clear();
199 }
200
DiskHandler(std::string fileName)201 DiskHandler::DiskHandler(std::string fileName) : fileName_(std::move(fileName)) {}
202
Write(const std::string & str)203 void DiskHandler::Write(const std::string &str)
204 {
205 std::lock_guard<std::mutex> guard(mutex_);
206 std::ofstream w(fileName_);
207 if (!w.is_open()) {
208 return;
209 }
210 w << str;
211 w.close();
212 }
213
Read()214 std::string DiskHandler::Read()
215 {
216 std::lock_guard<std::mutex> guard(mutex_);
217 std::ifstream r(fileName_);
218 if (!r.is_open()) {
219 return {};
220 }
221 std::stringstream b;
222 b << r.rdbuf();
223 r.close();
224 return b.str();
225 }
226
Delete()227 void DiskHandler::Delete()
228 {
229 std::lock_guard<std::mutex> guard(mutex_);
230 if (remove(fileName_.c_str()) < 0) {
231 NETSTACK_LOGI("remove file error %{public}d", errno);
232 }
233 }
234
LRUCacheDiskHandler(std::string fileName,size_t capacity)235 LRUCacheDiskHandler::LRUCacheDiskHandler(std::string fileName, size_t capacity)
236 : diskHandler_(std::move(fileName)),
237 capacity_(std::max<size_t>(std::min<size_t>(MAX_DISK_CACHE_SIZE, capacity), MIN_DISK_CACHE_SIZE))
238 {
239 }
240
SetCapacity(size_t capacity)241 void LRUCacheDiskHandler::SetCapacity(size_t capacity)
242 {
243 capacity_ = std::max<size_t>(std::min<size_t>(MAX_DISK_CACHE_SIZE, capacity), MIN_DISK_CACHE_SIZE);
244 WriteCacheToJsonFile();
245 }
246
Delete()247 void LRUCacheDiskHandler::Delete()
248 {
249 cache_.Clear();
250 diskHandler_.Delete();
251 }
252
ReadJsonValueFromFile()253 cJSON* LRUCacheDiskHandler::ReadJsonValueFromFile()
254 {
255 std::string jsonStr = diskHandler_.Read();
256 cJSON *root = cJSON_Parse(jsonStr.c_str());
257 if (root == nullptr) {
258 NETSTACK_LOGE("parse json not success, maybe file is broken");
259 return nullptr;
260 }
261 return root;
262 }
263
WriteJsonValueToFile(const cJSON * root)264 void LRUCacheDiskHandler::WriteJsonValueToFile(const cJSON *root)
265 {
266 char *jsonStr = cJSON_Print(root);
267 if (jsonStr == nullptr) {
268 NETSTACK_LOGE("write json failed");
269 return;
270 }
271 std::string s = jsonStr;
272 diskHandler_.Write(s);
273 free(jsonStr);
274 }
275
WriteCacheToJsonFile()276 void LRUCacheDiskHandler::WriteCacheToJsonFile()
277 {
278 LRUCache oldCache(capacity_);
279 cJSON *readRoot = ReadJsonValueFromFile();
280 oldCache.ReadCacheFromJsonValue(readRoot);
281 cJSON_Delete(readRoot);
282 oldCache.MergeOtherCache(cache_);
283 cJSON *writeRoot = oldCache.WriteCacheToJsonValue();
284 WriteJsonValueToFile(writeRoot);
285 cJSON_Delete(writeRoot);
286 cache_.Clear();
287 }
288
ReadCacheFromJsonFile()289 void LRUCacheDiskHandler::ReadCacheFromJsonFile()
290 {
291 cJSON *root = ReadJsonValueFromFile();
292 cache_.ReadCacheFromJsonValue(root);
293 cJSON_Delete(root);
294 }
295
Get(const std::string & key)296 std::unordered_map<std::string, std::string> LRUCacheDiskHandler::Get(const std::string &key)
297 {
298 auto valueFromMemory = cache_.Get(key);
299 if (!valueFromMemory.empty()) {
300 return valueFromMemory;
301 }
302
303 LRUCache diskCache(capacity_);
304 cJSON *root = ReadJsonValueFromFile();
305 diskCache.ReadCacheFromJsonValue(root);
306 cJSON_Delete(root);
307 auto valueFromDisk = diskCache.Get(key);
308 cache_.Put(key, valueFromDisk);
309 return valueFromDisk;
310 }
311
Put(const std::string & key,const std::unordered_map<std::string,std::string> & value)312 void LRUCacheDiskHandler::Put(const std::string &key, const std::unordered_map<std::string, std::string> &value)
313 {
314 cache_.Put(key, value);
315 }
316 } // namespace OHOS::NetStack::Http
317