[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / filesystem / DirectoryCache.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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
21 #include "DirectoryCache.h"
22 #include "settings/Settings.h"
23 #include "FileItem.h"
24 #include "threads/SingleLock.h"
25 #include "utils/log.h"
26 #include "utils/URIUtils.h"
27 #include "climits"
28
29 using namespace std;
30 using namespace XFILE;
31
32 CDirectoryCache::CDir::CDir(DIR_CACHE_TYPE cacheType)
33 {
34   m_cacheType = cacheType;
35   m_lastAccess = 0;
36   m_Items = new CFileItemList;
37   m_Items->SetFastLookup(true);
38 }
39
40 CDirectoryCache::CDir::~CDir()
41 {
42   delete m_Items;
43 }
44
45 void CDirectoryCache::CDir::SetLastAccess(unsigned int &accessCounter)
46 {
47   m_lastAccess = accessCounter++;
48 }
49
50 CDirectoryCache::CDirectoryCache(void)
51 {
52   m_accessCounter = 0;
53 #ifdef _DEBUG
54   m_cacheHits = 0;
55   m_cacheMisses = 0;
56 #endif
57 }
58
59 CDirectoryCache::~CDirectoryCache(void)
60 {
61 }
62
63 bool CDirectoryCache::GetDirectory(const CStdString& strPath, CFileItemList &items, bool retrieveAll)
64 {
65   CSingleLock lock (m_cs);
66
67   CStdString storedPath = URIUtils::SubstitutePath(strPath);
68   URIUtils::RemoveSlashAtEnd(storedPath);
69
70   ciCache i = m_cache.find(storedPath);
71   if (i != m_cache.end())
72   {
73     CDir* dir = i->second;
74     if (dir->m_cacheType == XFILE::DIR_CACHE_ALWAYS ||
75        (dir->m_cacheType == XFILE::DIR_CACHE_ONCE && retrieveAll))
76     {
77       items.Copy(*dir->m_Items);
78       dir->SetLastAccess(m_accessCounter);
79 #ifdef _DEBUG
80       m_cacheHits+=items.Size();
81 #endif
82       return true;
83     }
84   }
85   return false;
86 }
87
88 void CDirectoryCache::SetDirectory(const CStdString& strPath, const CFileItemList &items, DIR_CACHE_TYPE cacheType)
89 {
90   if (cacheType == DIR_CACHE_NEVER)
91     return; // nothing to do
92
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);
105
106   CStdString storedPath = URIUtils::SubstitutePath(strPath);
107   URIUtils::RemoveSlashAtEnd(storedPath);
108
109   ClearDirectory(storedPath);
110
111   CheckIfFull();
112
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));
117 }
118
119 void CDirectoryCache::ClearFile(const CStdString& strFile)
120 {
121   CStdString strPath;
122   URIUtils::GetDirectory(strFile, strPath);
123   ClearDirectory(strPath);
124 }
125
126 void CDirectoryCache::ClearDirectory(const CStdString& strPath)
127 {
128   CSingleLock lock (m_cs);
129
130   CStdString storedPath = URIUtils::SubstitutePath(strPath);
131   URIUtils::RemoveSlashAtEnd(storedPath);
132
133   iCache i = m_cache.find(storedPath);
134   if (i != m_cache.end())
135     Delete(i);
136 }
137
138 void CDirectoryCache::ClearSubPaths(const CStdString& strPath)
139 {
140   CSingleLock lock (m_cs);
141
142   CStdString storedPath = URIUtils::SubstitutePath(strPath);
143   URIUtils::RemoveSlashAtEnd(storedPath);
144
145   iCache i = m_cache.begin();
146   while (i != m_cache.end())
147   {
148     CStdString path = i->first;
149     if (strncmp(path.c_str(), storedPath.c_str(), storedPath.GetLength()) == 0)
150       Delete(i++);
151     else
152       i++;
153   }
154 }
155
156 void CDirectoryCache::AddFile(const CStdString& strFile)
157 {
158   CSingleLock lock (m_cs);
159
160   CStdString strPath;
161   URIUtils::GetDirectory(strFile, strPath);
162   URIUtils::RemoveSlashAtEnd(strPath);
163
164   ciCache i = m_cache.find(strPath);
165   if (i != m_cache.end())
166   {
167     CDir *dir = i->second;
168     CFileItemPtr item(new CFileItem(strFile, false));
169     dir->m_Items->Add(item);
170     dir->SetLastAccess(m_accessCounter);
171   }
172 }
173
174 bool CDirectoryCache::FileExists(const CStdString& strFile, bool& bInCache)
175 {
176   CSingleLock lock (m_cs);
177   bInCache = false;
178
179   CStdString strPath;
180   URIUtils::GetDirectory(strFile, strPath);
181   URIUtils::RemoveSlashAtEnd(strPath);
182
183   ciCache i = m_cache.find(strPath);
184   if (i != m_cache.end())
185   {
186     bInCache = true;
187     CDir *dir = i->second;
188     dir->SetLastAccess(m_accessCounter);
189 #ifdef _DEBUG
190     m_cacheHits++;
191 #endif
192     return dir->m_Items->Contains(strFile);
193   }
194 #ifdef _DEBUG
195   m_cacheMisses++;
196 #endif
197   return false;
198 }
199
200 void CDirectoryCache::Clear()
201 {
202   // this routine clears everything
203   CSingleLock lock (m_cs);
204
205   iCache i = m_cache.begin();
206   while (i != m_cache.end() )
207     Delete(i++);
208 }
209
210 void CDirectoryCache::InitCache(set<CStdString>& dirs)
211 {
212   set<CStdString>::iterator it;
213   for (it = dirs.begin(); it != dirs.end(); ++it)
214   {
215     const CStdString& strDir = *it;
216     CFileItemList items;
217     CDirectory::GetDirectory(strDir, items, "", DIR_FLAG_NO_FILE_DIRS);
218     items.Clear();
219   }
220 }
221
222 void CDirectoryCache::ClearCache(set<CStdString>& dirs)
223 {
224   iCache i = m_cache.begin();
225   while (i != m_cache.end())
226   {
227     if (dirs.find(i->first) != dirs.end())
228       Delete(i++);
229     else
230       i++;
231   }
232 }
233
234 void CDirectoryCache::CheckIfFull()
235 {
236   CSingleLock lock (m_cs);
237   static const unsigned int max_cached_dirs = 10;
238
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++)
243   {
244     // ensure dirs that are always cached aren't cleared
245     if (i->second->m_cacheType != DIR_CACHE_ALWAYS)
246     {
247       if (lastAccessed == m_cache.end() || i->second->GetLastAccess() < lastAccessed->second->GetLastAccess())
248         lastAccessed = i;
249       numCached++;
250     }
251   }
252   if (lastAccessed != m_cache.end() && numCached >= max_cached_dirs)
253     Delete(lastAccessed);
254 }
255
256 void CDirectoryCache::Delete(iCache it)
257 {
258   CDir* dir = it->second;
259   delete dir;
260   m_cache.erase(it);
261 }
262
263 #ifdef _DEBUG
264 void CDirectoryCache::PrintStats() const
265 {
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++)
273   {
274     CDir *dir = i->second;
275     oldest = min(oldest, dir->GetLastAccess());
276     numItems += dir->m_Items->Size();
277     numDirs++;
278   }
279   CLog::Log(LOGDEBUG, "%s - %u folders cached, with %u items total.  Oldest is %u, current is %u", __FUNCTION__, numDirs, numItems, oldest, m_accessCounter);
280 }
281 #endif