PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/filesystem/DirectoryCache.cpp

https://gitlab.com/fflayol/xbmc
C++ | 281 lines | 207 code | 39 blank | 35 comment | 38 complexity | c913609a56ac4849e98f2c3b3ad75e50 MD5 | raw file
  1. /*
  2. * Copyright (C) 2005-2013 Team XBMC
  3. * http://xbmc.org
  4. *
  5. * This Program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This Program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with XBMC; see the file COPYING. If not, see
  17. * <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include "DirectoryCache.h"
  21. #include "FileItem.h"
  22. #include "threads/SingleLock.h"
  23. #include "utils/log.h"
  24. #include "utils/URIUtils.h"
  25. #include "utils/StringUtils.h"
  26. #include "climits"
  27. #include <algorithm>
  28. // Maximum number of directories to keep in our cache
  29. #define MAX_CACHED_DIRS 50
  30. using namespace XFILE;
  31. CDirectoryCache::CDir::CDir(DIR_CACHE_TYPE cacheType)
  32. {
  33. m_cacheType = cacheType;
  34. m_lastAccess = 0;
  35. m_Items = new CFileItemList;
  36. m_Items->SetFastLookup(true);
  37. }
  38. CDirectoryCache::CDir::~CDir()
  39. {
  40. delete m_Items;
  41. }
  42. void CDirectoryCache::CDir::SetLastAccess(unsigned int &accessCounter)
  43. {
  44. m_lastAccess = accessCounter++;
  45. }
  46. CDirectoryCache::CDirectoryCache(void)
  47. {
  48. m_accessCounter = 0;
  49. #ifdef _DEBUG
  50. m_cacheHits = 0;
  51. m_cacheMisses = 0;
  52. #endif
  53. }
  54. CDirectoryCache::~CDirectoryCache(void)
  55. {
  56. }
  57. bool CDirectoryCache::GetDirectory(const std::string& strPath, CFileItemList &items, bool retrieveAll)
  58. {
  59. CSingleLock lock (m_cs);
  60. std::string storedPath = strPath;
  61. URIUtils::RemoveSlashAtEnd(storedPath);
  62. ciCache i = m_cache.find(storedPath);
  63. if (i != m_cache.end())
  64. {
  65. CDir* dir = i->second;
  66. if (dir->m_cacheType == XFILE::DIR_CACHE_ALWAYS ||
  67. (dir->m_cacheType == XFILE::DIR_CACHE_ONCE && retrieveAll))
  68. {
  69. items.Copy(*dir->m_Items);
  70. dir->SetLastAccess(m_accessCounter);
  71. #ifdef _DEBUG
  72. m_cacheHits+=items.Size();
  73. #endif
  74. return true;
  75. }
  76. }
  77. return false;
  78. }
  79. void CDirectoryCache::SetDirectory(const std::string& strPath, const CFileItemList &items, DIR_CACHE_TYPE cacheType)
  80. {
  81. if (cacheType == DIR_CACHE_NEVER)
  82. return; // nothing to do
  83. // caches the given directory using a copy of the items, rather than the items
  84. // themselves. The reason we do this is because there is often some further
  85. // processing on the items (stacking, transparent rars/zips for instance) that
  86. // alters the URL of the items. If we shared the pointers, we'd have problems
  87. // as the URLs in the cache would have changed, so things such as
  88. // CDirectoryCache::FileExists() would fail for files that really do exist (just their
  89. // URL's have been altered). This is called from CFile::Exists() which causes
  90. // all sorts of hassles.
  91. // IDEALLY, any further processing on the item would actually create a new item
  92. // instead of altering it, but we can't really enforce that in an easy way, so
  93. // this is the best solution for now.
  94. CSingleLock lock (m_cs);
  95. std::string storedPath = strPath;
  96. URIUtils::RemoveSlashAtEnd(storedPath);
  97. ClearDirectory(storedPath);
  98. CheckIfFull();
  99. CDir* dir = new CDir(cacheType);
  100. dir->m_Items->Copy(items);
  101. dir->SetLastAccess(m_accessCounter);
  102. m_cache.insert(std::pair<std::string, CDir*>(storedPath, dir));
  103. }
  104. void CDirectoryCache::ClearFile(const std::string& strFile)
  105. {
  106. ClearDirectory(URIUtils::GetDirectory(strFile));
  107. }
  108. void CDirectoryCache::ClearDirectory(const std::string& strPath)
  109. {
  110. CSingleLock lock (m_cs);
  111. std::string storedPath = strPath;
  112. URIUtils::RemoveSlashAtEnd(storedPath);
  113. iCache i = m_cache.find(storedPath);
  114. if (i != m_cache.end())
  115. Delete(i);
  116. }
  117. void CDirectoryCache::ClearSubPaths(const std::string& strPath)
  118. {
  119. CSingleLock lock (m_cs);
  120. std::string storedPath = strPath;
  121. URIUtils::RemoveSlashAtEnd(storedPath);
  122. iCache i = m_cache.begin();
  123. while (i != m_cache.end())
  124. {
  125. if (StringUtils::StartsWith(i->first, storedPath))
  126. Delete(i++);
  127. else
  128. i++;
  129. }
  130. }
  131. void CDirectoryCache::AddFile(const std::string& strFile)
  132. {
  133. CSingleLock lock (m_cs);
  134. std::string strPath = URIUtils::GetDirectory(strFile);
  135. URIUtils::RemoveSlashAtEnd(strPath);
  136. ciCache i = m_cache.find(strPath);
  137. if (i != m_cache.end())
  138. {
  139. CDir *dir = i->second;
  140. CFileItemPtr item(new CFileItem(strFile, false));
  141. dir->m_Items->Add(item);
  142. dir->SetLastAccess(m_accessCounter);
  143. }
  144. }
  145. bool CDirectoryCache::FileExists(const std::string& strFile, bool& bInCache)
  146. {
  147. CSingleLock lock (m_cs);
  148. bInCache = false;
  149. std::string strPath(strFile);
  150. URIUtils::RemoveSlashAtEnd(strPath);
  151. std::string storedPath = URIUtils::GetDirectory(strPath);
  152. URIUtils::RemoveSlashAtEnd(storedPath);
  153. ciCache i = m_cache.find(storedPath);
  154. if (i != m_cache.end())
  155. {
  156. bInCache = true;
  157. CDir *dir = i->second;
  158. dir->SetLastAccess(m_accessCounter);
  159. #ifdef _DEBUG
  160. m_cacheHits++;
  161. #endif
  162. return (URIUtils::PathEquals(strPath, storedPath) || dir->m_Items->Contains(strFile));
  163. }
  164. #ifdef _DEBUG
  165. m_cacheMisses++;
  166. #endif
  167. return false;
  168. }
  169. void CDirectoryCache::Clear()
  170. {
  171. // this routine clears everything
  172. CSingleLock lock (m_cs);
  173. iCache i = m_cache.begin();
  174. while (i != m_cache.end() )
  175. Delete(i++);
  176. }
  177. void CDirectoryCache::InitCache(std::set<std::string>& dirs)
  178. {
  179. std::set<std::string>::iterator it;
  180. for (it = dirs.begin(); it != dirs.end(); ++it)
  181. {
  182. const std::string& strDir = *it;
  183. CFileItemList items;
  184. CDirectory::GetDirectory(strDir, items, "", DIR_FLAG_NO_FILE_DIRS);
  185. items.Clear();
  186. }
  187. }
  188. void CDirectoryCache::ClearCache(std::set<std::string>& dirs)
  189. {
  190. iCache i = m_cache.begin();
  191. while (i != m_cache.end())
  192. {
  193. if (dirs.find(i->first) != dirs.end())
  194. Delete(i++);
  195. else
  196. i++;
  197. }
  198. }
  199. void CDirectoryCache::CheckIfFull()
  200. {
  201. CSingleLock lock (m_cs);
  202. // find the last accessed folder, and remove if the number of cached folders is too many
  203. iCache lastAccessed = m_cache.end();
  204. unsigned int numCached = 0;
  205. for (iCache i = m_cache.begin(); i != m_cache.end(); i++)
  206. {
  207. // ensure dirs that are always cached aren't cleared
  208. if (i->second->m_cacheType != DIR_CACHE_ALWAYS)
  209. {
  210. if (lastAccessed == m_cache.end() || i->second->GetLastAccess() < lastAccessed->second->GetLastAccess())
  211. lastAccessed = i;
  212. numCached++;
  213. }
  214. }
  215. if (lastAccessed != m_cache.end() && numCached >= MAX_CACHED_DIRS)
  216. Delete(lastAccessed);
  217. }
  218. void CDirectoryCache::Delete(iCache it)
  219. {
  220. CDir* dir = it->second;
  221. delete dir;
  222. m_cache.erase(it);
  223. }
  224. #ifdef _DEBUG
  225. void CDirectoryCache::PrintStats() const
  226. {
  227. CSingleLock lock (m_cs);
  228. CLog::Log(LOGDEBUG, "%s - total of %u cache hits, and %u cache misses", __FUNCTION__, m_cacheHits, m_cacheMisses);
  229. // run through and find the oldest and the number of items cached
  230. unsigned int oldest = UINT_MAX;
  231. unsigned int numItems = 0;
  232. unsigned int numDirs = 0;
  233. for (ciCache i = m_cache.begin(); i != m_cache.end(); i++)
  234. {
  235. CDir *dir = i->second;
  236. oldest = std::min(oldest, dir->GetLastAccess());
  237. numItems += dir->m_Items->Size();
  238. numDirs++;
  239. }
  240. CLog::Log(LOGDEBUG, "%s - %u folders cached, with %u items total. Oldest is %u, current is %u", __FUNCTION__, numDirs, numItems, oldest, m_accessCounter);
  241. }
  242. #endif