mirror of
https://github.com/Team-Resurgent/XBMC4Xbox.git
synced 2026-08-15 11:18:13 -07:00
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
385 lines
9.3 KiB
C++
385 lines
9.3 KiB
C++
/*
|
|
* Copyright (C) 2005-2013 Team XBMC
|
|
* http://xbmc.org
|
|
*
|
|
* This Program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This Program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with XBMC; see the file COPYING. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "system.h"
|
|
#include "utils/log.h"
|
|
#include "DirectoryCache.h"
|
|
#include "utils/URIUtils.h"
|
|
#include "settings/Settings.h"
|
|
#include "FileItem.h"
|
|
|
|
using namespace std;
|
|
using namespace XFILE;
|
|
|
|
CDirectoryCache g_directoryCache;
|
|
|
|
CDirectoryCache::CDir::CDir(DIR_CACHE_TYPE cacheType)
|
|
{
|
|
m_cacheType = cacheType;
|
|
m_lastAccess = 0;
|
|
m_Items = new CFileItemList;
|
|
m_Items->SetFastLookup(true);
|
|
}
|
|
|
|
CDirectoryCache::CDir::~CDir()
|
|
{
|
|
delete m_Items;
|
|
}
|
|
|
|
void CDirectoryCache::CDir::SetLastAccess(unsigned int &accessCounter)
|
|
{
|
|
m_lastAccess = accessCounter++;
|
|
}
|
|
|
|
CDirectoryCache::CDirectoryCache(void)
|
|
{
|
|
m_iThumbCacheRefCount = 0;
|
|
m_iMusicThumbCacheRefCount = 0;
|
|
m_accessCounter = 0;
|
|
#ifdef _DEBUG
|
|
m_cacheHits = 0;
|
|
m_cacheMisses = 0;
|
|
#endif
|
|
}
|
|
|
|
CDirectoryCache::~CDirectoryCache(void)
|
|
{
|
|
}
|
|
|
|
bool CDirectoryCache::GetDirectory(const CStdString& strPath, CFileItemList &items, bool retrieveAll)
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
CStdString storedPath = URIUtils::SubstitutePath(strPath);
|
|
URIUtils::RemoveSlashAtEnd(storedPath);
|
|
|
|
ciCache i = m_cache.find(storedPath);
|
|
if (i != m_cache.end())
|
|
{
|
|
CDir* dir = i->second;
|
|
if (dir->m_cacheType == XFILE::DIR_CACHE_ALWAYS ||
|
|
(dir->m_cacheType == XFILE::DIR_CACHE_ONCE && retrieveAll))
|
|
{
|
|
items.Copy(*dir->m_Items);
|
|
dir->SetLastAccess(m_accessCounter);
|
|
#ifdef _DEBUG
|
|
m_cacheHits+=items.Size();
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CDirectoryCache::SetDirectory(const CStdString& strPath, const CFileItemList &items, DIR_CACHE_TYPE cacheType)
|
|
{
|
|
if (cacheType == DIR_CACHE_NEVER)
|
|
return; // nothing to do
|
|
|
|
// caches the given directory using a copy of the items, rather than the items
|
|
// themselves. The reason we do this is because there is often some further
|
|
// processing on the items (stacking, transparent rars/zips for instance) that
|
|
// alters the URL of the items. If we shared the pointers, we'd have problems
|
|
// as the URLs in the cache would have changed, so things such as
|
|
// CDirectoryCache::FileExists() would fail for files that really do exist (just their
|
|
// URL's have been altered). This is called from CFile::Exists() which causes
|
|
// all sorts of hassles.
|
|
// IDEALLY, any further processing on the item would actually create a new item
|
|
// instead of altering it, but we can't really enforce that in an easy way, so
|
|
// this is the best solution for now.
|
|
CSingleLock lock (m_cs);
|
|
|
|
CStdString storedPath = URIUtils::SubstitutePath(strPath);
|
|
URIUtils::RemoveSlashAtEnd(storedPath);
|
|
|
|
ClearDirectory(storedPath);
|
|
|
|
CheckIfFull();
|
|
|
|
CDir* dir = new CDir(cacheType);
|
|
dir->m_Items->Copy(items);
|
|
dir->SetLastAccess(m_accessCounter);
|
|
m_cache.insert(pair<CStdString, CDir*>(storedPath, dir));
|
|
}
|
|
|
|
void CDirectoryCache::ClearFile(const CStdString& strFile)
|
|
{
|
|
CStdString strPath;
|
|
URIUtils::GetDirectory(strFile, strPath);
|
|
ClearDirectory(strPath);
|
|
}
|
|
|
|
void CDirectoryCache::ClearDirectory(const CStdString& strPath)
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
CStdString storedPath = URIUtils::SubstitutePath(strPath);
|
|
URIUtils::RemoveSlashAtEnd(storedPath);
|
|
|
|
iCache i = m_cache.find(storedPath);
|
|
if (i != m_cache.end())
|
|
Delete(i);
|
|
}
|
|
|
|
void CDirectoryCache::ClearSubPaths(const CStdString& strPath)
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
CStdString storedPath = URIUtils::SubstitutePath(strPath);
|
|
URIUtils::RemoveSlashAtEnd(storedPath);
|
|
|
|
iCache i = m_cache.begin();
|
|
while (i != m_cache.end())
|
|
{
|
|
CStdString path = i->first;
|
|
if (strncmp(path.c_str(), storedPath.c_str(), storedPath.GetLength()) == 0)
|
|
Delete(i++);
|
|
else
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void CDirectoryCache::AddFile(const CStdString& strFile)
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
CStdString strPath;
|
|
URIUtils::GetDirectory(strFile, strPath);
|
|
URIUtils::RemoveSlashAtEnd(strPath);
|
|
|
|
ciCache i = m_cache.find(strPath);
|
|
if (i != m_cache.end())
|
|
{
|
|
CDir *dir = i->second;
|
|
CFileItemPtr item(new CFileItem(strFile, false));
|
|
dir->m_Items->Add(item);
|
|
dir->SetLastAccess(m_accessCounter);
|
|
}
|
|
}
|
|
|
|
bool CDirectoryCache::FileExists(const CStdString& strFile, bool& bInCache)
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
bInCache = false;
|
|
|
|
CStdString strPath;
|
|
URIUtils::GetDirectory(strFile, strPath);
|
|
URIUtils::RemoveSlashAtEnd(strPath);
|
|
|
|
ciCache i = m_cache.find(strPath);
|
|
if (i != m_cache.end())
|
|
{
|
|
bInCache = true;
|
|
CDir *dir = i->second;
|
|
dir->SetLastAccess(m_accessCounter);
|
|
#ifdef _DEBUG
|
|
m_cacheHits++;
|
|
#endif
|
|
return dir->m_Items->Contains(strFile);
|
|
}
|
|
#ifdef _DEBUG
|
|
m_cacheMisses++;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void CDirectoryCache::Clear()
|
|
{
|
|
// this routine clears everything except things we always cache
|
|
CSingleLock lock (m_cs);
|
|
|
|
iCache i = m_cache.begin();
|
|
while (i != m_cache.end() )
|
|
{
|
|
if (!IsCacheDir(i->first))
|
|
Delete(i++);
|
|
else
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void CDirectoryCache::InitCache(set<CStdString>& dirs)
|
|
{
|
|
set<CStdString>::iterator it;
|
|
for (it = dirs.begin(); it != dirs.end(); ++it)
|
|
{
|
|
const CStdString& strDir = *it;
|
|
CFileItemList items;
|
|
CDirectory::GetDirectory(strDir, items, "", false);
|
|
items.Clear();
|
|
}
|
|
}
|
|
|
|
void CDirectoryCache::ClearCache(set<CStdString>& dirs)
|
|
{
|
|
iCache i = m_cache.begin();
|
|
while (i != m_cache.end())
|
|
{
|
|
if (dirs.find(i->first) != dirs.end())
|
|
Delete(i++);
|
|
else
|
|
i++;
|
|
}
|
|
}
|
|
|
|
bool CDirectoryCache::IsCacheDir(const CStdString &strPath) const
|
|
{
|
|
if (m_thumbDirs.find(strPath) == m_thumbDirs.end())
|
|
return false;
|
|
if (m_musicThumbDirs.find(strPath) == m_musicThumbDirs.end())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CDirectoryCache::InitThumbCache()
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
if (m_iThumbCacheRefCount > 0)
|
|
{
|
|
m_iThumbCacheRefCount++;
|
|
return ;
|
|
}
|
|
m_iThumbCacheRefCount++;
|
|
|
|
// Init video, pictures cache directories
|
|
if (m_thumbDirs.size() == 0)
|
|
{
|
|
// thumbnails directories
|
|
/* m_thumbDirs.insert(g_settings.GetThumbnailsFolder());
|
|
for (unsigned int hex=0; hex < 16; hex++)
|
|
{
|
|
CStdString strHex;
|
|
strHex.Format("\\%x",hex);
|
|
m_thumbDirs.insert(g_settings.GetThumbnailsFolder() + strHex);
|
|
}*/
|
|
}
|
|
|
|
InitCache(m_thumbDirs);
|
|
}
|
|
|
|
void CDirectoryCache::ClearThumbCache()
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
if (m_iThumbCacheRefCount > 1)
|
|
{
|
|
m_iThumbCacheRefCount--;
|
|
return ;
|
|
}
|
|
|
|
m_iThumbCacheRefCount--;
|
|
ClearCache(m_thumbDirs);
|
|
}
|
|
|
|
void CDirectoryCache::InitMusicThumbCache()
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
if (m_iMusicThumbCacheRefCount > 0)
|
|
{
|
|
m_iMusicThumbCacheRefCount++;
|
|
return ;
|
|
}
|
|
m_iMusicThumbCacheRefCount++;
|
|
|
|
// Init music cache directories
|
|
if (m_musicThumbDirs.size() == 0)
|
|
{
|
|
// music thumbnails directories
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
CStdString hex, folder;
|
|
hex.Format("%x", i);
|
|
URIUtils::AddFileToFolder(g_settings.GetMusicThumbFolder(), hex, folder);
|
|
m_musicThumbDirs.insert(folder);
|
|
}
|
|
}
|
|
|
|
InitCache(m_musicThumbDirs);
|
|
}
|
|
|
|
void CDirectoryCache::ClearMusicThumbCache()
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
|
|
if (m_iMusicThumbCacheRefCount > 1)
|
|
{
|
|
m_iMusicThumbCacheRefCount--;
|
|
return ;
|
|
}
|
|
|
|
m_iMusicThumbCacheRefCount--;
|
|
ClearCache(m_musicThumbDirs);
|
|
}
|
|
|
|
void CDirectoryCache::CheckIfFull()
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
static const unsigned int max_cached_dirs = 10;
|
|
|
|
// find the last accessed folder, and remove if the number of cached folders is too many
|
|
iCache lastAccessed = m_cache.end();
|
|
unsigned int numCached = 0;
|
|
for (iCache i = m_cache.begin(); i != m_cache.end(); i++)
|
|
{
|
|
// ensure dirs that are always cached aren't cleared
|
|
if (!IsCacheDir(i->first) && i->second->m_cacheType != DIR_CACHE_ALWAYS)
|
|
{
|
|
if (lastAccessed == m_cache.end() || i->second->GetLastAccess() < lastAccessed->second->GetLastAccess())
|
|
lastAccessed = i;
|
|
numCached++;
|
|
}
|
|
}
|
|
if (lastAccessed != m_cache.end() && numCached >= max_cached_dirs)
|
|
Delete(lastAccessed);
|
|
}
|
|
|
|
void CDirectoryCache::Delete(iCache it)
|
|
{
|
|
CDir* dir = it->second;
|
|
delete dir;
|
|
m_cache.erase(it);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
void CDirectoryCache::PrintStats() const
|
|
{
|
|
CSingleLock lock (m_cs);
|
|
CLog::Log(LOGDEBUG, "%s - total of %u cache hits, and %u cache misses", __FUNCTION__, m_cacheHits, m_cacheMisses);
|
|
// run through and find the oldest and the number of items cached
|
|
unsigned int oldest = UINT_MAX;
|
|
unsigned int numItems = 0;
|
|
unsigned int numDirs = 0;
|
|
for (ciCache i = m_cache.begin(); i != m_cache.end(); i++)
|
|
{
|
|
if (!IsCacheDir(i->first))
|
|
{
|
|
CDir *dir = i->second;
|
|
oldest = min(oldest, dir->GetLastAccess());
|
|
numItems += dir->m_Items->Size();
|
|
numDirs++;
|
|
}
|
|
}
|
|
CLog::Log(LOGDEBUG, "%s - %u folders cached, with %u items total. Oldest is %u, current is %u", __FUNCTION__, numDirs, numItems, oldest, m_accessCounter);
|
|
}
|
|
#endif
|