Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

470 lines
12 KiB

//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "datalinker.h"
#include "dbg.h"
#include "memalloc.h"
namespace DataLinker
{
const char g_cFillChar = '\xDE';
Stream::Stream()
{
m_data.reserve( 16 );
m_lastTarget = 1; // target 0 and 1 are special targets
m_alignBits = 3; // alignment 4 is default
m_size = 0;
m_stackBlocks.reserve( 4 ); // we'll probably never nest deeper than this
}
Chunk::Chunk( uint reserve, uint offset )
{
m_offset = offset;
m_capacity = reserve;
m_size = 0;
m_recentSize = 0;
m_data = ( byte* )MemAlloc_AllocAligned( reserve, 16 );
memset( m_data, g_cFillChar, reserve );
}
void Chunk::Free()
{
MemAlloc_FreeAligned( m_data );
}
Stream::~Stream()
{
for ( std::vector<Chunk>::iterator it = m_data.begin(); it != m_data.end(); ++it )
it->Free();
}
void* Stream::WriteBytes( uint numBytes )
{
EnsureAvailable( numBytes );
Chunk &chunk = m_data.back();
byte *p = chunk.m_data + chunk.m_size;
memset( p, 0, numBytes );
chunk.m_recentSize = m_size;
chunk.m_size += numBytes;
m_size += numBytes;
return p;
}
void Stream::EnsureAvailable( uint addCapacity )
{
Assert( addCapacity < 0x10000000 ); // we don't support >1Gb of data yet
if ( m_data.empty() )
{
// first allocation
m_data.push_back( Chunk( MAX( addCapacity*4u, 4096u ), 0 ) );
}
else if ( m_data.back().GetAvailable() < addCapacity )
{
m_data.push_back( Chunk( MAX( addCapacity*4u, m_data.back().m_capacity*2u ), m_size ) );
}
}
int Stream::GetOffsetTo( const void *ptr )const
{
for ( std::vector<Chunk>::const_reverse_iterator it = m_data.rbegin(); it != m_data.rend(); ++it )
{
uint offsetOfPtr = ( uint )( ( ( const byte* )ptr ) - it->m_data );
if ( offsetOfPtr <= it->m_size )
return offsetOfPtr + it->m_offset;
}
return -1; // invalid offset
}
bool Stream::Compile( void *pBuffer )
{
{
byte *p = ( byte* )pBuffer;
uint offset = 0;
for ( std::vector<Chunk>::const_iterator it = m_data.begin(); it != m_data.end(); ++it )
{
Assert( it->m_offset == offset );
memcpy( p + offset, it->m_data, it->m_size );
offset += it->m_size;
}
Assert( m_size == offset );
}
byte *data = ( byte* )pBuffer;
// for all link sources, try to resolve them
uint numUnresolved = 0;
for ( LinkSourceMap::const_iterator it = m_mapSources.begin(); it != m_mapSources.end(); ++it )
{
const LinkSourceDesc_t &source = it->second;
int offsetOfSource = it->first;
Assert( uint( offsetOfSource ) < m_size ); // check alignment?
int* pOffset = ( int* )( data + offsetOfSource );
if ( source.m_targetId == kSpecialTargetNull )
{
*pOffset = 0; // special case of target fixed to NULL
}
else if ( source.m_targetId < 0 || source.m_targetId > m_lastTarget )
{
Msg( "Unresolved link source @0x%X '%s'\n", offsetOfSource, source.m_pDescription );
numUnresolved ++;
}
else
{
// try to find the target
// if special target (id == 0): target is not in the target map; all the information is resolved directly through the LinkSourceDesc_t
const LinkTargetDesc_t &target = source.m_targetId == kSpecialTargetDefault ? source.m_defaultTarget : m_mapTargets[source.m_targetId];
switch ( target.m_type )
{
case kTargetNull:
*pOffset = 0; // special case of target set to NULL later
break;
case kTargetResolved:
switch ( source.m_type )
{
case kOtRelative32bit:
*pOffset = target.m_resolvedOffset - offsetOfSource;
break;
case kOtRelative16bit:
{
int offset = target.m_resolvedOffset - offsetOfSource;
if ( offset > 0x7FFF || offset < -0x8000 )
{
Msg( "Link source @%d '%s' is out of range (%d doesn't fit to 16 bits)\n", offsetOfSource, source.m_pDescription, offset );
numUnresolved++;
}
else
{
*( short* )pOffset = ( short )offset;
}
}
break;
case kOtAbsolute32bit:
*pOffset = target.m_resolvedOffset;
break;
}
break;
default:
Msg( "Unresolved link source @%d '%s'\n", offsetOfSource, source.m_pDescription );
numUnresolved++;
break;
}
}
}
if ( numUnresolved )
{
Error( "%u unresolved links found\n", numUnresolved );
return false;
}
else
return true;
}
void Stream::Align( uint nAlignment, int nOffset )
{
if ( nAlignment & ( nAlignment - 1 ) )
{
Error( "Wrong alignment %d\n", nAlignment );
}
//WriteBytes(((m_size+nAlignment-1) & ~(nAlignment-1)) - m_size);
WriteBytes( ( nOffset - m_size ) & ( nAlignment - 1 ) );
}
void Stream::Link( LinkSource_t linkSource, LinkTarget_t linkTarget, const char *szDescription )
{
LinkSourceMap::iterator itFind = m_mapSources.find( linkSource.m_offset );
if ( itFind == m_mapSources.end() )
{
Error( "cannot find source @%d - %s\n", linkSource.m_offset, szDescription );
}
itFind->second.m_targetId = linkTarget.m_id;
itFind->second.m_defaultTarget.m_type = kTargetUnresolved; // un-set default target for validation; we are not referring to it any more
Assert( linkTarget.m_id != kSpecialTargetDefault );
}
LinkSource_t Stream::LinkToHere( int *pOffset, const char *szDescription )
{
return Link( pOffset, NewTargetHere(), szDescription );
}
LinkSource_t Stream::Link( int32 *pOffset, LinkTarget_t linkTarget, const char *szDescription )
{
int offsetToOffset = GetOffsetTo( pOffset );
LinkSource_t linkSource = NewLinkSource( offsetToOffset, kOtRelative32bit, linkTarget.m_id, szDescription );
return linkSource;
}
void Stream::Link( int32 *pOffset, const void *pTarget )
{
if ( !pTarget )
{
*pOffset = 0;
return;
}
int offsetToOffset = GetOffsetTo( pOffset );
int offsetToTarget = GetOffsetTo( pTarget );
if ( offsetToOffset < 0 || offsetToTarget < 0 )
{
Error( "Link(%p,%p) can't find offsets (%d,%d)\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
}
if ( offsetToOffset == offsetToTarget )
{
Warning( "Link(%p,%p) is self-referencing (%d==%d), this is prohibited by this kind of offset. It'll be interpreted as Null offset at runtime\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
}
*pOffset = offsetToTarget - offsetToOffset;
}
void Stream::Link( int16 *pOffset, const void *pTarget )
{
if ( !pTarget )
{
*pOffset = 0;
return;
}
int offsetToOffset = GetOffsetTo( pOffset );
int offsetToTarget = GetOffsetTo( pTarget );
if ( offsetToOffset < 0 || offsetToTarget < 0 )
{
Error( "Link(%p,%p) can't find offsets (%d,%d)\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
}
int offset = offsetToTarget - offsetToOffset;
int16 offset16 = ( int16 )offset;
if ( offset != ( int )offset16 )
{
Error( "Link16(%p,%p) offsets are too far (%d,%d)\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
}
*pOffset = offset16;
}
LinkSource_t Stream::NewOffset( int *pOffset, const char *szDescription )
{
return NewLinkSource( GetOffsetTo( pOffset ), kOtRelative32bit, kSpecialTargetUndefined, szDescription );
}
LinkSource_t Stream::NewLinkSource( int offsetToOffset, OffsetTypeEnum type, int linkTargetId, const char *szDescription )
{
Assert( !( offsetToOffset & 3 ) );
if ( offsetToOffset < 0 && offsetToOffset + sizeof( int ) > m_size )
{
Error( "Wrong offset spec. Most probably you called an extra Write() before calling WriteOffset() - %s\n", szDescription );
}
LinkSource_t linkSource;
linkSource.m_offset = offsetToOffset;
LinkSourceDesc_t &lsd = m_mapSources[offsetToOffset];
lsd.m_targetId = linkTargetId;
lsd.m_type = type;
lsd.m_pDescription = szDescription;
return linkSource;
}
LinkSource_t Stream::WriteOffset( const char *szDescription )
{
return WriteOffset( LinkTarget_t(), szDescription );
}
LinkSource_t Stream::WriteOffset( LinkTarget_t linkTarget, const char *szDescription )
{
int offsetToOffset = m_size;
Write<int32>();
return NewLinkSource( offsetToOffset, kOtRelative32bit, linkTarget.m_id, szDescription );
}
LinkTarget_t Stream::NewTarget()// create new, unresolved target
{
LinkTarget_t linkTarget;
linkTarget.m_id = ++m_lastTarget; // shouldn't be 0 or -1
return linkTarget;
}
LinkTarget_t Stream::NewTarget( void *pWhere )
{
int offsetToTarget = GetOffsetTo( pWhere );
if ( offsetToTarget < 0 )
Error( "Invalid new target @%p\n", pWhere );
LinkTarget_t linkTarget = NewTarget();
SetTarget( linkTarget, offsetToTarget );
return linkTarget;
}
LinkTarget_t Stream::NewTargetHere() // creates a target right here
{
LinkTarget_t linkTarget = NewTarget();
SetTargetHere( linkTarget );
return linkTarget;
}
void Stream::SetTarget( LinkTarget_t linkTarget, int offsetToTarget ) // sets the given target to point to offsetToTarget
{
LinkTargetDesc_t &target = m_mapTargets[linkTarget.m_id];
target.m_type = kTargetResolved;
target.m_resolvedOffset = offsetToTarget;
}
void Stream::SetTargetHere( LinkTarget_t linkTarget ) // sets the given target to point to right here
{
if ( linkTarget.m_id > 0 && linkTarget.m_id <= m_lastTarget )
{
SetTarget( linkTarget, m_size );
}
else
{
Warning( "Trying to set invalid target %d to here (absolute offset %d)\n", linkTarget.m_id, m_size );
}
}
void Stream::SetTargetNull( LinkTarget_t linkTarget ) // set this target to point to NULL
{
if ( linkTarget.m_id > 0 && linkTarget.m_id <= m_lastTarget )
{
m_mapTargets[linkTarget.m_id].m_type = kTargetNull;
}
else
{
Warning( "Trying to set invalid target %d to null\n", linkTarget.m_id );
}
}
bool Stream::IsDeclared( LinkTarget_t linkTarget )const
{
return m_mapTargets.find( linkTarget.m_id ) != m_mapTargets.end();
}
bool Stream::IsSet( LinkTarget_t linkTarget )const
{
LinkTargetMap::const_iterator itFind = m_mapTargets.find( linkTarget.m_id );
if ( itFind == m_mapTargets.end() )
return false; // it can't be set when it hasn't been declared
return itFind->second.m_resolvedOffset >= 0;
}
bool Stream::IsDefined( LinkSource_t linkSource )const
{
LinkSourceMap::const_iterator itFind = m_mapSources.find( linkSource.m_offset );
return itFind != m_mapSources.end();
}
bool Stream::IsLinked( LinkSource_t linkSource )const
{
LinkSourceMap::const_iterator itFind = m_mapSources.find( linkSource.m_offset );
if ( itFind == m_mapSources.end() )
return false;
return itFind->second.m_targetId >= 0;
}
void Stream::Begin( const char *szName, uint flags )
{
Block_t newBlock;
if ( !m_stackBlocks.empty() )
newBlock.m_name = m_stackBlocks.back().m_name + ":" + szName;
else
newBlock.m_name = szName;
newBlock.m_tell = Tell();
m_stackBlocks.push_back( newBlock );
}
void Stream::End()
{
if ( !m_stackBlocks.empty() )
{
const Block_t &curBlock = m_stackBlocks.back();
m_mapBlockStats[curBlock.m_name].m_size += Tell() - curBlock.m_tell;
m_stackBlocks.pop_back();
}
}
void Stream::PrintStats()
{
for ( BlockStatsMap::const_iterator it = m_mapBlockStats.begin(); it != m_mapBlockStats.end(); ++it )
Msg( "%-60s %7.1f KiB\n", it->first.c_str(), it->second.m_size / 1024. );
Msg( "%-60s -----------\n", "" );
Msg( "%-60s %7.1f KiB\n", "DataLinker total stream size", m_size / 1024. );
}
void Stream::ClearStats()
{
m_mapBlockStats.clear();
}
const char* Stream::WriteAndLinkString( Offset_t<char> *pOffset, const char *pString )
{
if ( pString )
{
StringTableElement_t &ste = m_stringTable[pString];
if ( !ste.m_pString )
{
int nLen = Q_strlen( pString );
ste.m_nOffset = Tell();
ste.m_pString = ( char* )this->WriteBytes( nLen + 1 );
Q_memcpy( ste.m_pString, pString, nLen + 1 );
}
Assert( !Q_strcmp( ste.m_pString, pString ) );
int offsetToOffset = GetOffsetTo( pOffset );
pOffset->offset = ste.m_nOffset - offsetToOffset;
return ste.m_pString;
}
else
{
pOffset->offset = 0; // make the offset NULL
return NULL;
}
}
}