csgo-2018-source/resourcefile/resourcestream.cpp

266 lines
6.5 KiB
C++
Raw Permalink Normal View History

2021-07-25 12:11:47 +08:00
//===== Copyright c 1996-2008, Valve Corporation, All rights reserved. ======//
//
// Purpose: Implementation of various variants of resource stream
//
// $NoKeywords: $
//===========================================================================//
#if defined( _WIN32 )
#include <windows.h>
#else
#include <sys/mman.h>
#endif
#include "resourcefile/resourcestream.h"
#include "dbg.h"
#include "memalloc.h"
CResourceStream::CResourceStream( )
{
m_pData = NULL;
m_nCommitted = 0;
m_nUsed = 0;
m_nAlignBits = 3;
m_nMaxAlignment = 4;
}
CResourceStreamVM::CResourceStreamVM( uint nReserveSize )
{
m_pData = NULL;
m_nReserved = 0;
ReserveVirtualMemory( nReserveSize );
m_nAlignBits = 3; // alignment 4 is default
m_nMaxAlignment = 4;
m_nUsed = 0;
m_nCommitted = 0;
}
CResourceStreamVM::~CResourceStreamVM( )
{
ReleaseVirtualMemory( );
}
CResourceStreamFixed::CResourceStreamFixed( void *pPreallocatedData, uint nPreallocatedDataSize ):
m_bOwnMemory( false )
{
m_pData = ( uint8* )pPreallocatedData;
m_nCommitted = nPreallocatedDataSize;
// pre-initialized stream; the memory passed to this function may not be initialized to zero, and resource stream assumes it to be, so clear it
Assert( pPreallocatedData == m_pData );
V_memset( pPreallocatedData, 0, nPreallocatedDataSize );
}
CResourceStreamFixed::CResourceStreamFixed( uint nPreallocatedDataSize ):
m_bOwnMemory( true )
{
m_pData = new uint8[ nPreallocatedDataSize ];
m_nCommitted = nPreallocatedDataSize;
// pre-initialized stream; the memory passed to this function may not be initialized to zero, and resource stream assumes it to be, so clear it
V_memset( m_pData, 0, nPreallocatedDataSize );
}
CResourceStreamFixed::~CResourceStreamFixed()
{
// prevent the parent class from trying to deallocate the buffer that doesn't belong to it
m_pData = NULL;
m_nCommitted = 0;
}
void CResourceStreamFixed::Commit( uint nNewCommit )
{
if ( nNewCommit > m_nCommitted )
{
Plat_FatalError( "CResourceStreamFixed: trying to write past the end of allocated memory (new commit %u, allocated %u)\n", nNewCommit, m_nCommitted );
}
}
CResourceStreamGrowable::CResourceStreamGrowable( uint nReserveDataSize )
{
m_pData = nReserveDataSize ? new uint8[ nReserveDataSize ] : NULL;
m_nCommitted = nReserveDataSize;
}
CResourceStreamGrowable::~CResourceStreamGrowable( )
{
delete[] m_pData;
}
void CResourceStreamGrowable::Commit( uint nNewCommit )
{
if ( nNewCommit > m_nCommitted )
{
uint nPaddedCommit = ( nNewCommit * 2 + 63 ) & ~63;
uint8 *pNewData = new uint8[ nPaddedCommit ];
V_memcpy( pNewData, m_pData, m_nUsed );
AssertDbg( !( ( ( uintp ) pNewData ) & 0xF ) );
delete[]m_pData;
m_pData = pNewData;
m_nCommitted = nPaddedCommit;
}
}
void CResourceStreamVM::ReserveVirtualMemory( uint nAddressSize )
{
Assert ( m_pData == NULL );
nAddressSize = ( nAddressSize + COMMIT_STEP - 1 ) & ~( COMMIT_STEP - 1 );
m_nReserved = MAX( nAddressSize, COMMIT_STEP );
for ( ;; )
{
#if defined( PLATFORM_WINDOWS )
m_pData = ( uint8* )VirtualAlloc( NULL, m_nReserved, MEM_RESERVE, PAGE_READWRITE );
#else
#ifdef PLATFORM_OSX
int nFlags = MAP_ANON | MAP_PRIVATE;
#else
int nFlags = MAP_ANONYMOUS | MAP_PRIVATE;
#endif
m_pData = ( uint8* )::mmap( NULL, m_nReserved, PROT_WRITE | PROT_READ, nFlags, -1, 0 );
#endif
if ( !m_pData )
{
m_nReserved /= 2;
if ( m_nReserved < COMMIT_STEP )
{
Plat_FatalError( "Cannot allocate virtual address space in CResourceStreamVM::ReserveVirtualMemory, error %u\n", errno );
}
}
else
{
return;
}
}
}
void CResourceStreamVM::ReleaseVirtualMemory()
{
if ( m_pData != NULL )
{
#if defined( PLATFORM_WINDOWS )
VirtualFree( m_pData, m_nReserved, MEM_RELEASE );
#else
munmap( m_pData, m_nReserved );
#endif
}
m_pData = NULL;
m_nReserved = 0;
m_nUsed = 0;
m_nCommitted = 0;
}
void CResourceStreamVM::CloneStream( CResourceStreamVM& copyFromStream )
{
// see if we need to reinitialize
if ( m_nReserved != copyFromStream.m_nReserved )
{
ReleaseVirtualMemory();
ReserveVirtualMemory( copyFromStream.m_nReserved );
}
else
{
if ( m_nUsed > 0)
{
memset( m_pData, 0, m_nUsed );
}
m_nUsed = 0;
}
Assert ( m_nReserved >= copyFromStream.m_nUsed );
memcpy ( AllocateBytes( copyFromStream.m_nUsed ), copyFromStream.m_pData, copyFromStream.m_nUsed );
m_nAlignBits = copyFromStream.m_nAlignBits;
m_nMaxAlignment = copyFromStream.m_nMaxAlignment;
}
void* CResourceStream::AllocateBytes( uint numBytes )
{
EnsureAvailable( numBytes );
void *pBlock = m_pData + m_nUsed;
m_nUsed += numBytes;
return pBlock;
}
void CResourceStreamVM::Commit( uint nNewCommit )
{
if ( nNewCommit > m_nReserved )
{
Warning( "--------------------WARNING----------------------\n" );
Warning( "--------------------WARNING----------------------\n" );
Warning( "--------------------WARNING----------------------\n" );
Warning( "-- Increase the size of the resource stream --\n" );
Warning( "-- Need %d bytes, but only have %d bytes\n", nNewCommit, m_nReserved );
Warning( "--------------------WARNING----------------------\n" );
Warning( "--------------------WARNING----------------------\n" );
Warning( "--------------------WARNING----------------------\n" );
Plat_FatalError( "Failed trying to Commit %u bytes, used %u bytes, reserved %u bytes\n", nNewCommit, m_nUsed, m_nReserved );
}
else if ( nNewCommit > m_nCommitted )
{
nNewCommit = ( nNewCommit + COMMIT_STEP - 1 ) & ~( COMMIT_STEP - 1 );
// expensive call. Not to be called frequently
#if defined( PLATFORM_WINDOWS )
VirtualAlloc( m_pData + m_nCommitted, nNewCommit - m_nCommitted, MEM_COMMIT, PAGE_READWRITE );
#else
mprotect( m_pData + m_nCommitted, nNewCommit - m_nCommitted, PROT_READ|PROT_WRITE );
#endif
m_nCommitted = nNewCommit;
}
}
void CResourceStream::Align( uint nAlignment, int nOffset )
{
m_nMaxAlignment = MAX( m_nMaxAlignment, nAlignment );
if ( uintp( m_pData ) & ( nAlignment - 1 ) )
{
Plat_FatalError( "You can't align data in resource stream more than its memory buffer. Please allocate the memory aligned." );
}
else if ( nAlignment & ( nAlignment - 1 ) )
{
Plat_FatalError( "Wrong alignment %d\n", nAlignment );
}
else
{
int padSize = ( nOffset - m_nUsed ) & ( nAlignment - 1 );
if ( padSize > 0 )
{
// Extend the buffer to align and fill the hole with a constant value
void *pBuffer = AllocateBytes( padSize );
V_memset( pBuffer, 0, padSize );
}
}
}
void CResourceStream::PrintStats()
{
Msg( "ResourceFile: %d/%d @%p align %u\n", m_nUsed, m_nCommitted, m_pData, m_nAlignBits );
}
void CResourceStream::ClearStats()
{
}