Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A simple class for performing safe and in-expression sprintf-style
  4. // string formatting
  5. //
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #ifndef FMTSTR_H
  9. #define FMTSTR_H
  10. #include <stdarg.h>
  11. #include <stdio.h>
  12. #include "tier0/platform.h"
  13. #include "tier0/dbg.h"
  14. #include "tier1/strtools.h"
  15. #if defined( _WIN32 )
  16. #pragma once
  17. #endif
  18. #if defined(POSIX)
  19. #pragma GCC visibility push(hidden)
  20. #endif
  21. //=============================================================================
  22. // using macro to be compatable with GCC
  23. #define FmtStrVSNPrintf( szBuf, nBufSize, bQuietTruncation, ppszFormat, nPrevLen, lastArg ) \
  24. do \
  25. { \
  26. int result; \
  27. va_list arg_ptr; \
  28. bool bTruncated = false; \
  29. static int scAsserted = 0; \
  30. \
  31. va_start(arg_ptr, lastArg); \
  32. result = V_vsnprintfRet( (szBuf), (nBufSize)-1, (*(ppszFormat)), arg_ptr, &bTruncated ); \
  33. va_end(arg_ptr); \
  34. \
  35. (szBuf)[(nBufSize)-1] = 0; \
  36. if ( bTruncated && !(bQuietTruncation) && scAsserted < 5 ) \
  37. { \
  38. Warning( "FmtStrVSNPrintf truncated to %d without QUIET_TRUNCATION specified!\n", ( int )( nBufSize ) ); \
  39. AssertMsg( 0, "FmtStrVSNPrintf truncated without QUIET_TRUNCATION specified!\n" ); \
  40. scAsserted++; \
  41. } \
  42. m_nLength = nPrevLen + result; \
  43. } \
  44. while (0)
  45. // using macro to be compatable with GCC
  46. #define FmtStrVSNPrintfNoLengthFixup( szBuf, nBufSize, bQuietTruncation, ppszFormat, nPrevLen, lastArg ) \
  47. do \
  48. { \
  49. int result; \
  50. va_list arg_ptr; \
  51. bool bTruncated = false; \
  52. static int scAsserted = 0; \
  53. \
  54. va_start(arg_ptr, lastArg); \
  55. result = V_vsnprintfRet( (szBuf), (nBufSize)-1, (*(ppszFormat)), arg_ptr, &bTruncated ); \
  56. va_end(arg_ptr); \
  57. \
  58. (szBuf)[(nBufSize)-1] = 0; \
  59. if ( bTruncated && !(bQuietTruncation) && scAsserted < 5 ) \
  60. { \
  61. Warning( "FmtStrVSNPrintf truncated to %d without QUIET_TRUNCATION specified!\n", ( int )( nBufSize ) ); \
  62. AssertMsg( 0, "FmtStrVSNPrintf truncated without QUIET_TRUNCATION specified!\n" ); \
  63. scAsserted++; \
  64. } \
  65. } \
  66. while (0)
  67. //-----------------------------------------------------------------------------
  68. //
  69. // Purpose: String formatter with specified size
  70. //
  71. template <int SIZE_BUF, bool QUIET_TRUNCATION = false >
  72. class CFmtStrN
  73. {
  74. public:
  75. CFmtStrN()
  76. {
  77. InitQuietTruncation();
  78. m_szBuf[0] = 0;
  79. m_nLength = 0;
  80. }
  81. // Standard C formatting
  82. CFmtStrN(PRINTF_FORMAT_STRING const char *pszFormat, ...) FMTFUNCTION( 2, 3 )
  83. {
  84. InitQuietTruncation();
  85. FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat );
  86. }
  87. // Use this for pass-through formatting
  88. CFmtStrN(const char ** ppszFormat, ...)
  89. {
  90. InitQuietTruncation();
  91. FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, ppszFormat, 0, ppszFormat );
  92. }
  93. // Explicit reformat
  94. const char *sprintf(PRINTF_FORMAT_STRING const char *pszFormat, ...) FMTFUNCTION( 2, 3 )
  95. {
  96. InitQuietTruncation();
  97. FmtStrVSNPrintf(m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat );
  98. return m_szBuf;
  99. }
  100. // Use this for va_list formatting
  101. const char *sprintf_argv(const char *pszFormat, va_list arg_ptr)
  102. {
  103. int result;
  104. bool bTruncated = false;
  105. static int s_nWarned = 0;
  106. InitQuietTruncation();
  107. result = V_vsnprintfRet( m_szBuf, SIZE_BUF - 1, pszFormat, arg_ptr, &bTruncated );
  108. m_szBuf[SIZE_BUF - 1] = 0;
  109. if ( bTruncated && !m_bQuietTruncation && ( s_nWarned < 5 ) )
  110. {
  111. Warning( "CFmtStr truncated to %d without QUIET_TRUNCATION specified!\n", SIZE_BUF );
  112. AssertMsg( 0, "CFmtStr truncated without QUIET_TRUNCATION specified!\n" );
  113. s_nWarned++;
  114. }
  115. m_nLength = V_strlen( m_szBuf );
  116. return m_szBuf;
  117. }
  118. // Use this for pass-through formatting
  119. void VSprintf(const char **ppszFormat, ...)
  120. {
  121. InitQuietTruncation();
  122. FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, ppszFormat, 0, ppszFormat );
  123. }
  124. // Compatible API with CUtlString for converting to const char*
  125. const char *Get( ) const { return m_szBuf; }
  126. const char *String( ) const { return m_szBuf; }
  127. // Use for access
  128. operator const char *() const { return m_szBuf; }
  129. char *Access() { return m_szBuf; }
  130. // Access template argument
  131. static inline int GetMaxLength() { return SIZE_BUF-1; }
  132. CFmtStrN<SIZE_BUF,QUIET_TRUNCATION> & operator=( const char *pchValue )
  133. {
  134. V_strncpy( m_szBuf, pchValue, SIZE_BUF );
  135. m_nLength = V_strlen( m_szBuf );
  136. return *this;
  137. }
  138. CFmtStrN<SIZE_BUF,QUIET_TRUNCATION> & operator+=( const char *pchValue )
  139. {
  140. Append( pchValue );
  141. return *this;
  142. }
  143. int Length() const { return m_nLength; }
  144. void SetLength( int nLength )
  145. {
  146. m_nLength = Min( nLength, SIZE_BUF - 1 );
  147. m_szBuf[m_nLength] = '\0';
  148. }
  149. void Clear()
  150. {
  151. m_szBuf[0] = 0;
  152. m_nLength = 0;
  153. }
  154. void AppendFormat( PRINTF_FORMAT_STRING const char *pchFormat, ... ) FMTFUNCTION( 2, 3 )
  155. {
  156. char *pchEnd = m_szBuf + m_nLength;
  157. FmtStrVSNPrintf( pchEnd, SIZE_BUF - m_nLength, m_bQuietTruncation, &pchFormat, m_nLength, pchFormat );
  158. }
  159. void AppendFormatV( const char *pchFormat, va_list args );
  160. void Append( const char *pchValue )
  161. {
  162. // This function is close to the metal to cut down on the CPU cost
  163. // of the previous incantation of Append which was implemented as
  164. // AppendFormat( "%s", pchValue ). This implementation, though not
  165. // as easy to read, instead does a strcpy from the existing end
  166. // point of the CFmtStrN. This brings something like a 10-20x speedup
  167. // in my rudimentary tests. It isn't using V_strncpy because that
  168. // function doesn't return the number of characters copied, which
  169. // we need to adjust m_nLength. Doing the V_strncpy with a V_strlen
  170. // afterwards took twice as long as this implementations in tests,
  171. // so V_strncpy's implementation was used to write this method.
  172. char *pDest = m_szBuf + m_nLength;
  173. const int maxLen = SIZE_BUF - m_nLength;
  174. char *pLast = pDest + maxLen - 1;
  175. while ( (pDest < pLast) && (*pchValue != 0) )
  176. {
  177. *pDest = *pchValue;
  178. ++pDest; ++pchValue;
  179. }
  180. *pDest = 0;
  181. m_nLength = pDest - m_szBuf;
  182. }
  183. //optimized version of append for just adding a single character
  184. void Append( char ch )
  185. {
  186. if( m_nLength < SIZE_BUF - 1 )
  187. {
  188. m_szBuf[ m_nLength ] = ch;
  189. m_nLength++;
  190. m_szBuf[ m_nLength ] = '\0';
  191. }
  192. }
  193. void AppendIndent( uint32 unCount, char chIndent = '\t' );
  194. void SetQuietTruncation( bool bQuiet ) { m_bQuietTruncation = bQuiet; }
  195. protected:
  196. virtual void InitQuietTruncation()
  197. {
  198. m_bQuietTruncation = QUIET_TRUNCATION;
  199. }
  200. bool m_bQuietTruncation;
  201. private:
  202. char m_szBuf[SIZE_BUF];
  203. int m_nLength;
  204. };
  205. // Version which will not assert if strings are truncated
  206. template < int SIZE_BUF >
  207. class CFmtStrQuietTruncationN : public CFmtStrN<SIZE_BUF, true >
  208. {
  209. };
  210. template< int SIZE_BUF, bool QUIET_TRUNCATION >
  211. void CFmtStrN< SIZE_BUF, QUIET_TRUNCATION >::AppendIndent( uint32 unCount, char chIndent )
  212. {
  213. Assert( Length() + unCount < SIZE_BUF );
  214. if( Length() + unCount >= SIZE_BUF )
  215. unCount = SIZE_BUF - (1+Length());
  216. for ( uint32 x = 0; x < unCount; x++ )
  217. {
  218. m_szBuf[ m_nLength++ ] = chIndent;
  219. }
  220. m_szBuf[ m_nLength ] = '\0';
  221. }
  222. template< int SIZE_BUF, bool QUIET_TRUNCATION >
  223. void CFmtStrN< SIZE_BUF, QUIET_TRUNCATION >::AppendFormatV( const char *pchFormat, va_list args )
  224. {
  225. int cubPrinted = V_vsnprintf( m_szBuf+Length(), SIZE_BUF - Length(), pchFormat, args );
  226. m_nLength += cubPrinted;
  227. }
  228. #if defined(POSIX)
  229. #pragma GCC visibility pop
  230. #endif
  231. //-----------------------------------------------------------------------------
  232. //
  233. // Purpose: Default-sized string formatter
  234. //
  235. #define FMTSTR_STD_LEN 256
  236. typedef CFmtStrN<FMTSTR_STD_LEN> CFmtStr;
  237. typedef CFmtStrQuietTruncationN<FMTSTR_STD_LEN> CFmtStrQuietTruncation;
  238. typedef CFmtStrN<1024> CFmtStr1024;
  239. typedef CFmtStrN<8192> CFmtStrMax;
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Fast-path number-to-string helper (with optional quoting)
  242. // Derived off of the Steam CNumStr but with a few tweaks, such as
  243. // trimming off the in-our-cases-unnecessary strlen calls (by not
  244. // storing the length in the class).
  245. //-----------------------------------------------------------------------------
  246. class CNumStr
  247. {
  248. public:
  249. CNumStr() { m_szBuf[0] = 0; }
  250. explicit CNumStr( bool b ) { SetBool( b ); }
  251. explicit CNumStr( int8 n8 ) { SetInt8( n8 ); }
  252. explicit CNumStr( uint8 un8 ) { SetUint8( un8 ); }
  253. explicit CNumStr( int16 n16 ) { SetInt16( n16 ); }
  254. explicit CNumStr( uint16 un16 ) { SetUint16( un16 ); }
  255. explicit CNumStr( int32 n32 ) { SetInt32( n32 ); }
  256. explicit CNumStr( uint32 un32 ) { SetUint32( un32 ); }
  257. explicit CNumStr( int64 n64 ) { SetInt64( n64 ); }
  258. explicit CNumStr( uint64 un64 ) { SetUint64( un64 ); }
  259. #if defined(COMPILER_GCC) && defined(PLATFORM_64BITS)
  260. explicit CNumStr( lint64 n64 ) { SetInt64( (int64)n64 ); }
  261. explicit CNumStr( ulint64 un64 ) { SetUint64( (uint64)un64 ); }
  262. #endif
  263. explicit CNumStr( double f ) { SetDouble( f ); }
  264. explicit CNumStr( float f ) { SetFloat( f ); }
  265. inline void SetBool( bool b ) { Q_memcpy( m_szBuf, b ? "1" : "0", 2 ); }
  266. #ifdef _WIN32
  267. inline void SetInt8( int8 n8 ) { _itoa( (int32)n8, m_szBuf, 10 ); }
  268. inline void SetUint8( uint8 un8 ) { _itoa( (int32)un8, m_szBuf, 10 ); }
  269. inline void SetInt16( int16 n16 ) { _itoa( (int32)n16, m_szBuf, 10 ); }
  270. inline void SetUint16( uint16 un16 ) { _itoa( (int32)un16, m_szBuf, 10 ); }
  271. inline void SetInt32( int32 n32 ) { _itoa( n32, m_szBuf, 10 ); }
  272. inline void SetUint32( uint32 un32 ) { _i64toa( (int64)un32, m_szBuf, 10 ); }
  273. inline void SetInt64( int64 n64 ) { _i64toa( n64, m_szBuf, 10 ); }
  274. inline void SetUint64( uint64 un64 ) { _ui64toa( un64, m_szBuf, 10 ); }
  275. #else
  276. inline void SetInt8( int8 n8 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)n8 ); }
  277. inline void SetUint8( uint8 un8 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)un8 ); }
  278. inline void SetInt16( int16 n16 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)n16 ); }
  279. inline void SetUint16( uint16 un16 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)un16 ); }
  280. inline void SetInt32( int32 n32 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", n32 ); }
  281. inline void SetUint32( uint32 un32 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%u", un32 ); }
  282. inline void SetInt64( int64 n64 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%lld", n64 ); }
  283. inline void SetUint64( uint64 un64 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%llu", un64 ); }
  284. #endif
  285. inline void SetDouble( double f ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%.18g", f ); }
  286. inline void SetFloat( float f ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%.18g", f ); }
  287. inline void SetHexUint64( uint64 un64 ) { Q_binarytohex( (byte *)&un64, sizeof( un64 ), m_szBuf, sizeof( m_szBuf ) ); }
  288. operator const char *() const { return m_szBuf; }
  289. const char* String() const { return m_szBuf; }
  290. void AddQuotes()
  291. {
  292. Assert( m_szBuf[0] != '"' );
  293. const int nLength = Q_strlen( m_szBuf );
  294. Q_memmove( m_szBuf + 1, m_szBuf, nLength );
  295. m_szBuf[0] = '"';
  296. m_szBuf[nLength + 1] = '"';
  297. m_szBuf[nLength + 2] = 0;
  298. }
  299. protected:
  300. char m_szBuf[28]; // long enough to hold 18 digits of precision, a decimal, a - sign, e+### suffix, and quotes
  301. };
  302. //=============================================================================
  303. bool BGetLocalFormattedDateAndTime( time_t timeVal, char *pchDate, int cubDate, char *pchTime, int cubTime );
  304. bool BGetLocalFormattedDate( time_t timeVal, char *pchDate, int cubDate );
  305. bool BGetLocalFormattedTime( time_t timeVal, char *pchTime, int cubTime );
  306. #endif // FMTSTR_H