| #include <sstream> |
| |
| #include "tsystem.h" |
| #include "tcachedlevel.h" |
| |
| #include "tcodec.h" |
| |
| #include "texception.h" |
| |
| |
| |
| #include "tconvert.h" |
| |
| #if defined(LINUX) || defined(FREEBSD) |
| #include "texception.h" |
| |
| #include <unistd.h> |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #endif |
| |
| #ifdef MACOSX |
| #include "sys/mman.h" |
| #include "sys/errno.h" |
| #endif |
| |
| |
| #define DWORDLONG_LO_DWORD(dwl64) ((DWORD)(dwl64)) |
| #define DWORDLONG_HI_DWORD(dwl64) ((DWORD)(dwl64 >> 32)) |
| |
| |
| |
| |
| |
| |
| |
| class ImpPD { |
| public: |
| ImpPD(const TFilePath &fn) |
| : m_fname(fn) |
| , m_chunkSize(0) |
| , m_currentFileSize(0) |
| , m_fileMapAddress(0) |
| , m_mapOffset(0) { |
| TFileStatus fileStatus(fn); |
| if (fileStatus.doesExist()) |
| m_currentFileSize = fileStatus.getSize(); |
| else |
| m_currentFileSize = ImpPD::m_defaultFileSize; |
| }; |
| virtual ~ImpPD() {} |
| virtual void openFile(const TFilePath &, TINT64 fileSize) = 0; |
| virtual void setCurrentView(int pos, int &newLowPos, int &newHiPos) = 0; |
| TFilePath m_fname; |
| |
| TINT64 m_chunkSize; |
| TINT64 m_currentFileSize; |
| void *m_fileMapAddress; |
| TINT64 m_mapOffset; |
| |
| |
| static TINT64 m_defaultFileSize; |
| TUINT32 m_viewSize; |
| TINT64 m_reallocSize; |
| }; |
| |
| TINT64 ImpPD::m_defaultFileSize(100 * 1024 * 1024); |
| |
| |
| |
| class TDiskCachePersist::Imp { |
| public: |
| Imp(const TFilePath &fp); |
| ~Imp(); |
| |
| bool put(int frame, UCHAR *data, TUINT32 dataSize); |
| UCHAR *get(int pos, TUINT32 *size); |
| void openFile(const TFilePath &fp, TINT64 fileSize); |
| void setCurrentView(int frame) { |
| if (!m_force && ((m_lowFrame <= frame) && (frame < m_hiFrame))) |
| return; |
| m_force = false; |
| m_impPD->setCurrentView(frame, m_lowFrame, m_hiFrame); |
| } |
| |
| ImpPD *m_impPD; |
| |
| int m_lowFrame, m_hiFrame; |
| TThread::Mutex m_mutex; |
| |
| bool m_force; |
| }; |
| |
| #ifdef WIN32 |
| class ImpPDW : public ImpPD { |
| private: |
| string getLastErrorMessage() { |
| LPVOID lpMsgBuf; |
| |
| DWORD err = GetLastError(); |
| FormatMessage( |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
| FORMAT_MESSAGE_IGNORE_INSERTS, |
| NULL, err, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPTSTR)&lpMsgBuf, 0, NULL); |
| |
| string msg((LPCTSTR)lpMsgBuf); |
| |
| |
| LocalFree(lpMsgBuf); |
| return msg; |
| } |
| |
| public: |
| ImpPDW(const TFilePath &fp); |
| ~ImpPDW(); |
| void openFile(const TFilePath &fname, TINT64 fileSize); |
| void setCurrentView(int pos, int &newLowPos, int &newHiPos); |
| |
| private: |
| HANDLE m_hFile; |
| HANDLE m_hMap; |
| |
| SYSTEM_INFO m_systemInfo; |
| }; |
| |
| #else |
| |
| class ImpPDX : public ImpPD { |
| private: |
| class TCachedLevelException : public TException { |
| static string msgFromErrorCode(int errorCode) { |
| switch (errorCode) { |
| case EBADF: |
| return " fd is not a valid file descriptor (and MAP_ANONYMOUS was " |
| "not set)."; |
| break; |
| |
| |
| |
| |
| |
| |
| |
| case EINVAL: |
| return "We don't like start or length or offset. (E.g., they are " |
| "too large, or not aligned on a PAGESIZE boundary.)"; |
| break; |
| |
| case ETXTBSY: |
| return "MAP_DENYWRITE was set but the object specified by fd is open " |
| "for writing."; |
| break; |
| |
| case EAGAIN: |
| return "The file has been locked, or too much memory has been " |
| "locked."; |
| break; |
| |
| case ENOMEM: |
| return "No memory is available."; |
| break; |
| |
| default: |
| char *sysErr = strerror(errorCode); |
| ostringstream os; |
| os << errorCode << '\0'; |
| return string(sysErr) + "(" + os.str() + ")"; |
| break; |
| } |
| return ""; |
| } |
| |
| public: |
| TCachedLevelException(int errorCode) |
| : TException(msgFromErrorCode(errorCode)) {} |
| ~TCachedLevelException() {} |
| }; |
| |
| public: |
| ImpPDX(const TFilePath &fp); |
| ~ImpPDX(); |
| void openFile(const TFilePath &fname, TINT64 fileSize); |
| void setCurrentView(int pos, int &newLowPos, int &newHiPos); |
| |
| private: |
| int m_fd; |
| size_t m_pageSize; |
| }; |
| |
| #endif |
| |
| |
| |
| |
| TDiskCachePersist::TDiskCachePersist(TRasterCodec *codec, const TFilePath &fp) |
| : TCachePersist(codec), m_imp(new Imp(fp)) {} |
| |
| |
| |
| TDiskCachePersist::~TDiskCachePersist() { delete m_imp; } |
| |
| |
| |
| void TDiskCachePersist::setFrameSize(int lx, int ly, int bpp) { |
| m_imp->m_impPD->m_chunkSize = lx * ly * (bpp >> 3) + m_codec->getHeaderSize(); |
| m_imp->m_force = true; |
| } |
| |
| |
| |
| TRasterP TDiskCachePersist::doGetRaster(int frame) { |
| TRasterP rasP; |
| TUINT32 size; |
| UCHAR *src = m_imp->get(frame, &size); |
| m_codec->decompress(src, size, rasP); |
| delete[] src; |
| return rasP; |
| } |
| |
| |
| |
| bool TDiskCachePersist::doGetRaster(int frame, TRaster32P &ras) const { |
| assert(false); |
| return false; |
| } |
| |
| |
| |
| bool TDiskCachePersist::doPutRaster(int frame, const TRasterP &ras) { |
| UCHAR *outData = 0; |
| TINT32 outDataSize = 0; |
| m_codec->compress(ras, 1, &outData, outDataSize); |
| bool cached = m_imp->put(frame, outData, outDataSize); |
| delete[] outData; |
| return cached; |
| } |
| |
| |
| |
| UCHAR *TDiskCachePersist::getRawData(int frame, TINT32 &size, int &lx, |
| int &ly) { |
| TUINT32 inDataSize; |
| UCHAR *src = m_imp->get(frame, &inDataSize); |
| return m_codec->removeHeader(src, inDataSize, size, lx, ly); |
| } |
| |
| |
| |
| TDiskCachePersist::Imp::Imp(const TFilePath &fp) : m_impPD(0) { |
| #ifdef WIN32 |
| m_impPD = new ImpPDW(fp); |
| #else |
| m_impPD = new ImpPDX(fp); |
| #endif |
| m_impPD->m_currentFileSize = TFileStatus(fp).doesExist() |
| ? TFileStatus(fp).getSize() |
| : 0; |
| } |
| |
| |
| |
| TDiskCachePersist::Imp::~Imp() { delete m_impPD; } |
| |
| |
| |
| #ifdef WIN32 |
| ImpPDW::ImpPDW(const TFilePath &fp) : ImpPD(fp), m_hFile(0), m_hMap(0) { |
| GetSystemInfo(&m_systemInfo); |
| |
| m_viewSize = 100 * 1024 * 1024; |
| m_reallocSize = 250 * 1024 * 1024; |
| |
| TINT64 allocUnitCount = m_reallocSize / m_systemInfo.dwAllocationGranularity; |
| |
| |
| if ((m_reallocSize % m_systemInfo.dwAllocationGranularity) != 0) |
| ++allocUnitCount; |
| |
| m_reallocSize = allocUnitCount * m_systemInfo.dwAllocationGranularity; |
| |
| TINT64 fileSize = m_defaultFileSize; |
| |
| TFileStatus fileStatus(fp); |
| if (fileStatus.doesExist()) fileSize = fileStatus.getSize(); |
| |
| try { |
| openFile(fp, fileSize); |
| } catch (TException &e) { |
| m_currentFileSize = 0; |
| throw e; |
| } |
| |
| m_currentFileSize = fileSize; |
| } |
| |
| |
| |
| ImpPDW::~ImpPDW() { |
| if (m_fileMapAddress) UnmapViewOfFile(m_fileMapAddress); |
| CloseHandle(m_hMap); |
| CloseHandle(m_hFile); |
| } |
| |
| |
| |
| void ImpPDW::openFile(const TFilePath &fname, TINT64 fileSize) { |
| DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; |
| DWORD dwShareMode = 0; |
| |
| |
| |
| LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL; |
| |
| DWORD dwCreationDisposition = OPEN_ALWAYS; |
| DWORD dwFlagsAndAttributes = |
| FILE_FLAG_SEQUENTIAL_SCAN; |
| |
| HANDLE hTemplateFile = NULL; |
| |
| m_hFile = CreateFileW(fname.getWideString().c_str(), |
| dwDesiredAccess, |
| dwShareMode, |
| NULL, |
| dwCreationDisposition, |
| dwFlagsAndAttributes, |
| hTemplateFile |
| ); |
| |
| if (m_hFile == INVALID_HANDLE_VALUE) { |
| string errMsg = getLastErrorMessage(); |
| throw TException(wstring(L"Unable to open cache file: ") + |
| fname.getWideString() + L"\n" + toWideString(errMsg)); |
| } |
| |
| DWORD flProtect = PAGE_READWRITE; |
| DWORD dwMaximumSizeHigh = DWORDLONG_HI_DWORD(fileSize); |
| DWORD dwMaximumSizeLow = DWORDLONG_LO_DWORD(fileSize); |
| LPCTSTR lpName = NULL; |
| |
| m_hMap = CreateFileMapping(m_hFile, |
| NULL, |
| flProtect, |
| dwMaximumSizeHigh, |
| dwMaximumSizeLow, |
| lpName |
| ); |
| |
| if (m_hMap == NULL) { |
| string errMsg = getLastErrorMessage(); |
| CloseHandle(m_hFile); |
| m_hFile = 0; |
| throw TException("Unable to create file mapping. " + errMsg); |
| } |
| } |
| |
| |
| |
| void ImpPDW::setCurrentView(int frame, int &newLowFrame, int &newHiFrame) { |
| if (m_fileMapAddress) UnmapViewOfFile(m_fileMapAddress); |
| |
| newLowFrame = frame; |
| |
| newHiFrame = newLowFrame + TINT32(m_viewSize / m_chunkSize); |
| |
| DWORD allocGranularity = m_systemInfo.dwAllocationGranularity; |
| TINT64 viewOffset = |
| (TINT64(newLowFrame * m_chunkSize) / allocGranularity) * allocGranularity; |
| m_mapOffset = newLowFrame * m_chunkSize - viewOffset; |
| |
| TINT64 fileSize = newHiFrame * m_chunkSize; |
| |
| if ((fileSize > m_currentFileSize) || !m_hMap) |
| { |
| CloseHandle(m_hMap); |
| m_hMap = 0; |
| CloseHandle(m_hFile); |
| m_hFile = 0; |
| |
| TINT64 allocUnitCount = fileSize / m_reallocSize; |
| |
| if ((fileSize % m_reallocSize) != 0) ++allocUnitCount; |
| |
| fileSize = allocUnitCount * m_reallocSize; |
| |
| openFile(m_fname, fileSize); |
| m_currentFileSize = fileSize; |
| } |
| |
| DWORD dwDesiredAccess = FILE_MAP_WRITE; |
| m_fileMapAddress = MapViewOfFile( |
| m_hMap, |
| dwDesiredAccess, |
| DWORDLONG_HI_DWORD( |
| viewOffset), |
| DWORDLONG_LO_DWORD( |
| viewOffset), |
| m_viewSize); |
| |
| if (m_fileMapAddress == NULL) { |
| string errMsg = getLastErrorMessage(); |
| CloseHandle(m_hMap); |
| m_hMap = 0; |
| CloseHandle(m_hFile); |
| m_hFile = 0; |
| |
| throw TException("Unable to memory map cache file. " + errMsg); |
| } |
| } |
| #else |
| |
| ImpPDX::ImpPDX(const TFilePath &fp) : ImpPD(fp), m_fd(-1) { |
| |
| m_pageSize = getpagesize(); |
| openFile(m_fname, 0); |
| assert(m_fd >= 0); |
| } |
| |
| |
| |
| ImpPDX::~ImpPDX() { |
| if (m_fileMapAddress) munmap(m_fileMapAddress, m_viewSize); |
| close(m_fd); |
| m_fd = 0; |
| } |
| |
| |
| |
| void ImpPDX::openFile(const TFilePath &fname, TINT64 fileSize) { |
| assert(0); |
| |
| |
| |
| |
| |
| |
| } |
| |
| void ImpPDX::setCurrentView(int pos, int &newLowPos, int &newHiPos) { |
| newLowPos = pos; |
| newHiPos = newLowPos + (m_viewSize / m_chunkSize); |
| |
| assert(m_fd >= 0); |
| if (m_fileMapAddress) |
| if (munmap(m_fileMapAddress, m_viewSize) != 0) |
| throw TCachedLevelException(errno); |
| void *start = 0; |
| int flags = MAP_SHARED; |
| size_t viewOffset = ((newLowPos * m_chunkSize) / m_pageSize) * m_pageSize; |
| m_mapOffset = newLowPos * m_chunkSize - viewOffset; |
| |
| assert(!"controllare le dimensioni"); |
| unsigned long lastByte = |
| (unsigned long)(((newHiPos * m_chunkSize) / (double)m_pageSize + 0.5) * |
| m_pageSize); |
| |
| if (lastByte > m_currentFileSize) |
| { |
| unsigned long bu = |
| (unsigned long)((lastByte / (double)m_reallocSize + 0.5) * |
| m_reallocSize); |
| bu = (unsigned long)((bu / (double)m_pageSize + 0.5) * m_pageSize); |
| |
| m_currentFileSize += bu; |
| |
| std::cout << "new cache size " << m_currentFileSize << std::endl; |
| if (lseek(m_fd, m_currentFileSize, SEEK_SET) == -1) |
| throw TCachedLevelException(errno); |
| if (write(m_fd, "", 1) == -1) throw TCachedLevelException(errno); |
| if (ftruncate(m_fd, m_currentFileSize) == -1) |
| throw TCachedLevelException(errno); |
| } |
| |
| m_fileMapAddress = |
| mmap(start, m_viewSize, PROT_READ | PROT_WRITE, flags, m_fd, viewOffset); |
| if (m_fileMapAddress == (void *)-1) throw TCachedLevelException(errno); |
| } |
| |
| #endif |
| |
| |
| #ifndef WIN32 |
| #define ULONGLONG unsigned long long |
| #endif |
| bool TDiskCachePersist::Imp::put(int frame, UCHAR *data, TUINT32 dataSize) { |
| if (dataSize != m_impPD->m_chunkSize) return false; |
| |
| TThread::ScopedLock sl(m_mutex); |
| |
| setCurrentView(frame); |
| ULONGLONG offset = (frame - m_lowFrame) * m_impPD->m_chunkSize; |
| UCHAR *dst = (UCHAR *)m_impPD->m_fileMapAddress + offset; |
| memcpy(dst + m_impPD->m_mapOffset, data, dataSize); |
| return true; |
| } |
| |
| |
| |
| UCHAR *TDiskCachePersist::Imp::get(int pos, TUINT32 *size) { |
| UCHAR *ret = new UCHAR[TINT32(m_impPD->m_chunkSize)]; |
| |
| TThread::ScopedLock sl(m_mutex); |
| setCurrentView(pos); |
| ULONGLONG offset = (pos - m_lowFrame) * m_impPD->m_chunkSize; |
| UCHAR *src = |
| (UCHAR *)m_impPD->m_fileMapAddress + offset + m_impPD->m_mapOffset; |
| memcpy(ret, src, TINT32(m_impPD->m_chunkSize)); |
| *size = TUINT32(m_impPD->m_chunkSize); |
| return ret; |
| } |
| |
| |
| |
| |
| |
| |
| class TRasterCache::Data { |
| public: |
| Data(TCachePersist *cp) |
| : m_cp(cp) |
| , m_size(0, 0) |
| , m_prefetchEnabled(false) |
| , m_prefetchedFrame(-1) |
| , m_frameToPrefetch(-1) |
| , m_preLoader(1, true) {} |
| ~Data() {} |
| |
| class FrameData { |
| public: |
| bool m_valid; |
| |
| ~FrameData() {} |
| }; |
| |
| bool isFrameCached(int frame) const; |
| |
| TDimension m_size; |
| int m_bpp; |
| |
| typedef map<int, FrameData> Status; |
| Status m_status; |
| TCachePersist *m_cp; |
| TThread::Mutex m_accessMutex; |
| |
| TThread::Executor m_preLoader; |
| bool m_prefetchEnabled; |
| int m_prefetchedFrame; |
| int m_frameToPrefetch; |
| TRasterP m_prefetchedRas; |
| }; |
| |
| bool TRasterCache::Data::isFrameCached(int frame) const { |
| |
| Data::Status::const_iterator it = m_status.find(frame); |
| if (it == m_status.end()) return false; |
| Data::FrameData fd = it->second; |
| return fd.m_valid; |
| } |
| |
| |
| |
| namespace { |
| |
| class Load : public TThread::Runnable { |
| public: |
| Load(int frameToPrefetch, TCachePersist *cp, int &prefetchedFrame, |
| TRasterP &prefetchedRas) |
| : m_frame(frameToPrefetch) |
| , m_cp(cp) |
| , m_prefetchedFrame(prefetchedFrame) |
| , m_prefetchedRas(prefetchedRas) {} |
| |
| void run(); |
| |
| private: |
| int m_frame; |
| TCachePersist *m_cp; |
| int &m_prefetchedFrame; |
| TRasterP &m_prefetchedRas; |
| }; |
| |
| void Load::run() { |
| m_prefetchedRas = m_cp->doGetRaster(m_frame); |
| m_prefetchedFrame = m_frame; |
| } |
| }; |
| |
| |
| |
| TRasterCache::TRasterCache(TCachePersist *cp) : m_data(new Data(cp)) {} |
| |
| |
| |
| TRasterCache::~TRasterCache() { delete m_data; } |
| |
| |
| void TRasterCache::setMode(const TDimension &size, int bpp) { |
| TThread::ScopedLock sl(m_data->m_accessMutex); |
| |
| m_data->m_size = size; |
| m_data->m_bpp = bpp; |
| |
| m_data->m_cp->setFrameSize(size.lx, size.ly, bpp); |
| invalidate(); |
| } |
| |
| |
| |
| void TRasterCache::getMode(TDimension &size, int &bpp) const { |
| size = m_data->m_size; |
| bpp = m_data->m_bpp; |
| } |
| |
| |
| |
| TRasterP TRasterCache::getRaster(int frame) const { |
| TThread::ScopedLock sl(m_data->m_accessMutex); |
| |
| if (m_data->m_prefetchEnabled) { |
| |
| |
| |
| |
| |
| { |
| m_data->m_preLoader.clear(); |
| |
| } |
| |
| TRasterP ras; |
| if (frame == m_data->m_prefetchedFrame) |
| ras = m_data->m_prefetchedRas; |
| else |
| ras = m_data->m_cp->doGetRaster(frame); |
| |
| if (isFrameCached(frame + 1)) { |
| |
| |
| m_data->m_frameToPrefetch = frame + 1; |
| m_data->m_preLoader.addTask( |
| new Load(m_data->m_frameToPrefetch, m_data->m_cp, |
| m_data->m_prefetchedFrame, m_data->m_prefetchedRas)); |
| } |
| |
| return ras; |
| } else { |
| return m_data->m_cp->doGetRaster(frame); |
| } |
| } |
| |
| |
| |
| bool TRasterCache::getRaster(int frame, TRaster32P &ras) const { |
| TThread::ScopedLock sl(m_data->m_accessMutex); |
| if (m_data->isFrameCached(frame)) { |
| bool rc = m_data->m_cp->doGetRaster(frame, ras); |
| assert(rc); |
| return true; |
| } else |
| return false; |
| } |
| |
| |
| |
| void TRasterCache::putRaster(int frame, const TRasterP &ras) { |
| TThread::ScopedLock sl(m_data->m_accessMutex); |
| Data::Status::iterator it = m_data->m_status.find(frame); |
| bool cached = false; |
| try { |
| cached = m_data->m_cp->doPutRaster(frame, ras); |
| } catch (TException &e) { |
| if (it != m_data->m_status.end()) { |
| Data::FrameData fd; |
| fd.m_valid = false; |
| m_data->m_status[frame] = fd; |
| } |
| throw e; |
| } |
| if (cached) { |
| Data::FrameData fd; |
| fd.m_valid = true; |
| m_data->m_status[frame] = fd; |
| } |
| } |
| |
| |
| |
| UCHAR *TRasterCache::getRawData(int frame, TINT32 &size, int &lx, |
| int &ly) const { |
| return m_data->m_cp->getRawData(frame, size, lx, ly); |
| } |
| |
| |
| |
| bool TRasterCache::isFrameCached(int frame) const { |
| TThread::ScopedLock sl(m_data->m_accessMutex); |
| return m_data->isFrameCached(frame); |
| } |
| |
| |
| |
| void TRasterCache::invalidate() { |
| TThread::ScopedLock sl(m_data->m_accessMutex); |
| m_data->m_status.clear(); |
| m_data->m_cp->onInvalidate(); |
| } |
| |
| |
| |
| void TRasterCache::invalidate(int startFrame, int endFrame) { |
| assert(startFrame <= endFrame); |
| TThread::ScopedLock sl(m_data->m_accessMutex); |
| |
| Data::Status::iterator low = m_data->m_status.lower_bound(startFrame); |
| Data::Status::iterator hi = m_data->m_status.upper_bound(endFrame); |
| |
| #ifdef _DEBUG |
| int count = m_data->m_status.size(); |
| |
| if (low != m_data->m_status.end() && hi != m_data->m_status.end()) { |
| int ll = low->first; |
| int hh = hi->first; |
| assert(ll <= hh); |
| } |
| #endif |
| |
| if (low != m_data->m_status.end()) { |
| m_data->m_status.erase(low, hi); |
| m_data->m_cp->onInvalidate(startFrame, endFrame); |
| } |
| } |
| |
| |
| |
| void TRasterCache::enablePrefetch(bool newState) { |
| m_data->m_prefetchEnabled = newState; |
| } |
| |
| |
| |
| bool TRasterCache::isPrefetchEnabled() const { |
| return m_data->m_prefetchEnabled; |
| } |
| |
| |
| |
| TUINT64 TRasterCache::getUsedSpace() { return m_data->m_cp->getUsedSpace(); } |
| |
| |
| |
| |
| |
| |
| class TRamCachePersist::Imp { |
| friend class TRamCachePersist; |
| |
| public: |
| Imp() : m_cacheSize(0), m_chunks() {} |
| ~Imp() { |
| for (CompressedChunks::iterator it = m_chunks.begin(); it != m_chunks.end(); |
| ++it) { |
| CompressedChunk *cc = it->second; |
| m_cacheSize -= cc->m_size; |
| delete cc; |
| } |
| assert(m_cacheSize == 0); |
| |
| m_chunks.clear(); |
| } |
| |
| class CompressedChunk { |
| public: |
| CompressedChunk(UCHAR *buffer, int size) : m_buffer(buffer), m_size(size) {} |
| ~CompressedChunk() { delete[] m_buffer; } |
| UCHAR *m_buffer; |
| int m_size; |
| }; |
| |
| typedef map<int, CompressedChunk *> CompressedChunks; |
| CompressedChunks m_chunks; |
| TUINT64 m_cacheSize; |
| }; |
| |
| TRamCachePersist::TRamCachePersist(TRasterCodec *codec) |
| : TCachePersist(codec), m_imp(new Imp) {} |
| |
| |
| |
| TRamCachePersist::~TRamCachePersist() { delete m_imp; } |
| |
| |
| |
| TRasterP TRamCachePersist::doGetRaster(int frame) |
| |
| { |
| Imp::CompressedChunks::const_iterator it = m_imp->m_chunks.find(frame); |
| if (it == m_imp->m_chunks.end()) return TRasterP(); |
| Imp::CompressedChunk *cc = it->second; |
| assert(cc); |
| TRasterP rasP; |
| m_codec->decompress(cc->m_buffer, cc->m_size, rasP); |
| return rasP; |
| } |
| |
| |
| |
| bool TRamCachePersist::doGetRaster(int frame, TRaster32P &ras) const { |
| Imp::CompressedChunks::const_iterator it = m_imp->m_chunks.find(frame); |
| if (it == m_imp->m_chunks.end()) return false; |
| Imp::CompressedChunk *cc = it->second; |
| assert(cc); |
| TRasterP rasP(ras); |
| m_codec->decompress(cc->m_buffer, cc->m_size, rasP); |
| return true; |
| } |
| |
| |
| |
| bool TRamCachePersist::doPutRaster(int frame, const TRasterP &ras) { |
| Imp::CompressedChunks::iterator it = m_imp->m_chunks.find(frame); |
| if (it != m_imp->m_chunks.end()) { |
| m_imp->m_cacheSize -= it->second->m_size; |
| delete it->second; |
| m_imp->m_chunks.erase(it); |
| } |
| |
| UCHAR *outData = 0; |
| TINT32 outDataSize = 0; |
| m_codec->compress(ras, 1, &outData, outDataSize); |
| m_imp->m_cacheSize += outDataSize; |
| Imp::CompressedChunk *cc = new Imp::CompressedChunk(outData, outDataSize); |
| m_imp->m_chunks.insert(Imp::CompressedChunks::value_type(frame, cc)); |
| return true; |
| } |
| |
| |
| |
| void TRamCachePersist::onInvalidate() { |
| for (Imp::CompressedChunks::iterator it = m_imp->m_chunks.begin(); |
| it != m_imp->m_chunks.end(); ++it) { |
| Imp::CompressedChunk *cc = it->second; |
| m_imp->m_cacheSize -= cc->m_size; |
| delete cc; |
| } |
| |
| m_imp->m_chunks.clear(); |
| } |
| |
| |
| |
| void TRamCachePersist::onInvalidate(int startFrame, |
| int endFrame) { |
| assert(startFrame <= endFrame); |
| |
| for (int frame = startFrame; frame <= endFrame; ++frame) { |
| Imp::CompressedChunks::iterator it = m_imp->m_chunks.find(frame); |
| if (it != m_imp->m_chunks.end()) { |
| m_imp->m_cacheSize -= it->second->m_size; |
| delete it->second; |
| m_imp->m_chunks.erase(it); |
| } |
| } |
| } |
| |
| |
| |
| UCHAR *TRamCachePersist::getRawData(int frame, TINT32 &size, int &lx, int &ly) { |
| Imp::CompressedChunks::const_iterator it = m_imp->m_chunks.find(frame); |
| if (it == m_imp->m_chunks.end()) return 0; |
| Imp::CompressedChunk *cc = it->second; |
| assert(cc); |
| return m_codec->removeHeader(cc->m_buffer, cc->m_size, size, lx, ly); |
| } |
| |
| |
| |
| TUINT64 TRamCachePersist::getUsedSpace() { return m_imp->m_cacheSize; } |
| |
| |
| |
| void TDiskCachePersist::onInvalidate() { |
| |
| } |
| |
| |
| |
| void TDiskCachePersist::onInvalidate(int startFrame, int endFrame) { |
| |
| } |
| |
| |
| |
| TUINT64 TDiskCachePersist::getUsedSpace() { |
| assert(0); |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifdef WIN32 |
| namespace { |
| |
| class ZFile { |
| public: |
| ZFile(const TFilePath &fp, bool directIO, bool asyncIO); |
| ~ZFile(); |
| |
| void open(); |
| |
| int read(BYTE buf[], int size, TINT64 qwOffset) const; |
| int write(BYTE buf[], int size, TINT64 qwOffset) const; |
| |
| void waitForAsyncIOCompletion() const; |
| |
| TFilePath getFilePath() const { return m_filepath; } |
| |
| int getBytesPerSector() const { return m_bytesPerSector; } |
| |
| static void CALLBACK FileIOCompletionRoutine(DWORD errCode, |
| DWORD byteTransferred, |
| LPOVERLAPPED overlapped); |
| |
| private: |
| TFilePath m_filepath; |
| bool m_directIO; |
| bool m_asyncIO; |
| DWORD m_bytesPerSector; |
| |
| HANDLE m_fileHandle; |
| HANDLE m_writeNotPending; |
| }; |
| |
| |
| |
| ZFile::ZFile(const TFilePath &fp, bool directIO, bool asyncIO) |
| : m_filepath(fp) |
| , m_directIO(directIO) |
| , m_asyncIO(asyncIO) |
| , m_fileHandle(0) |
| , m_writeNotPending(0) { |
| DWORD sectorsPerCluster; |
| DWORD numberOfFreeClusters; |
| DWORD totalNumberOfClusters; |
| |
| TFilePathSet disks = TSystem::getDisks(); |
| |
| TFilePath disk = fp; |
| while (std::find(disks.begin(), disks.end(), disk) == disks.end()) |
| disk = disk.getParentDir(); |
| |
| BOOL ret = GetDiskFreeSpaceW(disk.getWideString().c_str(), |
| §orsPerCluster, |
| &m_bytesPerSector, |
| &numberOfFreeClusters, |
| &totalNumberOfClusters |
| ); |
| |
| if (m_asyncIO) m_writeNotPending = CreateEvent(NULL, TRUE, TRUE, NULL); |
| } |
| |
| |
| |
| ZFile::~ZFile() { |
| if (m_fileHandle) CloseHandle(m_fileHandle); |
| |
| if (m_writeNotPending) CloseHandle(m_writeNotPending); |
| } |
| |
| |
| |
| void ZFile::open() { |
| DWORD flagsAndAttributes = 0; |
| flagsAndAttributes = m_directIO ? FILE_FLAG_NO_BUFFERING : 0UL; |
| flagsAndAttributes |= m_asyncIO ? FILE_FLAG_OVERLAPPED : 0UL; |
| |
| |
| m_fileHandle = |
| CreateFileW(m_filepath.getWideString().c_str(), |
| GENERIC_READ | GENERIC_WRITE, |
| 0, |
| NULL, |
| OPEN_ALWAYS, |
| flagsAndAttributes, |
| NULL); |
| |
| if (m_fileHandle == INVALID_HANDLE_VALUE) { |
| m_fileHandle = 0; |
| |
| char errorMessage[2048]; |
| |
| DWORD error = GetLastError(); |
| FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 2048, |
| NULL); |
| |
| throw TException(errorMessage); |
| } |
| } |
| |
| |
| |
| int ZFile::read(BYTE buf[], int size, TINT64 qwOffset) const { |
| assert(size % m_bytesPerSector == 0); |
| assert(qwOffset % m_bytesPerSector == 0); |
| |
| char msg[2048] = ""; |
| unsigned long bytesToRead; |
| unsigned long bytesRead; |
| |
| OVERLAPPED overLapped; |
| memset(&overLapped, 0, sizeof(overLapped)); |
| |
| #define DWORDLONG_LO_DWORD(dwl64) ((DWORD)(dwl64)) |
| #define DWORDLONG_HI_DWORD(dwl64) ((DWORD)(dwl64 >> 32)) |
| |
| |
| overLapped.Offset = DWORDLONG_LO_DWORD(qwOffset); |
| overLapped.OffsetHigh = DWORDLONG_HI_DWORD(qwOffset); |
| |
| if (m_asyncIO) { |
| overLapped.hEvent = CreateEvent(NULL, |
| TRUE, |
| FALSE, |
| NULL); |
| } else |
| overLapped.hEvent = NULL; |
| |
| bytesToRead = size; |
| |
| |
| int result = ReadFile(m_fileHandle, |
| (void *)buf, |
| bytesToRead, |
| &bytesRead, |
| &overLapped); |
| |
| if (!result) { |
| DWORD error = GetLastError(); |
| if (m_asyncIO && ERROR_IO_PENDING == error) { |
| if (!GetOverlappedResult(m_fileHandle, &overLapped, &bytesRead, TRUE)) { |
| char errorMessage[2048]; |
| FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, |
| 2048, NULL); |
| |
| throw TException(errorMessage); |
| } |
| } else { |
| char errorMessage[2048]; |
| FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, |
| 2048, NULL); |
| |
| throw TException(errorMessage); |
| } |
| } |
| |
| return bytesRead; |
| } |
| |
| |
| |
| int ZFile::write(BYTE buf[], int size, TINT64 qwOffset) const { |
| assert(size % m_bytesPerSector == 0); |
| assert(qwOffset % m_bytesPerSector == 0); |
| |
| char msg[2048] = ""; |
| unsigned long bytesToWrite; |
| unsigned long bytesWritten = 0; |
| |
| int result; |
| |
| if (m_asyncIO) { |
| OVERLAPPED *overLapped = new OVERLAPPED; |
| memset(overLapped, 0, sizeof(OVERLAPPED)); |
| |
| |
| overLapped->Offset = DWORDLONG_LO_DWORD(qwOffset); |
| overLapped->OffsetHigh = DWORDLONG_HI_DWORD(qwOffset); |
| overLapped->hEvent = NULL; |
| |
| bytesToWrite = size; |
| |
| result = WriteFileEx(m_fileHandle, |
| (void *)buf, |
| bytesToWrite, |
| overLapped, |
| &ZFile::FileIOCompletionRoutine); |
| |
| ResetEvent(m_writeNotPending); |
| } else { |
| OVERLAPPED overLapped; |
| memset(&overLapped, 0, sizeof(overLapped)); |
| |
| |
| overLapped.Offset = DWORDLONG_LO_DWORD(qwOffset); |
| overLapped.OffsetHigh = DWORDLONG_HI_DWORD(qwOffset); |
| overLapped.hEvent = NULL; |
| |
| bytesToWrite = size; |
| |
| result = WriteFile(m_fileHandle, |
| (void *)buf, |
| bytesToWrite, |
| &bytesWritten, |
| &overLapped); |
| } |
| |
| if (!result) { |
| char errorMessage[2048]; |
| DWORD error = GetLastError(); |
| FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0UL, error, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 2048, |
| NULL); |
| |
| throw TException(errorMessage); |
| } |
| |
| return bytesWritten; |
| } |
| |
| |
| |
| void ZFile::waitForAsyncIOCompletion() const { |
| if (m_asyncIO) { |
| WaitForSingleObjectEx(m_writeNotPending, INFINITE, TRUE); |
| SetEvent(m_writeNotPending); |
| } |
| } |
| |
| |
| |
| void CALLBACK ZFile::FileIOCompletionRoutine(DWORD errCode, |
| DWORD byteTransferred, |
| LPOVERLAPPED overlapped) { |
| delete overlapped; |
| } |
| |
| |
| |
| |
| class BufferQueue { |
| public: |
| class Item { |
| public: |
| Item(int frame, UCHAR *buffer, int bufferSize, int chunkSize) |
| : m_frame(frame) |
| , m_buffer(buffer) |
| , m_bufferSize(bufferSize) |
| , m_chunkSize(chunkSize) {} |
| |
| int m_frame; |
| UCHAR *m_buffer; |
| int m_bufferSize; |
| TINT64 m_chunkSize; |
| }; |
| |
| BufferQueue(int capacity, int allocUnit) |
| : m_capacity(capacity) |
| , m_allocUnit(allocUnit) |
| , m_notEmpty() |
| , m_notFull() |
| , m_mutex() |
| , m_bufferCount(0) |
| , m_nextPutItem(0) |
| , m_nextGetItem(0) { |
| for (int i = 0; i < m_capacity; ++i) |
| m_items.push_back(Item(-1, (UCHAR *)0, 0, 0)); |
| } |
| |
| ~BufferQueue() { |
| for (int i = 0; i < m_capacity; ++i) delete[] m_items[i].m_buffer; |
| } |
| |
| void put(int frame, UCHAR *buffer, int bufferSize, int chunkSize) { |
| TThread::ScopedLock sl(m_mutex); |
| while (m_bufferCount == m_capacity) m_notFull.wait(sl); |
| |
| if (m_items[m_nextPutItem].m_chunkSize != chunkSize) { |
| delete[] m_items[m_nextPutItem].m_buffer; |
| m_items[m_nextPutItem].m_buffer = new UCHAR[chunkSize]; |
| m_items[m_nextPutItem].m_chunkSize = chunkSize; |
| } |
| |
| memcpy(m_items[m_nextPutItem].m_buffer, buffer, bufferSize); |
| m_items[m_nextPutItem].m_frame = frame; |
| |
| m_nextPutItem = (m_nextPutItem + 1) % m_capacity; |
| ++m_bufferCount; |
| m_notEmpty.notifyOne(); |
| } |
| |
| BufferQueue::Item get() { |
| TThread::ScopedLock sl(m_mutex); |
| |
| while (m_bufferCount == 0) m_notEmpty.wait(sl); |
| |
| m_notFull.notifyOne(); |
| |
| BufferQueue::Item item = m_items[m_nextGetItem]; |
| |
| m_nextGetItem = (m_nextGetItem + 1) % m_capacity; |
| --m_bufferCount; |
| return item; |
| } |
| |
| void saveOne(ZFile *file) { |
| TThread::ScopedLock sl(m_mutex); |
| |
| while (m_bufferCount == 0) m_notEmpty.wait(sl); |
| |
| m_notFull.notifyOne(); |
| |
| BufferQueue::Item item = m_items[m_nextGetItem]; |
| |
| m_nextGetItem = (m_nextGetItem + 1) % m_capacity; |
| --m_bufferCount; |
| |
| TINT64 pos = item.m_frame * item.m_chunkSize; |
| TINT64 sectorCount = pos / m_allocUnit; |
| |
| if ((pos % m_allocUnit) != 0) ++sectorCount; |
| |
| pos = sectorCount * m_allocUnit; |
| file->write(item.m_buffer, (TINT32)item.m_chunkSize, pos); |
| } |
| |
| int size() { |
| TThread::ScopedLock sl(m_mutex); |
| return m_bufferCount; |
| } |
| |
| private: |
| int m_capacity; |
| int m_allocUnit; |
| |
| TThread::Condition m_notEmpty; |
| TThread::Condition m_notFull; |
| TThread::Mutex m_mutex; |
| |
| vector<Item> m_items; |
| |
| int m_bufferCount; |
| int m_nextPutItem; |
| int m_nextGetItem; |
| }; |
| |
| |
| |
| |
| class WriteBufferTask : public TThread::Runnable { |
| public: |
| WriteBufferTask(ZFile *file, BufferQueue *bufferQueue) |
| : m_file(file), m_bufferQueue(bufferQueue) {} |
| |
| void run(); |
| |
| ZFile *m_file; |
| BufferQueue *m_bufferQueue; |
| }; |
| |
| |
| |
| void WriteBufferTask::run() { |
| while (true) { |
| TThread::milestone(); |
| |
| try { |
| m_bufferQueue->saveOne(m_file); |
| m_file->waitForAsyncIOCompletion(); |
| } catch (TException & ) { |
| } catch (...) { |
| } |
| } |
| } |
| |
| } |
| |
| |
| |
| |
| class TDiskCachePersist2::Imp { |
| public: |
| Imp(const TFilePath &fp, bool asyncWrite) |
| : m_chunkSize(0) |
| , m_readBuffer(0) |
| , m_file(new ZFile(fp, true, asyncWrite)) |
| , m_asyncWrite(asyncWrite) |
| , m_executor(0) |
| , m_bufferQueue(0) { |
| m_file->open(); |
| m_allocUnit = m_file->getBytesPerSector(); |
| |
| if (m_asyncWrite) { |
| m_executor = new TThread::Executor(); |
| m_bufferQueue = new BufferQueue(4, m_allocUnit); |
| m_executor->addTask(new WriteBufferTask(m_file, m_bufferQueue)); |
| } |
| } |
| |
| ~Imp() { |
| delete m_file; |
| delete[] m_readBuffer; |
| |
| if (m_executor) { |
| m_executor->cancel(); |
| delete m_executor; |
| } |
| |
| delete m_bufferQueue; |
| } |
| |
| bool put(int frame, UCHAR *data, TUINT32 dataSize); |
| UCHAR *get(int pos, TUINT32 *size); |
| |
| TThread::Mutex m_mutex; |
| TINT64 m_chunkSize; |
| |
| ZFile *m_file; |
| int m_allocUnit; |
| UCHAR *m_readBuffer; |
| int m_lx; |
| int m_ly; |
| int m_bpp; |
| |
| bool m_asyncWrite; |
| TThread::Executor *m_executor; |
| BufferQueue *m_bufferQueue; |
| }; |
| |
| |
| |
| bool TDiskCachePersist2::Imp::put(int frame, UCHAR *data, TUINT32 dataSize) { |
| if (dataSize != m_chunkSize) return false; |
| |
| TINT64 pos = frame * m_chunkSize; |
| TINT64 sectorCount = pos / m_allocUnit; |
| |
| if ((pos % m_allocUnit) != 0) ++sectorCount; |
| |
| pos = sectorCount * m_allocUnit; |
| |
| m_file->write(data, dataSize, pos); |
| return true; |
| } |
| |
| |
| |
| UCHAR *TDiskCachePersist2::Imp::get(int frame, TUINT32 *size) { |
| UCHAR *ret = new UCHAR[TINT32(m_chunkSize)]; |
| |
| TThread::ScopedLock sl(m_mutex); |
| |
| TINT64 pos = frame * m_chunkSize; |
| TINT64 sectorCount = pos / m_allocUnit; |
| |
| if ((pos % m_allocUnit) != 0) ++sectorCount; |
| |
| pos = sectorCount * m_allocUnit; |
| |
| m_file->read(ret, TINT32(m_chunkSize), pos); |
| *size = TUINT32(m_chunkSize); |
| return ret; |
| } |
| |
| |
| |
| TDiskCachePersist2::TDiskCachePersist2(TRasterCodec *codec, |
| const TFilePath &fullpath) |
| : TCachePersist(codec), m_imp(new Imp(fullpath, false )) {} |
| |
| |
| |
| TDiskCachePersist2::~TDiskCachePersist2() { delete m_imp; } |
| |
| |
| |
| void TDiskCachePersist2::setFrameSize(int lx, int ly, int bpp) { |
| m_imp->m_lx = lx; |
| m_imp->m_ly = ly; |
| m_imp->m_bpp = bpp; |
| |
| |
| |
| if (m_codec) |
| m_imp->m_chunkSize = m_codec->getMaxCompressionSize(lx * ly * (bpp >> 3)); |
| else |
| m_imp->m_chunkSize = lx * ly * (bpp >> 3); |
| |
| TINT64 allocUnitCount = m_imp->m_chunkSize / m_imp->m_allocUnit; |
| if ((m_imp->m_chunkSize % m_imp->m_allocUnit) != 0) ++allocUnitCount; |
| |
| m_imp->m_chunkSize = allocUnitCount * m_imp->m_allocUnit; |
| delete[] m_imp->m_readBuffer; |
| m_imp->m_readBuffer = 0; |
| } |
| |
| |
| |
| TRasterP TDiskCachePersist2::doGetRaster(int frame) { |
| TRasterP outRas; |
| TUINT32 size; |
| |
| if (!m_imp->m_readBuffer) |
| m_imp->m_readBuffer = new UCHAR[TINT32(m_imp->m_chunkSize)]; |
| |
| TINT64 pos = frame * m_imp->m_chunkSize; |
| TINT64 sectorCount = pos / m_imp->m_allocUnit; |
| |
| if ((pos % m_imp->m_allocUnit) != 0) ++sectorCount; |
| |
| pos = sectorCount * m_imp->m_allocUnit; |
| |
| m_imp->m_file->read(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), pos); |
| size = TUINT32(m_imp->m_chunkSize); |
| |
| if (m_codec) |
| m_codec->decompress(m_imp->m_readBuffer, size, outRas); |
| else { |
| switch (m_imp->m_bpp) { |
| case 32: { |
| TRaster32P ras(m_imp->m_lx, m_imp->m_ly); |
| outRas = ras; |
| } break; |
| case 64: { |
| TRaster64P ras(m_imp->m_lx, m_imp->m_ly); |
| outRas = ras; |
| } break; |
| default: |
| throw TException("unsupported pixel format"); |
| break; |
| } |
| |
| unsigned int rasSize = outRas->getRowSize() * outRas->getLy(); |
| assert(size >= rasSize); |
| outRas->lock(); |
| memcpy(outRas->getRawData(), m_imp->m_readBuffer, rasSize); |
| outRas->unlock(); |
| } |
| return outRas; |
| } |
| |
| |
| |
| bool TDiskCachePersist2::doGetRaster(int frame, TRaster32P &ras) const { |
| if (!m_imp->m_readBuffer) |
| m_imp->m_readBuffer = new UCHAR[TINT32(m_imp->m_chunkSize)]; |
| |
| TINT64 pos = frame * m_imp->m_chunkSize; |
| TINT64 sectorCount = pos / m_imp->m_allocUnit; |
| |
| if ((pos % m_imp->m_allocUnit) != 0) ++sectorCount; |
| |
| pos = sectorCount * m_imp->m_allocUnit; |
| |
| TRasterP rasP = ras; |
| if (m_codec) { |
| m_imp->m_file->read(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), pos); |
| m_codec->decompress(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), rasP); |
| assert(rasP->getSize() == ras->getSize()); |
| ras->copy(rasP); |
| } else { |
| assert(ras->getLx() == ras->getWrap()); |
| int rasSize = ras->getRowSize() * ras->getLy(); |
| ras->lock(); |
| if (rasSize == m_imp->m_chunkSize) |
| m_imp->m_file->read(ras->getRawData(), TINT32(m_imp->m_chunkSize), pos); |
| else { |
| assert(rasSize < m_imp->m_chunkSize); |
| m_imp->m_file->read(m_imp->m_readBuffer, TINT32(m_imp->m_chunkSize), pos); |
| memcpy(ras->getRawData(), m_imp->m_readBuffer, rasSize); |
| } |
| ras->unlock(); |
| } |
| return true; |
| } |
| |
| |
| |
| bool TDiskCachePersist2::doPutRaster(int frame, const TRasterP &inRas) { |
| UCHAR *outData = 0; |
| TINT32 outDataSize = 0; |
| int actualDataSize = 0; |
| |
| bool deleteDataBuffer = false; |
| if (m_codec) { |
| m_codec->compress(inRas, m_imp->m_allocUnit, &outData, outDataSize); |
| deleteDataBuffer = true; |
| ; |
| actualDataSize = outDataSize; |
| } else { |
| assert(inRas->getLx() == inRas->getWrap()); |
| int rasSize = inRas->getLx() * inRas->getLy() * inRas->getPixelSize(); |
| |
| outDataSize = TINT32(m_imp->m_chunkSize); |
| inRas->lock(); |
| if (rasSize == m_imp->m_chunkSize) |
| outData = inRas->getRawData(); |
| else { |
| if (!m_imp->m_readBuffer) |
| m_imp->m_readBuffer = new UCHAR[TINT32(m_imp->m_chunkSize)]; |
| |
| memcpy(m_imp->m_readBuffer, inRas->getRawData(), rasSize); |
| outData = m_imp->m_readBuffer; |
| } |
| inRas->unlock(); |
| actualDataSize = rasSize; |
| } |
| |
| assert((outDataSize % m_imp->m_allocUnit) == 0); |
| |
| bool cached = true; |
| if (m_imp->m_asyncWrite) |
| m_imp->m_bufferQueue->put(frame, outData, actualDataSize, outDataSize); |
| else |
| cached = m_imp->put(frame, outData, outDataSize); |
| |
| if (deleteDataBuffer) delete[] outData; |
| return cached; |
| } |
| |
| |
| |
| UCHAR *TDiskCachePersist2::getRawData(int frame, TINT32 &size, int &lx, |
| int &ly) { |
| TUINT32 inDataSize; |
| UCHAR *src = m_imp->get(frame, &inDataSize); |
| return m_codec->removeHeader(src, inDataSize, size, lx, ly); |
| } |
| |
| |
| |
| void TDiskCachePersist2::onInvalidate() {} |
| |
| |
| |
| void TDiskCachePersist2::onInvalidate(int startFrame, int endFrame) {} |
| |
| |
| |
| TUINT64 TDiskCachePersist2::getUsedSpace() { |
| TFileStatus fs(m_imp->m_file->getFilePath()); |
| return fs.getSize(); |
| } |
| |
| |
| #endif // WIN32 |