2022-11-22 08:57:33 +01:00
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
//
// $Header: $
// $NoKeywords: $
//
// Serialization buffer
//===========================================================================//
# pragma warning (disable : 4514)
# include "tier1/utlbuffer.h"
# include "tier1/strtools.h"
# include "tier1/characterset.h"
// memdbgon must be the last include file in a .cpp file!!!
2023-05-11 21:35:54 +02:00
# include "tier0/memdbgon.h"
2022-11-22 08:57:33 +01:00
//-----------------------------------------------------------------------------
// Character conversions for C strings
//-----------------------------------------------------------------------------
class CUtlCStringConversion : public CUtlCharConversion
{
public :
2023-08-29 22:05:20 +02:00
CUtlCStringConversion ( char nEscapeChar , const char * pDelimiter , ssize_t nCount , ConversionArray_t * pArray ) ;
2022-11-22 08:57:33 +01:00
// Finds a conversion for the passed-in string, returns length
2023-08-29 22:05:20 +02:00
virtual char FindConversion ( const char * pString , ssize_t * pLength ) ;
2022-11-22 08:57:33 +01:00
private :
char m_pConversion [ 256 ] ;
} ;
//-----------------------------------------------------------------------------
// Character conversions for no-escape sequence strings
//-----------------------------------------------------------------------------
class CUtlNoEscConversion : public CUtlCharConversion
{
public :
2023-08-29 22:05:20 +02:00
CUtlNoEscConversion ( char nEscapeChar , const char * pDelimiter , ssize_t nCount , ConversionArray_t * pArray ) :
2022-11-22 08:57:33 +01:00
CUtlCharConversion ( nEscapeChar , pDelimiter , nCount , pArray ) { }
// Finds a conversion for the passed-in string, returns length
2023-08-29 22:05:20 +02:00
virtual char FindConversion ( const char * pString , ssize_t * pLength ) { * pLength = 0 ; return 0 ; }
2022-11-22 08:57:33 +01:00
} ;
//-----------------------------------------------------------------------------
// List of character conversions
//-----------------------------------------------------------------------------
2024-01-12 00:17:53 +01:00
BEGIN_CUSTOM_CHAR_CONVERSION ( CUtlCStringConversion , s_StringCharConversion , " \" " , ' \\ ' )
{
' \n ' , " n "
} ,
{ ' \t ' , " t " } ,
{ ' \v ' , " v " } ,
{ ' \b ' , " b " } ,
{ ' \r ' , " r " } ,
{ ' \f ' , " f " } ,
{ ' \a ' , " a " } ,
{ ' \\ ' , " \\ " } ,
{ ' \? ' , " \ ? " } ,
{ ' \' ' , " \' " } ,
{ ' \" ' , " \" " } ,
END_CUSTOM_CHAR_CONVERSION ( CUtlCStringConversion , s_StringCharConversion , " \" " , ' \\ ' )
CUtlCharConversion * GetCStringCharConversion ( )
{
return & s_StringCharConversion ;
}
BEGIN_CUSTOM_CHAR_CONVERSION ( CUtlNoEscConversion , s_NoEscConversion , " \" " , 0x7F )
{
0x7F , " "
} ,
END_CUSTOM_CHAR_CONVERSION ( CUtlNoEscConversion , s_NoEscConversion , " \" " , 0x7F )
CUtlCharConversion * GetNoEscCharConversion ( )
{
return & s_NoEscConversion ;
}
2022-11-22 08:57:33 +01:00
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
CUtlCStringConversion : : CUtlCStringConversion ( char nEscapeChar , const char * pDelimiter , ssize_t nCount , ConversionArray_t * pArray ) :
2022-11-22 08:57:33 +01:00
CUtlCharConversion ( nEscapeChar , pDelimiter , nCount , pArray )
{
memset ( m_pConversion , 0x0 , sizeof ( m_pConversion ) ) ;
2023-08-29 22:05:20 +02:00
for ( ssize_t i = 0 ; i < nCount ; + + i )
2022-11-22 08:57:33 +01:00
{
m_pConversion [ ( unsigned char ) ( pArray [ i ] . m_pReplacementString [ 0 ] ) ] = pArray [ i ] . m_nActualChar ;
}
}
// Finds a conversion for the passed-in string, returns length
2023-08-29 22:05:20 +02:00
char CUtlCStringConversion : : FindConversion ( const char * pString , ssize_t * pLength )
2022-11-22 08:57:33 +01:00
{
2024-01-12 00:17:53 +01:00
const char c = m_pConversion [ ( unsigned char ) ( pString [ 0 ] ) ] ;
2022-11-22 08:57:33 +01:00
* pLength = ( c ! = ' \0 ' ) ? 1 : 0 ;
return c ;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
CUtlCharConversion : : CUtlCharConversion ( char nEscapeChar , const char * pDelimiter , ssize_t nCount , ConversionArray_t * pArray )
2022-11-22 08:57:33 +01:00
{
m_nEscapeChar = nEscapeChar ;
m_pDelimiter = pDelimiter ;
m_nCount = nCount ;
m_nDelimiterLength = V_strlen ( pDelimiter ) ;
m_nMaxConversionLength = 0 ;
memset ( m_pReplacements , 0 , sizeof ( m_pReplacements ) ) ;
2023-08-29 22:05:20 +02:00
for ( ssize_t i = 0 ; i < nCount ; + + i )
2022-11-22 08:57:33 +01:00
{
m_pList [ i ] = pArray [ i ] . m_nActualChar ;
ConversionInfo_t & info = m_pReplacements [ ( unsigned char ) ( m_pList [ i ] ) ] ;
Assert ( info . m_pReplacementString = = 0 ) ;
info . m_pReplacementString = pArray [ i ] . m_pReplacementString ;
info . m_nLength = V_strlen ( info . m_pReplacementString ) ;
if ( info . m_nLength > m_nMaxConversionLength )
{
m_nMaxConversionLength = info . m_nLength ;
}
}
}
//-----------------------------------------------------------------------------
// Escape character + delimiter
//-----------------------------------------------------------------------------
char CUtlCharConversion : : GetEscapeChar ( ) const
{
return m_nEscapeChar ;
}
const char * CUtlCharConversion : : GetDelimiter ( ) const
{
return m_pDelimiter ;
}
2023-08-29 22:05:20 +02:00
ssize_t CUtlCharConversion : : GetDelimiterLength ( ) const
2022-11-22 08:57:33 +01:00
{
return m_nDelimiterLength ;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
const char * CUtlCharConversion : : GetConversionString ( char c ) const
{
return m_pReplacements [ ( unsigned char ) c ] . m_pReplacementString ;
}
2023-08-29 22:05:20 +02:00
ssize_t CUtlCharConversion : : GetConversionLength ( char c ) const
2022-11-22 08:57:33 +01:00
{
return m_pReplacements [ ( unsigned char ) c ] . m_nLength ;
}
2023-08-29 22:05:20 +02:00
ssize_t CUtlCharConversion : : MaxConversionLength ( ) const
2022-11-22 08:57:33 +01:00
{
return m_nMaxConversionLength ;
}
//-----------------------------------------------------------------------------
// Finds a conversion for the passed-in string, returns length
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
char CUtlCharConversion : : FindConversion ( const char * pString , ssize_t * pLength )
2022-11-22 08:57:33 +01:00
{
2023-08-29 22:05:20 +02:00
for ( ssize_t i = 0 ; i < m_nCount ; + + i )
2022-11-22 08:57:33 +01:00
{
if ( ! V_strcmp ( pString , m_pReplacements [ ( unsigned char ) ( m_pList [ i ] ) ] . m_pReplacementString ) )
{
* pLength = m_pReplacements [ ( unsigned char ) ( m_pList [ i ] ) ] . m_nLength ;
return m_pList [ i ] ;
}
}
* pLength = 0 ;
return ' \0 ' ;
}
//-----------------------------------------------------------------------------
// constructors
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
CUtlBuffer : : CUtlBuffer ( ssize_t growSize , ssize_t initSize , int nFlags ) :
2022-11-22 08:57:33 +01:00
m_Error ( 0 )
{
//MEM_ALLOC_CREDIT();
m_Memory . Init ( growSize , initSize ) ;
m_Get = 0 ;
m_Put = 0 ;
m_nTab = 0 ;
m_nOffset = 0 ;
2023-04-02 17:34:42 +02:00
m_Flags = ( unsigned char ) nFlags ;
2022-11-22 08:57:33 +01:00
if ( ( initSize ! = 0 ) & & ! IsReadOnly ( ) )
{
m_nMaxPut = - 1 ;
AddNullTermination ( m_Put ) ;
}
else
{
m_nMaxPut = 0 ;
}
SetOverflowFuncs ( & CUtlBuffer : : GetOverflow , & CUtlBuffer : : PutOverflow ) ;
}
2023-08-29 22:05:20 +02:00
CUtlBuffer : : CUtlBuffer ( const void * pBuffer , ssize_t nSize , int nFlags ) :
2022-11-22 08:57:33 +01:00
m_Memory ( ( unsigned char * ) pBuffer , nSize ) , m_Error ( 0 )
{
Assert ( nSize ! = 0 ) ;
m_Get = 0 ;
m_Put = 0 ;
m_nTab = 0 ;
m_nOffset = 0 ;
2023-04-02 17:34:42 +02:00
m_Flags = ( unsigned char ) nFlags ;
2022-11-22 08:57:33 +01:00
if ( IsReadOnly ( ) )
{
m_nMaxPut = m_Put = nSize ;
}
else
{
m_nMaxPut = - 1 ;
AddNullTermination ( m_Put ) ;
}
SetOverflowFuncs ( & CUtlBuffer : : GetOverflow , & CUtlBuffer : : PutOverflow ) ;
}
CUtlBuffer : : CUtlBuffer ( const CUtlBuffer & copyFrom )
: m_Get ( copyFrom . m_Get )
, m_Put ( copyFrom . m_Put )
, m_Error ( copyFrom . m_Error )
, m_Flags ( copyFrom . m_Flags )
, m_Reserved ( copyFrom . m_Reserved )
# if defined( _GAMECONSOLE )
, pad ( copyFrom . pad )
# endif
, m_nTab ( copyFrom . m_nTab )
, m_nMaxPut ( copyFrom . m_nMaxPut )
, m_nOffset ( copyFrom . m_nOffset )
, m_GetOverflowFunc ( copyFrom . m_GetOverflowFunc )
, m_PutOverflowFunc ( copyFrom . m_PutOverflowFunc )
, m_Byteswap ( copyFrom . m_Byteswap )
{
if ( copyFrom . m_Memory . Count ( ) > 0 )
{
Assert ( false ) ; // This is a slow path, don't do this.
// copy memory
m_Memory . EnsureCapacity ( copyFrom . m_Memory . Count ( ) ) ;
memcpy ( m_Memory . Base ( ) , copyFrom . m_Memory . Base ( ) , copyFrom . m_Memory . Count ( ) ) ;
}
}
CUtlBuffer & CUtlBuffer : : operator = ( const CUtlBuffer & copyFrom )
{
if ( copyFrom . m_Memory . Count ( ) > 0 )
{
Assert ( false ) ; // This is a slow path, don't do this.
if ( this ! = & copyFrom )
{
m_Memory . Purge ( ) ;
m_Memory . EnsureCapacity ( copyFrom . m_Memory . Count ( ) ) ;
memcpy ( m_Memory . Base ( ) , copyFrom . m_Memory . Base ( ) , copyFrom . m_Memory . Count ( ) ) ;
}
}
m_Get = copyFrom . m_Get ;
m_Put = copyFrom . m_Put ;
m_Error = copyFrom . m_Error ;
m_Flags = copyFrom . m_Flags ;
m_Reserved = copyFrom . m_Reserved ;
# if defined( _GAMECONSOLE )
pad = copyFrom . pad ;
# endif
m_nTab = copyFrom . m_nTab ;
m_nMaxPut = copyFrom . m_nMaxPut ;
m_nOffset = copyFrom . m_nOffset ;
m_GetOverflowFunc = copyFrom . m_GetOverflowFunc ;
m_PutOverflowFunc = copyFrom . m_PutOverflowFunc ;
m_Byteswap = copyFrom . m_Byteswap ;
return * this ;
}
# if VALVE_CPP11
CUtlBuffer : : CUtlBuffer ( CUtlBuffer & & moveFrom ) // = default
: m_Memory ( Move ( moveFrom . m_Memory ) )
, m_Get ( Move ( moveFrom . m_Get ) )
, m_Put ( Move ( moveFrom . m_Put ) )
, m_Error ( Move ( moveFrom . m_Error ) )
, m_Flags ( Move ( moveFrom . m_Flags ) )
, m_Reserved ( Move ( moveFrom . m_Reserved ) )
# if defined( _GAMECONSOLE )
, pad ( Move ( moveFrom . pad ) )
# endif
, m_nTab ( Move ( moveFrom . m_nTab ) )
, m_nMaxPut ( Move ( moveFrom . m_nMaxPut ) )
, m_nOffset ( Move ( moveFrom . m_nOffset ) )
, m_GetOverflowFunc ( Move ( moveFrom . m_GetOverflowFunc ) )
, m_PutOverflowFunc ( Move ( moveFrom . m_PutOverflowFunc ) )
, m_Byteswap ( Move ( moveFrom . m_Byteswap ) )
{ }
CUtlBuffer & CUtlBuffer : : operator = ( CUtlBuffer & & moveFrom ) // = default
{
m_Memory = Move ( moveFrom . m_Memory ) ;
m_Get = Move ( moveFrom . m_Get ) ;
m_Put = Move ( moveFrom . m_Put ) ;
m_Error = Move ( moveFrom . m_Error ) ;
m_Flags = Move ( moveFrom . m_Flags ) ;
m_Reserved = Move ( moveFrom . m_Reserved ) ;
# if defined( _GAMECONSOLE )
pad = Move ( moveFrom . pad ) ;
# endif
m_nTab = Move ( moveFrom . m_nTab ) ;
m_nMaxPut = Move ( moveFrom . m_nMaxPut ) ;
m_nOffset = Move ( moveFrom . m_nOffset ) ;
m_GetOverflowFunc = Move ( moveFrom . m_GetOverflowFunc ) ;
m_PutOverflowFunc = Move ( moveFrom . m_PutOverflowFunc ) ;
m_Byteswap = Move ( moveFrom . m_Byteswap ) ;
return * this ;
}
# endif
//-----------------------------------------------------------------------------
// Modifies the buffer to be binary or text; Blows away the buffer and the CONTAINS_CRLF value.
//-----------------------------------------------------------------------------
void CUtlBuffer : : SetBufferType ( bool bIsText , bool bContainsCRLF )
{
# ifdef _DEBUG
// If the buffer is empty, there is no opportunity for this stuff to fail
if ( TellMaxPut ( ) ! = 0 )
{
if ( IsText ( ) )
{
if ( bIsText )
{
Assert ( ContainsCRLF ( ) = = bContainsCRLF ) ;
}
else
{
Assert ( ContainsCRLF ( ) ) ;
}
}
else
{
if ( bIsText )
{
Assert ( bContainsCRLF ) ;
}
}
}
# endif
if ( bIsText )
{
m_Flags | = TEXT_BUFFER ;
}
else
{
m_Flags & = ~ TEXT_BUFFER ;
}
if ( bContainsCRLF )
{
m_Flags | = CONTAINS_CRLF ;
}
else
{
m_Flags & = ~ CONTAINS_CRLF ;
}
}
//-----------------------------------------------------------------------------
// Attaches the buffer to external memory....
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : SetExternalBuffer ( void * pMemory , ssize_t nSize , ssize_t nInitialPut , int nFlags )
2022-11-22 08:57:33 +01:00
{
m_Memory . SetExternalBuffer ( ( unsigned char * ) pMemory , nSize ) ;
// Reset all indices; we just changed memory
m_Get = 0 ;
m_Put = nInitialPut ;
m_nTab = 0 ;
m_Error = 0 ;
m_nOffset = 0 ;
2023-04-02 17:34:42 +02:00
m_Flags = ( unsigned char ) nFlags ;
2022-11-22 08:57:33 +01:00
m_nMaxPut = - 1 ;
AddNullTermination ( m_Put ) ;
}
//-----------------------------------------------------------------------------
// Assumes an external buffer but manages its deletion
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : AssumeMemory ( void * pMemory , ssize_t nSize , ssize_t nInitialPut , int nFlags )
2022-11-22 08:57:33 +01:00
{
m_Memory . AssumeMemory ( ( unsigned char * ) pMemory , nSize ) ;
// Reset all indices; we just changed memory
m_Get = 0 ;
m_Put = nInitialPut ;
m_nTab = 0 ;
m_Error = 0 ;
m_nOffset = 0 ;
2023-04-02 17:34:42 +02:00
m_Flags = ( unsigned char ) nFlags ;
2022-11-22 08:57:33 +01:00
m_nMaxPut = - 1 ;
AddNullTermination ( m_Put ) ;
}
//-----------------------------------------------------------------------------
// Allows the caller to control memory
//-----------------------------------------------------------------------------
void * CUtlBuffer : : DetachMemory ( )
{
// Reset all indices; we just changed memory
m_Get = 0 ;
m_Put = 0 ;
m_nTab = 0 ;
m_Error = 0 ;
m_nOffset = 0 ;
return m_Memory . DetachMemory ( ) ;
}
//-----------------------------------------------------------------------------
// Makes sure we've got at least this much memory
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : EnsureCapacity ( ssize_t num )
2022-11-22 08:57:33 +01:00
{
//MEM_ALLOC_CREDIT();
// Add one extra for the null termination
num + = 1 ;
if ( m_Memory . IsExternallyAllocated ( ) )
{
if ( IsGrowable ( ) & & ( m_Memory . NumAllocated ( ) < num ) )
{
m_Memory . ConvertToGrowableMemory ( 0 ) ;
}
else
{
num - = 1 ;
}
}
m_Memory . EnsureCapacity ( num ) ;
}
//-----------------------------------------------------------------------------
// Base get method from which all others derive
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : Get ( void * pMem , ssize_t size )
2022-11-22 08:57:33 +01:00
{
if ( size > 0 & & CheckGet ( size ) )
{
memcpy ( pMem , & m_Memory [ m_Get - m_nOffset ] , size ) ;
m_Get + = size ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// This will get at least 1 byte and up to nSize bytes.
// It will return the number of bytes actually read.
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : GetUpTo ( void * pMem , ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
if ( CheckArbitraryPeekGet ( 0 , nSize ) )
{
memcpy ( pMem , & m_Memory [ m_Get - m_nOffset ] , nSize ) ;
m_Get + = nSize ;
return nSize ;
}
return 0 ;
}
//-----------------------------------------------------------------------------
// Eats whitespace
//-----------------------------------------------------------------------------
void CUtlBuffer : : EatWhiteSpace ( )
{
if ( IsText ( ) & & IsValid ( ) )
{
while ( CheckGet ( sizeof ( char ) ) )
{
if ( ! V_isspace ( * ( const unsigned char * ) PeekGet ( ) ) )
break ;
m_Get + = sizeof ( char ) ;
}
}
}
//-----------------------------------------------------------------------------
// Eats C++ style comments
//-----------------------------------------------------------------------------
bool CUtlBuffer : : EatCPPComment ( )
{
if ( IsText ( ) & & IsValid ( ) )
{
// If we don't have a a c++ style comment next, we're done
const char * pPeek = ( const char * ) PeekGet ( 2 * sizeof ( char ) , 0 ) ;
if ( ! pPeek | | ( pPeek [ 0 ] ! = ' / ' ) | | ( pPeek [ 1 ] ! = ' / ' ) )
return false ;
// Deal with c++ style comments
m_Get + = 2 ;
// read complete line
for ( char c = GetChar ( ) ; IsValid ( ) ; c = GetChar ( ) )
{
if ( c = = ' \n ' )
break ;
}
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Peeks how much whitespace to eat
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : PeekWhiteSpace ( ssize_t nOffset )
2022-11-22 08:57:33 +01:00
{
if ( ! IsText ( ) | | ! IsValid ( ) )
return 0 ;
while ( CheckPeekGet ( nOffset , sizeof ( char ) ) )
{
if ( ! V_isspace ( * ( unsigned char * ) PeekGet ( nOffset ) ) )
break ;
nOffset + = sizeof ( char ) ;
}
return nOffset ;
}
//-----------------------------------------------------------------------------
// Peek size of sting to come, check memory bound
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : PeekStringLength ( )
2022-11-22 08:57:33 +01:00
{
if ( ! IsValid ( ) )
return 0 ;
2022-12-26 17:01:14 +01:00
// Eat preceding whitespace
2023-08-29 22:05:20 +02:00
ssize_t nOffset = 0 ;
2022-11-22 08:57:33 +01:00
if ( IsText ( ) )
{
nOffset = PeekWhiteSpace ( nOffset ) ;
}
2023-08-29 22:05:20 +02:00
ssize_t nStartingOffset = nOffset ;
2022-11-22 08:57:33 +01:00
do
{
2023-08-29 22:05:20 +02:00
ssize_t nPeekAmount = 128 ;
2022-11-22 08:57:33 +01:00
// NOTE: Add 1 for the terminating zero!
if ( ! CheckArbitraryPeekGet ( nOffset , nPeekAmount ) )
{
if ( nOffset = = nStartingOffset )
return 0 ;
return nOffset - nStartingOffset + 1 ;
}
const char * pTest = ( const char * ) PeekGet ( nOffset ) ;
if ( ! IsText ( ) )
{
2023-08-29 22:05:20 +02:00
for ( ssize_t i = 0 ; i < nPeekAmount ; + + i )
2022-11-22 08:57:33 +01:00
{
// The +1 here is so we eat the terminating 0
if ( pTest [ i ] = = 0 )
return ( i + nOffset - nStartingOffset + 1 ) ;
}
}
else
{
2023-08-29 22:05:20 +02:00
for ( ssize_t i = 0 ; i < nPeekAmount ; + + i )
2022-11-22 08:57:33 +01:00
{
// The +1 here is so we eat the terminating 0
if ( V_isspace ( ( unsigned char ) pTest [ i ] ) | | ( pTest [ i ] = = 0 ) )
return ( i + nOffset - nStartingOffset + 1 ) ;
}
}
nOffset + = nPeekAmount ;
} while ( true ) ;
}
//-----------------------------------------------------------------------------
// Peek size of line to come, check memory bound
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : PeekLineLength ( )
2022-11-22 08:57:33 +01:00
{
if ( ! IsValid ( ) )
return 0 ;
2023-08-29 22:05:20 +02:00
ssize_t nOffset = 0 ;
ssize_t nStartingOffset = nOffset ;
2022-11-22 08:57:33 +01:00
do
{
2023-08-29 22:05:20 +02:00
ssize_t nPeekAmount = 128 ;
2022-11-22 08:57:33 +01:00
// NOTE: Add 1 for the terminating zero!
if ( ! CheckArbitraryPeekGet ( nOffset , nPeekAmount ) )
{
if ( nOffset = = nStartingOffset )
return 0 ;
return nOffset - nStartingOffset + 1 ;
}
const char * pTest = ( const char * ) PeekGet ( nOffset ) ;
2023-08-29 22:05:20 +02:00
for ( ssize_t i = 0 ; i < nPeekAmount ; + + i )
2022-11-22 08:57:33 +01:00
{
// The +2 here is so we eat the terminating '\n' and 0
if ( pTest [ i ] = = ' \n ' | | pTest [ i ] = = ' \r ' )
return ( i + nOffset - nStartingOffset + 2 ) ;
// The +1 here is so we eat the terminating 0
if ( pTest [ i ] = = 0 )
return ( i + nOffset - nStartingOffset + 1 ) ;
}
nOffset + = nPeekAmount ;
} while ( true ) ;
}
//-----------------------------------------------------------------------------
// Does the next bytes of the buffer match a pattern?
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : PeekStringMatch ( ssize_t nOffset , const char * pString , ssize_t nLen )
2022-11-22 08:57:33 +01:00
{
if ( ! CheckPeekGet ( nOffset , nLen ) )
return false ;
return ! V_strncmp ( ( const char * ) PeekGet ( nOffset ) , pString , nLen ) ;
}
//-----------------------------------------------------------------------------
// This version of PeekStringLength converts \" to \\ and " to \, etc.
// It also reads a " at the beginning and end of the string
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : PeekDelimitedStringLength ( CUtlCharConversion * pConv , bool bActualSize )
2022-11-22 08:57:33 +01:00
{
if ( ! IsText ( ) | | ! pConv )
return PeekStringLength ( ) ;
2022-12-26 17:01:14 +01:00
// Eat preceding whitespace
2023-08-29 22:05:20 +02:00
ssize_t nOffset = 0 ;
2022-11-22 08:57:33 +01:00
if ( IsText ( ) )
{
nOffset = PeekWhiteSpace ( nOffset ) ;
}
if ( ! PeekStringMatch ( nOffset , pConv - > GetDelimiter ( ) , pConv - > GetDelimiterLength ( ) ) )
return 0 ;
// Try to read ending ", but don't accept \"
2023-08-29 22:05:20 +02:00
ssize_t nActualStart = nOffset ;
2022-11-22 08:57:33 +01:00
nOffset + = pConv - > GetDelimiterLength ( ) ;
2023-08-29 22:05:20 +02:00
ssize_t nLen = 1 ; // Starts at 1 for the '\0' termination
2022-11-22 08:57:33 +01:00
do
{
if ( PeekStringMatch ( nOffset , pConv - > GetDelimiter ( ) , pConv - > GetDelimiterLength ( ) ) )
break ;
if ( ! CheckPeekGet ( nOffset , 1 ) )
break ;
char c = * ( const char * ) PeekGet ( nOffset ) ;
+ + nLen ;
+ + nOffset ;
if ( c = = pConv - > GetEscapeChar ( ) )
{
2023-08-29 22:05:20 +02:00
ssize_t nLength = pConv - > MaxConversionLength ( ) ;
2022-11-22 08:57:33 +01:00
if ( ! CheckArbitraryPeekGet ( nOffset , nLength ) )
break ;
pConv - > FindConversion ( ( const char * ) PeekGet ( nOffset ) , & nLength ) ;
nOffset + = nLength ;
}
} while ( true ) ;
return bActualSize ? nLen : nOffset - nActualStart + pConv - > GetDelimiterLength ( ) + 1 ;
}
//-----------------------------------------------------------------------------
// Reads a null-terminated string
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : GetString ( char * pString , ssize_t nMaxChars )
2022-11-22 08:57:33 +01:00
{
if ( ! IsValid ( ) )
{
* pString = 0 ;
return ;
}
Assert ( nMaxChars > 0 ) ;
if ( nMaxChars < = 0 )
{
return ;
}
// Remember, this *includes* the null character
// It will be 0, however, if the buffer is empty.
2023-08-29 22:05:20 +02:00
ssize_t nLen = PeekStringLength ( ) ;
2022-11-22 08:57:33 +01:00
if ( IsText ( ) )
{
EatWhiteSpace ( ) ;
}
if ( nLen < = 0 )
{
* pString = 0 ;
m_Error | = GET_OVERFLOW ;
return ;
}
2023-08-29 22:05:20 +02:00
const ssize_t nCharsToRead = Min ( nLen , nMaxChars ) - 1 ;
2022-11-22 08:57:33 +01:00
Get ( pString , nCharsToRead ) ;
pString [ nCharsToRead ] = 0 ;
if ( nLen > ( nCharsToRead + 1 ) )
{
SeekGet ( SEEK_CURRENT , nLen - ( nCharsToRead + 1 ) ) ;
}
// Read the terminating NULL in binary formats
if ( ! IsText ( ) )
{
//VerifyEquals(GetChar(), 0);
}
}
//-----------------------------------------------------------------------------
// Reads up to and including the first \n
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : GetLine ( char * pLine , ssize_t nMaxChars )
2022-11-22 08:57:33 +01:00
{
//Assert( IsText() && !ContainsCRLF() );
if ( ! IsValid ( ) )
{
* pLine = 0 ;
return ;
}
if ( nMaxChars = = 0 )
{
2023-02-12 17:54:15 +01:00
nMaxChars = LLONG_MAX ;
2022-11-22 08:57:33 +01:00
}
// Remember, this *includes* the null character
// It will be 0, however, if the buffer is empty.
2023-08-29 22:05:20 +02:00
ssize_t nLen = PeekLineLength ( ) ;
2022-11-22 08:57:33 +01:00
if ( nLen = = 0 )
{
* pLine = 0 ;
m_Error | = GET_OVERFLOW ;
return ;
}
// Strip off the terminating NULL
if ( nLen < = nMaxChars )
{
Get ( pLine , nLen - 1 ) ;
pLine [ nLen - 1 ] = 0 ;
}
else
{
Get ( pLine , nMaxChars - 1 ) ;
pLine [ nMaxChars - 1 ] = 0 ;
SeekGet ( SEEK_CURRENT , nLen - 1 - nMaxChars ) ;
}
}
//-----------------------------------------------------------------------------
// This version of GetString converts \ to \\ and " to \", etc.
// It also places " at the beginning and end of the string
//-----------------------------------------------------------------------------
char CUtlBuffer : : GetDelimitedCharInternal ( CUtlCharConversion * pConv )
{
char c = GetChar ( ) ;
if ( c = = pConv - > GetEscapeChar ( ) )
{
2023-08-29 22:05:20 +02:00
ssize_t nLength = pConv - > MaxConversionLength ( ) ;
2022-11-22 08:57:33 +01:00
if ( ! CheckArbitraryPeekGet ( 0 , nLength ) )
return ' \0 ' ;
c = pConv - > FindConversion ( ( const char * ) PeekGet ( ) , & nLength ) ;
SeekGet ( SEEK_CURRENT , nLength ) ;
}
return c ;
}
char CUtlBuffer : : GetDelimitedChar ( CUtlCharConversion * pConv )
{
if ( ! IsText ( ) | | ! pConv )
return GetChar ( ) ;
return GetDelimitedCharInternal ( pConv ) ;
}
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : GetDelimitedString ( CUtlCharConversion * pConv , char * pString , ssize_t nMaxChars )
2022-11-22 08:57:33 +01:00
{
if ( ! IsText ( ) | | ! pConv )
{
GetString ( pString , nMaxChars ) ;
return ;
}
if ( ! IsValid ( ) )
{
* pString = 0 ;
return ;
}
if ( nMaxChars = = 0 )
{
2023-02-12 17:54:15 +01:00
nMaxChars = LLONG_MAX ;
2022-11-22 08:57:33 +01:00
}
EatWhiteSpace ( ) ;
if ( ! PeekStringMatch ( 0 , pConv - > GetDelimiter ( ) , pConv - > GetDelimiterLength ( ) ) )
return ;
// Pull off the starting delimiter
SeekGet ( SEEK_CURRENT , pConv - > GetDelimiterLength ( ) ) ;
2023-08-29 22:05:20 +02:00
ssize_t nRead = 0 ;
2022-11-22 08:57:33 +01:00
while ( IsValid ( ) )
{
if ( PeekStringMatch ( 0 , pConv - > GetDelimiter ( ) , pConv - > GetDelimiterLength ( ) ) )
{
SeekGet ( SEEK_CURRENT , pConv - > GetDelimiterLength ( ) ) ;
break ;
}
char c = GetDelimitedCharInternal ( pConv ) ;
if ( nRead < nMaxChars )
{
pString [ nRead ] = c ;
+ + nRead ;
}
}
if ( nRead > = nMaxChars )
{
nRead = nMaxChars - 1 ;
}
pString [ nRead ] = ' \0 ' ;
}
//-----------------------------------------------------------------------------
// Checks if a get is ok
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : CheckGet ( ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
if ( m_Error & GET_OVERFLOW )
return false ;
if ( TellMaxPut ( ) < m_Get + nSize )
{
m_Error | = GET_OVERFLOW ;
return false ;
}
if ( ( m_Get < m_nOffset ) | | ( m_Memory . NumAllocated ( ) < m_Get - m_nOffset + nSize ) )
{
if ( ! OnGetOverflow ( nSize ) )
{
m_Error | = GET_OVERFLOW ;
return false ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Checks if a peek get is ok
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : CheckPeekGet ( ssize_t nOffset , ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
if ( m_Error & GET_OVERFLOW )
return false ;
// Checking for peek can't set the overflow flag
bool bOk = CheckGet ( nOffset + nSize ) ;
m_Error & = ~ GET_OVERFLOW ;
return bOk ;
}
//-----------------------------------------------------------------------------
// Call this to peek arbitrarily long into memory. It doesn't fail unless
// it can't read *anything* new
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : CheckArbitraryPeekGet ( ssize_t nOffset , ssize_t & nIncrement )
2022-11-22 08:57:33 +01:00
{
if ( TellGet ( ) + nOffset > = TellMaxPut ( ) )
{
nIncrement = 0 ;
return false ;
}
if ( TellGet ( ) + nOffset + nIncrement > TellMaxPut ( ) )
{
nIncrement = TellMaxPut ( ) - TellGet ( ) - nOffset ;
}
// NOTE: CheckPeekGet could modify TellMaxPut for streaming files
// We have to call TellMaxPut again here
CheckPeekGet ( nOffset , nIncrement ) ;
2023-08-29 22:05:20 +02:00
ssize_t nMaxGet = TellMaxPut ( ) - TellGet ( ) ;
2022-11-22 08:57:33 +01:00
if ( nMaxGet < nIncrement )
{
nIncrement = nMaxGet ;
}
return ( nIncrement ! = 0 ) ;
}
//-----------------------------------------------------------------------------
// Peek part of the butt
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
const void * CUtlBuffer : : PeekGet ( ssize_t nMaxSize , ssize_t nOffset )
2022-11-22 08:57:33 +01:00
{
if ( ! CheckPeekGet ( nOffset , nMaxSize ) )
return NULL ;
return & m_Memory [ m_Get + nOffset - m_nOffset ] ;
}
//-----------------------------------------------------------------------------
// Change where I'm reading
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : SeekGet ( SeekType_t type , ssize_t offset )
2022-11-22 08:57:33 +01:00
{
switch ( type )
{
case SEEK_HEAD :
m_Get = offset ;
break ;
case SEEK_CURRENT :
m_Get + = offset ;
break ;
case SEEK_TAIL :
m_Get = m_nMaxPut - offset ;
break ;
}
if ( m_Get > m_nMaxPut )
{
m_Error | = GET_OVERFLOW ;
}
else
{
m_Error & = ~ GET_OVERFLOW ;
if ( m_Get < m_nOffset | | m_Get > = m_nOffset + Size ( ) )
{
OnGetOverflow ( - 1 ) ;
}
}
}
//-----------------------------------------------------------------------------
// Parse...
//-----------------------------------------------------------------------------
# pragma warning ( disable : 4706 )
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : VaScanf ( const char * pFmt , va_list list )
2022-11-22 08:57:33 +01:00
{
Assert ( pFmt ) ;
if ( m_Error | | ! IsText ( ) )
return 0 ;
2023-08-29 22:05:20 +02:00
ssize_t numScanned = 0 ;
2022-11-22 08:57:33 +01:00
char c ;
2023-04-08 19:00:28 +02:00
while ( ( c = * pFmt + + ) )
2022-11-22 08:57:33 +01:00
{
// Stop if we hit the end of the buffer
if ( m_Get > = TellMaxPut ( ) )
{
m_Error | = GET_OVERFLOW ;
break ;
}
switch ( c )
{
case ' ' :
// eat all whitespace
EatWhiteSpace ( ) ;
break ;
case ' % ' :
{
// Conversion character... try to convert baby!
char type = * pFmt + + ;
if ( type = = 0 )
return numScanned ;
switch ( type )
{
case ' c ' :
{
char * ch = va_arg ( list , char * ) ;
if ( CheckPeekGet ( 0 , sizeof ( char ) ) )
{
* ch = * ( const char * ) PeekGet ( ) ;
+ + m_Get ;
}
else
{
* ch = 0 ;
return numScanned ;
}
}
break ;
case ' h ' :
{
if ( * pFmt = = ' d ' | | * pFmt = = ' i ' )
{
if ( ! GetTypeText ( * va_arg ( list , int16 * ) ) )
return numScanned ; // only support short ints, don't bother with hex
}
else if ( * pFmt = = ' u ' )
{
if ( ! GetTypeText ( * va_arg ( list , uint16 * ) ) )
return numScanned ;
}
else
return numScanned ;
+ + pFmt ;
}
break ;
case ' I ' :
{
if ( * pFmt + + ! = ' 6 ' | | * pFmt + + ! = ' 4 ' )
return numScanned ; // only support "I64d" and "I64u"
if ( * pFmt = = ' d ' )
{
if ( ! GetTypeText ( * va_arg ( list , int64 * ) ) )
return numScanned ;
}
else if ( * pFmt = = ' u ' )
{
if ( ! GetTypeText ( * va_arg ( list , uint64 * ) ) )
return numScanned ;
}
else
{
return numScanned ;
}
+ + pFmt ;
}
break ;
case ' i ' :
case ' d ' :
{
int32 * pArg = va_arg ( list , int32 * ) ;
if ( ! GetTypeText ( * pArg ) )
return numScanned ;
}
break ;
case ' x ' :
{
uint32 * pArg = va_arg ( list , uint32 * ) ;
if ( ! GetTypeText ( * pArg , 16 ) )
return numScanned ;
}
break ;
case ' u ' :
{
uint32 * pArg = va_arg ( list , uint32 * ) ;
if ( ! GetTypeText ( * pArg ) )
return numScanned ;
}
break ;
case ' l ' :
{
// we currently support %lf and %lld
if ( * pFmt = = ' f ' )
{
if ( ! GetTypeText ( * va_arg ( list , double * ) ) )
return numScanned ;
}
else if ( * pFmt = = ' l ' & & * + + pFmt = = ' d ' )
{
if ( ! GetTypeText ( * va_arg ( list , int64 * ) ) )
return numScanned ;
}
else
return numScanned ;
}
break ;
case ' f ' :
{
float * pArg = va_arg ( list , float * ) ;
if ( ! GetTypeText ( * pArg ) )
return numScanned ;
}
break ;
case ' s ' :
{
char * s = va_arg ( list , char * ) ;
GetString ( s , 64 ) ; // [SECURITY EXPLOIT: Scanf %s should be deprecated as malicious data can overrun stack buffers! Here we'd assume that at least 64 bytes are available on the stack, and even if not this shouldn't give attracker much room for code execution]
}
break ;
default :
{
// unimplemented scanf type
Assert ( 0 ) ;
return numScanned ;
}
break ;
}
+ + numScanned ;
}
break ;
default :
{
// Here we have to match the format string character
// against what's in the buffer or we're done.
if ( ! CheckPeekGet ( 0 , sizeof ( char ) ) )
return numScanned ;
if ( c ! = * ( const char * ) PeekGet ( ) )
return numScanned ;
+ + m_Get ;
}
}
}
return numScanned ;
}
# pragma warning ( default : 4706 )
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : Scanf ( const char * pFmt , . . . )
2022-11-22 08:57:33 +01:00
{
va_list args ;
va_start ( args , pFmt ) ;
2023-08-29 22:05:20 +02:00
ssize_t count = VaScanf ( pFmt , args ) ;
2022-11-22 08:57:33 +01:00
va_end ( args ) ;
return count ;
}
//-----------------------------------------------------------------------------
// Advance the get index until after the particular string is found
// Do not eat whitespace before starting. Return false if it failed
//-----------------------------------------------------------------------------
bool CUtlBuffer : : GetToken ( const char * pToken )
{
Assert ( pToken ) ;
// Look for the token
2023-08-29 22:05:20 +02:00
ssize_t nLen = V_strlen ( pToken ) ;
2022-11-22 08:57:33 +01:00
// First time through on streaming, check what we already have loaded
// if we have enough loaded to do the check
2023-08-29 22:05:20 +02:00
ssize_t nMaxSize = Size ( ) - ( TellGet ( ) - m_nOffset ) ;
2022-11-22 08:57:33 +01:00
if ( nMaxSize < = nLen )
{
nMaxSize = Size ( ) ;
}
2023-08-29 22:05:20 +02:00
ssize_t nSizeRemaining = TellMaxPut ( ) - TellGet ( ) ;
2022-11-22 08:57:33 +01:00
2023-08-29 22:05:20 +02:00
ssize_t nGet = TellGet ( ) ;
2022-11-22 08:57:33 +01:00
while ( nSizeRemaining > = nLen )
{
bool bOverFlow = ( nSizeRemaining > nMaxSize ) ;
2023-08-29 22:05:20 +02:00
ssize_t nSizeToCheck = bOverFlow ? nMaxSize : nSizeRemaining ;
2022-11-22 08:57:33 +01:00
if ( ! CheckPeekGet ( 0 , nSizeToCheck ) )
break ;
const char * pBufStart = ( const char * ) PeekGet ( ) ;
const char * pFoundEnd = V_strnistr ( pBufStart , pToken , nSizeToCheck ) ;
// Time to be careful: if we are in a state of overflow
// (namely, there's more of the buffer beyond the current window)
// we could be looking for 'foo' for example, and find 'foobar'
// if 'foo' happens to be the last 3 characters of the current window
size_t nOffset = ( size_t ) pFoundEnd - ( size_t ) pBufStart ;
2023-08-29 22:05:20 +02:00
bool bPotentialMismatch = ( bOverFlow & & ( ( ssize_t ) nOffset = = Size ( ) - nLen ) ) ;
2022-11-22 08:57:33 +01:00
if ( ! pFoundEnd | | bPotentialMismatch )
{
nSizeRemaining - = nSizeToCheck ;
if ( ! pFoundEnd & & ( nSizeRemaining < nLen ) )
break ;
// Second time through, stream as much in as possible
// But keep the last portion of the current buffer
// since we couldn't check it against stuff outside the window
nSizeRemaining + = nLen ;
nMaxSize = Size ( ) ;
SeekGet ( CUtlBuffer : : SEEK_CURRENT , nSizeToCheck - nLen ) ;
continue ;
}
// Seek past the end of the found string
2023-08-29 22:05:20 +02:00
SeekGet ( CUtlBuffer : : SEEK_CURRENT , ( ssize_t ) ( nOffset + nLen ) ) ;
2022-11-22 08:57:33 +01:00
return true ;
}
// Didn't find a match, leave the get index where it was to start with
SeekGet ( CUtlBuffer : : SEEK_HEAD , nGet ) ;
return false ;
}
//-----------------------------------------------------------------------------
// (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).
// Note the delimiter checks are case-insensitive.
// If successful, the get index is advanced and the function returns true,
// otherwise the index is not advanced and the function returns false.
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : ParseToken ( const char * pStartingDelim , const char * pEndingDelim , char * pString , ssize_t nMaxLen )
2022-11-22 08:57:33 +01:00
{
2023-08-29 22:05:20 +02:00
ssize_t nCharsToCopy = 0 ;
ssize_t nCurrentGet = 0 ;
2022-11-22 08:57:33 +01:00
size_t nEndingDelimLen ;
// Starting delimiter is optional
char emptyBuf = ' \0 ' ;
if ( ! pStartingDelim )
{
pStartingDelim = & emptyBuf ;
}
// Ending delimiter is not
Assert ( pEndingDelim & & pEndingDelim [ 0 ] ) ;
nEndingDelimLen = V_strlen ( pEndingDelim ) ;
2023-08-29 22:05:20 +02:00
ssize_t nStartGet = TellGet ( ) ;
ssize_t nTokenStart = - 1 ;
2022-11-22 08:57:33 +01:00
char nCurrChar ;
EatWhiteSpace ( ) ;
while ( * pStartingDelim )
{
nCurrChar = * pStartingDelim + + ;
if ( ! V_isspace ( ( unsigned char ) nCurrChar ) )
{
if ( tolower ( GetChar ( ) ) ! = tolower ( nCurrChar ) )
goto parseFailed ;
}
else
{
EatWhiteSpace ( ) ;
}
}
EatWhiteSpace ( ) ;
nTokenStart = TellGet ( ) ;
if ( ! GetToken ( pEndingDelim ) )
goto parseFailed ;
nCurrentGet = TellGet ( ) ;
2023-02-12 17:54:15 +01:00
nCharsToCopy = ( ( nCurrentGet - nEndingDelimLen ) - nTokenStart ) ;
2022-11-22 08:57:33 +01:00
if ( nCharsToCopy > = nMaxLen )
{
nCharsToCopy = nMaxLen - 1 ;
}
if ( nCharsToCopy > 0 )
{
SeekGet ( CUtlBuffer : : SEEK_HEAD , nTokenStart ) ;
Get ( pString , nCharsToCopy ) ;
if ( ! IsValid ( ) )
goto parseFailed ;
// Eat trailing whitespace
for ( ; nCharsToCopy > 0 ; - - nCharsToCopy )
{
if ( ! V_isspace ( ( unsigned char ) pString [ nCharsToCopy - 1 ] ) )
break ;
}
}
pString [ nCharsToCopy ] = ' \0 ' ;
// Advance the Get index
SeekGet ( CUtlBuffer : : SEEK_HEAD , nCurrentGet ) ;
return true ;
parseFailed :
// Revert the get index
SeekGet ( SEEK_HEAD , nStartGet ) ;
pString [ 0 ] = ' \0 ' ;
return false ;
}
//-----------------------------------------------------------------------------
// Parses the next token, given a set of character breaks to stop at
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
ssize_t CUtlBuffer : : ParseToken ( characterset_t * pBreaks , char * pTokenBuf , ssize_t nMaxLen , bool bParseComments )
2022-11-22 08:57:33 +01:00
{
Assert ( nMaxLen > 0 ) ;
pTokenBuf [ 0 ] = 0 ;
// skip whitespace + comments
while ( true )
{
if ( ! IsValid ( ) )
return - 1 ;
EatWhiteSpace ( ) ;
if ( bParseComments )
{
if ( ! EatCPPComment ( ) )
break ;
}
else
{
break ;
}
}
char c = GetChar ( ) ;
// End of buffer
if ( c = = 0 )
return - 1 ;
// handle quoted strings specially
if ( c = = ' \" ' )
{
2023-08-29 22:05:20 +02:00
ssize_t nLen = 0 ;
2022-11-22 08:57:33 +01:00
while ( IsValid ( ) )
{
c = GetChar ( ) ;
if ( c = = ' \" ' | | ! c )
{
pTokenBuf [ nLen ] = 0 ;
return nLen ;
}
pTokenBuf [ nLen ] = c ;
if ( + + nLen = = nMaxLen )
{
pTokenBuf [ nLen - 1 ] = 0 ;
return nMaxLen ;
}
}
2023-02-12 17:54:15 +01:00
// In this case, we hit the end of the buffer before hitting the end quote
2022-11-22 08:57:33 +01:00
pTokenBuf [ nLen ] = 0 ;
return nLen ;
}
// parse single characters
if ( IN_CHARACTERSET ( * pBreaks , c ) )
{
pTokenBuf [ 0 ] = c ;
pTokenBuf [ 1 ] = 0 ;
return 1 ;
}
// parse a regular word
2023-08-29 22:05:20 +02:00
ssize_t nLen = 0 ;
2022-11-22 08:57:33 +01:00
while ( true )
{
pTokenBuf [ nLen ] = c ;
if ( + + nLen = = nMaxLen )
{
pTokenBuf [ nLen - 1 ] = 0 ;
return nMaxLen ;
}
c = GetChar ( ) ;
if ( ! IsValid ( ) )
break ;
if ( IN_CHARACTERSET ( * pBreaks , c ) | | c = = ' \" ' | | c < = ' ' )
2023-06-23 00:27:24 +02:00
{
SeekGet ( SEEK_CURRENT , - 1 ) ;
2022-11-22 08:57:33 +01:00
break ;
2023-06-23 00:27:24 +02:00
}
2022-11-22 08:57:33 +01:00
}
pTokenBuf [ nLen ] = 0 ;
return nLen ;
}
//-----------------------------------------------------------------------------
// Serialization
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : Put ( const void * pMem , ssize_t size )
2022-11-22 08:57:33 +01:00
{
if ( size & & CheckPut ( size ) )
{
memcpy ( & m_Memory [ m_Put - m_nOffset ] , pMem , size ) ;
m_Put + = size ;
AddNullTermination ( m_Put ) ;
}
}
//-----------------------------------------------------------------------------
// Writes a null-terminated string
//-----------------------------------------------------------------------------
void CUtlBuffer : : PutString ( const char * pString )
{
if ( ! IsText ( ) )
{
if ( pString )
{
// Not text? append a null at the end.
2023-08-29 22:05:20 +02:00
ssize_t nLen = V_strlen ( pString ) + 1 ;
2022-11-22 08:57:33 +01:00
Put ( pString , nLen * sizeof ( char ) ) ;
return ;
}
else
{
PutTypeBin < char > ( 0 ) ;
}
}
else if ( pString )
{
2023-08-29 22:05:20 +02:00
ssize_t nTabCount = ( m_Flags & AUTO_TABS_DISABLED ) ? 0 : m_nTab ;
2022-11-22 08:57:33 +01:00
if ( nTabCount > 0 )
{
if ( WasLastCharacterCR ( ) )
{
PutTabs ( ) ;
}
const char * pEndl = strchr ( pString , ' \n ' ) ;
while ( pEndl )
{
size_t nSize = ( size_t ) pEndl - ( size_t ) pString + sizeof ( char ) ;
2023-02-12 17:54:15 +01:00
Put ( pString , nSize ) ;
2022-11-22 08:57:33 +01:00
pString = pEndl + 1 ;
if ( * pString )
{
PutTabs ( ) ;
pEndl = strchr ( pString , ' \n ' ) ;
}
else
{
pEndl = NULL ;
}
}
}
2023-08-29 22:05:20 +02:00
ssize_t nLen = V_strlen ( pString ) ;
2022-11-22 08:57:33 +01:00
if ( nLen )
{
Put ( pString , nLen * sizeof ( char ) ) ;
}
}
}
//-----------------------------------------------------------------------------
// This version of PutString converts \ to \\ and " to \", etc.
// It also places " at the beginning and end of the string
//-----------------------------------------------------------------------------
inline void CUtlBuffer : : PutDelimitedCharInternal ( CUtlCharConversion * pConv , char c )
{
2023-08-29 22:05:20 +02:00
ssize_t l = pConv - > GetConversionLength ( c ) ;
2022-11-22 08:57:33 +01:00
if ( l = = 0 )
{
PutChar ( c ) ;
}
else
{
PutChar ( pConv - > GetEscapeChar ( ) ) ;
Put ( pConv - > GetConversionString ( c ) , l ) ;
}
}
void CUtlBuffer : : PutDelimitedChar ( CUtlCharConversion * pConv , char c )
{
if ( ! IsText ( ) | | ! pConv )
{
PutChar ( c ) ;
return ;
}
PutDelimitedCharInternal ( pConv , c ) ;
}
void CUtlBuffer : : PutDelimitedString ( CUtlCharConversion * pConv , const char * pString )
{
if ( ! IsText ( ) | | ! pConv )
{
PutString ( pString ) ;
return ;
}
if ( WasLastCharacterCR ( ) )
{
PutTabs ( ) ;
}
Put ( pConv - > GetDelimiter ( ) , pConv - > GetDelimiterLength ( ) ) ;
2022-11-24 15:41:52 +01:00
size_t nLen = pString ? V_strlen ( pString ) : 0 ;
for ( size_t i = 0 ; i < nLen ; + + i )
2022-11-22 08:57:33 +01:00
{
PutDelimitedCharInternal ( pConv , pString [ i ] ) ;
}
if ( WasLastCharacterCR ( ) )
{
PutTabs ( ) ;
}
Put ( pConv - > GetDelimiter ( ) , pConv - > GetDelimiterLength ( ) ) ;
}
void CUtlBuffer : : VaPrintf ( const char * pFmt , va_list list )
{
char temp [ 8192 ] ;
2023-08-29 22:05:20 +02:00
//ssize_t nLen = V_vsnprintf(temp, sizeof(temp), pFmt, list);
2022-11-22 08:57:33 +01:00
//ErrorIfNot(nLen < sizeof(temp), ("CUtlBuffer::VaPrintf: String overflowed buffer [%d]\n", sizeof(temp)));
PutString ( temp ) ;
}
void CUtlBuffer : : Printf ( const char * pFmt , . . . )
{
va_list args ;
va_start ( args , pFmt ) ;
VaPrintf ( pFmt , args ) ;
va_end ( args ) ;
}
//-----------------------------------------------------------------------------
// Calls the overflow functions
//-----------------------------------------------------------------------------
void CUtlBuffer : : SetOverflowFuncs ( UtlBufferOverflowFunc_t getFunc , UtlBufferOverflowFunc_t putFunc )
{
m_GetOverflowFunc = getFunc ;
m_PutOverflowFunc = putFunc ;
}
//-----------------------------------------------------------------------------
// Calls the overflow functions
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : OnPutOverflow ( ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
return ( this - > * m_PutOverflowFunc ) ( nSize ) ;
}
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : OnGetOverflow ( ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
return ( this - > * m_GetOverflowFunc ) ( nSize ) ;
}
//-----------------------------------------------------------------------------
// Checks if a put is ok
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : PutOverflow ( ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
//MEM_ALLOC_CREDIT();
if ( m_Memory . IsExternallyAllocated ( ) )
{
if ( ! IsGrowable ( ) )
return false ;
m_Memory . ConvertToGrowableMemory ( 0 ) ;
}
while ( Size ( ) < m_Put - m_nOffset + nSize )
{
m_Memory . Grow ( ) ;
}
return true ;
}
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : GetOverflow ( ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
return false ;
}
//-----------------------------------------------------------------------------
// Checks if a put is ok
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
bool CUtlBuffer : : CheckPut ( ssize_t nSize )
2022-11-22 08:57:33 +01:00
{
if ( ( m_Error & PUT_OVERFLOW ) | | IsReadOnly ( ) )
return false ;
if ( ( m_Put < m_nOffset ) | | ( m_Memory . NumAllocated ( ) < m_Put - m_nOffset + nSize ) )
{
if ( ! OnPutOverflow ( nSize ) )
{
m_Error | = PUT_OVERFLOW ;
return false ;
}
}
return true ;
}
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : SeekPut ( SeekType_t type , ssize_t offset )
2022-11-22 08:57:33 +01:00
{
2023-08-29 22:05:20 +02:00
ssize_t nNextPut = m_Put ;
2022-11-22 08:57:33 +01:00
switch ( type )
{
case SEEK_HEAD :
nNextPut = offset ;
break ;
case SEEK_CURRENT :
nNextPut + = offset ;
break ;
case SEEK_TAIL :
nNextPut = m_nMaxPut - offset ;
break ;
}
// Force a write of the data
// FIXME: We could make this more optimal potentially by writing out
// the entire buffer if you seek outside the current range
// NOTE: This call will write and will also seek the file to nNextPut.
OnPutOverflow ( - nNextPut - 1 ) ;
m_Put = nNextPut ;
AddNullTermination ( m_Put ) ;
}
void CUtlBuffer : : ActivateByteSwapping ( bool bActivate )
{
m_Byteswap . ActivateByteSwapping ( bActivate ) ;
}
void CUtlBuffer : : SetBigEndian ( bool bigEndian )
{
m_Byteswap . SetTargetBigEndian ( bigEndian ) ;
}
bool CUtlBuffer : : IsBigEndian ( void )
{
return m_Byteswap . IsTargetBigEndian ( ) ;
}
//-----------------------------------------------------------------------------
// null terminate the buffer
// 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 and avoid LHS on PPC.
//-----------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
void CUtlBuffer : : AddNullTermination ( ssize_t nPut )
2022-11-22 08:57:33 +01:00
{
if ( nPut > m_nMaxPut )
{
if ( ! IsReadOnly ( ) & & ( ( m_Error & PUT_OVERFLOW ) = = 0 ) )
{
// Add null termination value
if ( CheckPut ( 1 ) )
{
m_Memory [ nPut - m_nOffset ] = 0 ;
}
else
{
// Restore the overflow state, it was valid before...
m_Error & = ~ PUT_OVERFLOW ;
}
}
m_nMaxPut = nPut ;
}
}
//-----------------------------------------------------------------------------
// 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 CUtlBuffer : : ConvertCRLF ( CUtlBuffer & outBuf )
{
if ( ! IsText ( ) | | ! outBuf . IsText ( ) )
return false ;
if ( ContainsCRLF ( ) = = outBuf . ContainsCRLF ( ) )
return false ;
2023-08-29 22:05:20 +02:00
ssize_t nInCount = TellMaxPut ( ) ;
2022-11-22 08:57:33 +01:00
outBuf . Purge ( ) ;
outBuf . EnsureCapacity ( nInCount ) ;
bool bFromCRLF = ContainsCRLF ( ) ;
// Start reading from the beginning
2023-08-29 22:05:20 +02:00
ssize_t nGet = TellGet ( ) ;
ssize_t nPut = TellPut ( ) ;
ssize_t nGetDelta = 0 ;
ssize_t nPutDelta = 0 ;
2022-11-22 08:57:33 +01:00
const char * pBase = ( const char * ) Base ( ) ;
intptr_t nCurrGet = 0 ;
while ( nCurrGet < nInCount )
{
const char * pCurr = & pBase [ nCurrGet ] ;
if ( bFromCRLF )
{
2023-08-29 22:05:20 +02:00
const char * pNext = V_strnistr ( pCurr , " \r \n " , ( ssize_t ) nInCount - ( ssize_t ) nCurrGet ) ;
2022-11-22 08:57:33 +01:00
if ( ! pNext )
{
outBuf . Put ( pCurr , nInCount - nCurrGet ) ;
break ;
}
intptr_t nBytes = ( intptr_t ) pNext - ( intptr_t ) pCurr ;
2023-02-12 17:54:15 +01:00
outBuf . Put ( pCurr , nBytes ) ;
2022-11-22 08:57:33 +01:00
outBuf . PutChar ( ' \n ' ) ;
nCurrGet + = nBytes + 2 ;
if ( nGet > = nCurrGet - 1 )
{
- - nGetDelta ;
}
if ( nPut > = nCurrGet - 1 )
{
- - nPutDelta ;
}
}
else
{
2023-08-29 22:05:20 +02:00
const char * pNext = V_strnchr ( pCurr , ' \n ' , ( ssize_t ) nInCount - ( ssize_t ) nCurrGet ) ;
2022-11-22 08:57:33 +01:00
if ( ! pNext )
{
2023-08-29 22:05:20 +02:00
outBuf . Put ( pCurr , ( ssize_t ) nInCount - ( ssize_t ) nCurrGet ) ;
2022-11-22 08:57:33 +01:00
break ;
}
intptr_t nBytes = ( intptr_t ) pNext - ( intptr_t ) pCurr ;
2023-08-29 22:05:20 +02:00
outBuf . Put ( pCurr , ( ssize_t ) nBytes ) ;
2022-11-22 08:57:33 +01:00
outBuf . PutChar ( ' \r ' ) ;
outBuf . PutChar ( ' \n ' ) ;
nCurrGet + = nBytes + 1 ;
if ( nGet > = nCurrGet )
{
+ + nGetDelta ;
}
if ( nPut > = nCurrGet )
{
+ + nPutDelta ;
}
}
}
Assert ( nPut + nPutDelta < = outBuf . TellMaxPut ( ) ) ;
outBuf . SeekGet ( SEEK_HEAD , nGet + nGetDelta ) ;
outBuf . SeekPut ( SEEK_HEAD , nPut + nPutDelta ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Fast swap
//-----------------------------------------------------------------------------
void CUtlBuffer : : Swap ( CUtlBuffer & buf )
{
V_swap ( m_Get , buf . m_Get ) ;
V_swap ( m_Put , buf . m_Put ) ;
V_swap ( m_nMaxPut , buf . m_nMaxPut ) ;
V_swap ( m_Error , buf . m_Error ) ;
m_Memory . Swap ( buf . m_Memory ) ;
}
//-----------------------------------------------------------------------------
// Fast swap w/ a CUtlMemory.
//-----------------------------------------------------------------------------
void CUtlBuffer : : Swap ( CUtlMemory < uint8 > & mem )
{
m_Get = 0 ;
m_Put = mem . Count ( ) ;
m_nMaxPut = mem . Count ( ) ;
m_Error = 0 ;
m_Memory . Swap ( mem ) ;
}
//---------------------------------------------------------------------------
// Implementation of CUtlInplaceBuffer
//---------------------------------------------------------------------------
2023-08-29 22:05:20 +02:00
CUtlInplaceBuffer : : CUtlInplaceBuffer ( ssize_t growSize /* = 0 */ , ssize_t initSize /* = 0 */ , int nFlags /* = 0 */ ) :
2022-11-22 08:57:33 +01:00
CUtlBuffer ( growSize , initSize , nFlags )
{
}
2023-08-29 22:05:20 +02:00
bool CUtlInplaceBuffer : : InplaceGetLinePtr ( char * * ppszInBufferPtr , ssize_t * pnLineLength )
2022-11-22 08:57:33 +01:00
{
Assert ( IsText ( ) & & ! ContainsCRLF ( ) ) ;
2023-08-29 22:05:20 +02:00
ssize_t nLineLen = PeekLineLength ( ) ;
2022-11-22 08:57:33 +01:00
if ( nLineLen < = 1 )
{
SeekGet ( SEEK_TAIL , 0 ) ;
return false ;
}
- - nLineLen ; // because it accounts for putting a terminating null-character
char * pszLine = ( char * ) const_cast < void * > ( PeekGet ( ) ) ;
SeekGet ( SEEK_CURRENT , nLineLen ) ;
// Set the out args
if ( ppszInBufferPtr )
* ppszInBufferPtr = pszLine ;
if ( pnLineLength )
* pnLineLength = nLineLen ;
return true ;
}
char * CUtlInplaceBuffer : : InplaceGetLinePtr ( void )
{
char * pszLine = NULL ;
2023-08-29 22:05:20 +02:00
ssize_t nLineLen = 0 ;
2022-11-22 08:57:33 +01:00
if ( InplaceGetLinePtr ( & pszLine , & nLineLen ) )
{
Assert ( nLineLen > = 1 ) ;
switch ( pszLine [ nLineLen - 1 ] )
{
case ' \n ' :
case ' \r ' :
pszLine [ nLineLen - 1 ] = 0 ;
if ( - - nLineLen )
{
switch ( pszLine [ nLineLen - 1 ] )
{
case ' \n ' :
case ' \r ' :
pszLine [ nLineLen - 1 ] = 0 ;
break ;
}
}
break ;
default :
Assert ( pszLine [ nLineLen ] = = 0 ) ;
break ;
}
}
return pszLine ;
}