r5sdk/r5dev/public/tier1/utlbuffer.h
Kawe Mazidjatari 9f428f1567 Move tier0 lib headers to public
This commit does not change any logic or behavior of the code.
2023-04-06 23:50:48 +02:00

1453 lines
38 KiB
C++

//===== 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 <tier0/annotations.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, int64 nCount, ConversionArray_t* pArray);
char GetEscapeChar() const;
const char* GetDelimiter() const;
int64 GetDelimiterLength() const;
const char* GetConversionString(char c) const;
int64 GetConversionLength(char c) const;
int64 MaxConversionLength() const;
// Finds a conversion for the passed-in string, returns length
virtual char FindConversion(const char* pString, int64* pLength);
protected:
struct ConversionInfo_t
{
int64 m_nLength;
char* m_pReplacementString;
};
char m_nEscapeChar;
const char* m_pDelimiter;
int64 m_nDelimiterLength;
int64 m_nCount;
int64 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)(int64 nSize);
// Constructors for growable + external buffers for serialization/unserialization
CUtlBuffer(int64 growSize = 0, int64 initSize = 0, int nFlags = 0);
CUtlBuffer(const void* pBuffer, int64 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, int64 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(int64 num);
// Access for direct read into buffer
void* AccessForDirectRead(int64 nBytes);
// Attaches the buffer to external memory....
void SetExternalBuffer(void* pMemory, int64 nSize, int64 nInitialPut, int nFlags = 0);
bool IsExternallyAllocated() const;
void AssumeMemory(void* pMemory, int64 nSize, int64 nInitialPut, int nFlags = 0);
void* Detach();
void* DetachMemory();
// copies data from another buffer
void CopyBuffer(const CUtlBuffer& buffer);
void CopyBuffer(const void* pubData, int64 cubData);
void Swap(CUtlBuffer& buf);
void Swap(CUtlMemory<uint8>& mem);
FORCEINLINE void ActivateByteSwappingIfBigEndian(void)
{
if V_CONSTEXPR((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, int64 nMaxChars);
bool Get(void* pMem, int64 size);
void GetLine(char* pLine, int64 nMaxChars);
// Used for getting objects that have a byteswap datadesc defined
template <typename T> void GetObjects(T* dest, int64 count = 1);
// This will get at least 1 byte and up to nSize bytes.
// It will return the number of bytes actually read.
int64 GetUpTo(void* pMem, int64 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, int64 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.
int64 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
int64 PeekDelimitedStringLength(CUtlCharConversion* pConv, bool bActualSize = true);
// Just like scanf, but doesn't work in binary mode
int64 Scanf(SCANF_FORMAT_STRING const char* pFmt, ...);
int64 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, int64 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)
int64 ParseToken(characterset_t* pBreaks, char* pTokenBuf, int64 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, int64 size);
// Used for putting objects that have a byteswap datadesc defined
template <typename T> void PutObjects(T* src, int64 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(int64 offset = 0);
const void* PeekGet(int64 offset = 0) const;
const void* PeekGet(int64 nMaxSize, int64 nOffset);
// Where am I writing (put)/reading (get)?
int64 TellPut() const;
int64 TellGet() const;
// What's the most I've ever written?
int64 TellMaxPut() const;
// How many bytes remain to be read?
// NOTE: This is not accurate for streaming text files; it overshoots
int64 GetBytesRemaining() const;
// Change where I'm writing (put)/reading (get)
void SeekPut(SeekType_t type, int64 offset);
void SeekGet(SeekType_t type, int64 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
int64 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(int64 nSize);
bool OnGetOverflow(int64 nSize);
protected:
// Checks if a get/put is ok
bool CheckPut(int64 size);
bool CheckGet(int64 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(int64 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(int64 nSize);
bool GetOverflow(int64 nSize);
// Does the next bytes of the buffer match a pattern?
bool PeekStringMatch(int64 nOffset, const char* pString, int64 nLen);
// Peek size of line to come, check memory bound
int64 PeekLineLength();
// How much whitespace should I skip?
int64 PeekWhiteSpace(int64 nOffset);
// Checks if a peek get is ok
bool CheckPeekGet(int64 nOffset, int64 nSize);
// Call this to peek arbitrarily long into memory. It doesn't fail unless
// it can't read *anything* new
bool CheckArbitraryPeekGet(int64 nOffset, int64& 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;
int64 m_Get;
int64 m_Put;
unsigned char m_Error;
unsigned char m_Flags;
unsigned char m_Reserved;
#if defined( _GAMECONSOLE )
unsigned char pad;
#endif
int m_nTab;
int64 m_nMaxPut;
int64 m_nOffset;
UtlBufferOverflowFunc_t m_GetOverflowFunc;
UtlBufferOverflowFunc_t m_PutOverflowFunc;
CByteSwap m_Byteswap;
void* m_pUnk; // Possibly padding?
const char* m_pName;
int64 m_Count; // Unknown count.
};
static_assert(sizeof(CUtlBuffer) == 0x70);
// 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 Vector3D& 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(int64 growSize = 0, int64 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 */ int64* 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 int64 CUtlBuffer::TellGet() const
{
return m_Get;
}
//-----------------------------------------------------------------------------
// How many bytes remain to be read?
//-----------------------------------------------------------------------------
inline int64 CUtlBuffer::GetBytesRemaining() const
{
return m_nMaxPut - TellGet();
}
//-----------------------------------------------------------------------------
// What am I reading?
//-----------------------------------------------------------------------------
inline const void* CUtlBuffer::PeekGet(int64 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, int64 count)
{
for (int64 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)))
{
uintptr_t pData = (uintptr_t)PeekGet();
if V_CONSTEXPR((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)))
{
uintptr_t pData = (uintptr_t)PeekGet();
if V_CONSTEXPR((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
int64 nLength = 128;
if (!CheckArbitraryPeekGet(0, nLength))
{
value = 0;
return false;
}
char* pStart = (char*)PeekGet();
char* pEnd = pStart;
value = StringToNumber< T >(pStart, &pEnd, nRadix);
int64 nBytesRead = (int64)(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*)GetUnsignedInt64();
#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 int64 CUtlBuffer::TellPut() const
{
return m_Put;
}
//-----------------------------------------------------------------------------
// What's the most I've ever written?
//-----------------------------------------------------------------------------
inline int64 CUtlBuffer::TellMaxPut() const
{
return m_nMaxPut;
}
//-----------------------------------------------------------------------------
// What am I reading?
//-----------------------------------------------------------------------------
inline void* CUtlBuffer::PeekPut(int64 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, int64 count)
{
for (int64 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 int64 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(int64 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())
//{
// memset(pTmpLine, 0, sizeof(pTmpLine));
// Get(pTmpLine, MIN((size_t)GetBytesRemaining(), sizeof(pTmpLine) - 1));
// DevMsg(eDLL_T::COMMON, _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, int64 cubData)
{
Clear();
if (cubData)
{
Put(pubData, cubData);
}
}
#endif // UTLBUFFER_H