2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
21 #include "DirectoryCache.h"
22 #include "settings/Settings.h"
24 #include "threads/SingleLock.h"
25 #include "utils/log.h"
26 #include "utils/URIUtils.h"
30 using namespace XFILE;
32 CDirectoryCache::CDir::CDir(DIR_CACHE_TYPE cacheType)
34 m_cacheType = cacheType;
36 m_Items = new CFileItemList;
37 m_Items->SetFastLookup(true);
40 CDirectoryCache::CDir::~CDir()
45 void CDirectoryCache::CDir::SetLastAccess(unsigned int &accessCounter)
47 m_lastAccess = accessCounter++;
50 CDirectoryCache::CDirectoryCache(void)
59 CDirectoryCache::~CDirectoryCache(void)
63 bool CDirectoryCache::GetDirectory(const CStdString& strPath, CFileItemList &items, bool retrieveAll)
65 CSingleLock lock (m_cs);
67 CStdString storedPath = URIUtils::SubstitutePath(strPath);
68 URIUtils::RemoveSlashAtEnd(storedPath);
70 ciCache i = m_cache.find(storedPath);
71 if (i != m_cache.end())
73 CDir* dir = i->second;
74 if (dir->m_cacheType == XFILE::DIR_CACHE_ALWAYS ||
75 (dir->m_cacheType == XFILE::DIR_CACHE_ONCE && retrieveAll))
77 items.Copy(*dir->m_Items);
78 dir->SetLastAccess(m_accessCounter);
80 m_cacheHits+=items.Size();
88 void CDirectoryCache::SetDirectory(const CStdString& strPath, const CFileItemList &items, DIR_CACHE_TYPE cacheType)
90 if (cacheType == DIR_CACHE_NEVER)
91 return; // nothing to do
93 // caches the given directory using a copy of the items, rather than the items
94 // themselves. The reason we do this is because there is often some further
95 // processing on the items (stacking, transparent rars/zips for instance) that
96 // alters the URL of the items. If we shared the pointers, we'd have problems
97 // as the URLs in the cache would have changed, so things such as
98 // CDirectoryCache::FileExists() would fail for files that really do exist (just their
99 // URL's have been altered). This is called from CFile::Exists() which causes
100 // all sorts of hassles.
101 // IDEALLY, any further processing on the item would actually create a new item
102 // instead of altering it, but we can't really enforce that in an easy way, so
103 // this is the best solution for now.
104 CSingleLock lock (m_cs);
106 CStdString storedPath = URIUtils::SubstitutePath(strPath);
107 URIUtils::RemoveSlashAtEnd(storedPath);
109 ClearDirectory(storedPath);
113 CDir* dir = new CDir(cacheType);
114 dir->m_Items->Copy(items);
115 dir->SetLastAccess(m_accessCounter);
116 m_cache.insert(pair<CStdString, CDir*>(storedPath, dir));
119 void CDirectoryCache::ClearFile(const CStdString& strFile)
122 URIUtils::GetDirectory(strFile, strPath);
123 ClearDirectory(strPath);
126 void CDirectoryCache::ClearDirectory(const CStdString& strPath)
128 CSingleLock lock (m_cs);
130 CStdString storedPath = URIUtils::SubstitutePath(strPath);
131 URIUtils::RemoveSlashAtEnd(storedPath);
133 iCache i = m_cache.find(storedPath);
134 if (i != m_cache.end())
138 void CDirectoryCache::ClearSubPaths(const CStdString& strPath)
140 CSingleLock lock (m_cs);
142 CStdString storedPath = URIUtils::SubstitutePath(strPath);
143 URIUtils::RemoveSlashAtEnd(storedPath);
145 iCache i = m_cache.begin();
146 while (i != m_cache.end())
148 CStdString path = i->first;
149 if (strncmp(path.c_str(), storedPath.c_str(), storedPath.GetLength()) == 0)
156 void CDirectoryCache::AddFile(const CStdString& strFile)
158 CSingleLock lock (m_cs);
161 URIUtils::GetDirectory(strFile, strPath);
162 URIUtils::RemoveSlashAtEnd(strPath);
164 ciCache i = m_cache.find(strPath);
165 if (i != m_cache.end())
167 CDir *dir = i->second;
168 CFileItemPtr item(new CFileItem(strFile, false));
169 dir->m_Items->Add(item);
170 dir->SetLastAccess(m_accessCounter);
174 bool CDirectoryCache::FileExists(const CStdString& strFile, bool& bInCache)
176 CSingleLock lock (m_cs);
180 URIUtils::GetDirectory(strFile, strPath);
181 URIUtils::RemoveSlashAtEnd(strPath);
183 ciCache i = m_cache.find(strPath);
184 if (i != m_cache.end())
187 CDir *dir = i->second;
188 dir->SetLastAccess(m_accessCounter);
192 return dir->m_Items->Contains(strFile);
200 void CDirectoryCache::Clear()
202 // this routine clears everything
203 CSingleLock lock (m_cs);
205 iCache i = m_cache.begin();
206 while (i != m_cache.end() )
210 void CDirectoryCache::InitCache(set<CStdString>& dirs)
212 set<CStdString>::iterator it;
213 for (it = dirs.begin(); it != dirs.end(); ++it)
215 const CStdString& strDir = *it;
217 CDirectory::GetDirectory(strDir, items, "", DIR_FLAG_NO_FILE_DIRS);
222 void CDirectoryCache::ClearCache(set<CStdString>& dirs)
224 iCache i = m_cache.begin();
225 while (i != m_cache.end())
227 if (dirs.find(i->first) != dirs.end())
234 void CDirectoryCache::CheckIfFull()
236 CSingleLock lock (m_cs);
237 static const unsigned int max_cached_dirs = 10;
239 // find the last accessed folder, and remove if the number of cached folders is too many
240 iCache lastAccessed = m_cache.end();
241 unsigned int numCached = 0;
242 for (iCache i = m_cache.begin(); i != m_cache.end(); i++)
244 // ensure dirs that are always cached aren't cleared
245 if (i->second->m_cacheType != DIR_CACHE_ALWAYS)
247 if (lastAccessed == m_cache.end() || i->second->GetLastAccess() < lastAccessed->second->GetLastAccess())
252 if (lastAccessed != m_cache.end() && numCached >= max_cached_dirs)
253 Delete(lastAccessed);
256 void CDirectoryCache::Delete(iCache it)
258 CDir* dir = it->second;
264 void CDirectoryCache::PrintStats() const
266 CSingleLock lock (m_cs);
267 CLog::Log(LOGDEBUG, "%s - total of %u cache hits, and %u cache misses", __FUNCTION__, m_cacheHits, m_cacheMisses);
268 // run through and find the oldest and the number of items cached
269 unsigned int oldest = UINT_MAX;
270 unsigned int numItems = 0;
271 unsigned int numDirs = 0;
272 for (ciCache i = m_cache.begin(); i != m_cache.end(); i++)
274 CDir *dir = i->second;
275 oldest = min(oldest, dir->GetLastAccess());
276 numItems += dir->m_Items->Size();
279 CLog::Log(LOGDEBUG, "%s - %u folders cached, with %u items total. Oldest is %u, current is %u", __FUNCTION__, numDirs, numItems, oldest, m_accessCounter);