summaryrefslogtreecommitdiff
path: root/Runtime/Serialize/FileCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Serialize/FileCache.cpp')
-rw-r--r--Runtime/Serialize/FileCache.cpp461
1 files changed, 461 insertions, 0 deletions
diff --git a/Runtime/Serialize/FileCache.cpp b/Runtime/Serialize/FileCache.cpp
new file mode 100644
index 0000000..c297ed6
--- /dev/null
+++ b/Runtime/Serialize/FileCache.cpp
@@ -0,0 +1,461 @@
+#include "UnityPrefix.h"
+#include "FileCache.h"
+#include "Runtime/Utilities/algorithm_utility.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+#if SUPPORT_SERIALIZE_WRITE
+#include "Runtime/Utilities/FileUtilities.h"
+#endif
+
+using namespace std;
+
+#define USE_OPEN_FILE_CACHE UNITY_EDITOR
+
+#if USE_OPEN_FILE_CACHE
+
+struct OpenFilesCache
+{
+ enum { kOpenedFileCacheCount = 5 };
+ File* m_Cache[kOpenedFileCacheCount];
+ UInt32 m_TimeStamps[kOpenedFileCacheCount];
+ UInt32 m_TimeStamp;
+
+ OpenFilesCache ()
+ {
+ m_TimeStamp = 0;
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ m_Cache[i] = NULL;
+ m_TimeStamps[i] = 0;
+ }
+ }
+
+ void OpenCached (File* theFile, const std::string& thePath)
+ {
+ m_TimeStamp++;
+
+ // find cache, don't do anything if we are in the cache
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ if (theFile == m_Cache[i])
+ {
+ m_TimeStamps[i] = m_TimeStamp;
+ return;
+ }
+ }
+
+ // Find Least recently used cache entry
+ UInt32 lruTimeStamp = m_TimeStamps[0];
+ int lruIndex = 0;
+ for (int i=1;i<kOpenedFileCacheCount;i++)
+ {
+ if (m_TimeStamps[i] < lruTimeStamp)
+ {
+ lruTimeStamp = m_TimeStamps[i];
+ lruIndex = i;
+ }
+ }
+
+ // replace the least recently used cache entry
+ if (m_Cache[lruIndex] != NULL)
+ {
+ #if UNITY_OSX
+ m_Cache[lruIndex]->Lock (File::kNone, false);
+ #endif
+ m_Cache[lruIndex]->Close ();
+ }
+
+ m_Cache[lruIndex] = theFile;
+ m_TimeStamps[lruIndex] = m_TimeStamp;
+
+ if(!theFile->Open (thePath, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ ErrorString(Format("Could not open file %s for read", thePath.c_str()));
+
+ #if UNITY_OSX
+ theFile->Lock (File::kShared, false);
+ #endif
+ }
+
+ void ForceCloseAll ()
+ {
+ // Find and close cache
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ if (m_Cache[i] != NULL)
+ ForceClose (m_Cache[i]);
+ }
+ }
+
+ void ForceClose (File* cachable)
+ {
+ // Find and close cache
+ for (int i=0;i<kOpenedFileCacheCount;i++)
+ {
+ if (m_Cache[i] == cachable)
+ {
+ #if UNITY_OSX
+ m_Cache[i]->Lock (File::kNone, false);
+ #endif
+ m_Cache[i]->Close ();
+ m_Cache[i] = NULL;
+ m_TimeStamps[i] = 0;
+ return;
+ }
+ }
+ }
+};
+OpenFilesCache gOpenFilesCache;
+
+void ForceCloseAllOpenFileCaches ()
+{
+ gOpenFilesCache.ForceCloseAll ();
+}
+
+#endif
+
+
+CacheReaderBase::~CacheReaderBase ()
+{}
+
+CacheWriterBase::~CacheWriterBase ()
+{}
+
+FileCacherRead::FileCacherRead (const string& pathName, size_t cacheSize, size_t cacheCount)
+{
+ m_RootHeader = GET_CURRENT_ALLOC_ROOT_HEADER();
+ m_Path = PathToAbsolutePath(pathName);
+
+ // Initialize Cache
+ m_MaxCacheCount = cacheCount;
+ m_CacheSize = cacheSize;
+ m_TimeStamp = 0;
+
+ // Get File size
+ m_FileSize = ::GetFileLength(m_Path);
+
+ #if !USE_OPEN_FILE_CACHE
+ if(!m_File.Open(m_Path, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ ErrorString(Format("Could not open file %s for read", m_Path.c_str()));
+
+ // Make the file non-overwritable by the cache (to make caching behaviour imitate windows)
+ #if UNITY_OSX
+ m_File.Lock (File::kShared, false);
+ #endif
+
+ #endif
+ #if DEBUG_LINEAR_FILE_ACCESS
+ m_LastFileAccessPosition = 0;
+ #endif
+}
+
+FileCacherRead::~FileCacherRead ()
+{
+ for (CacheBlocks::iterator i = m_CacheBlocks.begin ();i != m_CacheBlocks.end ();++i)
+ {
+ AssertIf (i->second.lockCount);
+ UNITY_FREE(kMemFile,i->second.data);
+ }
+
+ m_CacheBlocks.clear ();
+
+ #if USE_OPEN_FILE_CACHE
+ gOpenFilesCache.ForceClose (&m_File);
+ #else
+ // Make the file overwritable by the cache.
+ #if UNITY_OSX
+ m_File.Lock (File::kNone, false);
+ #endif
+
+ m_File.Close();
+
+ #endif
+}
+
+void FileCacherRead::ReadCacheBlock (CacheBlock& cacheBlock)
+{
+ int block = cacheBlock.block;
+ // Watch out for not reading over eof
+ int readSize = min<int> (m_CacheSize, m_FileSize - block * m_CacheSize);
+
+ // load the data from disk
+ // only if the physical file contains any data for this block
+ if (readSize > 0)
+ {
+ #if USE_OPEN_FILE_CACHE
+ gOpenFilesCache.OpenCached (&m_File, m_Path);
+ #endif
+
+ #if DEBUG_LINEAR_FILE_ACCESS
+ size_t position = block * m_CacheSize;
+ #endif
+
+ m_File.Read (block * m_CacheSize, cacheBlock.data, readSize);
+
+ #if DEBUG_LINEAR_FILE_ACCESS
+ // printf_console("ACCESS: [%08x] %s %i bytes @ %i to %08x\n",this, __FUNCTION__, readSize, block * m_CacheSize, cacheBlock.data);
+ std::string fileName = GetLastPathNameComponent(m_Path);
+ if (position < m_LastFileAccessPosition && fileName != "mainData" && fileName != "unity default resources")
+ {
+ ErrorString(Format("File access: %s is not linear Reading: %d Seek position: %d", fileName.c_str(), position, m_LastFileAccessPosition));
+ }
+
+ m_LastFileAccessPosition = position + readSize;
+ #endif
+ }
+}
+
+void FileCacherRead::DirectRead (void* data, size_t position, size_t size)
+{
+ // load the data from disk
+ // only if the physical file contains any data for this block
+ FatalErrorIf (m_FileSize - position < size);
+
+ #if USE_OPEN_FILE_CACHE
+ gOpenFilesCache.OpenCached (&m_File, m_Path);
+ #endif
+
+ m_File.Read (position, data, size);
+
+ #if DEBUG_LINEAR_FILE_ACCESS
+ // printf_console("ACCCESS: [%08x] %s %i bytes @ %i to %08x\n",this, __FUNCTION__, size, position, data);
+ std::string fileName = GetLastPathNameComponent(m_Path);
+ if (position < m_LastFileAccessPosition && fileName != "mainData" && fileName != "unity default resources")
+ {
+ ErrorString(Format("File access: %s is not linear Reading: %d Seek position: %d", fileName.c_str(), position, m_LastFileAccessPosition));
+ }
+
+ m_LastFileAccessPosition = position + size;
+ #endif
+}
+
+
+bool FileCacherRead::FreeSingleCache ()
+{
+ unsigned lowestTimeStamp = -1;
+ CacheBlocks::iterator lowestCacheBlock = m_CacheBlocks.end ();
+
+ CacheBlocks::iterator i;
+ for (i=m_CacheBlocks.begin ();i != m_CacheBlocks.end ();i++)
+ {
+ if (i->second.lockCount == 0)
+ {
+ if (i->second.timeStamp < lowestTimeStamp)
+ {
+ lowestTimeStamp = i->second.timeStamp;
+ lowestCacheBlock = i;
+ }
+ }
+ }
+
+ if (lowestCacheBlock != m_CacheBlocks.end ())
+ {
+ CacheBlock& block = lowestCacheBlock->second;
+ UNITY_FREE(kMemFile,block.data);
+ m_CacheBlocks.erase (lowestCacheBlock);
+ return true;
+ }
+ else
+ return false;
+}
+
+FileCacherRead::CacheBlock& FileCacherRead::AllocateCacheBlock (int block)
+{
+ AssertIf (m_CacheBlocks.count (block));
+ CacheBlock cacheBlock;
+ cacheBlock.block = block;
+ cacheBlock.lockCount = 0;
+ cacheBlock.timeStamp = 0;
+ cacheBlock.data = (UInt8*)UNITY_MALLOC(MemLabelId(kMemFileId, m_RootHeader),m_CacheSize);
+ m_CacheBlocks[block] = cacheBlock;
+ return m_CacheBlocks[block];
+}
+
+void FileCacherRead::LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+{
+ CacheBlock* newCacheBlock;
+ CacheBlocks::iterator i = m_CacheBlocks.find (block);
+ if (i != m_CacheBlocks.end ())
+ newCacheBlock = &i->second;
+ else
+ {
+ // Make room for a new cache block
+ if (m_CacheBlocks.size () >= m_MaxCacheCount)
+ FreeSingleCache ();
+
+ newCacheBlock = &AllocateCacheBlock (block);
+ ReadCacheBlock (*newCacheBlock);
+ }
+ AssertIf (newCacheBlock->block != block);
+
+ newCacheBlock->timeStamp = ++m_TimeStamp;
+ newCacheBlock->lockCount++;
+
+ *startPos = newCacheBlock->data;
+ *endPos = newCacheBlock->data + min<int> (m_FileSize - block * GetCacheSize (), GetCacheSize ());
+}
+
+void FileCacherRead::UnlockCacheBlock (int block)
+{
+ AssertIf (!m_CacheBlocks.count (block));
+ CacheBlock& cacheBlock = m_CacheBlocks.find (block)->second;
+ cacheBlock.lockCount--;
+ if (cacheBlock.lockCount == 0)
+ {
+ // LockCacheBlock sometimes locks more caches than m_MaxCacheCount
+ // Thus we should Free them as soon as they become unlocked
+ if (m_CacheBlocks.size () > m_MaxCacheCount)
+ FreeSingleCache ();
+ }
+}
+
+std::string FileCacherRead::GetPathName() const
+{
+ return m_Path;
+}
+
+#if SUPPORT_SERIALIZE_WRITE
+FileCacherWrite::FileCacherWrite ()
+{
+ m_Success = true;
+ m_Block = -1;
+ m_Locked = false;
+ m_CacheSize = 0;
+ m_DataCache = NULL;
+}
+
+void FileCacherWrite::InitWriteFile (const std::string& pathName, size_t cacheSize)
+{
+ m_Path = PathToAbsolutePath(pathName);
+ m_Success = true;
+
+ m_File.Open(m_Path, File::kWritePermission);
+ // file we're writing to always is a temporary, non-indexable file
+ SetFileFlags(m_Path, kAllFileFlags, kFileFlagDontIndex|kFileFlagTemporary);
+
+ m_Block = -1;
+ m_Locked = false;
+ m_CacheSize = cacheSize;
+ m_DataCache = (UInt8*)UNITY_MALLOC (kMemFile, m_CacheSize);
+}
+
+bool FileCacherWrite::CompleteWriting (size_t size)
+{
+ Assert(m_Block != -1);
+
+ size_t remainingData = size - (m_Block * m_CacheSize);
+ Assert(remainingData <= m_CacheSize);
+
+ m_Success &= m_File.Write(m_Block * m_CacheSize, m_DataCache, remainingData);
+
+ return m_Success;
+}
+
+bool FileCacherWrite::WriteHeaderAndCloseFile (void* data, size_t position, size_t size)
+{
+ Assert(position == 0);
+ if (size != 0)
+ m_Success &= m_File.Write(position, data, size);
+ m_Success &= m_File.Close();
+
+ return m_Success;
+}
+
+
+FileCacherWrite::~FileCacherWrite()
+{
+ if (m_DataCache)
+ {
+ UNITY_FREE (kMemFile, m_DataCache);
+ m_DataCache = NULL;
+ }
+
+ m_File.Close();
+}
+
+void FileCacherWrite::LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+{
+ AssertIf (block == -1);
+ Assert (block == m_Block || m_Block+1 == block );
+ Assert (!m_Locked);
+
+ if (m_Block != block)
+ {
+ AssertIf (m_Locked != 0);
+
+ if (m_Block != -1)
+ m_Success &= m_File.Write(m_DataCache, m_CacheSize);
+
+ m_Block = block;
+ }
+
+ *startPos = m_DataCache;
+ *endPos = m_DataCache + m_CacheSize;
+ m_Locked++;
+}
+
+void FileCacherWrite::UnlockCacheBlock (int block)
+{
+ Assert (block == m_Block);
+ Assert (m_Locked);
+
+ m_Locked = false;
+}
+
+
+std::string FileCacherWrite::GetPathName() const
+{
+ return m_Path;
+}
+
+#endif // SUPPORT_SERIALIZE_WRITE
+
+
+MemoryCacherReadBlocks::MemoryCacherReadBlocks (UInt8** blocks, int size, size_t cacheBlockSize)
+: m_CacheBlockSize(cacheBlockSize)
+, m_Memory(blocks)
+, m_FileSize(size)
+{
+}
+
+
+MemoryCacherReadBlocks::~MemoryCacherReadBlocks ()
+{
+}
+
+void MemoryCacherReadBlocks::LockCacheBlock (int block, UInt8** startPos, UInt8** endPos)
+{
+ /// VERIFY OUT OF BOUNDS!!! ???
+ AssertIf(block > m_FileSize / m_CacheBlockSize);
+ *startPos = m_Memory[block];
+ *endPos = *startPos + min<int> (GetFileLength () - block * m_CacheBlockSize, m_CacheBlockSize);
+}
+
+void MemoryCacherReadBlocks::DirectRead (void* data, size_t position, size_t size)
+{
+ ReadFileCache(*this, data, position, size);
+}
+
+void ReadFileCache (CacheReaderBase& cacher, void* data, size_t position, size_t size)
+{
+ UInt8 *cacheStart, *cacheEnd;
+ UInt8 *from, *fromClamped, *to, *toClamped;
+
+ int block = position / cacher.GetCacheSize ();
+ int lastBlock = (position + size - 1) / cacher.GetCacheSize ();
+
+ while (block <= lastBlock)
+ {
+ cacher.LockCacheBlock (block, &cacheStart, &cacheEnd);
+
+ // copy data from oldblock and unlock
+ from = cacheStart + (position - block * cacher.GetCacheSize ());
+ fromClamped = clamp (from, cacheStart, cacheEnd);
+ to = cacheStart + (position + size - block * cacher.GetCacheSize ());
+ toClamped = clamp<UInt8*> (to, cacheStart, cacheEnd);
+ memcpy ((UInt8*)data + (fromClamped - from), fromClamped, toClamped - fromClamped);
+
+ cacher.UnlockCacheBlock (block);
+ block++;
+ }
+}