[Test] Added tests for CUtil::SplitParams
[xbmc.git] / xbmc / filesystem / DirectoryCache.cpp
blobf3fd1fb7aeba75e0be04396f06501aea1bde8e21
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "DirectoryCache.h"
11 #include "Directory.h"
12 #include "FileItem.h"
13 #include "URL.h"
14 #include "utils/StringUtils.h"
15 #include "utils/URIUtils.h"
16 #include "utils/log.h"
18 #include <algorithm>
19 #include <climits>
20 #include <mutex>
22 // Maximum number of directories to keep in our cache
23 #define MAX_CACHED_DIRS 50
25 using namespace XFILE;
27 CDirectoryCache::CDir::CDir(DIR_CACHE_TYPE cacheType) : m_Items(std::make_unique<CFileItemList>())
29 m_cacheType = cacheType;
30 m_lastAccess = 0;
31 m_Items->SetIgnoreURLOptions(true);
32 m_Items->SetFastLookup(true);
35 CDirectoryCache::CDir::~CDir() = default;
37 void CDirectoryCache::CDir::SetLastAccess(unsigned int &accessCounter)
39 m_lastAccess = accessCounter++;
42 CDirectoryCache::CDirectoryCache(void)
44 m_accessCounter = 0;
45 #ifdef _DEBUG
46 m_cacheHits = 0;
47 m_cacheMisses = 0;
48 #endif
51 CDirectoryCache::~CDirectoryCache(void) = default;
53 bool CDirectoryCache::GetDirectory(const std::string& strPath, CFileItemList &items, bool retrieveAll)
55 std::unique_lock<CCriticalSection> lock(m_cs);
57 // Get rid of any URL options, else the compare may be wrong
58 std::string storedPath = CURL(strPath).GetWithoutOptions();
59 URIUtils::RemoveSlashAtEnd(storedPath);
61 auto i = m_cache.find(storedPath);
62 if (i != m_cache.end())
64 CDir& dir = i->second;
65 if (dir.m_cacheType == XFILE::DIR_CACHE_ALWAYS ||
66 (dir.m_cacheType == XFILE::DIR_CACHE_ONCE && retrieveAll))
68 items.Copy(*dir.m_Items);
69 dir.SetLastAccess(m_accessCounter);
70 #ifdef _DEBUG
71 m_cacheHits+=items.Size();
72 #endif
73 return true;
76 return false;
79 void CDirectoryCache::SetDirectory(const std::string& strPath, const CFileItemList &items, DIR_CACHE_TYPE cacheType)
81 if (cacheType == DIR_CACHE_NEVER)
82 return; // nothing to do
84 // caches the given directory using a copy of the items, rather than the items
85 // themselves. The reason we do this is because there is often some further
86 // processing on the items (stacking, transparent rars/zips for instance) that
87 // alters the URL of the items. If we shared the pointers, we'd have problems
88 // as the URLs in the cache would have changed, so things such as
89 // CDirectoryCache::FileExists() would fail for files that really do exist (just their
90 // URL's have been altered). This is called from CFile::Exists() which causes
91 // all sorts of hassles.
92 // IDEALLY, any further processing on the item would actually create a new item
93 // instead of altering it, but we can't really enforce that in an easy way, so
94 // this is the best solution for now.
95 std::unique_lock<CCriticalSection> lock(m_cs);
97 // Get rid of any URL options, else the compare may be wrong
98 std::string storedPath = CURL(strPath).GetWithoutOptions();
99 URIUtils::RemoveSlashAtEnd(storedPath);
101 ClearDirectory(storedPath);
103 CheckIfFull();
105 CDir dir(cacheType);
106 dir.m_Items->Copy(items);
107 dir.SetLastAccess(m_accessCounter);
108 m_cache.emplace(storedPath, std::move(dir));
111 void CDirectoryCache::ClearFile(const std::string& strFile)
113 // Get rid of any URL options, else the compare may be wrong
114 std::string strFile2 = CURL(strFile).GetWithoutOptions();
115 URIUtils::RemoveSlashAtEnd(strFile2);
117 ClearDirectory(URIUtils::GetDirectory(strFile2));
120 void CDirectoryCache::ClearDirectory(const std::string& strPath)
122 std::unique_lock<CCriticalSection> lock(m_cs);
124 // Get rid of any URL options, else the compare may be wrong
125 std::string storedPath = CURL(strPath).GetWithoutOptions();
126 URIUtils::RemoveSlashAtEnd(storedPath);
128 m_cache.erase(storedPath);
131 void CDirectoryCache::ClearSubPaths(const std::string& strPath)
133 std::unique_lock<CCriticalSection> lock(m_cs);
135 // Get rid of any URL options, else the compare may be wrong
136 std::string storedPath = CURL(strPath).GetWithoutOptions();
138 auto i = m_cache.begin();
139 while (i != m_cache.end())
141 if (URIUtils::PathHasParent(i->first, storedPath))
142 m_cache.erase(i++);
143 else
144 i++;
148 void CDirectoryCache::AddFile(const std::string& strFile)
150 std::unique_lock<CCriticalSection> lock(m_cs);
152 // Get rid of any URL options, else the compare may be wrong
153 std::string strPath = URIUtils::GetDirectory(CURL(strFile).GetWithoutOptions());
154 URIUtils::RemoveSlashAtEnd(strPath);
156 auto i = m_cache.find(strPath);
157 if (i != m_cache.end())
159 CDir& dir = i->second;
160 CFileItemPtr item(new CFileItem(strFile, false));
161 dir.m_Items->Add(item);
162 dir.SetLastAccess(m_accessCounter);
166 bool CDirectoryCache::FileExists(const std::string& strFile, bool& bInCache)
168 std::unique_lock<CCriticalSection> lock(m_cs);
169 bInCache = false;
171 // Get rid of any URL options, else the compare may be wrong
172 std::string strPath = CURL(strFile).GetWithoutOptions();
173 URIUtils::RemoveSlashAtEnd(strPath);
174 std::string storedPath = URIUtils::GetDirectory(strPath);
175 URIUtils::RemoveSlashAtEnd(storedPath);
177 auto i = m_cache.find(storedPath);
178 if (i != m_cache.end())
180 bInCache = true;
181 CDir& dir = i->second;
182 dir.SetLastAccess(m_accessCounter);
183 #ifdef _DEBUG
184 m_cacheHits++;
185 #endif
186 return (URIUtils::PathEquals(strPath, storedPath) || dir.m_Items->Contains(strFile));
188 #ifdef _DEBUG
189 m_cacheMisses++;
190 #endif
191 return false;
194 void CDirectoryCache::Clear()
196 // this routine clears everything
197 std::unique_lock<CCriticalSection> lock(m_cs);
198 m_cache.clear();
201 void CDirectoryCache::InitCache(const std::set<std::string>& dirs)
203 for (const std::string& strDir : dirs)
205 CFileItemList items;
206 CDirectory::GetDirectory(strDir, items, "", DIR_FLAG_NO_FILE_DIRS);
207 items.Clear();
211 void CDirectoryCache::ClearCache(std::set<std::string>& dirs)
213 auto i = m_cache.begin();
214 while (i != m_cache.end())
216 if (dirs.find(i->first) != dirs.end())
217 m_cache.erase(i++);
218 else
219 i++;
223 void CDirectoryCache::CheckIfFull()
225 std::unique_lock<CCriticalSection> lock(m_cs);
227 // find the last accessed folder, and remove if the number of cached folders is too many
228 auto lastAccessed = m_cache.end();
229 unsigned int numCached = 0;
230 for (auto i = m_cache.begin(); i != m_cache.end(); i++)
232 // ensure dirs that are always cached aren't cleared
233 if (i->second.m_cacheType != DIR_CACHE_ALWAYS)
235 if (lastAccessed == m_cache.end() ||
236 i->second.GetLastAccess() < lastAccessed->second.GetLastAccess())
237 lastAccessed = i;
238 numCached++;
241 if (lastAccessed != m_cache.end() && numCached >= MAX_CACHED_DIRS)
242 m_cache.erase(lastAccessed);
245 #ifdef _DEBUG
246 void CDirectoryCache::PrintStats() const
248 std::unique_lock<CCriticalSection> lock(m_cs);
249 CLog::Log(LOGDEBUG, "{} - total of {} cache hits, and {} cache misses", __FUNCTION__, m_cacheHits,
250 m_cacheMisses);
251 // run through and find the oldest and the number of items cached
252 unsigned int oldest = UINT_MAX;
253 unsigned int numItems = 0;
254 unsigned int numDirs = 0;
255 for (auto i = m_cache.begin(); i != m_cache.end(); i++)
257 const CDir& dir = i->second;
258 oldest = std::min(oldest, dir.GetLastAccess());
259 numItems += dir.m_Items->Size();
260 numDirs++;
262 CLog::Log(LOGDEBUG, "{} - {} folders cached, with {} items total. Oldest is {}, current is {}",
263 __FUNCTION__, numDirs, numItems, oldest, m_accessCounter);
265 #endif