//===== Copyright c 1996-2008, Valve Corporation, All rights reserved. ======// // // Purpose: Implementation of various variants of resource stream // // $NoKeywords: $ //===========================================================================// #if defined( _WIN32 ) #include #else #include #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() { }