diff options
author | Yotam Barnoy | 2010-05-26 14:43:25 +0000 |
---|---|---|
committer | Yotam Barnoy | 2010-05-26 14:43:25 +0000 |
commit | 8b54efd8a62a68a0544dfc5438c4efe42c0f79f1 (patch) | |
tree | 8e4f94dc14dd172d812857cd6b5592fdb7b90a83 | |
parent | af8a82aa13f2a316f6a9b4fd857230cbf87c6b1c (diff) | |
download | scummvm-rg350-8b54efd8a62a68a0544dfc5438c4efe42c0f79f1.tar.gz scummvm-rg350-8b54efd8a62a68a0544dfc5438c4efe42c0f79f1.tar.bz2 scummvm-rg350-8b54efd8a62a68a0544dfc5438c4efe42c0f79f1.zip |
PSP: implemented basic file cache. Turns out the PSP reads 1 byte as fast as it reads 1 KB.
svn-id: r49243
-rw-r--r-- | backends/fs/psp/psp-stream.cpp | 249 | ||||
-rw-r--r-- | backends/fs/psp/psp-stream.h | 31 |
2 files changed, 196 insertions, 84 deletions
diff --git a/backends/fs/psp/psp-stream.cpp b/backends/fs/psp/psp-stream.cpp index 8cb7dfea17..d3d6311836 100644 --- a/backends/fs/psp/psp-stream.cpp +++ b/backends/fs/psp/psp-stream.cpp @@ -32,28 +32,26 @@ #include <errno.h> +#define MIN2(a,b) ((a < b) ? a : b) +#define MIN3(a,b,c) ( (a < b) ? (a < c ? a : c) : (b < c ? b : c) ) + //#define __PSP_PRINT_TO_FILE__ //#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */ //#define __PSP_DEBUG_PRINT__ /* For debug printouts */ #include "backends/platform/psp/trace.h" PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode) - : StdioStream((void *)1), _path(path), _writeMode(writeMode) { + : StdioStream((void *)1), _path(path), _writeMode(writeMode), + _ferror(false), _pos(0), + _physicalPos(0), _fileSize(0), _inCache(false), + _cacheStartOffset(-1), _cache(0), + _errorSuspend(0), _errorSource(0), + _errorPos(0), _errorHandle(0), _suspendCount(0) { DEBUG_ENTER_FUNC(); - assert(!path.empty()); + // assert(!path.empty()); // do we need this? _handle = (void *)0; // Need to do this since base class asserts not 0. - _ferror = false; - _feof = false; - _pos = 0; - - /* for error checking */ - _errorSuspend = 0; - _errorSource = 0; - _errorPos = 0; - _errorHandle = 0; - _suspendCount = 0; } PSPIoStream::~PSPIoStream() { @@ -63,9 +61,12 @@ PSPIoStream::~PSPIoStream() { PSP_DEBUG_PRINT_FUNC("Suspended\n"); PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended - // Must do this before fclose() or resume() will reopen. + // Must do this before fclose() or resume() will reopen. - fclose((FILE *)_handle); // We don't need a critical section(?). Worst case, the handle gets closed on its own + fclose((FILE *)_handle); // We don't need a critical section. Worst case, the handle gets closed on its own + + if (_cache) + free(_cache); PowerMan.endCriticalSection(); } @@ -82,6 +83,16 @@ void *PSPIoStream::open() { _handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open + if (_handle) { + // Get the file size + fseek((FILE *)_handle, 0, SEEK_END); // go to the end + _fileSize = ftell((FILE *)_handle); + fseek((FILE *)_handle, 0, SEEK_SET); // back to the beginning + + // Allocate the cache + _cache = (char *)memalign(64, CACHE_SIZE); + } + PowerMan.registerSuspend(this); // Register with the powermanager to be suspended PowerMan.endCriticalSection(); @@ -91,100 +102,177 @@ void *PSPIoStream::open() { bool PSPIoStream::err() const { DEBUG_ENTER_FUNC(); - if (_ferror) - PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], _errorPos[%d], _errorHandle[%p], suspendCount[%d]\n", - _ferror, _errorSource, _errorSuspend, _pos, _errorPos, _errorHandle, _suspendCount); + if (_ferror) // We dump since no printing to screen with suspend + PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], \ + _errorPos[%d], _errorHandle[%p], suspendCount[%d]\n", + _ferror, _errorSource, _errorSuspend, _pos, + _errorPos, _errorHandle, _suspendCount); return _ferror; } void PSPIoStream::clearErr() { - _ferror = false; // Remove regular error bit + _ferror = false; } bool PSPIoStream::eos() const { - return _feof; + return (_pos >= _fileSize); } int32 PSPIoStream::pos() const { return _pos; } - int32 PSPIoStream::size() const { - DEBUG_ENTER_FUNC(); - if (PowerMan.beginCriticalSection() == PowerManager::Blocked) - PSP_DEBUG_PRINT_FUNC("Suspended\n"); - - fseek((FILE *)_handle, 0, SEEK_END); - int32 length = ftell((FILE *)_handle); - fseek((FILE *)_handle, _pos, SEEK_SET); - - if (_pos < 0 || length < 0) { // Check for errors - _errorSource = 2; - PSP_ERROR("pos[%d] or length[%d] < 0!\n", _pos, length); - _ferror = true; - length = -1; // If our oldPos is bad, we want length to be bad too to signal - clearerr((FILE *)_handle); - } - - PowerMan.endCriticalSection(); - - return length; + return _fileSize; } bool PSPIoStream::seek(int32 offs, int whence) { DEBUG_ENTER_FUNC(); - - // Check if we can access the file - if (PowerMan.beginCriticalSection() == PowerManager::Blocked) - PSP_DEBUG_PRINT_FUNC("Suspended\n"); - - int ret = fseek((FILE *)_handle, offs, whence); - - if (ret != 0) { + PSP_DEBUG_PRINT_FUNC("offset[%d], whence[%d], _pos[%d], _physPos[%d]\n", offs, whence, _pos, _physicalPos); + bool success = true; + + int32 posToSearchFor = 0; + switch (whence) { + case SEEK_CUR: + posToSearchFor = _pos; + break; + case SEEK_END: + posToSearchFor = _fileSize; // unsure. Does it take us here or to EOS - 1? + break; + } + posToSearchFor += offs; + + // Check for bad values + if (posToSearchFor < 0 || posToSearchFor > _fileSize) { _ferror = true; - PSP_ERROR("fseek returned with [%d], non-zero\n", ret); - clearerr((FILE *)_handle); - _feof = feof((FILE *)_handle); - _errorSource = 3; - } else { // everything ok - _feof = false; // Reset eof flag since we know it was ok + return false; } - - _pos = ftell((FILE *)_handle); // update pos - - PowerMan.endCriticalSection(); - - return (ret == 0); + + // See if we can find it in cache + if (isOffsetInCache(posToSearchFor)) { + PSP_DEBUG_PRINT("seek offset[%d] found in cache. Cache starts[%d]\n", posToSearchFor, _cacheStartOffset); + _inCache = true; + } else { // not in cache + _inCache = false; + } + _pos = posToSearchFor; + return success; } +// for debugging +/* +void printBuffer(byte *ptr, uint32 len) { + for (int i = 0; i < len; i++) { + PSP_INFO_PRINT("%x ", *ptr); + ptr++; + } + PSP_INFO_PRINT("\n"); +}*/ + uint32 PSPIoStream::read(void *ptr, uint32 len) { DEBUG_ENTER_FUNC(); - // Check if we can access the file + PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d], ptr[%p]\n", _path.c_str(), len, ptr); + + byte *destPtr = (byte *)ptr; + uint32 lenFromFile = len; // how much we read from the actual file + uint32 lenFromCache = 0; // how much we read from cache + uint32 lenRemainingInFile = _fileSize - _pos; + + if (lenFromFile > lenRemainingInFile) + lenFromFile = lenRemainingInFile; + + // Are we in cache? + if (_inCache && isCacheValid()) { + uint32 offsetInCache = _pos - _cacheStartOffset; + // We can read at most what's in the cache or the remaining size of the file + lenFromCache = MIN2(lenFromFile, CACHE_SIZE - offsetInCache); // unsure + + PSP_DEBUG_PRINT("reading %d bytes from cache to %p. pos[%d] physPos[%d] cacheStart[%d]\n", lenFromCache, destPtr, _pos, _physicalPos, _cacheStartOffset); + + memcpy(destPtr, &_cache[offsetInCache], lenFromCache); + _pos += lenFromCache; + + if (lenFromCache < lenFromFile) { // there's more to copy from the file + lenFromFile -= lenFromCache; + lenRemainingInFile -= lenFromCache; // since we moved pos + destPtr += lenFromCache; + } else { // we're done + // debug + //if (len < 10) printBuffer((byte *)ptr, len); + return lenFromCache; // how much we actually read + } + } + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) PSP_DEBUG_PRINT_FUNC("Suspended\n"); - - PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len); - - size_t ret = fread((byte *)ptr, 1, len, (FILE *)_handle); - - _pos += ret; // Update pos - - if (ret != len) { // Check for eof - _feof = feof((FILE *)_handle); - if (!_feof) { // It wasn't an eof. Must be an error + + + synchronizePhysicalPos(); // we need to update our physical position + + if (lenFromFile <= MIN_READ_SIZE) { // We load the cache in case the read is small enough + // This optimization is based on the principle that reading 1 byte is as expensive as 1000 bytes + uint32 lenToCopyToCache = MIN2((uint32)MIN_READ_SIZE, lenRemainingInFile); // at most remaining file size + + PSP_DEBUG_PRINT("filling cache with %d bytes from physicalPos[%d]. cacheStart[%d], pos[%d], fileSize[%d]\n", lenToCopyToCache, _physicalPos, _cacheStartOffset, _pos, _fileSize); + + size_t ret = fread(_cache, 1, lenToCopyToCache, (FILE *)_handle); + if (ret != lenToCopyToCache) { + PSP_ERROR("in filling cache, failed to get %d bytes. Only got %d\n", lenToCopyToCache, ret); + _ferror = true; + clearerr((FILE *)_handle); + } + _cacheStartOffset = _physicalPos; + _inCache = true; + + _physicalPos += ret; + + PSP_DEBUG_PRINT("copying %d bytes from cache to %p\n", lenFromFile, destPtr); + + // Copy to the destination buffer from cache + memcpy(destPtr, _cache, lenFromFile); + _pos += lenFromFile; + + } else { // Too big for cache. No caching + PSP_DEBUG_PRINT("reading %d bytes from file to %p. Pos[%d], physPos[%d]\n", lenFromFile, destPtr, _pos, _physicalPos); + size_t ret = fread(destPtr, 1, lenFromFile, (FILE *)_handle); + + _physicalPos += ret; // Update pos + _pos = _physicalPos; + + if (ret != lenFromFile) { // error + PSP_ERROR("fread returned [%d] instead of len[%d]\n", ret, lenFromFile); _ferror = true; clearerr((FILE *)_handle); - _pos = ftell((FILE *)_handle); // Update our position - _errorSource = 4; - PSP_ERROR("fread returned ret[%d] instead of len[%d]\n", ret, len); + _errorSource = 4; } + _inCache = false; } PowerMan.endCriticalSection(); - return ret; + // debug + //if (len < 10) printBuffer((byte *)ptr, len); + return lenFromCache + lenFromFile; // total of what was copied +} + +// TODO: Test if seeking backwards/forwards has any effect on performance +inline bool PSPIoStream::synchronizePhysicalPos() { + if (_pos != _physicalPos) { + if (fseek((FILE *)_handle, _pos - _physicalPos, SEEK_CUR) != 0) + return false; + _physicalPos = _pos; + } + + return true; +} + +inline bool PSPIoStream::isOffsetInCache(uint32 offset) { + if (_cacheStartOffset != -1 && + offset >= (uint32)_cacheStartOffset && + offset < (uint32)(_cacheStartOffset + CACHE_SIZE)) + return true; + return false; } uint32 PSPIoStream::write(const void *ptr, uint32 len) { @@ -195,9 +283,17 @@ uint32 PSPIoStream::write(const void *ptr, uint32 len) { PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len); + synchronizePhysicalPos(); + size_t ret = fwrite(ptr, 1, len, (FILE *)_handle); - _pos += ret; + // If we're making the file bigger, adjust the size + if (_physicalPos + (int)ret > _fileSize) + _fileSize = _physicalPos + ret; + _physicalPos += ret; + _pos = _physicalPos; + _inCache = false; + _cacheStartOffset = -1; // invalidate cache if (ret != len) { // Set error _ferror = true; @@ -286,6 +382,9 @@ int PSPIoStream::resume() { // Resume our previous position if (_handle > 0 && _pos > 0) { ret = fseek((FILE *)_handle, _pos, SEEK_SET); + + _physicalPos = _pos; + _inCache = false; if (ret != 0) { // Check for problem _errorSuspend = ResumeError; diff --git a/backends/fs/psp/psp-stream.h b/backends/fs/psp/psp-stream.h index 673630b685..10d5016852 100644 --- a/backends/fs/psp/psp-stream.h +++ b/backends/fs/psp/psp-stream.h @@ -35,25 +35,38 @@ */ class PSPIoStream : public StdioStream, public Suspendable { protected: - Common::String _path; /* Need to maintain for reopening after suspend */ - bool _writeMode; /* "" */ - int _pos; /* "" */ - mutable int _ferror; /* Save file ferror */ - mutable bool _feof; /* and eof */ - + Common::String _path; + int _fileSize; + bool _writeMode; // for resuming in the right mode + int _physicalPos; // position in the real file + int _pos; // position. Sometimes virtual + bool _inCache; // whether we're in cache (virtual) mode + enum { SuspendError = 2, ResumeError = 3 }; - int _errorSuspend; + enum { + CACHE_SIZE = 1024, + MIN_READ_SIZE = 1024 // reading less than 1024 takes exactly the same time as 1024 + }; + + // For caching + char *_cache; + int _cacheStartOffset; // starting offset of the cache. -1 when cache is invalid + + mutable int _ferror; // file error state + int _errorSuspend; // for debugging mutable int _errorSource; - - // Error checking int _errorPos; void * _errorHandle; int _suspendCount; + bool synchronizePhysicalPos(); // synchronize the physical and virtual positions + bool isOffsetInCache(uint32 pos); // check if an offset is found in cache + bool isCacheValid() { return _cacheStartOffset != -1; } + public: /** |