//===== 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 //----------------------------------------------------------------------------- // 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 ( _get ), static_cast ( _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& 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, 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 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 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 void GetType(T& dest); template void GetTypeBin(T& dest); template bool GetTypeText(T& value, int nRadix = 10); template void GetObject(T* src); template void PutType(T src); template void PutTypeBin(T src); template void PutObject(T* src); // be sure to also update the copy constructor // and SwapCopy() when adding members. CUtlMemory 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 inline void CUtlBuffer::GetObject(T* dest) { if (CheckGet(sizeof(T))) { if (!m_Byteswap.IsSwappingBytes() || (sizeof(T) == 1)) { *dest = *(T*)PeekGet(); } else { m_Byteswap.SwapFieldsToTargetEndian(dest, (T*)PeekGet()); } m_Get += sizeof(T); } else { Q_memset(&dest, 0, sizeof(T)); } } template inline void CUtlBuffer::GetObjects(T* dest, int64 count) { for (int64 i = 0; i < count; ++i, ++dest) { GetObject(dest); } } template inline void CUtlBuffer::GetTypeBin(T& dest) { if (CheckGet(sizeof(T))) { if (!m_Byteswap.IsSwappingBytes() || (sizeof(T) == 1)) { dest = *(T*)PeekGet(); } else { m_Byteswap.SwapBufferToTargetEndian(&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 ((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 ((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 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 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 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*)PeekPut(), src); } m_Put += sizeof(T); AddNullTermination(m_Put); } } template inline void CUtlBuffer::PutObjects(T* src, int64 count) { for (int64 i = 0; i < count; ++i, ++src) { PutObject(src); } } template 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*)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(&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(&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 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('\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