967 lines
22 KiB
C++
967 lines
22 KiB
C++
//===== Copyright © Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: Resource Stream is conceptually memory file where you write the data
|
|
// that you want to live both on disk and in memory. The constraint then is
|
|
// that the data must be movable in memory, since you don't know in advance,
|
|
// when you're creating the data, what will be its start address once it gets
|
|
// loaded. To accomplish this, we use CResourcePointer and CResourceArray
|
|
// in places of pointers and dynamic arrays. They store offsets rather than
|
|
// absolute addresses of the referenced objects, thus they are moveable.
|
|
// On Intel, the available addressing modes make the access to CResourcePointer
|
|
// as fast as pointer dereferencing in most cases. On architectures with
|
|
// rigid addressing, adding the base address is still virtually free.
|
|
//
|
|
// $NoKeywords: $
|
|
//===========================================================================//
|
|
|
|
#include "resourcefile/schema.h"
|
|
|
|
#ifndef RESOURCESTREAM_H
|
|
#define RESOURCESTREAM_H
|
|
|
|
|
|
#ifdef COMPILER_MSVC
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "tier0/platform.h"
|
|
#include "tier0/basetypes.h"
|
|
#include "tier0/dbg.h"
|
|
#include "tier1/strtools.h"
|
|
|
|
class CResourceStream;
|
|
|
|
|
|
inline byte *ResolveOffset( const int32 *pOffset )
|
|
{
|
|
int offset = *pOffset;
|
|
return offset ? ( ( byte* )pOffset ) + offset : NULL;
|
|
}
|
|
|
|
inline byte *ResolveOffsetFast( const int32 *pOffset )
|
|
{
|
|
int offset = *pOffset;
|
|
AssertDbg( offset != 0 );
|
|
return ( ( byte* )pOffset ) + offset;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
class CLockedResource
|
|
{
|
|
private:
|
|
T *m_pData; // data; may be const
|
|
uint m_nCount; // number of allocated data elements
|
|
//uint m_nStride; // normally sizeof(T), but may be not
|
|
//uint m_nClassCRC;
|
|
public:
|
|
CLockedResource(): m_pData( NULL ), m_nCount( 0 ) {}
|
|
CLockedResource( T *pData, uint nCount ): m_pData( pData ), m_nCount( nCount ) {}
|
|
|
|
// emulates pointer arithmetics
|
|
CLockedResource<T> operator + ( int nOffset ) { Assert( m_nCount <= (uint)nOffset); return CLockedResource<T>( m_pData + nOffset, m_nCount - (uint)nOffset ); }
|
|
|
|
operator const T* ()const { return m_pData; }
|
|
operator T* () { return m_pData; }
|
|
|
|
T* operator ->() { return m_pData; }
|
|
const T* operator ->() const { return m_pData; }
|
|
|
|
uint Count()const {return m_nCount;}
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
class CUnlockedResource
|
|
{
|
|
private:
|
|
CResourceStream *m_pStream; // data; may be const
|
|
uint32 m_nOffset;
|
|
uint32 m_nCount; // number of allocated data elements
|
|
//uint m_nStride; // normally sizeof(T), but may be not
|
|
//uint m_nClassCRC;
|
|
public:
|
|
CUnlockedResource( ): m_pStream( NULL ), m_nOffset( 0 ), m_nCount( 0 ) {}
|
|
CUnlockedResource( CResourceStream *pStream, T *pData, uint nCount );
|
|
bool IsValid( )const { return m_pStream != NULL; }
|
|
void Reset( ) { m_pStream = NULL; }
|
|
|
|
// emulates pointer arithmetics
|
|
CUnlockedResource<T> operator + ( int nOffset )
|
|
{
|
|
Assert( m_nCount <= ( uint ) nOffset );
|
|
return CUnlockedResource<T>( m_pStream, GetPtr() + nOffset, m_nCount - ( uint ) nOffset );
|
|
}
|
|
|
|
operator const T* ( )const { return GetPtr(); }
|
|
operator T* ( ) { return GetPtr(); }
|
|
|
|
T* operator ->( ) { return GetPtr(); }
|
|
const T* operator ->( ) const { return GetPtr(); }
|
|
|
|
uint Count( )const { return m_nCount; }
|
|
T* GetPtr( );
|
|
const T* GetPtr( )const;
|
|
};
|
|
|
|
|
|
|
|
|
|
// AT RUN-TIME ONLY the offset converts automatically into pointers to the appropritate type
|
|
// at tool-time, you should use LinkSource_t and LinkTarget_t
|
|
class CResourcePointerBase
|
|
{
|
|
protected:
|
|
int32 m_nOffset;
|
|
public:
|
|
CResourcePointerBase() : m_nOffset( 0 ) {}
|
|
|
|
bool operator == ( int zero )const
|
|
{
|
|
AssertDbg( zero == 0 );
|
|
return m_nOffset == zero;
|
|
}
|
|
|
|
bool IsNull()const
|
|
{
|
|
return m_nOffset == 0;
|
|
}
|
|
|
|
int32 NotNull()const
|
|
{
|
|
return m_nOffset;
|
|
}
|
|
|
|
int32 GetOffset() const
|
|
{
|
|
return m_nOffset;
|
|
}
|
|
|
|
const byte* GetUncheckedRawPtr() const
|
|
{
|
|
// assumes non-null; returns garbage if this is a null pointer
|
|
return ResolveOffsetFast( &m_nOffset );
|
|
}
|
|
|
|
byte* GetUncheckedRawPtr()
|
|
{
|
|
// assumes non-null; returns garbage if this is a null pointer
|
|
return ResolveOffsetFast( &m_nOffset );
|
|
}
|
|
|
|
const byte* GetRawPtr() const
|
|
{
|
|
return ResolveOffset( &m_nOffset );
|
|
}
|
|
|
|
byte* GetRawPtr()
|
|
{
|
|
return ResolveOffset( &m_nOffset );
|
|
}
|
|
|
|
void SetRawPtr( const void* p )
|
|
{
|
|
if ( p == NULL )
|
|
{
|
|
m_nOffset = 0;
|
|
}
|
|
else
|
|
{
|
|
intp nOffset = ( intp )p - ( intp )&m_nOffset;
|
|
m_nOffset = ( int32 )nOffset;
|
|
AssertDbg( m_nOffset == nOffset );
|
|
}
|
|
}
|
|
|
|
void SetNull()
|
|
{
|
|
m_nOffset = 0;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
class CResourcePointer: public CResourcePointerBase
|
|
{
|
|
public:
|
|
FORCEINLINE const T* GetUncheckedPtr() const
|
|
{
|
|
// assumes non-null; returns garbage if this is a null pointer
|
|
AssertDbg( m_nOffset != 0 );
|
|
byte *ptr = ResolveOffsetFast( &m_nOffset );
|
|
return ( const T* )ptr;
|
|
}
|
|
|
|
FORCEINLINE const T* GetPtr() const
|
|
{
|
|
byte *ptr = ResolveOffset( &m_nOffset );
|
|
return ( const T* )ptr;
|
|
}
|
|
|
|
FORCEINLINE operator const T*() const
|
|
{
|
|
return GetPtr();
|
|
}
|
|
|
|
FORCEINLINE const T* operator->() const
|
|
{
|
|
return GetPtr();
|
|
}
|
|
|
|
void operator = ( const T* pT )
|
|
{
|
|
SetRawPtr( pT );
|
|
}
|
|
|
|
void SetPtr( const T* pT )
|
|
{
|
|
SetRawPtr( pT );
|
|
}
|
|
|
|
// FIXME: Should these be in a 'CResourceWritablePointer' subclass?
|
|
// There are plenty of cases where we know that a CResourcePointer should be read-only
|
|
|
|
public:
|
|
FORCEINLINE T* GetUncheckedPtr()
|
|
{
|
|
// assumes non-null; returns garbage if this is a null pointer
|
|
Assert( m_nOffset != 0 );
|
|
byte *ptr = ResolveOffsetFast( &m_nOffset );
|
|
return ( T* )ptr;
|
|
}
|
|
|
|
FORCEINLINE T* GetPtr()
|
|
{
|
|
byte *ptr = ResolveOffset( &m_nOffset );
|
|
return ( T* )ptr;
|
|
}
|
|
|
|
FORCEINLINE operator T*()
|
|
{
|
|
return GetPtr();
|
|
}
|
|
|
|
FORCEINLINE T* operator->()
|
|
{
|
|
return GetPtr();
|
|
}
|
|
|
|
public:
|
|
void Unsafe_OutOfBoundsAllocate()
|
|
{
|
|
SetPtr( new T() );
|
|
}
|
|
|
|
void Unsafe_OutOfBoundsFree()
|
|
{
|
|
delete GetPtr();
|
|
}
|
|
|
|
void SwapBytes()
|
|
{
|
|
m_nOffset = DWordSwapC( m_nOffset );
|
|
}
|
|
};
|
|
|
|
// never construct this - only the Resource stream may construct or load data that contains ResourceArray or ResourcePointer; use LockedResource or naked pointer or CUtlBuffer or something like that instead
|
|
|
|
class CResourceArrayBase
|
|
{
|
|
public:
|
|
CResourceArrayBase()
|
|
{
|
|
m_nOffset = 0;
|
|
m_nCount = 0;
|
|
}
|
|
|
|
bool operator == ( int zero )const
|
|
{
|
|
AssertDbg( zero == 0 );
|
|
return m_nOffset == zero;
|
|
}
|
|
|
|
bool IsNull()const
|
|
{
|
|
return m_nOffset == 0;
|
|
}
|
|
|
|
int32 NotNull()const
|
|
{
|
|
return m_nOffset;
|
|
}
|
|
|
|
const byte* GetRawPtr() const
|
|
{
|
|
// validate
|
|
return ResolveOffset( &m_nOffset );
|
|
}
|
|
|
|
byte* GetRawPtr()
|
|
{
|
|
// validate
|
|
return ResolveOffset( &m_nOffset );
|
|
}
|
|
|
|
int Count() const
|
|
{
|
|
return m_nCount;
|
|
}
|
|
|
|
void WriteDirect( int nCount, void *pData )
|
|
{
|
|
if ( pData == NULL )
|
|
{
|
|
AssertDbg( nCount == 0 );
|
|
m_nOffset = 0;
|
|
m_nCount = 0;
|
|
}
|
|
else
|
|
{
|
|
m_nOffset = ((intp)pData) - (intp)&m_nOffset;
|
|
m_nCount = nCount;
|
|
}
|
|
}
|
|
|
|
void SwapMemberBytes()
|
|
{
|
|
m_nCount = DWordSwapC( m_nCount );
|
|
m_nOffset = DWordSwapC( m_nOffset );
|
|
}
|
|
|
|
private:
|
|
CResourceArrayBase(const CResourceArrayBase&rThat ){} // private: we don't want to recompute offsets every time we copy a structure
|
|
protected:
|
|
int32 m_nOffset;
|
|
uint32 m_nCount;
|
|
};
|
|
|
|
template < typename T >
|
|
class CResourceArray: public CResourceArrayBase
|
|
{
|
|
public:
|
|
CResourceArray(): CResourceArrayBase() {}
|
|
|
|
private:
|
|
CResourceArray( const CResourceArray<T>&rThat ){} // private: we don't want to recompute offsets every time we copy a structure
|
|
public:
|
|
const T& operator []( int nIndex ) const
|
|
{
|
|
AssertDbg( (uint)nIndex < m_nCount );
|
|
return this->GetPtr()[nIndex];
|
|
}
|
|
|
|
T& operator []( int nIndex )
|
|
{
|
|
AssertDbg( (uint)nIndex < m_nCount );
|
|
return this->GetPtr()[nIndex];
|
|
}
|
|
|
|
const T& Element( int nIndex ) const
|
|
{
|
|
AssertDbg( (uint)nIndex < m_nCount );
|
|
return this->GetPtr()[nIndex];
|
|
}
|
|
|
|
T& Element( int nIndex )
|
|
{
|
|
AssertDbg( (uint)nIndex < m_nCount );
|
|
return this->GetPtr()[nIndex];
|
|
}
|
|
|
|
CResourceArray<T>& operator = ( const CLockedResource<T> & lockedResource )
|
|
{
|
|
m_nOffset = ( lockedResource.Count() ) ? ( ((intp)(const T*)lockedResource) - (intp)&m_nOffset ) : 0;
|
|
m_nCount = lockedResource.Count();
|
|
return *this;
|
|
}
|
|
|
|
CResourceArray<T>& operator = ( const CResourceArray<T> & that )
|
|
{
|
|
m_nOffset = ( that.Count() ) ? ( ((intp)(const T*)that.GetPtr()) - (intp)&m_nOffset ) : 0;
|
|
m_nCount = that.Count();
|
|
return *this;
|
|
}
|
|
|
|
const T* GetPtr() const
|
|
{
|
|
return ( const T* )GetRawPtr();
|
|
}
|
|
|
|
T* GetPtr()
|
|
{
|
|
return ( T* )GetRawPtr();
|
|
}
|
|
|
|
const T* Base() const
|
|
{
|
|
return ( const T* )GetRawPtr();
|
|
}
|
|
|
|
T* Base()
|
|
{
|
|
return ( T* )GetRawPtr();
|
|
}
|
|
|
|
bool IsEmpty() const
|
|
{
|
|
return m_nCount == 0;
|
|
}
|
|
|
|
operator CLockedResource<T> () {return CLockedResource<T>( GetPtr(), Count() ) ; }
|
|
|
|
// Temporary functions
|
|
|
|
void Unsafe_OutOfBoundsAllocate( int nCount )
|
|
{
|
|
const T* pAlloc = new T[nCount];
|
|
m_nOffset = ((intp)(const T*)pAlloc) - (intp)&m_nOffset;
|
|
m_nCount = nCount;
|
|
}
|
|
|
|
void Unsafe_OutOfBoundsPurgeAndFreeRPs()
|
|
{
|
|
for ( uint32 i = 0; i < m_nCount; ++i )
|
|
{
|
|
Element(i).Unsafe_OutOfBoundsFree();
|
|
}
|
|
|
|
delete[] GetPtr();
|
|
}
|
|
|
|
// Remove all the NULL pointers from this resource array and shorten it (without changing any allocations)
|
|
// (assumes that the elements of the array can be equality-tested with NULL)
|
|
inline void CoalescePointerArrayInPlace()
|
|
{
|
|
int nValidElements = 0;
|
|
int nArrayLen = Count();
|
|
T* pBase = GetPtr();
|
|
|
|
for ( int i = 0; i < nArrayLen; ++i )
|
|
{
|
|
if ( pBase[i] == 0 )
|
|
continue;
|
|
|
|
if ( nValidElements != i )
|
|
{
|
|
pBase[nValidElements].SetRawPtr( pBase[i].GetRawPtr() );
|
|
}
|
|
|
|
nValidElements++;
|
|
}
|
|
|
|
m_nCount = nValidElements;
|
|
}
|
|
|
|
// Enable support for range-based loops in C++ 11
|
|
inline T* begin()
|
|
{
|
|
return GetPtr();
|
|
}
|
|
|
|
inline const T* begin() const
|
|
{
|
|
return GetPtr( );
|
|
}
|
|
|
|
inline T* end()
|
|
{
|
|
return GetPtr() + m_nCount;
|
|
}
|
|
|
|
inline const T* end() const
|
|
{
|
|
return GetPtr( ) + m_nCount;
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// this class may be useful at runtime to use fast serialize interface (without data linking)
|
|
//
|
|
class CResourceStream
|
|
{
|
|
public:
|
|
// Constructor, destructor
|
|
CResourceStream( );
|
|
virtual ~CResourceStream( ) {} // make sure to implement proper cleanup in derived classes
|
|
|
|
// Methods used to allocate space in the stream
|
|
// just use Allocate<uint8>(100) to simply allocate 100 bytes of crap
|
|
void *AllocateBytes( uint nCount );
|
|
template <typename T> CLockedResource<T> Allocate( uint count = 1 );
|
|
template <typename T> CUnlockedResource<T> AllocateUnaligned( uint count = 1 );
|
|
|
|
// Methods that write data into the stream
|
|
template <typename T> CLockedResource<T> Write( const T &x );
|
|
template <typename T> CUnlockedResource<T> WriteUnaligned( const T &x );
|
|
CLockedResource<float> WriteFloat( float x );
|
|
CLockedResource<uint64> WriteU64( uint64 x );
|
|
CLockedResource<uint32> WriteU32( uint32 x );
|
|
CLockedResource<uint16> WriteU16( uint16 x );
|
|
CLockedResource<uint8> WriteByte( byte x );
|
|
CLockedResource<char> WriteString( const char *pString );
|
|
CLockedResource<char> WriteStringMaxLen( const char *pString, int nMaxLen ); // will never read beyond pString[nMaxLen-1] and will force-null-terminate if necessary
|
|
|
|
// Methods to force alignment of the next data written into the stream
|
|
void Align( uint nAlignment, int nOffset = 0 );
|
|
void AlignPointer();
|
|
|
|
// How much data have we written into the stream?
|
|
uint32 Tell() const;
|
|
uint32 Tell( const void * pPast )const;
|
|
const void *TellPtr() const;
|
|
void Rollback( uint32 nPreviousTell );
|
|
void Rollback( const void *pPreviousTell );
|
|
|
|
void* Compile( );
|
|
template <class Memory>
|
|
void* CompileToMemory( Memory &memory );
|
|
uint GetTotalSize() const;
|
|
void Barrier() {} // no pointers crossing barriers allowed! all pointers are invalidated
|
|
void PrintStats();
|
|
void ClearStats();
|
|
|
|
// This is the max alignment ever used within the stream
|
|
uint GetStreamAlignment() const;
|
|
|
|
void* GetDataPtr( uint Offset = 0 );
|
|
template <typename T> T* GetDataPtrTypeAligned( uint Offset );
|
|
|
|
void Clear();
|
|
|
|
protected:
|
|
void EnsureAvailable( uint nAddCapacity )
|
|
{
|
|
Assert( nAddCapacity < 0x40000000 ); // we don't support >1Gb of data yet
|
|
uint nNewCommit = m_nUsed + nAddCapacity;
|
|
if ( nNewCommit > m_nCommitted )
|
|
{
|
|
Commit( nNewCommit ); // Commit is only called when necessary, as it may be expensive
|
|
}
|
|
}
|
|
// the only logic entry the derived class needs to reimplement, besides constructor+destructor pair.
|
|
// depending on how expensive it is to call this function, it may commit (allocate) large chunks of extra memory, that's totally ok
|
|
virtual void Commit( uint nNewCommit ) = 0;
|
|
|
|
template <typename T> friend class CUnlockedResource;
|
|
|
|
protected:
|
|
uint8 *m_pData; // the reserved virtual address space address; or just the start of allocated block of memory if virtual memory is not being used
|
|
|
|
uint m_nCommitted; // how much memory was already committed (or allocated, in case no virtual memory is being used)
|
|
uint m_nUsed; // this is the amount of currently used/allocated data, in bytes; may be unaligned
|
|
|
|
uint m_nAlignBits; // the ( max alignment - 1 ) of the current block of data
|
|
uint m_nMaxAlignment;
|
|
};
|
|
|
|
|
|
class CResourceStreamVM: public CResourceStream
|
|
{
|
|
public:
|
|
// Constructor, destructor
|
|
CResourceStreamVM( uint nReserveSize = 16 * 1024 * 1024 );
|
|
virtual ~CResourceStreamVM( ) OVERRIDE;
|
|
void CloneStream( CResourceStreamVM& copyFromStream );
|
|
protected:
|
|
virtual void Commit( uint nNewCommit ) OVERRIDE;
|
|
void ReserveVirtualMemory( uint nAddressSize );
|
|
void ReleaseVirtualMemory( );
|
|
enum
|
|
{
|
|
COMMIT_STEP = 64 * 1024
|
|
};
|
|
protected:
|
|
uint m_nReserved; // the reserved virtual address space for the block of data
|
|
};
|
|
|
|
|
|
// use this class to create resources efficiently in runtime using your own allocator
|
|
// supply a fixed buffer; it belongs to the caller
|
|
class CResourceStreamFixed: public CResourceStream
|
|
{
|
|
private:
|
|
bool m_bOwnMemory;
|
|
public:
|
|
CResourceStreamFixed( uint nPreallocateDataSize );// NOTE: the preallocated data is Zeroed in this constructor
|
|
CResourceStreamFixed( void *pPreallocatedData, uint nPreallocatedDataSize ); // NOTE: the preallocated data is Zeroed in this constructor
|
|
virtual ~CResourceStreamFixed() OVERRIDE;
|
|
|
|
int GetSlack(); // remaining bytes
|
|
virtual void Commit( uint nNewCommit );
|
|
};
|
|
|
|
class CResourceStreamGrowable: public CResourceStream
|
|
{
|
|
public:
|
|
CResourceStreamGrowable( uint nReserveDataSize );
|
|
~CResourceStreamGrowable( );
|
|
|
|
virtual void Commit( uint nNewCommit );
|
|
uint8 *Detach( )
|
|
{
|
|
uint8 *pData = m_pData;
|
|
m_pData = 0;
|
|
m_nCommitted = 0;
|
|
m_nUsed = 0;
|
|
m_pData = NULL;
|
|
return pData;
|
|
}
|
|
};
|
|
|
|
|
|
class CLockedResourceAutoAggregator
|
|
{
|
|
protected:
|
|
CResourceStreamVM *m_pStream;
|
|
const void * m_pTellStart;
|
|
uint m_nTellStart;
|
|
public:
|
|
CLockedResourceAutoAggregator( CResourceStreamVM *pStream, const void * pTellStart )
|
|
{
|
|
m_pStream = pStream;
|
|
m_pTellStart = pTellStart;
|
|
m_nTellStart = pStream->Tell( pTellStart );
|
|
}
|
|
|
|
CLockedResource<uint8> GetAggregate()
|
|
{
|
|
return CLockedResource<uint8>( ( uint8* )m_pTellStart, m_pStream->Tell() - m_nTellStart );
|
|
}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Specialization for strings
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class CResourceString: public CResourcePointer<char>
|
|
{
|
|
public:
|
|
CResourceString& operator = ( const CLockedResource<char> & lockedCharResource )
|
|
{
|
|
SetPtr( (const char*)lockedCharResource );
|
|
return *this;
|
|
}
|
|
|
|
CResourceString& operator = ( const CResourcePointerBase & lockedCharResource )
|
|
{
|
|
SetPtr( ( const char* )lockedCharResource.GetRawPtr() );
|
|
return *this;
|
|
}
|
|
|
|
// empty strings can be serialized as a null pointer (instead of a pointer to '\0')
|
|
|
|
const char* GetPtr() const
|
|
{
|
|
if ( GetRawPtr() == NULL )
|
|
{
|
|
return "";
|
|
}
|
|
else
|
|
{
|
|
return ( const char* )GetRawPtr();
|
|
}
|
|
}
|
|
|
|
char* GetPtr()
|
|
{
|
|
if ( GetRawPtr() == NULL )
|
|
{
|
|
return ( char* )"";
|
|
}
|
|
else
|
|
{
|
|
return ( char* )GetRawPtr();
|
|
}
|
|
}
|
|
|
|
operator const char* ()const
|
|
{
|
|
if ( GetRawPtr() == NULL )
|
|
{
|
|
return "";
|
|
}
|
|
else
|
|
{
|
|
return ( char* )GetRawPtr();
|
|
}
|
|
}
|
|
|
|
void Unsafe_OutOfBoundsCopy( const char* pStr )
|
|
{
|
|
int nLen = V_strlen(pStr);
|
|
char* pAlloc = new char[nLen+1];
|
|
V_strcpy( pAlloc, pStr );
|
|
SetPtr( pAlloc );
|
|
}
|
|
|
|
bool IsEmpty() const
|
|
{
|
|
return IsNull() || !*GetUncheckedRawPtr();
|
|
}
|
|
|
|
|
|
private:
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// Disable comparison operators for ResourceStrings, otherwise they'll
|
|
// be implicitly converted to char* (we don't want to actually implement
|
|
// these because it's ambiguous whether it's case-sensitive or not.
|
|
bool operator==( const CResourceString &rhs ) const;
|
|
bool operator!=( const CResourceString &rhs ) const;
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Inline methods
|
|
//-----------------------------------------------------------------------------
|
|
template <typename T>
|
|
inline CLockedResource<T> CResourceStream::Allocate( uint nCount )
|
|
{
|
|
// TODO: insert reflection code here
|
|
uint nAlignment = VALIGNOF( T );
|
|
Align( nAlignment );
|
|
T *ptr = AllocateUnaligned< T >( nCount );
|
|
// Construct
|
|
for ( uint i = 0; i < nCount; i++ )
|
|
{
|
|
Construct< T >( &ptr[ i ] );
|
|
}
|
|
|
|
return CLockedResource<T>( ptr, nCount );
|
|
}
|
|
|
|
template <typename T>
|
|
inline CUnlockedResource<T> CResourceStream::AllocateUnaligned( uint nCount )
|
|
{
|
|
// Allocate
|
|
return CUnlockedResource< T >( this, ( T* )AllocateBytes( nCount * sizeof( T ) ), nCount );
|
|
}
|
|
|
|
inline void CResourceStream::AlignPointer()
|
|
{
|
|
Align( sizeof( intp ) );
|
|
}
|
|
|
|
template <typename T>
|
|
inline CUnlockedResource<T> CResourceStream::WriteUnaligned( const T &x )
|
|
{
|
|
CUnlockedResource<T> pMemory = AllocateUnaligned<T>( );
|
|
V_memcpy( pMemory, &x, sizeof( T ) );
|
|
return pMemory;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline CLockedResource<T> CResourceStream::Write( const T &x )
|
|
{
|
|
CLockedResource<T> memory = Allocate<T>();
|
|
*memory = x;
|
|
return memory;
|
|
}
|
|
|
|
inline CLockedResource<float> CResourceStream::WriteFloat( float x )
|
|
{
|
|
return Write( x );
|
|
}
|
|
|
|
|
|
inline CLockedResource<uint64> CResourceStream::WriteU64( uint64 x )
|
|
{
|
|
return Write( x );
|
|
}
|
|
|
|
inline CLockedResource<uint32> CResourceStream::WriteU32( uint32 x )
|
|
{
|
|
return Write( x );
|
|
}
|
|
|
|
inline CLockedResource<uint16> CResourceStream::WriteU16( uint16 x )
|
|
{
|
|
return Write( x );
|
|
}
|
|
|
|
inline CLockedResource<uint8> CResourceStream::WriteByte( byte x )
|
|
{
|
|
return Write( x );
|
|
}
|
|
|
|
inline CLockedResource<char> CResourceStream::WriteString( const char *pString )
|
|
{
|
|
int nLength = pString ? V_strlen( pString ) : 0;
|
|
|
|
if ( nLength == 0 )
|
|
{
|
|
return CLockedResource<char>( NULL, 0 );
|
|
}
|
|
else
|
|
{
|
|
CLockedResource<char> memory = Allocate<char>(nLength+1);
|
|
memcpy( (char*)memory, pString, nLength+1 );
|
|
return memory;
|
|
}
|
|
}
|
|
|
|
inline CLockedResource<char> CResourceStream::WriteStringMaxLen( const char *pString, int nMaxLen )
|
|
{
|
|
int nStrLen = 0;
|
|
while ( pString && nStrLen < nMaxLen && pString[nStrLen] != '\0' )
|
|
{
|
|
nStrLen++;
|
|
}
|
|
|
|
if ( nStrLen == 0 )
|
|
{
|
|
return CLockedResource<char>( NULL, 0 );
|
|
}
|
|
else
|
|
{
|
|
CLockedResource<char> memory = Allocate<char>( nStrLen + 1 ); // +1 for null term
|
|
memcpy( (char*)memory, pString, nStrLen );
|
|
((char*)memory)[nStrLen] = '\0';
|
|
return memory;
|
|
}
|
|
}
|
|
|
|
inline uint32 CResourceStream::Tell() const
|
|
{
|
|
return m_nUsed;
|
|
}
|
|
|
|
|
|
inline void CResourceStream::Rollback( uint32 nPreviousTell )
|
|
{
|
|
Assert( nPreviousTell <= m_nUsed );
|
|
m_nUsed = nPreviousTell;
|
|
}
|
|
|
|
inline void CResourceStream::Rollback( const void *pPreviousTell )
|
|
{
|
|
Assert( pPreviousTell > m_pData && pPreviousTell <= m_pData + m_nUsed );
|
|
m_nUsed = ( ( uint8* ) pPreviousTell ) - m_pData;
|
|
}
|
|
|
|
|
|
inline uint32 CResourceStream::Tell( const void * pPast )const
|
|
{
|
|
// we do not reallocate the reserved memory , so the pointers do not invalidate and m_pData stays the same, and all absolute addresses stay the same in memory
|
|
uint nTell = uintp( pPast ) - uintp( m_pData );
|
|
AssertDbg( nTell <= m_nUsed );
|
|
return nTell;
|
|
}
|
|
|
|
inline const void *CResourceStream::TellPtr() const
|
|
{
|
|
return m_pData + m_nUsed;
|
|
}
|
|
|
|
inline void* CResourceStream::Compile( )
|
|
{
|
|
return m_pData;
|
|
}
|
|
|
|
template< class Memory >
|
|
inline void* CResourceStream::CompileToMemory( Memory &memory )
|
|
{
|
|
memory.Init( 0, GetTotalSize() );
|
|
V_memcpy( memory.Base(), Compile(), GetTotalSize() );
|
|
return memory.Base();
|
|
}
|
|
|
|
inline uint CResourceStream::GetTotalSize() const
|
|
{
|
|
return m_nUsed;
|
|
}
|
|
|
|
inline uint CResourceStream::GetStreamAlignment() const
|
|
{
|
|
return m_nMaxAlignment;
|
|
}
|
|
|
|
inline void* CResourceStream::GetDataPtr( uint nOffset )
|
|
{
|
|
if ( nOffset > m_nUsed )
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return m_pData + nOffset;
|
|
}
|
|
}
|
|
|
|
|
|
inline void CResourceStream::Clear()
|
|
{
|
|
V_memset( m_pData, 0, m_nCommitted );
|
|
m_nUsed = 0;
|
|
}
|
|
|
|
template <typename T>
|
|
inline T* CResourceStream::GetDataPtrTypeAligned( uint Offset )
|
|
{
|
|
uint nAlignment = VALIGNOF( T );
|
|
Offset += ( ( 0 - Offset ) & ( nAlignment - 1 ) );
|
|
return ( T* ) GetDataPtr( Offset );
|
|
}
|
|
|
|
template <typename T>
|
|
inline const T* OffsetPointer( const T *p, intp nOffsetBytes )
|
|
{
|
|
return p ? ( const T* )( intp( p ) + nOffsetBytes ) : NULL;
|
|
}
|
|
|
|
template <typename T>
|
|
inline T* ConstCastOffsetPointer( const T *p, intp nOffsetBytes )
|
|
{
|
|
return p ? ( T* ) ( intp( p ) + nOffsetBytes ) : NULL;
|
|
}
|
|
|
|
template < typename T >
|
|
inline CLockedResource< T > CloneArray( CResourceStream *pStream, const T *pArray, uint nCount )
|
|
{
|
|
if ( nCount > 0 && pArray )
|
|
{
|
|
CLockedResource< T > pOut = pStream->Allocate< T >( nCount );
|
|
V_memcpy( pOut, pArray, nCount * sizeof( T ) );
|
|
return pOut;
|
|
}
|
|
else
|
|
{
|
|
return CLockedResource< T >( );
|
|
}
|
|
}
|
|
|
|
|
|
inline int CResourceStreamFixed::GetSlack( )
|
|
{
|
|
return m_nCommitted - m_nUsed;
|
|
}
|
|
|
|
|
|
template < typename T >
|
|
CUnlockedResource< T >::CUnlockedResource( CResourceStream *pStream, T *pData, uint nCount ):
|
|
m_pStream( pStream ),
|
|
m_nOffset( ( ( uint8* ) pData ) - pStream->m_pData ),
|
|
m_nCount( nCount )
|
|
{
|
|
AssertDbg( ( ( uint8* ) pData ) >= pStream->m_pData && ( ( uint8* ) ( pData + nCount ) <= pStream->m_pData + pStream->m_nUsed ) );
|
|
AssertDbg( m_nOffset <= pStream->m_nUsed && m_nOffset + nCount * sizeof( T ) <= pStream->m_nUsed );
|
|
}
|
|
|
|
template < typename T >
|
|
inline T* CUnlockedResource< T >::GetPtr( )
|
|
{
|
|
return ( T* )m_pStream->GetDataPtr( m_nOffset );
|
|
}
|
|
|
|
template < typename T >
|
|
inline const T* CUnlockedResource< T >::GetPtr( )const
|
|
{
|
|
return ( const T * )m_pStream->GetDataPtr( m_nOffset );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif // RESOURCESTREAM_H
|