//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: A simple class for performing safe and in-expression sprintf-style // string formatting // // $NoKeywords: $ //=============================================================================// #ifndef FMTSTR_H #define FMTSTR_H #include #include #include "tier0/platform.h" #include "tier0/dbg.h" #include "tier1/strtools.h" #if defined( _WIN32 ) #pragma once #endif //============================================================================= // using macro to be compatable with GCC #define FmtStrVSNPrintf( szBuf, nBufSize, bQuietTruncation, ppszFormat, nPrevLen, lastArg ) \ do \ { \ int result; \ va_list arg_ptr; \ bool bTruncated = false; \ static int scAsserted = 0; \ \ va_start(arg_ptr, lastArg); \ result = V_vsnprintfRet( (szBuf), (nBufSize)-1, (*(ppszFormat)), arg_ptr, &bTruncated ); \ va_end(arg_ptr); \ \ (szBuf)[(nBufSize)-1] = 0; \ if ( bTruncated && !(bQuietTruncation) && scAsserted < 5 ) \ { \ Warning( "FmtStrVSNPrintf truncated to %d without QUIET_TRUNCATION specified!\n", ( int )( nBufSize ) ); \ AssertMsg( 0, "FmtStrVSNPrintf truncated without QUIET_TRUNCATION specified!\n" ); \ scAsserted++; \ } \ m_nLength = nPrevLen + result; \ } \ while (0) // using macro to be compatable with GCC #define FmtStrVSNPrintfNoLengthFixup( szBuf, nBufSize, bQuietTruncation, ppszFormat, nPrevLen, lastArg ) \ do \ { \ int result; \ va_list arg_ptr; \ bool bTruncated = false; \ static int scAsserted = 0; \ \ va_start(arg_ptr, lastArg); \ result = V_vsnprintfRet( (szBuf), (nBufSize)-1, (*(ppszFormat)), arg_ptr, &bTruncated ); \ va_end(arg_ptr); \ \ (szBuf)[(nBufSize)-1] = 0; \ if ( bTruncated && !(bQuietTruncation) && scAsserted < 5 ) \ { \ Warning( "FmtStrVSNPrintf truncated to %d without QUIET_TRUNCATION specified!\n", ( int )( nBufSize ) ); \ AssertMsg( 0, "FmtStrVSNPrintf truncated without QUIET_TRUNCATION specified!\n" ); \ scAsserted++; \ } \ } \ while (0) //----------------------------------------------------------------------------- // // Purpose: String formatter with specified size // template class CFmtStrN { public: CFmtStrN() { InitQuietTruncation(); m_szBuf[0] = 0; m_nLength = 0; } // Standard C formatting CFmtStrN(PRINTF_FORMAT_STRING const char *pszFormat, ...) FMTFUNCTION( 2, 3 ) { InitQuietTruncation(); FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat ); } // Use this for pass-through formatting CFmtStrN(const char ** ppszFormat, ...) { InitQuietTruncation(); FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, ppszFormat, 0, ppszFormat ); } // Explicit reformat const char *sprintf(PRINTF_FORMAT_STRING const char *pszFormat, ...) FMTFUNCTION( 2, 3 ) { InitQuietTruncation(); FmtStrVSNPrintf(m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat ); return m_szBuf; } // Same as sprintf above, but for compatibility with Steam interface const char *Format( PRINTF_FORMAT_STRING const char *pszFormat, ... ) FMTFUNCTION( 2, 3 ) { InitQuietTruncation(); FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat ); return m_szBuf; } // Use this for va_list formatting const char *sprintf_argv(const char *pszFormat, va_list arg_ptr) { int result; bool bTruncated = false; static int s_nWarned = 0; InitQuietTruncation(); result = V_vsnprintfRet( m_szBuf, SIZE_BUF - 1, pszFormat, arg_ptr, &bTruncated ); m_szBuf[SIZE_BUF - 1] = 0; if ( bTruncated && !m_bQuietTruncation && ( s_nWarned < 5 ) ) { Warning( "CFmtStr truncated to %d without QUIET_TRUNCATION specified!\n", SIZE_BUF ); AssertMsg( 0, "CFmtStr truncated without QUIET_TRUNCATION specified!\n" ); s_nWarned++; } m_nLength = V_strlen( m_szBuf ); return m_szBuf; } // Use this for pass-through formatting void VSprintf(const char **ppszFormat, ...) { InitQuietTruncation(); FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, ppszFormat, 0, ppszFormat ); } // Compatible API with CUtlString for converting to const char* const char *Get( ) const { return m_szBuf; } const char *String( ) const { return m_szBuf; } // Use for access operator const char *() const { return m_szBuf; } char *Access() { return m_szBuf; } // Access template argument static inline int GetMaxLength() { return SIZE_BUF-1; } CFmtStrN & operator=( const char *pchValue ) { V_strncpy( m_szBuf, pchValue, SIZE_BUF ); m_nLength = V_strlen( m_szBuf ); return *this; } CFmtStrN & operator+=( const char *pchValue ) { Append( pchValue ); return *this; } int Length() const { return m_nLength; } void SetLength( int nLength ) { m_nLength = Min( nLength, SIZE_BUF - 1 ); m_szBuf[m_nLength] = '\0'; } void Clear() { m_szBuf[0] = 0; m_nLength = 0; } void AppendFormat( PRINTF_FORMAT_STRING const char *pchFormat, ... ) FMTFUNCTION( 2, 3 ) { char *pchEnd = m_szBuf + m_nLength; FmtStrVSNPrintf( pchEnd, SIZE_BUF - m_nLength, m_bQuietTruncation, &pchFormat, m_nLength, pchFormat ); } void AppendFormatV( const char *pchFormat, va_list args ); void Append( const char *pchValue ) { // This function is close to the metal to cut down on the CPU cost // of the previous incantation of Append which was implemented as // AppendFormat( "%s", pchValue ). This implementation, though not // as easy to read, instead does a strcpy from the existing end // point of the CFmtStrN. This brings something like a 10-20x speedup // in my rudimentary tests. It isn't using V_strncpy because that // function doesn't return the number of characters copied, which // we need to adjust m_nLength. Doing the V_strncpy with a V_strlen // afterwards took twice as long as this implementations in tests, // so V_strncpy's implementation was used to write this method. char *pDest = m_szBuf + m_nLength; const int maxLen = SIZE_BUF - m_nLength; char *pLast = pDest + maxLen - 1; while ( (pDest < pLast) && (*pchValue != 0) ) { *pDest = *pchValue; ++pDest; ++pchValue; } *pDest = 0; m_nLength = pDest - m_szBuf; } //optimized version of append for just adding a single character void Append( char ch ) { if( m_nLength < SIZE_BUF - 1 ) { m_szBuf[ m_nLength ] = ch; m_nLength++; m_szBuf[ m_nLength ] = '\0'; } } void AppendIndent( uint32 unCount, char chIndent = '\t' ); void SetQuietTruncation( bool bQuiet ) { m_bQuietTruncation = bQuiet; } protected: virtual void InitQuietTruncation() { m_bQuietTruncation = QUIET_TRUNCATION; } bool m_bQuietTruncation; private: char m_szBuf[SIZE_BUF]; int m_nLength; }; // Version which will not assert if strings are truncated template < int SIZE_BUF > class CFmtStrQuietTruncationN : public CFmtStrN { }; template< int SIZE_BUF, bool QUIET_TRUNCATION > void CFmtStrN< SIZE_BUF, QUIET_TRUNCATION >::AppendIndent( uint32 unCount, char chIndent ) { Assert( Length() + unCount < SIZE_BUF ); if( Length() + unCount >= SIZE_BUF ) unCount = SIZE_BUF - (1+Length()); for ( uint32 x = 0; x < unCount; x++ ) { m_szBuf[ m_nLength++ ] = chIndent; } m_szBuf[ m_nLength ] = '\0'; } template< int SIZE_BUF, bool QUIET_TRUNCATION > void CFmtStrN< SIZE_BUF, QUIET_TRUNCATION >::AppendFormatV( const char *pchFormat, va_list args ) { int cubPrinted = V_vsnprintf( m_szBuf+Length(), SIZE_BUF - Length(), pchFormat, args ); m_nLength += cubPrinted; } //----------------------------------------------------------------------------- // // Purpose: Default-sized string formatter // #define FMTSTR_STD_LEN 256 typedef CFmtStrN CFmtStr; typedef CFmtStrQuietTruncationN CFmtStrQuietTruncation; typedef CFmtStrN<32> CFmtStr32; typedef CFmtStrN<1024> CFmtStr1024; typedef CFmtStrN<8192> CFmtStrMax; //----------------------------------------------------------------------------- // Purpose: Fast-path number-to-string helper (with optional quoting) // Derived off of the Steam CNumStr but with a few tweaks, such as // trimming off the in-our-cases-unnecessary strlen calls (by not // storing the length in the class). //----------------------------------------------------------------------------- class CNumStr { public: CNumStr() { m_szBuf[0] = 0; } explicit CNumStr( bool b ) { SetBool( b ); } explicit CNumStr( int8 n8 ) { SetInt8( n8 ); } explicit CNumStr( uint8 un8 ) { SetUint8( un8 ); } explicit CNumStr( int16 n16 ) { SetInt16( n16 ); } explicit CNumStr( uint16 un16 ) { SetUint16( un16 ); } explicit CNumStr( int32 n32 ) { SetInt32( n32 ); } explicit CNumStr( uint32 un32 ) { SetUint32( un32 ); } explicit CNumStr( int64 n64 ) { SetInt64( n64 ); } explicit CNumStr( uint64 un64 ) { SetUint64( un64 ); } #if defined(COMPILER_GCC) && defined(PLATFORM_64BITS) explicit CNumStr( lint64 n64 ) { SetInt64( (int64)n64 ); } explicit CNumStr( ulint64 un64 ) { SetUint64( (uint64)un64 ); } #endif explicit CNumStr( double f ) { SetDouble( f ); } explicit CNumStr( float f ) { SetFloat( f ); } inline void SetBool( bool b ) { Q_memcpy( m_szBuf, b ? "1" : "0", 2 ); } #ifdef _WIN32 inline void SetInt8( int8 n8 ) { _itoa( (int32)n8, m_szBuf, 10 ); } inline void SetUint8( uint8 un8 ) { _itoa( (int32)un8, m_szBuf, 10 ); } inline void SetInt16( int16 n16 ) { _itoa( (int32)n16, m_szBuf, 10 ); } inline void SetUint16( uint16 un16 ) { _itoa( (int32)un16, m_szBuf, 10 ); } inline void SetInt32( int32 n32 ) { _itoa( n32, m_szBuf, 10 ); } inline void SetUint32( uint32 un32 ) { _i64toa( (int64)un32, m_szBuf, 10 ); } inline void SetInt64( int64 n64 ) { _i64toa( n64, m_szBuf, 10 ); } inline void SetUint64( uint64 un64 ) { _ui64toa( un64, m_szBuf, 10 ); } #else inline void SetInt8( int8 n8 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)n8 ); } inline void SetUint8( uint8 un8 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)un8 ); } inline void SetInt16( int16 n16 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)n16 ); } inline void SetUint16( uint16 un16 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)un16 ); } inline void SetInt32( int32 n32 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", n32 ); } inline void SetUint32( uint32 un32 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%u", un32 ); } inline void SetInt64( int64 n64 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%lld", n64 ); } inline void SetUint64( uint64 un64 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%llu", un64 ); } #endif inline void SetDouble( double f ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%.18g", f ); } inline void SetFloat( float f ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%.18g", f ); } inline void SetHexUint64( uint64 un64 ) { Q_binarytohex( (byte *)&un64, sizeof( un64 ), m_szBuf, sizeof( m_szBuf ) ); } operator const char *() const { return m_szBuf; } const char* String() const { return m_szBuf; } void AddQuotes() { Assert( m_szBuf[0] != '"' ); const int nLength = Q_strlen( m_szBuf ); Q_memmove( m_szBuf + 1, m_szBuf, nLength ); m_szBuf[0] = '"'; m_szBuf[nLength + 1] = '"'; m_szBuf[nLength + 2] = 0; } protected: char m_szBuf[28]; // long enough to hold 18 digits of precision, a decimal, a - sign, e+### suffix, and quotes }; //============================================================================= const int k_cchFormattedDate = 64; const int k_cchFormattedTime = 32; bool BGetLocalFormattedTime( time_t timeVal, char *pchDate, int cubDate, char *pchTime, int cubTime ); #endif // FMTSTR_H