• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "core/image/image_cache.h"
17 
18 #include <dirent.h>
19 #include <fstream>
20 #include <sys/stat.h>
21 
22 #include "core/image/image_object.h"
23 
24 namespace OHOS::Ace {
25 
26 std::shared_mutex ImageCache::cacheFilePathMutex_;
27 std::string ImageCache::cacheFilePath_;
28 
29 std::atomic<size_t> ImageCache::cacheFileLimit_ = 100 * 1024 * 1024; // the capacity is 100MB
30 
31 std::atomic<float> ImageCache::clearCacheFileRatio_ = 0.5f; // default clear ratio is 0.5
32 
33 bool ImageCache::hasSetCacheFileInfo_ = false;
34 
35 std::mutex ImageCache::cacheFileSizeMutex_;
36 int32_t ImageCache::cacheFileSize_ = 0;
37 
38 std::mutex ImageCache::cacheFileInfoMutex_;
39 std::list<FileInfo> ImageCache::cacheFileInfo_;
40 
41 template<typename T>
CacheWithCountLimitLRU(const std::string & key,const T & cacheObj,std::list<CacheNode<T>> & cacheList,std::unordered_map<std::string,typename std::list<CacheNode<T>>::iterator> & cache,const std::atomic<size_t> & capacity)42 void ImageCache::CacheWithCountLimitLRU(
43     const std::string& key,
44     const T& cacheObj,
45     std::list<CacheNode<T>>& cacheList,
46     std::unordered_map<std::string, typename std::list<CacheNode<T>>::iterator>& cache,
47     const std::atomic<size_t>& capacity)
48 {
49     auto iter = cache.find(key);
50     if (iter == cache.end()) {
51         if (cache.size() == capacity) {
52             cache.erase(cacheList.back().cacheKey);
53             cacheList.pop_back();
54         }
55         cacheList.emplace_front(key, cacheObj);
56         cache.emplace(key, cacheList.begin());
57     } else {
58         iter->second->cacheObj = cacheObj;
59         cacheList.splice(cacheList.begin(), cacheList, iter->second);
60         iter->second = cacheList.begin();
61     }
62 }
63 
64 template<typename T>
GetCacheObjWithCountLimitLRU(const std::string & key,std::list<CacheNode<T>> & cacheList,std::unordered_map<std::string,typename std::list<CacheNode<T>>::iterator> & cache)65 T ImageCache::GetCacheObjWithCountLimitLRU(
66     const std::string& key,
67     std::list<CacheNode<T>>& cacheList,
68     std::unordered_map<std::string, typename std::list<CacheNode<T>>::iterator>& cache)
69 {
70     auto iter = cache.find(key);
71     if (iter != cache.end()) {
72         cacheList.splice(cacheList.begin(), cacheList, iter->second);
73         iter->second = cacheList.begin();
74         return iter->second->cacheObj;
75     } else {
76         return nullptr;
77     }
78 }
79 
GetFromCacheFile(const std::string & filePath)80 bool ImageCache::GetFromCacheFile(const std::string& filePath)
81 {
82     std::lock_guard<std::mutex> lock(cacheFileInfoMutex_);
83     return GetFromCacheFileInner(filePath);
84 }
85 
GetFromCacheFileInner(const std::string & filePath)86 bool ImageCache::GetFromCacheFileInner(const std::string& filePath)
87 {
88     std::list<FileInfo>::iterator iter = std::find_if(cacheFileInfo_.begin(), cacheFileInfo_.end(),
89         [&filePath](const FileInfo& fileInfo) { return fileInfo.filePath == filePath; });
90     if (iter == cacheFileInfo_.end()) {
91         return false;
92     }
93     iter->accessTime = time(nullptr);
94     cacheFileInfo_.splice(cacheFileInfo_.end(), cacheFileInfo_, iter);
95     return true;
96 }
97 
CacheImage(const std::string & key,const std::shared_ptr<CachedImage> & image)98 void ImageCache::CacheImage(const std::string& key, const std::shared_ptr<CachedImage>& image)
99 {
100     if (key.empty() || capacity_ == 0) {
101         return;
102     }
103     std::scoped_lock lock(imageCacheMutex_, cacheListMutex_);
104     CacheWithCountLimitLRU<std::shared_ptr<CachedImage>>(key, image, cacheList_, imageCache_, capacity_);
105 }
106 
GetCacheImage(const std::string & key)107 std::shared_ptr<CachedImage> ImageCache::GetCacheImage(const std::string& key)
108 {
109     std::scoped_lock lock(imageCacheMutex_, cacheListMutex_);
110     return GetCacheObjWithCountLimitLRU<std::shared_ptr<CachedImage>>(key, cacheList_, imageCache_);
111 }
112 
CacheImgObj(const std::string & key,const RefPtr<ImageObject> & imgObj)113 void ImageCache::CacheImgObj(const std::string& key, const RefPtr<ImageObject>& imgObj)
114 {
115     if (key.empty() || imgObjCapacity_ == 0) {
116         return;
117     }
118     std::scoped_lock lock(cacheImgObjListMutex_, imgObjCacheMutex_);
119     CacheWithCountLimitLRU<RefPtr<ImageObject>>(key, imgObj, cacheImgObjList_, imgObjCache_, imgObjCapacity_);
120 }
121 
GetCacheImgObj(const std::string & key)122 RefPtr<ImageObject> ImageCache::GetCacheImgObj(const std::string& key)
123 {
124     std::scoped_lock lock(cacheImgObjListMutex_, imgObjCacheMutex_);
125     return GetCacheObjWithCountLimitLRU<RefPtr<ImageObject>>(key, cacheImgObjList_, imgObjCache_);
126 }
127 
CacheImageData(const std::string & key,const RefPtr<CachedImageData> & imageData)128 void ImageCache::CacheImageData(const std::string& key, const RefPtr<CachedImageData>& imageData)
129 {
130     if (key.empty() || !imageData || dataSizeLimit_ == 0) {
131         return;
132     }
133     std::scoped_lock lock(dataCacheListMutex_, imageDataCacheMutex_);
134     auto dataSize = imageData->GetSize();
135     if (dataSize > (dataSizeLimit_ >> 1)) { // if data is longer than half limit, do not cache it.
136         LOGW("data is %{public}d, bigger than half limit %{public}d, do not cache it",
137             static_cast<int32_t>(dataSize), static_cast<int32_t>(dataSizeLimit_ >> 1));
138         return;
139     }
140     auto iter = imageDataCache_.find(key);
141     if (iter == imageDataCache_.end()) {
142         if (!processImageDataCacheInner(dataSize)) {
143             return;
144         }
145         dataCacheList_.emplace_front(key, imageData);
146         imageDataCache_.emplace(key, dataCacheList_.begin());
147     } else {
148         auto oldSize = iter->second->imageDataPtr->GetSize();
149         if (oldSize != dataSize) {
150             curDataSize_ -= oldSize;
151             if (!processImageDataCacheInner(dataSize)) {
152                 return;
153             }
154         }
155         iter->second->imageDataPtr = imageData;
156         dataCacheList_.splice(dataCacheList_.begin(), dataCacheList_, iter->second);
157         iter->second = dataCacheList_.begin();
158     }
159 }
160 
processImageDataCacheInner(size_t dataSize)161 bool ImageCache::processImageDataCacheInner(size_t dataSize)
162 {
163     while (dataSize + curDataSize_ > dataSizeLimit_ && !dataCacheList_.empty()) {
164         curDataSize_ -= dataCacheList_.back().imageDataPtr->GetSize();
165         imageDataCache_.erase(dataCacheList_.back().imageDataKey);
166         dataCacheList_.pop_back();
167     }
168     if (dataSize + curDataSize_ > dataSizeLimit_) {
169         return false;
170     }
171     curDataSize_ += dataSize;
172     return true;
173 }
174 
GetCacheImageData(const std::string & key)175 RefPtr<CachedImageData> ImageCache::GetCacheImageData(const std::string& key)
176 {
177     std::scoped_lock lock(dataCacheListMutex_, imageDataCacheMutex_);
178     auto iter = imageDataCache_.find(key);
179     if (iter != imageDataCache_.end()) {
180         dataCacheList_.splice(dataCacheList_.begin(), dataCacheList_, iter->second);
181         iter->second = dataCacheList_.begin();
182         return iter->second->imageDataPtr;
183     } else {
184         return nullptr;
185     }
186 }
187 
188 
WriteCacheFile(const std::string & url,const void * const data,const size_t size)189 void ImageCache::WriteCacheFile(const std::string& url, const void * const data, const size_t size)
190 {
191     std::vector<std::string> removeVector;
192     std::string cacheNetworkFilePath = GetImageCacheFilePath(url);
193 
194     std::lock_guard<std::mutex> lock(cacheFileInfoMutex_);
195     // 1. first check if file has been cached.
196     if (ImageCache::GetFromCacheFileInner(cacheNetworkFilePath)) {
197         LOGI("file has been wrote %{private}s", cacheNetworkFilePath.c_str());
198         return;
199     }
200 
201     // 2. if not in dist, write file into disk.
202 #ifdef WINDOWS_PLATFORM
203     std::ofstream outFile(cacheNetworkFilePath, std::ios::binary);
204 #else
205     std::ofstream outFile(cacheNetworkFilePath, std::fstream::out);
206 #endif
207     if (!outFile.is_open()) {
208         LOGW("open cache file failed, cannot write.");
209         return;
210     }
211     outFile.write(reinterpret_cast<const char*>(data), size);
212 
213     cacheFileSize_ += size;
214     cacheFileInfo_.emplace_back(cacheNetworkFilePath, size, time(nullptr));
215     // check if cache files too big.
216     if (cacheFileSize_ > static_cast<int32_t>(cacheFileLimit_)) {
217         int32_t removeCount = static_cast<int32_t>(cacheFileInfo_.size() * clearCacheFileRatio_);
218         int32_t removeSize = 0;
219         auto iter = cacheFileInfo_.begin();
220         int32_t count = 0;
221         while (count < removeCount) {
222             removeSize += static_cast<int32_t>(iter->fileSize);
223             removeVector.push_back(iter->filePath);
224             iter++;
225             count++;
226         }
227         cacheFileInfo_.erase(cacheFileInfo_.begin(), iter);
228         cacheFileSize_ -= static_cast<int32_t>(removeSize);
229     }
230     // 3. clear files removed from cache list.
231     ClearCacheFile(removeVector);
232 }
233 
ClearCacheFile(const std::vector<std::string> & removeFiles)234 void ImageCache::ClearCacheFile(const std::vector<std::string>& removeFiles)
235 {
236     LOGD("begin to clear %{public}zu files: ", removeFiles.size());
237     for (auto iter : removeFiles) {
238         if (remove(iter.c_str()) != 0) {
239             LOGW("remove file %{private}s failed.", iter.c_str());
240             continue;
241         }
242     }
243 }
244 
SetCacheFileInfo()245 void ImageCache::SetCacheFileInfo()
246 {
247     std::lock_guard<std::mutex> lock(cacheFileInfoMutex_);
248     // Set cache file information only once.
249     if (hasSetCacheFileInfo_) {
250         return;
251     }
252     std::string cacheFilePath = GetImageCacheFilePath();
253     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(cacheFilePath.c_str()), closedir);
254     if (dir == nullptr) {
255         LOGW("cache file path wrong! maybe it is not set.");
256         return;
257     }
258     int32_t cacheFileSize = 0;
259     dirent* filePtr = readdir(dir.get());
260     while (filePtr != nullptr) {
261         // skip . or ..
262         if (filePtr->d_name[0] != '.') {
263             std::string filePath = cacheFilePath + "/" + std::string(filePtr->d_name);
264             struct stat fileStatus;
265             if (stat(filePath.c_str(), &fileStatus) == -1) {
266                 filePtr = readdir(dir.get());
267                 continue;
268             }
269             cacheFileInfo_.emplace_back(filePath, fileStatus.st_size, fileStatus.st_atime);
270             cacheFileSize += static_cast<int32_t>(fileStatus.st_size);
271         }
272         filePtr = readdir(dir.get());
273     }
274     cacheFileInfo_.sort();
275     cacheFileSize_ = cacheFileSize;
276     hasSetCacheFileInfo_ = true;
277 }
278 
279 } // namespace OHOS::Ace
280