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