📄 chunkres.cpp
字号:
// HX_RESULT
// Possible errors include: TBD.
//
HX_RESULT CChunkyRes::GetData(ULONG32 offset, char* buf, ULONG32 count, ULONG32* actual)
{
HX_RESULT theErr = HXR_OK;
int ndx;
ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
ULONG32 ulLastChunk = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
HX_ASSERT(ulFirstChunk < INT_MAX);
HX_ASSERT(ulLastChunk < INT_MAX);
int nFirstChunk = (int)ulFirstChunk;
int nLastChunk = (int)ulLastChunk;
HX_ASSERT(m_Chunks.GetSize() >= nLastChunk+1);
ULONG32 chunkOffset = offset - (ulFirstChunk*DEF_CHUNKYRES_CHUNK_SIZE);
ULONG32 chunkCount = count;
ULONG32 baseOffset = 0;
*actual = 0; // -fst
for (ndx = nFirstChunk; (ndx <= nLastChunk) && chunkCount; ndx++)
{
CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
if (!pChunk)
{
// with random access, it's feasible that there's a null chunk.
theErr = HXR_CHUNK_MISSING;
goto exit;
}
HX_ASSERT_VALID_PTR(pChunk);
ULONG32 chunkActual = 0;
// Actually get the data from the chunk!
ULONG32 chunkAmount = min(DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset,chunkCount);
theErr = pChunk->GetData(chunkOffset,buf+baseOffset,chunkAmount,&chunkActual);
if (theErr != HXR_OK)
{
goto exit;
}
// What?!?!
HX_ASSERT(chunkActual == chunkAmount);
*actual += chunkActual; // -fst
// reduce the chunk count...
chunkCount -= chunkAmount;
baseOffset += chunkAmount;
// only the first chunk has an offset!
chunkOffset = 0;
}
// Remember how many bytes have been served to the user,
// in case they want us to discard used data
m_ulUsedBytes = offset + *actual;
// Discard chunks that have been fully served to the user
if (m_bDiscardUsedData)
{
nLastChunk = (int)(m_ulUsedBytes/DEF_CHUNKYRES_CHUNK_SIZE);
for (ndx = m_ulFirstChunkIdx; ndx < nLastChunk - 1; ndx++)
{
CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
HX_ASSERT_VALID_PTR(pChunk);
UINT32 ulTempOffset = pChunk->GetTempFileOffset();
pChunk->DiscardDiskData();
// Increment the first valid chunk index
m_ulFirstChunkIdx++;
if (ulTempOffset)
{
// Add the disk space to the free space list
m_FreeDiskOffsets.AddHead((void*)ulTempOffset);
}
}
}
exit:
return theErr;
}
HX_RESULT
CChunkyRes::GetContiguousDataPointer(ULONG32 offset, char*& buf, ULONG32 count)
{
HX_RESULT theErr = HXR_OK;
HX_ASSERT(m_bDiscardUsedData == FALSE && m_bDisableDiskIO == FALSE);
ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
ULONG32 ulLastChunk = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
HX_ASSERT(ulFirstChunk < INT_MAX);
HX_ASSERT(ulLastChunk < INT_MAX);
// if the required data length spans two chunks, we cannot have
// contiguous memory
if (ulFirstChunk != ulLastChunk)
{
return HXR_FAIL;
}
int nFirstChunk = (int)ulFirstChunk;
if (m_Chunks.GetSize() < nFirstChunk+1)
{
m_Chunks.SetSize(nFirstChunk+1);
}
CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[nFirstChunk];
if (!pChunk)
{
pChunk = new CChunkyResChunk(this);
if (m_bDisableDiskIO)
{
pChunk->DisableDiskIO();
}
m_Chunks[nFirstChunk] = pChunk;
}
HX_ASSERT(m_Chunks.GetSize() >= nFirstChunk+1);
HX_ASSERT_VALID_PTR(pChunk);
ULONG32 chunkOffset = offset - (ulFirstChunk*DEF_CHUNKYRES_CHUNK_SIZE);
// Actually get the data from the chunk!
ULONG32 chunkAmount = min(DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset, count);
theErr = pChunk->GetContiguousDataPointer(chunkOffset,buf,chunkAmount);
return theErr;
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyRes::SetData()
//
// Purpose:
//
// Sets a block of data in a resource.
//
// Parameters:
//
// ULONG32 offset
// const char* buf
// ULONG32 count
//
// Return:
//
// HX_RESULT
// Possible errors include: TBD.
//
HX_RESULT CChunkyRes::SetData(ULONG32 offset, const char* buf, ULONG32 count)
{
HX_RESULT theErr = HXR_OK;
ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
ULONG32 ulLastChunk = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
HX_ASSERT(ulFirstChunk < INT_MAX);
HX_ASSERT(ulLastChunk < INT_MAX);
int nFirstChunk = (int)ulFirstChunk;
int nLastChunk = (int)ulLastChunk;
if (m_Chunks.GetSize() < nLastChunk+1)
{
m_Chunks.SetSize(nLastChunk+1);
}
ULONG32 chunkOffset = offset - (ulFirstChunk*DEF_CHUNKYRES_CHUNK_SIZE);
ULONG32 chunkCount = count;
ULONG32 baseOffset = 0;
for (int ndx = nFirstChunk; ndx <= nLastChunk; ndx++)
{
CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
if (!pChunk)
{
pChunk = new CChunkyResChunk(this);
if (m_bDisableDiskIO)
{
pChunk->DisableDiskIO();
}
m_Chunks[ndx] = pChunk;
}
// Actually set the data for the chunk!
theErr = pChunk->SetData(chunkOffset,buf+baseOffset,min(DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset,chunkCount));
if (theErr != HXR_OK)
{
goto exit;
}
// reduce the chunk count...
chunkCount -= (DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset);
baseOffset += (DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset);
// only the first chunk has an offset!
chunkOffset = 0;
}
exit:
return theErr;
}
void CChunkyRes::TrimDownMemoryMRU()
{
// If we have just reduced our allowed memory usage, then
// discard the least recently used chunks till we are under
// the ne threshold...
if (m_CurMemUsage > m_MemUsageThreshold)
{
while (!m_ChunksMemoryMRU->IsEmpty() && (m_CurMemUsage > m_MemUsageThreshold))
{
// Get the least recently used chunk.
CChunkyResChunk* pChunk = (CChunkyResChunk*)m_ChunksMemoryMRU->GetTail();
HX_ASSERT_VALID_PTR(pChunk);
// Discount its usage.
m_CurMemUsage -= pChunk->GetSize();
// Spill this chunk to disk...
pChunk->SpillToDisk();
// Remove the chunk from the end of the Memory MRU
m_ChunksMemoryMRU->RemoveTail();
// And add the chunk to the front of the Disk MRU
m_ChunksDiskMRU->AddHead(pChunk);
}
// How can this be?!?! Did you really mean to set the memory usage such
// that there are no chunks in memory?!?
HX_ASSERT(!m_ChunksMemoryMRU->IsEmpty());
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyResChunk::SetMemUsageThreshold()
//
// Purpose:
//
// Sets the memory usage threshold for the chunky resource chunks.
// If if chunk sizes amount to more than the threshold, then the
// least recently used ones will be spilled to disk.
//
// Parameters:
//
// ULONG32 memUsage
// Memory usage in bytes which will be allowed for all chunks before
// least recently used chunks will be spilled to disk.
//
// Return:
//
// None.
//
void CChunkyRes::SetMemUsageThreshold(ULONG32 memUsage)
{
m_MemUsageThreshold = memUsage;
TrimDownMemoryMRU();
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyRes::CChunkyRes()
//
// Purpose:
//
// Constructor for a chunky resource.
//
// Parameters:
//
// None.
//
// Return:
//
// N/A
//
CChunkyRes::CChunkyRes()
: m_Chunks()
, m_strTempFileName()
, m_ulNextTempFileChunk(DEF_START_CHUNK_OFFSET)
, m_bHasBeenOpened(FALSE)
, m_bDisableDiskIO(FALSE)
, m_bDiscardUsedData(FALSE)
, m_ulFirstChunkIdx(0)
, m_ulUsedBytes(0)
, m_pMutex(0)
, m_MemUsageThreshold(DEF_CHUNKYRES_MEM_THRESHOLD)
, m_CurMemUsage(0)
, m_ChunksMemoryMRU(NULL)
, m_ChunksDiskMRU(NULL)
, m_ChunkSize(DEF_CHUNKYRES_CHUNK_SIZE)
{
#if defined(THREADS_SUPPORTED)
HXMutex::MakeMutex(m_pMutex);
#else
HXMutex::MakeStubMutex(m_pMutex);
#endif
HX_ASSERT(m_pMutex);
m_ChunksMemoryMRU = new CHXSimpleList;
m_ChunksDiskMRU = new CHXSimpleList;
HX_ASSERT(m_ChunksMemoryMRU);
HX_ASSERT(m_ChunksDiskMRU);
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyRes::~CChunkyRes()
//
// Purpose:
//
// Destructor for a chunky resource.
//
// Parameters:
//
// None.
//
// Return:
//
// N/A
//
CChunkyRes::~CChunkyRes()
{
// If we are getting rid of the resource, then
// we should discard all of the chunks...
for (int ndx = 0; ndx < m_Chunks.GetSize(); ndx++)
{
CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
if (pChunk)
{
delete pChunk;
}
}
HX_RESULT theErr = DiscardDiskData();
HX_ASSERT(theErr == HXR_OK);
if(m_ChunksMemoryMRU)
{
HX_ASSERT(m_ChunksMemoryMRU->GetCount() == 0);
delete m_ChunksMemoryMRU;
m_ChunksMemoryMRU = NULL;
}
if(m_ChunksDiskMRU)
{
HX_ASSERT(m_ChunksDiskMRU->GetCount() == 0);
delete m_ChunksDiskMRU;
m_ChunksDiskMRU = NULL;
}
HX_DELETE(m_pMutex);
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyResChunk::MakeSureChunkIsInMemory()
//
// Purpose:
//
// Get a portion of the data for a chunk.
//
// Parameters:
//
// ULONG32 offset
// char* buf
// ULONG32 count
// ULONG32* actual
//
// Return:
//
// HX_RESULT
// Possible errors include: TBD.
//
HX_RESULT CChunkyResChunk::MakeSureChunkIsInMemory()
{
HX_RESULT theErr = HXR_OK;
// If we don't have a chunk pointer, then we aren't in
// memory...
if (!m_pChunkData)
{
// Find ourselves in the MRU list for Disk chunks...
LISTPOSITION pos = m_pChunkRes->m_ChunksDiskMRU->Find(this);
// If were found in the disk MRU list, then we have
// some work to do...
if (pos)
{
// First, remove ourselves from the disk list...
m_pChunkRes->m_ChunksDiskMRU->RemoveAt(pos);
// Load from disk...
theErr = LoadFromDisk();
if (theErr != HXR_OK)
{
goto exit;
}
}
else
{
#ifdef _DEBUG
{
// We shouldn't find ourselves in the MRU list
// for memory chunks... but we want to check!
LISTPOSITION pos = m_pChunkRes->m_ChunksMemoryMRU->Find(this);
// We shouldn't be in this list!!!!
HX_ASSERT(pos == NULL);
}
#endif // end _DEBUG section
m_pChunkData = new UCHAR[m_pChunkRes->m_ChunkSize];
HX_ASSERT(GetValidLength() == 0);
}
// Add to the front of the Memory list...
m_pChunkRes->m_ChunksMemoryMRU->AddHead(this);
m_pChunkRes->m_CurMemUsage += GetSize();
// Make sure we don't have to much info in memory...
if (!m_bDisableDiskIO)
{
m_pChunkRes->TrimDownMemoryMRU();
HX_ASSERT(m_pChunkData);
}
}
// If we are already in memory, then make sure we are at the
// top of the Memory MRU list!!!
else
{
// We should find ourselves in the MRU list
// for memory chunks... but we want to check!
LISTPOSITION pos = m_pChunkRes->m_ChunksMemoryMRU->Find(this);
// XXXNH: If we aren't in this list it means we were paged out,
// so we only need to put ourselves at the top of the MRU list
if (pos)
{
// First, remove ourselves from wherever we are in the
// Memory MRU list...
m_pChunkRes->m_ChunksMemoryMRU->RemoveAt(pos);
}
// And add ourselves to the top of the list!
m_pChunkRes->m_ChunksMemoryMRU->AddHead(this);
}
exit:
return theErr;
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyResChunk::GetValidLength()
//
// Purpose:
//
// Determines how much of a chunk is valid
//
// Parameters:
//
// ULONG32 offset
//
// Return:
//
// HX_RESULT
// Possible errors include: TBD.
//
ULONG32 CChunkyResChunk::GetValidLength(ULONG32 offset /* = 0 */) const
{
HX_ASSERT(offset < GetSize());
ULONG32 ulValidLength = 0;
LISTPOSITION rangePos = m_ValidRanges.GetHeadPosition();
if (rangePos)
{
do
{
ValidRange* pRange = (ValidRange*)m_ValidRanges.GetNext(rangePos);
HX_ASSERT(pRange);
// see if the offset points into this particular range
if (offset >= pRange->offset
&& offset <= pRange->offset + pRange->length)
{
ulValidLength = pRange->offset + pRange->length - offset;
}
}
while (rangePos);
}
return ulValidLength;
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyResChunk::GetData()
//
// Purpose:
//
// Get a portion of the data for a chunk.
//
// Parameters:
//
// ULONG32 offset
// char* buf
// ULONG32 count
// ULONG32* actual
//
// Return:
//
// HX_RESULT
// Possible errors include: TBD.
//
HX_RESULT CChunkyResChunk::GetData(ULONG32 offset, char* buf, ULONG32 count, ULONG32* actual)
{
HX_RESULT theErr;
if(!count)
{
*actual = count;
return HXR_OK;
}
// We should have a non-zero valid size
if (!GetValidLength(offset))
{
// This chunk must have been discarded
theErr = HXR_CHUNK_MISSING;
goto exit;
}
// Make sure this chunk is in memory!
theErr = MakeSureChunkIsInMemory();
if (theErr != HXR_OK)
{
goto exit;
}
// You can't read more than there is room in this chunk.
// CChunkyRes should prevent this case...
HX_ASSERT(offset+count <= GetSize());
// The call to MakeSureChunkIsInMemory() should have handled this!
HX_ASSERT_VALID_PTR(m_pChunkData);
*actual = min(count,GetValidLength(offset));
HX_ASSERT(*actual < UINT_MAX);
memcpy(buf,m_pChunkData+offset,(int)(*actual)); /* Flawfinder: ignore */
exit:
return theErr;
}
HX_RESULT
CChunkyResChunk::GetContiguousDataPointer(ULONG32 offset, char*& buf, ULONG32 count)
{
HX_RESULT theErr = HXR_OK;
if(!count)
{
theErr = HXR_FAIL;
goto exit;
}
// First, make sure this chunk is in memory!
theErr = MakeSureChunkIsInMemory();
if (theErr != HXR_OK)
{
goto exit;
}
// You can't write more than there is room in this chunk.
// CChunkyRes should prevent this case...
HX_ASSERT(offset+count <= GetSize());
// Currently, you must write to chunks in order from the
// start of the chunk first. Random access may come in the
// future...
HX_ASSERT(GetValidLength(offset) > 0); // needed still at all?
// The call to MakeSureChunkIsInMemory() should have handled this!
HX_ASSERT_VALID_PTR(m_pChunkData);
AddValidRange(offset, count);
HX_ASSERT(count < UINT_MAX);
buf = (char*) (m_pChunkData+offset);
m_bModified = TRUE;
exit:
return theErr;
}
/////////////////////////////////////////////////////////////////////////////
//
// Method:
//
// CChunkyResChunk::AddValidRange()
//
// Purpose:
//
// Mark a part of this CChunkyResChunk as valid
// Called from SetData.
//
// Parameters:
//
// ULONG32 offset
// ULONG32 length
// BOOL bValid
//
// Return:
//
// HX_RESULT
// Possible errors include: TBD.
//
HX_RESULT CChunkyResChunk::AddValidRange(ULONG32 offset, ULONG32 length, BOOL bValid /* = TRUE */)
{
HX_RESULT theErr = HXR_OK;
int nCount = m_ValidRanges.GetCount();
LISTPOSITION pos = m_ValidRanges.GetHeadPosition();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -