|
|
//====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =======//
//
// Purpose:
//
// $NoKeywords: $
//
// Serialization/unserialization buffer
//=============================================================================//
#ifndef UTLBUFFER_H
#define UTLBUFFER_H
#ifdef _WIN32
#pragma once
#endif
#include "unitlib/unitlib.h" // just here for tests - remove before checking in!!!
#include "tier1/utlmemory.h"
#include "tier1/byteswap.h"
#include <stdarg.h>
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
struct characterset_t;
//-----------------------------------------------------------------------------
// Description of character conversions for string output
// Here's an example of how to use the macros to define a character conversion
// BEGIN_CHAR_CONVERSION( CStringConversion, '\\' )
// { '\n', "n" },
// { '\t', "t" }
// END_CHAR_CONVERSION( CStringConversion, '\\' )
//-----------------------------------------------------------------------------
class CUtlCharConversion { public: struct ConversionArray_t { char m_nActualChar; char *m_pReplacementString; };
CUtlCharConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ); char GetEscapeChar() const; const char *GetDelimiter() const; int GetDelimiterLength() const;
const char *GetConversionString( char c ) const; int GetConversionLength( char c ) const; int MaxConversionLength() const;
// Finds a conversion for the passed-in string, returns length
virtual char FindConversion( const char *pString, int *pLength );
protected: struct ConversionInfo_t { int m_nLength; char *m_pReplacementString; };
char m_nEscapeChar; const char *m_pDelimiter; int m_nDelimiterLength; int m_nCount; int m_nMaxConversionLength; char m_pList[256]; ConversionInfo_t m_pReplacements[256]; };
#define BEGIN_CHAR_CONVERSION( _name, _delimiter, _escapeChar ) \
static CUtlCharConversion::ConversionArray_t s_pConversionArray ## _name[] = {
#define END_CHAR_CONVERSION( _name, _delimiter, _escapeChar ) \
}; \ CUtlCharConversion _name( _escapeChar, _delimiter, sizeof( s_pConversionArray ## _name ) / sizeof( CUtlCharConversion::ConversionArray_t ), s_pConversionArray ## _name );
#define BEGIN_CUSTOM_CHAR_CONVERSION( _className, _name, _delimiter, _escapeChar ) \
static CUtlCharConversion::ConversionArray_t s_pConversionArray ## _name[] = {
#define END_CUSTOM_CHAR_CONVERSION( _className, _name, _delimiter, _escapeChar ) \
}; \ _className _name( _escapeChar, _delimiter, sizeof( s_pConversionArray ## _name ) / sizeof( CUtlCharConversion::ConversionArray_t ), s_pConversionArray ## _name );
//-----------------------------------------------------------------------------
// Character conversions for C strings
//-----------------------------------------------------------------------------
CUtlCharConversion *GetCStringCharConversion();
//-----------------------------------------------------------------------------
// Character conversions for quoted strings, with no escape sequences
//-----------------------------------------------------------------------------
CUtlCharConversion *GetNoEscCharConversion();
//-----------------------------------------------------------------------------
// Macro to set overflow functions easily
//-----------------------------------------------------------------------------
#define SetUtlBufferOverflowFuncs( _get, _put ) \
SetOverflowFuncs( static_cast <UtlBufferOverflowFunc_t>( _get ), static_cast <UtlBufferOverflowFunc_t>( _put ) )
typedef unsigned short ushort;
template < class A > static const char *GetFmtStr( int nRadix = 10, bool bPrint = true ) { Assert( 0 ); return ""; } #if defined( LINUX ) || defined( __clang__ ) || ( defined( _MSC_VER ) && _MSC_VER >= 1900 )
template <> const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } template <> const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } template <> const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } template <> const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } template <> const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } template <> const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } template <> const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6
#else
template <> static const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } template <> static const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } template <> static const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } template <> static const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } template <> static const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } template <> static const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } template <> static const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6
#endif
//-----------------------------------------------------------------------------
// Command parsing..
//-----------------------------------------------------------------------------
class CUtlBuffer { // Brian has on his todo list to revisit this as there are issues in some cases with CUtlVector using operator = instead of copy construtor in InsertMultiple, etc.
// The unsafe case is something like this:
// CUtlVector< CUtlBuffer > vecFoo;
//
// CUtlBuffer buf;
// buf.Put( xxx );
// vecFoo.Insert( buf );
//
// This will cause memory corruption when vecFoo is cleared
//
//private:
// // Disallow copying
// CUtlBuffer( const CUtlBuffer & );// { Assert( 0 ); }
// CUtlBuffer &operator=( const CUtlBuffer & );// { Assert( 0 ); return *this; }
public: enum SeekType_t { SEEK_HEAD = 0, SEEK_CURRENT, SEEK_TAIL };
// flags
enum BufferFlags_t { TEXT_BUFFER = 0x1, // Describes how get + put work (as strings, or binary)
EXTERNAL_GROWABLE = 0x2, // This is used w/ external buffers and causes the utlbuf to switch to reallocatable memory if an overflow happens when Putting.
CONTAINS_CRLF = 0x4, // For text buffers only, does this contain \n or \n\r?
READ_ONLY = 0x8, // For external buffers; prevents null termination from happening.
AUTO_TABS_DISABLED = 0x10, // Used to disable/enable push/pop tabs
};
// Overflow functions when a get or put overflows
typedef bool (CUtlBuffer::*UtlBufferOverflowFunc_t)( int nSize );
// Constructors for growable + external buffers for serialization/unserialization
CUtlBuffer( int growSize = 0, int initSize = 0, int nFlags = 0 ); CUtlBuffer( const void* pBuffer, int size, int nFlags = 0 ); // This one isn't actually defined so that we catch contructors that are trying to pass a bool in as the third param.
CUtlBuffer( const void *pBuffer, int size, bool crap ) = delete;
// UtlBuffer objects should not be copyable; we do a slow copy if you use this but it asserts.
// (REI: I'd like to delete these but we have some python bindings that currently rely on being able to copy these objects)
CUtlBuffer( const CUtlBuffer& ); // = delete;
CUtlBuffer& operator= ( const CUtlBuffer& ); // = delete;
#if VALVE_CPP11
// UtlBuffer is non-copyable (same as CUtlMemory), but it is moveable. We would like to declare these with '= default'
// but unfortunately VS2013 isn't fully C++11 compliant, so we have to manually declare these in the boilerplate way.
CUtlBuffer( CUtlBuffer&& moveFrom ); // = default;
CUtlBuffer& operator= ( CUtlBuffer&& moveFrom ); // = default;
#endif
unsigned char GetFlags() const;
// NOTE: This will assert if you attempt to recast it in a way that
// is not compatible. The only valid conversion is binary-> text w/CRLF
void SetBufferType( bool bIsText, bool bContainsCRLF );
// Makes sure we've got at least this much memory
void EnsureCapacity( int num );
// Access for direct read into buffer
void * AccessForDirectRead( int nBytes );
// Attaches the buffer to external memory....
void SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags = 0 ); bool IsExternallyAllocated() const; void AssumeMemory( void *pMemory, int nSize, int nInitialPut, int nFlags = 0 ); void *Detach(); void* DetachMemory();
// copies data from another buffer
void CopyBuffer( const CUtlBuffer &buffer ); void CopyBuffer( const void *pubData, int cubData );
void Swap( CUtlBuffer &buf ); void Swap( CUtlMemory<uint8> &mem );
FORCEINLINE void ActivateByteSwappingIfBigEndian( void ) { if ( ( IsX360() || IsPS3() ) ) ActivateByteSwapping( true ); }
// Controls endian-ness of binary utlbufs - default matches the current platform
void ActivateByteSwapping( bool bActivate ); void SetBigEndian( bool bigEndian ); bool IsBigEndian( void );
// Resets the buffer; but doesn't free memory
void Clear();
// Clears out the buffer; frees memory
void Purge();
// Dump the buffer to stdout
void Spew( );
// Read stuff out.
// Binary mode: it'll just read the bits directly in, and characters will be
// read for strings until a null character is reached.
// Text mode: it'll parse the file, turning text #s into real numbers.
// GetString will read a string until a space is reached
char GetChar( ); unsigned char GetUnsignedChar( ); short GetShort( ); unsigned short GetUnsignedShort( ); int GetInt( ); int64 GetInt64( ); unsigned int GetIntHex( ); unsigned int GetUnsignedInt( ); uint64 GetUnsignedInt64( ); float GetFloat( ); double GetDouble( ); void * GetPtr(); void GetString( char* pString, int nMaxChars ); bool Get( void* pMem, int size ); void GetLine( char* pLine, int nMaxChars );
// Used for getting objects that have a byteswap datadesc defined
template <typename T> void GetObjects( T *dest, int count = 1 );
// This will get at least 1 byte and up to nSize bytes.
// It will return the number of bytes actually read.
int GetUpTo( void *pMem, int nSize );
// This version of GetString converts \" to \\ and " to \, etc.
// It also reads a " at the beginning and end of the string
void GetDelimitedString( CUtlCharConversion *pConv, char *pString, int nMaxChars = 0 ); char GetDelimitedChar( CUtlCharConversion *pConv );
// This will return the # of characters of the string about to be read out
// NOTE: The count will *include* the terminating 0!!
// In binary mode, it's the number of characters until the next 0
// In text mode, it's the number of characters until the next space.
int PeekStringLength();
// This version of PeekStringLength converts \" to \\ and " to \, etc.
// It also reads a " at the beginning and end of the string
// NOTE: The count will *include* the terminating 0!!
// In binary mode, it's the number of characters until the next 0
// In text mode, it's the number of characters between "s (checking for \")
// Specifying false for bActualSize will return the pre-translated number of characters
// including the delimiters and the escape characters. So, \n counts as 2 characters when bActualSize == false
// and only 1 character when bActualSize == true
int PeekDelimitedStringLength( CUtlCharConversion *pConv, bool bActualSize = true );
// Just like scanf, but doesn't work in binary mode
int Scanf( SCANF_FORMAT_STRING const char* pFmt, ... ); int VaScanf( const char* pFmt, va_list list );
// Eats white space, advances Get index
void EatWhiteSpace();
// Eats C++ style comments
bool EatCPPComment();
// (For text buffers only)
// Parse a token from the buffer:
// Grab all text that lies between a starting delimiter + ending delimiter
// (skipping whitespace that leads + trails both delimiters).
// If successful, the get index is advanced and the function returns true,
// otherwise the index is not advanced and the function returns false.
bool ParseToken( const char *pStartingDelim, const char *pEndingDelim, char* pString, int nMaxLen );
// Advance the get index until after the particular string is found
// Do not eat whitespace before starting. Return false if it failed
// String test is case-insensitive.
bool GetToken( const char *pToken );
// Parses the next token, given a set of character breaks to stop at
// Returns the length of the token parsed in bytes (-1 if none parsed)
int ParseToken( characterset_t *pBreaks, char *pTokenBuf, int nMaxLen, bool bParseComments = true );
// Write stuff in
// Binary mode: it'll just write the bits directly in, and strings will be
// written with a null terminating character
// Text mode: it'll convert the numbers to text versions
// PutString will not write a terminating character
void PutChar( char c ); void PutUnsignedChar( unsigned char uc ); void PutShort( short s ); void PutUnsignedShort( unsigned short us ); void PutInt( int i ); void PutInt64( int64 i ); void PutUnsignedInt( unsigned int u ); void PutUnsignedInt64( uint64 u ); void PutFloat( float f ); void PutDouble( double d ); void PutPtr( void * ); // Writes the pointer, not the pointed to
void PutString( const char* pString ); void Put( const void* pMem, int size );
// Used for putting objects that have a byteswap datadesc defined
template <typename T> void PutObjects( T *src, int count = 1 );
// This version of PutString converts \ to \\ and " to \", etc.
// It also places " at the beginning and end of the string
void PutDelimitedString( CUtlCharConversion *pConv, const char *pString ); void PutDelimitedChar( CUtlCharConversion *pConv, char c );
// Just like printf, writes a terminating zero in binary mode
void Printf( PRINTF_FORMAT_STRING const char* pFmt, ... ) FMTFUNCTION( 2, 3 ); void VaPrintf( const char* pFmt, va_list list );
// What am I writing (put)/reading (get)?
void* PeekPut( int offset = 0 ); const void* PeekGet( int offset = 0 ) const; const void* PeekGet( int nMaxSize, int nOffset );
// Where am I writing (put)/reading (get)?
int TellPut( ) const; int TellGet( ) const;
// What's the most I've ever written?
int TellMaxPut( ) const;
// How many bytes remain to be read?
// NOTE: This is not accurate for streaming text files; it overshoots
int GetBytesRemaining() const;
// Change where I'm writing (put)/reading (get)
void SeekPut( SeekType_t type, int offset ); void SeekGet( SeekType_t type, int offset );
// Buffer base
const void* Base() const; void* Base();
// memory allocation size, does *not* reflect size written or read,
// use TellPut or TellGet for that
int Size() const;
// Am I a text buffer?
bool IsText() const;
// Can I grow if I'm externally allocated?
bool IsGrowable() const;
// Am I valid? (overflow or underflow error), Once invalid it stays invalid
bool IsValid() const;
// Do I contain carriage return/linefeeds?
bool ContainsCRLF() const;
// Am I read-only
bool IsReadOnly() const;
// Converts a buffer from a CRLF buffer to a CR buffer (and back)
// Returns false if no conversion was necessary (and outBuf is left untouched)
// If the conversion occurs, outBuf will be cleared.
bool ConvertCRLF( CUtlBuffer &outBuf );
// Push/pop pretty-printing tabs
void PushTab(); void PopTab();
// Temporarily disables pretty print
void EnableTabs( bool bEnable );
#if !defined( _GAMECONSOLE )
// Swap my internal memory with another buffer,
// and copy all of its other members
void SwapCopy( CUtlBuffer &other ) ; #endif
protected: // error flags
enum { PUT_OVERFLOW = 0x1, GET_OVERFLOW = 0x2, MAX_ERROR_FLAG = GET_OVERFLOW, };
void SetOverflowFuncs( UtlBufferOverflowFunc_t getFunc, UtlBufferOverflowFunc_t putFunc );
bool OnPutOverflow( int nSize ); bool OnGetOverflow( int nSize );
protected: // Checks if a get/put is ok
bool CheckPut( int size ); bool CheckGet( int size );
// NOTE: Pass in nPut here even though it is just a copy of m_Put. This is almost always called immediately
// after modifying m_Put and this lets it stay in a register
void AddNullTermination( int nPut );
// Methods to help with pretty-printing
bool WasLastCharacterCR(); void PutTabs();
// Help with delimited stuff
char GetDelimitedCharInternal( CUtlCharConversion *pConv ); void PutDelimitedCharInternal( CUtlCharConversion *pConv, char c );
// Default overflow funcs
bool PutOverflow( int nSize ); bool GetOverflow( int nSize );
// Does the next bytes of the buffer match a pattern?
bool PeekStringMatch( int nOffset, const char *pString, int nLen );
// Peek size of line to come, check memory bound
int PeekLineLength();
// How much whitespace should I skip?
int PeekWhiteSpace( int nOffset );
// Checks if a peek get is ok
bool CheckPeekGet( int nOffset, int nSize );
// Call this to peek arbitrarily long into memory. It doesn't fail unless
// it can't read *anything* new
bool CheckArbitraryPeekGet( int nOffset, int &nIncrement );
template <typename T> void GetType( T& dest ); template <typename T> void GetTypeBin( T& dest ); template <typename T> bool GetTypeText( T &value, int nRadix = 10 ); template <typename T> void GetObject( T *src );
template <typename T> void PutType( T src ); template <typename T> void PutTypeBin( T src ); template <typename T> void PutObject( T *src );
// be sure to also update the copy constructor
// and SwapCopy() when adding members.
CUtlMemory<unsigned char> m_Memory; int m_Get; int m_Put;
unsigned char m_Error; unsigned char m_Flags; unsigned char m_Reserved; #if defined( _GAMECONSOLE )
unsigned char pad; #endif
int m_nTab; int m_nMaxPut; int m_nOffset;
UtlBufferOverflowFunc_t m_GetOverflowFunc; UtlBufferOverflowFunc_t m_PutOverflowFunc;
CByteswap m_Byteswap; };
// Stream style output operators for CUtlBuffer
inline CUtlBuffer &operator<<( CUtlBuffer &b, char v ) { b.PutChar( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, unsigned char v ) { b.PutUnsignedChar( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, short v ) { b.PutShort( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, unsigned short v ) { b.PutUnsignedShort( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, int v ) { b.PutInt( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, unsigned int v ) { b.PutUnsignedInt( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, float v ) { b.PutFloat( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, double v ) { b.PutDouble( v ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, const char *pv ) { b.PutString( pv ); return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, const Vector &v ) { b << v.x << " " << v.y << " " << v.z; return b; }
inline CUtlBuffer &operator<<( CUtlBuffer &b, const Vector2D &v ) { b << v.x << " " << v.y; return b; }
class CUtlInplaceBuffer : public CUtlBuffer { public: CUtlInplaceBuffer( int growSize = 0, int initSize = 0, int nFlags = 0 );
//
// Routines returning buffer-inplace-pointers
//
public: //
// Upon success, determines the line length, fills out the pointer to the
// beginning of the line and the line length, advances the "get" pointer
// offset by the line length and returns "true".
//
// If end of file is reached or upon error returns "false".
//
// Note: the returned length of the line is at least one character because the
// trailing newline characters are also included as part of the line.
//
// Note: the pointer returned points into the local memory of this buffer, in
// case the buffer gets relocated or destroyed the pointer becomes invalid.
//
// e.g.: -------------
//
// char *pszLine;
// int nLineLen;
// while ( pUtlInplaceBuffer->InplaceGetLinePtr( &pszLine, &nLineLen ) )
// {
// ...
// }
//
// -------------
//
// @param ppszInBufferPtr on return points into this buffer at start of line
// @param pnLineLength on return holds num bytes accessible via (*ppszInBufferPtr)
//
// @returns true if line was successfully read
// false when EOF is reached or error occurs
//
bool InplaceGetLinePtr( /* out */ char **ppszInBufferPtr, /* out */ int *pnLineLength );
//
// Determines the line length, advances the "get" pointer offset by the line length,
// replaces the newline character with null-terminator and returns the initial pointer
// to now null-terminated line.
//
// If end of file is reached or upon error returns NULL.
//
// Note: the pointer returned points into the local memory of this buffer, in
// case the buffer gets relocated or destroyed the pointer becomes invalid.
//
// e.g.: -------------
//
// while ( char *pszLine = pUtlInplaceBuffer->InplaceGetLinePtr() )
// {
// ...
// }
//
// -------------
//
// @returns ptr-to-zero-terminated-line if line was successfully read and buffer modified
// NULL when EOF is reached or error occurs
//
char * InplaceGetLinePtr( void ); };
//-----------------------------------------------------------------------------
// Where am I reading?
//-----------------------------------------------------------------------------
inline int CUtlBuffer::TellGet( ) const { return m_Get; }
//-----------------------------------------------------------------------------
// How many bytes remain to be read?
//-----------------------------------------------------------------------------
inline int CUtlBuffer::GetBytesRemaining() const { return m_nMaxPut - TellGet(); }
//-----------------------------------------------------------------------------
// What am I reading?
//-----------------------------------------------------------------------------
inline const void* CUtlBuffer::PeekGet( int offset ) const { return &m_Memory[ m_Get + offset - m_nOffset ]; }
//-----------------------------------------------------------------------------
// Unserialization
//-----------------------------------------------------------------------------
template <typename T> inline void CUtlBuffer::GetObject( T *dest ) { if ( CheckGet( sizeof(T) ) ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { *dest = *(T *)PeekGet(); } else { m_Byteswap.SwapFieldsToTargetEndian<T>( dest, (T*)PeekGet() ); } m_Get += sizeof(T); } else { Q_memset( &dest, 0, sizeof(T) ); } }
template <typename T> inline void CUtlBuffer::GetObjects( T *dest, int count ) { for ( int i = 0; i < count; ++i, ++dest ) { GetObject<T>( dest ); } }
template <typename T> inline void CUtlBuffer::GetTypeBin( T &dest ) { if ( CheckGet( sizeof(T) ) ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { dest = *(T *)PeekGet(); } else { m_Byteswap.SwapBufferToTargetEndian<T>( &dest, (T*)PeekGet() ); } m_Get += sizeof(T); } else { dest = 0; } }
template <> inline void CUtlBuffer::GetTypeBin< float >( float &dest ) { if ( CheckGet( sizeof( float ) ) ) { uintp pData = (uintp)PeekGet(); if ( ( IsX360() || IsPS3() ) && ( pData & 0x03 ) ) { // handle unaligned read
((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; ((unsigned char*)&dest)[1] = ((unsigned char*)pData)[1]; ((unsigned char*)&dest)[2] = ((unsigned char*)pData)[2]; ((unsigned char*)&dest)[3] = ((unsigned char*)pData)[3]; } else { // aligned read
dest = *(float *)pData; } if ( m_Byteswap.IsSwappingBytes() ) { m_Byteswap.SwapBufferToTargetEndian< float >( &dest, &dest ); } m_Get += sizeof( float ); } else { dest = 0; } }
template <> inline void CUtlBuffer::GetTypeBin< double >( double &dest ) { if ( CheckGet( sizeof( double ) ) ) { uintp pData = (uintp)PeekGet(); if ( ( IsX360() || IsPS3() ) && ( pData & 0x07 ) ) { // handle unaligned read
((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; ((unsigned char*)&dest)[1] = ((unsigned char*)pData)[1]; ((unsigned char*)&dest)[2] = ((unsigned char*)pData)[2]; ((unsigned char*)&dest)[3] = ((unsigned char*)pData)[3]; ((unsigned char*)&dest)[4] = ((unsigned char*)pData)[4]; ((unsigned char*)&dest)[5] = ((unsigned char*)pData)[5]; ((unsigned char*)&dest)[6] = ((unsigned char*)pData)[6]; ((unsigned char*)&dest)[7] = ((unsigned char*)pData)[7]; } else { // aligned read
dest = *(double *)pData; } if ( m_Byteswap.IsSwappingBytes() ) { m_Byteswap.SwapBufferToTargetEndian< double >( &dest, &dest ); } m_Get += sizeof( double ); } else { dest = 0; } }
template < class T > inline T StringToNumber( char *pString, char **ppEnd, int nRadix ) { Assert( 0 ); *ppEnd = pString; return 0; }
template <> inline int8 StringToNumber( char *pString, char **ppEnd, int nRadix ) { return ( int8 )strtol( pString, ppEnd, nRadix ); }
template <> inline uint8 StringToNumber( char *pString, char **ppEnd, int nRadix ) { return ( uint8 )strtoul( pString, ppEnd, nRadix ); }
template <> inline int16 StringToNumber( char *pString, char **ppEnd, int nRadix ) { return ( int16 )strtol( pString, ppEnd, nRadix ); }
template <> inline uint16 StringToNumber( char *pString, char **ppEnd, int nRadix ) { return ( uint16 )strtoul( pString, ppEnd, nRadix ); }
template <> inline int32 StringToNumber( char *pString, char **ppEnd, int nRadix ) { return ( int32 )strtol( pString, ppEnd, nRadix ); }
template <> inline uint32 StringToNumber( char *pString, char **ppEnd, int nRadix ) { return ( uint32 )strtoul( pString, ppEnd, nRadix ); }
template <> inline int64 StringToNumber( char *pString, char **ppEnd, int nRadix ) { #if defined(_PS3) || defined(POSIX)
return ( int64 )strtoll( pString, ppEnd, nRadix ); #else // !_PS3
return ( int64 )_strtoi64( pString, ppEnd, nRadix ); #endif // _PS3
}
template <> inline float StringToNumber( char *pString, char **ppEnd, int nRadix ) { NOTE_UNUSED( nRadix ); return ( float )strtod( pString, ppEnd ); }
template <> inline double StringToNumber( char *pString, char **ppEnd, int nRadix ) { NOTE_UNUSED( nRadix ); return ( double )strtod( pString, ppEnd ); }
template <typename T> inline bool CUtlBuffer::GetTypeText( T &value, int nRadix /*= 10*/ ) { // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters
int nLength = 128; if ( !CheckArbitraryPeekGet( 0, nLength ) ) { value = 0; return false; }
char *pStart = (char*)PeekGet(); char* pEnd = pStart; value = StringToNumber< T >( pStart, &pEnd, nRadix );
int nBytesRead = (int)( pEnd - pStart ); if ( nBytesRead == 0 ) return false;
m_Get += nBytesRead; return true; }
template <typename T> inline void CUtlBuffer::GetType( T &dest ) { if (!IsText()) { GetTypeBin( dest ); } else { GetTypeText( dest ); } }
inline char CUtlBuffer::GetChar( ) { // LEGACY WARNING: this behaves differently than GetUnsignedChar()
char c; GetTypeBin( c ); // always reads as binary
return c; }
inline unsigned char CUtlBuffer::GetUnsignedChar( ) { // LEGACY WARNING: this behaves differently than GetChar()
unsigned char c; if (!IsText()) { GetTypeBin( c ); } else { c = ( unsigned char )GetUnsignedShort(); } return c; }
inline short CUtlBuffer::GetShort( ) { short s; GetType( s ); return s; }
inline unsigned short CUtlBuffer::GetUnsignedShort( ) { unsigned short s; GetType( s ); return s; }
inline int CUtlBuffer::GetInt( ) { int i; GetType( i ); return i; }
inline int64 CUtlBuffer::GetInt64( ) { int64 i; GetType( i ); return i; }
inline unsigned int CUtlBuffer::GetIntHex( ) { uint i; if (!IsText()) { GetTypeBin( i ); } else { GetTypeText( i, 16 ); } return i; }
inline unsigned int CUtlBuffer::GetUnsignedInt( ) { unsigned int i; GetType( i ); return i; }
inline uint64 CUtlBuffer::GetUnsignedInt64() { uint64 i; GetType( i ); return i; }
inline float CUtlBuffer::GetFloat( ) { float f; GetType( f ); return f; }
inline double CUtlBuffer::GetDouble( ) { double d; GetType( d ); return d; }
inline void *CUtlBuffer::GetPtr( ) { void *p; // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal
#if !defined(X64BITS) && !defined(PLATFORM_64BITS)
p = ( void* )GetUnsignedInt(); #else
p = ( void* )GetInt64(); #endif
return p; }
//-----------------------------------------------------------------------------
// Where am I writing?
//-----------------------------------------------------------------------------
inline unsigned char CUtlBuffer::GetFlags() const { return m_Flags; }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline bool CUtlBuffer::IsExternallyAllocated() const { return m_Memory.IsExternallyAllocated(); }
//-----------------------------------------------------------------------------
// Where am I writing?
//-----------------------------------------------------------------------------
inline int CUtlBuffer::TellPut( ) const { return m_Put; }
//-----------------------------------------------------------------------------
// What's the most I've ever written?
//-----------------------------------------------------------------------------
inline int CUtlBuffer::TellMaxPut( ) const { return m_nMaxPut; }
//-----------------------------------------------------------------------------
// What am I reading?
//-----------------------------------------------------------------------------
inline void* CUtlBuffer::PeekPut( int offset ) { return &m_Memory[m_Put + offset - m_nOffset]; }
//-----------------------------------------------------------------------------
// Various put methods
//-----------------------------------------------------------------------------
template <typename T> inline void CUtlBuffer::PutObject( T *src ) { if ( CheckPut( sizeof(T) ) ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { *(T *)PeekPut() = *src; } else { m_Byteswap.SwapFieldsToTargetEndian<T>( (T*)PeekPut(), src ); } m_Put += sizeof(T); AddNullTermination( m_Put ); } }
template <typename T> inline void CUtlBuffer::PutObjects( T *src, int count ) { for ( int i = 0; i < count; ++i, ++src ) { PutObject<T>( src ); } }
template <typename T> inline void CUtlBuffer::PutTypeBin( T src ) { if ( CheckPut( sizeof(T) ) ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { *(T *)PeekPut() = src; } else { m_Byteswap.SwapBufferToTargetEndian<T>( (T*)PeekPut(), &src ); } m_Put += sizeof(T); AddNullTermination( m_Put ); } }
#if defined( _GAMECONSOLE )
template <> inline void CUtlBuffer::PutTypeBin< float >( float src ) { if ( CheckPut( sizeof( src ) ) ) { if ( m_Byteswap.IsSwappingBytes() ) { m_Byteswap.SwapBufferToTargetEndian<float>( &src, &src ); }
//
// Write the data
//
unsigned pData = (unsigned)PeekPut(); if ( pData & 0x03 ) { // handle unaligned write
byte* dst = (byte*)pData; byte* srcPtr = (byte*)&src; dst[0] = srcPtr[0]; dst[1] = srcPtr[1]; dst[2] = srcPtr[2]; dst[3] = srcPtr[3]; } else { *(float *)pData = src; }
m_Put += sizeof(float); AddNullTermination( m_Put ); } }
template <> inline void CUtlBuffer::PutTypeBin< double >( double src ) { if ( CheckPut( sizeof( src ) ) ) { if ( m_Byteswap.IsSwappingBytes() ) { m_Byteswap.SwapBufferToTargetEndian<double>( &src, &src ); }
//
// Write the data
//
unsigned pData = (unsigned)PeekPut(); if ( pData & 0x07 ) { // handle unaligned write
byte* dst = (byte*)pData; byte* srcPtr = (byte*)&src; dst[0] = srcPtr[0]; dst[1] = srcPtr[1]; dst[2] = srcPtr[2]; dst[3] = srcPtr[3]; dst[4] = srcPtr[4]; dst[5] = srcPtr[5]; dst[6] = srcPtr[6]; dst[7] = srcPtr[7]; } else { *(double *)pData = src; }
m_Put += sizeof(double); AddNullTermination( m_Put ); } } #endif
template <typename T> inline void CUtlBuffer::PutType( T src ) { if (!IsText()) { PutTypeBin( src ); } else { Printf( GetFmtStr< T >(), src ); } }
//-----------------------------------------------------------------------------
// Methods to help with pretty-printing
//-----------------------------------------------------------------------------
inline bool CUtlBuffer::WasLastCharacterCR() { if ( !IsText() || (TellPut() == 0) ) return false; return ( *( const char * )PeekPut( -1 ) == '\n' ); }
inline void CUtlBuffer::PutTabs() { int nTabCount = ( m_Flags & AUTO_TABS_DISABLED ) ? 0 : m_nTab; for (int i = nTabCount; --i >= 0; ) { PutTypeBin<char>( '\t' ); } }
//-----------------------------------------------------------------------------
// Push/pop pretty-printing tabs
//-----------------------------------------------------------------------------
inline void CUtlBuffer::PushTab( ) { ++m_nTab; }
inline void CUtlBuffer::PopTab() { if ( --m_nTab < 0 ) { m_nTab = 0; } }
//-----------------------------------------------------------------------------
// Temporarily disables pretty print
//-----------------------------------------------------------------------------
inline void CUtlBuffer::EnableTabs( bool bEnable ) { if ( bEnable ) { m_Flags &= ~AUTO_TABS_DISABLED; } else { m_Flags |= AUTO_TABS_DISABLED; } }
inline void CUtlBuffer::PutChar( char c ) { if ( WasLastCharacterCR() ) { PutTabs(); }
PutTypeBin( c ); }
inline void CUtlBuffer::PutUnsignedChar( unsigned char c ) { if (!IsText()) { PutTypeBin( c ); } else { PutUnsignedShort( c ); } }
inline void CUtlBuffer::PutShort( short s ) { PutType( s ); }
inline void CUtlBuffer::PutUnsignedShort( unsigned short s ) { PutType( s ); }
inline void CUtlBuffer::PutInt( int i ) { PutType( i ); }
inline void CUtlBuffer::PutInt64( int64 i ) { PutType( i ); }
inline void CUtlBuffer::PutUnsignedInt( unsigned int u ) { PutType( u ); }
inline void CUtlBuffer::PutUnsignedInt64( uint64 i ) { PutType( i ); }
inline void CUtlBuffer::PutFloat( float f ) { PutType( f ); }
inline void CUtlBuffer::PutDouble( double d ) { PutType( d ); }
inline void CUtlBuffer::PutPtr( void *p ) { // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal
if (!IsText()) { PutTypeBin( p ); } else { Printf( "0x%p", p ); } }
//-----------------------------------------------------------------------------
// Am I a text buffer?
//-----------------------------------------------------------------------------
inline bool CUtlBuffer::IsText() const { return (m_Flags & TEXT_BUFFER) != 0; }
//-----------------------------------------------------------------------------
// Can I grow if I'm externally allocated?
//-----------------------------------------------------------------------------
inline bool CUtlBuffer::IsGrowable() const { return (m_Flags & EXTERNAL_GROWABLE) != 0; }
//-----------------------------------------------------------------------------
// Am I valid? (overflow or underflow error), Once invalid it stays invalid
//-----------------------------------------------------------------------------
inline bool CUtlBuffer::IsValid() const { return m_Error == 0; }
//-----------------------------------------------------------------------------
// Do I contain carriage return/linefeeds?
//-----------------------------------------------------------------------------
inline bool CUtlBuffer::ContainsCRLF() const { return IsText() && ((m_Flags & CONTAINS_CRLF) != 0); }
//-----------------------------------------------------------------------------
// Am I read-only
//-----------------------------------------------------------------------------
inline bool CUtlBuffer::IsReadOnly() const { return (m_Flags & READ_ONLY) != 0; }
//-----------------------------------------------------------------------------
// Buffer base and size
//-----------------------------------------------------------------------------
inline const void* CUtlBuffer::Base() const { return m_Memory.Base(); }
inline void* CUtlBuffer::Base() { return m_Memory.Base(); }
inline int CUtlBuffer::Size() const { return m_Memory.NumAllocated(); }
//-----------------------------------------------------------------------------
// Clears out the buffer; frees memory
//-----------------------------------------------------------------------------
inline void CUtlBuffer::Clear() { m_Get = 0; m_Put = 0; m_Error = 0; m_nOffset = 0; m_nMaxPut = -1; AddNullTermination( m_Put ); }
inline void CUtlBuffer::Purge() { m_Get = 0; m_Put = 0; m_nOffset = 0; m_nMaxPut = 0; m_Error = 0; m_Memory.Purge(); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline void *CUtlBuffer::AccessForDirectRead( int nBytes ) { Assert( m_Get == 0 && m_Put == 0 && m_nMaxPut == 0 ); EnsureCapacity( nBytes ); m_nMaxPut = nBytes; return Base(); }
inline void *CUtlBuffer::Detach() { void *p = m_Memory.Detach(); Clear(); return p; }
//-----------------------------------------------------------------------------
inline void CUtlBuffer::Spew( ) { SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
char pTmpLine[1024]; while( IsValid() && GetBytesRemaining() ) { V_memset( pTmpLine, 0, sizeof(pTmpLine) ); Get( pTmpLine, MIN( ( size_t )GetBytesRemaining(), sizeof(pTmpLine)-1 ) ); Msg( _T( "%s" ), pTmpLine ); } }
#if !defined(_GAMECONSOLE)
inline void CUtlBuffer::SwapCopy( CUtlBuffer &other ) { m_Get = other.m_Get; m_Put = other.m_Put; m_Error = other.m_Error; m_Flags = other.m_Flags; m_Reserved = other.m_Reserved; m_nTab = other.m_nTab; m_nMaxPut = other.m_nMaxPut; m_nOffset = other.m_nOffset; m_GetOverflowFunc = other.m_GetOverflowFunc; m_PutOverflowFunc = other.m_PutOverflowFunc; m_Byteswap = other.m_Byteswap;
m_Memory.Swap( other.m_Memory ); } #endif
inline void CUtlBuffer::CopyBuffer( const CUtlBuffer &buffer ) { CopyBuffer( buffer.Base(), buffer.TellPut() ); }
inline void CUtlBuffer::CopyBuffer( const void *pubData, int cubData ) { Clear(); if ( cubData ) { Put( pubData, cubData ); } }
#endif // UTLBUFFER_H
|