You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
11 KiB
366 lines
11 KiB
//========= Copyright 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 <stdarg.h> |
|
#include <stdio.h> |
|
#include "tier0/platform.h" |
|
#include "tier0/dbg.h" |
|
#include "tier1/strtools.h" |
|
|
|
#if defined( _WIN32 ) |
|
#pragma once |
|
#endif |
|
#if defined(POSIX) |
|
#pragma GCC visibility push(hidden) |
|
#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 <int SIZE_BUF, bool QUIET_TRUNCATION = false > |
|
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; |
|
} |
|
|
|
// 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<SIZE_BUF,QUIET_TRUNCATION> & operator=( const char *pchValue ) |
|
{ |
|
V_strncpy( m_szBuf, pchValue, SIZE_BUF ); |
|
m_nLength = V_strlen( m_szBuf ); |
|
return *this; |
|
} |
|
|
|
CFmtStrN<SIZE_BUF,QUIET_TRUNCATION> & 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<SIZE_BUF, true > |
|
{ |
|
}; |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
#if defined(POSIX) |
|
#pragma GCC visibility pop |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Purpose: Default-sized string formatter |
|
// |
|
|
|
#define FMTSTR_STD_LEN 256 |
|
|
|
typedef CFmtStrN<FMTSTR_STD_LEN> CFmtStr; |
|
typedef CFmtStrQuietTruncationN<FMTSTR_STD_LEN> CFmtStrQuietTruncation; |
|
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 |
|
|
|
}; |
|
|
|
|
|
//============================================================================= |
|
|
|
bool BGetLocalFormattedDateAndTime( time_t timeVal, char *pchDate, int cubDate, char *pchTime, int cubTime ); |
|
bool BGetLocalFormattedDate( time_t timeVal, char *pchDate, int cubDate ); |
|
bool BGetLocalFormattedTime( time_t timeVal, char *pchTime, int cubTime ); |
|
|
|
#endif // FMTSTR_H
|
|
|