chunkres.cpp

来自「symbian 下的helix player源代码」· C++ 代码 · 共 2,018 行 · 第 1/4 页

CPP
2,018
字号
//		HX_RESULT
//		Possible errors include: TBD.
//
HX_RESULT CChunkyRes::DiscardRange( ULONG32 offset, ULONG32 count )
{
    HX_RESULT theErr = HXR_OK;
    
    // Big picture of this function is that it takes
    // care of the end cases, where part of a chunk may
    // be invalidated; then it totally removes all the
    // chunks wholly contained by the range.
    
    ULONG32 ulOffsetIntoChunk;
    
    ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
    
    ulOffsetIntoChunk = offset % DEF_CHUNKYRES_CHUNK_SIZE;
    
    ULONG32 ulLastChunk  = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
    
    if (ulFirstChunk == ulLastChunk)
    {
	// if the range is all in one chunk, deal with that simplest
	// case and ignore the more complicated scenarios.
	
	CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulFirstChunk];
	
	HX_ASSERT(pChunk);
	
	pChunk->AddValidRange(ulOffsetIntoChunk, count, FALSE);
	
	return theErr;
    }
    
    if (ulOffsetIntoChunk)
    {
        // OK, we have a chunk that needs to be partially invalidated.
        
	CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulFirstChunk];
	
	HX_ASSERT(pChunk);
	
	pChunk->AddValidRange(ulOffsetIntoChunk, DEF_CHUNKYRES_CHUNK_SIZE - ulOffsetIntoChunk, FALSE);
	
        ulFirstChunk++;
    }
    
    ulOffsetIntoChunk = (offset+count) % DEF_CHUNKYRES_CHUNK_SIZE;
    if (ulOffsetIntoChunk)
    {
        // OK, the final chunk needs to be partially invalidated.
        
	CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulLastChunk];
	
	HX_ASSERT(pChunk);
	
	pChunk->AddValidRange(0, ulOffsetIntoChunk, FALSE);
    }
    
    for (ULONG32 ulWhichChunk = ulFirstChunk; ulWhichChunk < ulLastChunk; ulWhichChunk++)
    {
	CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulWhichChunk];

	// if the chunk doesn't (yet?) exist, then it's considered invalid.
	
	if (pChunk)
	{
	    ULONG32 ulTempOffset = pChunk->GetTempFileOffset();
	    if (ulTempOffset)
	    {
		m_FreeDiskOffsets.AddHead((void*)ulTempOffset);
	    }
	    
	    delete pChunk;
	    m_Chunks[ulWhichChunk] = NULL;
	}
    }
    
    return theErr;
}


/////////////////////////////////////////////////////////////////////////////
//
//	Method:
//
//		CChunkyRes::GetDiskUsage()
//
//	Purpose:
//
//		Returns the entire disk usage of a resource.
//
//	Parameters:
//
//		None.
//
//	Return:
//
//		ULONG32
//		Total amount of diskspace the resource's chunks consume.
//
ULONG32 CChunkyRes::GetDiskUsage() const
{
	return (m_Chunks.GetSize() * DEF_CHUNKYRES_CHUNK_SIZE);
}

BOOL CChunkyRes::HasPartialData(ULONG32 length, ULONG32 offset /* = 0 */)
{
	return (GetContiguousLength(offset) >= length);
}

ULONG32 CChunkyRes::GetContiguousLength(ULONG32 offset /* = 0 */)
{
	ULONG32 contiguousLength = 0;
	int ndx;

	int startNdx = offset / DEF_CHUNKYRES_CHUNK_SIZE;
	if (startNdx < m_Chunks.GetSize())
	{
		CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[startNdx];
		
		if (!pChunk) goto exit;
		
		contiguousLength = pChunk->GetValidLength(offset % DEF_CHUNKYRES_CHUNK_SIZE);
		
		if (contiguousLength != DEF_CHUNKYRES_CHUNK_SIZE - (offset % DEF_CHUNKYRES_CHUNK_SIZE))
		{
			goto exit;
		}
	}
	startNdx++;
	
	for (ndx = startNdx; ndx < m_Chunks.GetSize(); ndx++)
	{
		CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];

		// if there is no chunk then we are no longer contiguous.
		if (!pChunk)
		{
			break;
		}

		ULONG32 chunkLength = pChunk->GetValidLength();

		contiguousLength += chunkLength;

		// if this chunk is not the max length then we are no longer contiguous
		if (chunkLength < DEF_CHUNKYRES_CHUNK_SIZE)
		{
			break;
		}
	}

exit:
	return contiguousLength;
}

/////////////////////////////////////////////////////////////////////////////
//
//	Method:
//
//		CChunkyRes::GetData()
//
//	Purpose:
//
//		Gets a block of data out of a resource.
//
//	Parameters:
//
//		ULONG32 offset
//		char* buf
//		ULONG32 count
//		ULONG32* actual
//
//	Return:
//
//		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:
//

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?