stringbuilder_impl.h 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. //
  2. // Copyright (c) 2017-2023, Manticore Software LTD (https://manticoresearch.com)
  3. // Copyright (c) 2001-2016, Andrew Aksyonoff
  4. // Copyright (c) 2008-2016, Sphinx Technologies Inc
  5. // All rights reserved
  6. //
  7. // This program is free software; you can redistribute it and/or modify
  8. // it under the terms of the GNU General Public License. You should have
  9. // received a copy of the GPL license along with this program; if you
  10. // did not, you can find it at http://www.gnu.org
  11. //
  12. #include "num_conv.h"
  13. #if __has_include( <charconv>)
  14. #include <charconv>
  15. #endif
  16. inline void StringBuilder_c::AppendRawChunk ( Str_t sText ) // append without any commas
  17. {
  18. if ( !sText.second )
  19. return;
  20. GrowEnough ( sText.second + 1 ); // +1 because we'll put trailing \0 also
  21. memcpy ( m_szBuffer + m_iUsed, sText.first, sText.second );
  22. m_iUsed += sText.second;
  23. m_szBuffer[m_iUsed] = '\0';
  24. }
  25. inline StringBuilder_c& StringBuilder_c::SkipNextComma()
  26. {
  27. if ( !m_dDelimiters.IsEmpty() )
  28. m_dDelimiters.Last().SkipNext();
  29. return *this;
  30. }
  31. inline StringBuilder_c& StringBuilder_c::AppendName ( const Str_t& sName, bool bQuoted )
  32. {
  33. if ( ::IsEmpty ( sName ) )
  34. return *this;
  35. AppendChunk ( sName, bQuoted ? '"' : '\0' );
  36. GrowEnough ( 2 );
  37. m_szBuffer[m_iUsed] = ':';
  38. m_szBuffer[m_iUsed + 1] = '\0';
  39. m_iUsed += 1;
  40. return SkipNextComma();
  41. }
  42. inline StringBuilder_c& StringBuilder_c::AppendName ( const char* szName, bool bQuoted )
  43. {
  44. return AppendName ( FromSz ( szName ), bQuoted );
  45. }
  46. inline StringBuilder_c& StringBuilder_c::AppendChunk ( const Str_t& sChunk, char cQuote )
  47. {
  48. if ( !sChunk.second )
  49. return *this;
  50. auto sComma = Delim();
  51. int iQuote = cQuote != 0;
  52. GrowEnough ( sChunk.second + sComma.second + iQuote + iQuote + 1 ); // +1 because we'll put trailing \0 also
  53. if ( sComma.second )
  54. memcpy ( m_szBuffer + m_iUsed, sComma.first, sComma.second );
  55. if ( iQuote )
  56. m_szBuffer[m_iUsed + sComma.second] = cQuote;
  57. memcpy ( m_szBuffer + m_iUsed + sComma.second + iQuote, sChunk.first, sChunk.second );
  58. m_iUsed += sChunk.second + sComma.second + iQuote + iQuote;
  59. if ( iQuote )
  60. m_szBuffer[m_iUsed - 1] = cQuote;
  61. m_szBuffer[m_iUsed] = '\0';
  62. return *this;
  63. }
  64. inline StringBuilder_c& StringBuilder_c::AppendString ( const CSphString& sText, char cQuote )
  65. {
  66. return AppendChunk ( FromStr ( sText ), cQuote );
  67. }
  68. inline StringBuilder_c& StringBuilder_c::AppendString ( const char* szText, char cQuote )
  69. {
  70. return AppendChunk ( FromSz ( szText ), cQuote );
  71. }
  72. inline StringBuilder_c& StringBuilder_c::AppendString ( Str_t sText, char cQuote )
  73. {
  74. return AppendChunk ( sText, cQuote );
  75. }
  76. inline StringBuilder_c& StringBuilder_c::operator+= ( const char* sText )
  77. {
  78. if ( !sText || *sText == '\0' )
  79. return *this;
  80. return AppendChunk ( { sText, (int)strlen ( sText ) } );
  81. }
  82. inline StringBuilder_c& StringBuilder_c::operator<< ( const Str_t& sChunk )
  83. {
  84. return AppendChunk ( sChunk );
  85. }
  86. inline StringBuilder_c& StringBuilder_c::operator<< ( const VecTraits_T<char>& sText )
  87. {
  88. if ( sText.IsEmpty() )
  89. return *this;
  90. return AppendChunk ( { sText.begin(), sText.GetLength() } );
  91. }
  92. inline StringBuilder_c& StringBuilder_c::operator<< ( int iVal )
  93. {
  94. InitAddPrefix();
  95. GrowEnough ( 32 );
  96. #if __has_include( <charconv>)
  97. SetPos ( std::to_chars ( end(), AfterEnd(), iVal ).ptr );
  98. #else
  99. m_iUsed += sph::NtoA ( end(), iVal );
  100. #endif
  101. m_szBuffer[m_iUsed] = '\0';
  102. return *this;
  103. }
  104. inline StringBuilder_c& StringBuilder_c::operator<< ( long iVal )
  105. {
  106. InitAddPrefix();
  107. GrowEnough ( 32 );
  108. #if __has_include( <charconv>)
  109. SetPos ( std::to_chars ( end(), AfterEnd(), iVal ).ptr );
  110. #else
  111. m_iUsed += sph::NtoA ( end(), iVal );
  112. #endif
  113. m_szBuffer[m_iUsed] = '\0';
  114. return *this;
  115. }
  116. inline StringBuilder_c& StringBuilder_c::operator<< ( long long iVal )
  117. {
  118. InitAddPrefix();
  119. GrowEnough ( 32 );
  120. #if __has_include( <charconv>)
  121. SetPos ( std::to_chars ( end(), AfterEnd(), iVal ).ptr );
  122. #else
  123. m_iUsed += sph::NtoA ( end(), iVal );
  124. #endif
  125. m_szBuffer[m_iUsed] = '\0';
  126. return *this;
  127. }
  128. inline StringBuilder_c& StringBuilder_c::operator<< ( unsigned int uVal )
  129. {
  130. InitAddPrefix();
  131. GrowEnough ( 32 );
  132. #if __has_include( <charconv>)
  133. SetPos ( std::to_chars ( end(), AfterEnd(), uVal ).ptr );
  134. #else
  135. m_iUsed += sph::NtoA ( end(), uVal );
  136. #endif
  137. m_szBuffer[m_iUsed] = '\0';
  138. return *this;
  139. }
  140. inline StringBuilder_c& StringBuilder_c::operator<< ( unsigned long uVal )
  141. {
  142. InitAddPrefix();
  143. GrowEnough ( 32 );
  144. #if __has_include( <charconv>)
  145. SetPos ( std::to_chars ( end(), AfterEnd(), uVal ).ptr );
  146. #else
  147. m_iUsed += sph::NtoA ( end(), uVal );
  148. #endif
  149. m_szBuffer[m_iUsed] = '\0';
  150. return *this;
  151. }
  152. inline StringBuilder_c& StringBuilder_c::operator<< ( unsigned long long uVal )
  153. {
  154. InitAddPrefix();
  155. GrowEnough ( 32 );
  156. #if __has_include( <charconv>)
  157. SetPos ( std::to_chars ( end(), AfterEnd(), uVal ).ptr );
  158. #else
  159. m_iUsed += sph::NtoA ( end(), uVal );
  160. #endif
  161. m_szBuffer[m_iUsed] = '\0';
  162. return *this;
  163. }
  164. template<typename T>
  165. StringBuilder_c& StringBuilder_c::operator+= ( const T* pVal )
  166. {
  167. InitAddPrefix();
  168. AppendRawChunk ( FROMS ( "0x" ) );
  169. GrowEnough ( sizeof ( void* ) * 2 );
  170. m_iUsed += sph::NtoA ( end(), reinterpret_cast<uintptr_t> ( pVal ), 16, sizeof ( void* ) * 2, 0, '0' );
  171. m_szBuffer[m_iUsed] = '\0';
  172. return *this;
  173. }
  174. inline StringBuilder_c& StringBuilder_c::operator<< ( bool bVal )
  175. {
  176. if ( bVal )
  177. return *this << "true";
  178. return *this << "false";
  179. }
  180. inline void StringBuilder_c::Clear()
  181. {
  182. Rewind();
  183. m_dDelimiters.Reset();
  184. }
  185. inline void StringBuilder_c::Rewind()
  186. {
  187. if ( m_szBuffer )
  188. m_szBuffer[0] = '\0';
  189. m_iUsed = 0;
  190. }
  191. template<typename INT, int iBase, int iWidth, int iPrec, char cFill>
  192. inline void StringBuilder_c::NtoA ( INT uVal )
  193. {
  194. InitAddPrefix();
  195. const int MAX_NUMERIC_STR = 22;
  196. GrowEnough ( MAX_NUMERIC_STR + 1 );
  197. #if __has_include( <charconv>)
  198. if constexpr ( iWidth == 0 && iPrec == 0 && cFill == ' ' )
  199. SetPos ( std::to_chars ( end(), AfterEnd(), uVal, iBase ).ptr );
  200. else
  201. m_iUsed += sph::NtoA ( end(), uVal, iBase, iWidth, iPrec, cFill );
  202. #else
  203. m_iUsed += sph::NtoA ( end(), uVal, iBase, iWidth, iPrec, cFill );
  204. #endif
  205. m_szBuffer[m_iUsed] = '\0';
  206. }
  207. template<typename INT, int iPrec>
  208. inline void StringBuilder_c::IFtoA ( FixedFrac_T<INT, iPrec> tVal )
  209. {
  210. InitAddPrefix();
  211. const int MAX_NUMERIC_STR = 22;
  212. GrowEnough ( MAX_NUMERIC_STR + 1 );
  213. int iLen = sph::IFtoA ( end(), tVal.m_tVal, iPrec );
  214. m_iUsed += iLen;
  215. m_szBuffer[m_iUsed] = '\0';
  216. }
  217. // shrink, if necessary, to be able to fit at least iLen more chars
  218. inline void StringBuilder_c::GrowEnough ( int iLen )
  219. {
  220. if ( m_iUsed + iLen<m_iSize )
  221. return;
  222. Grow ( iLen );
  223. }
  224. inline void StringBuilder_c::InitAddPrefix()
  225. {
  226. if ( !m_szBuffer )
  227. InitBuffer();
  228. assert ( m_iUsed==0 || m_iUsed<m_iSize );
  229. auto sPrefix = Delim();
  230. if ( sPrefix.second ) // prepend delimiter first...
  231. {
  232. GrowEnough ( sPrefix.second );
  233. memcpy ( m_szBuffer + m_iUsed, sPrefix.first, sPrefix.second );
  234. m_iUsed += sPrefix.second;
  235. }
  236. }
  237. inline const Str_t & StringBuilder_c::Delim ()
  238. {
  239. if ( m_dDelimiters.IsEmpty ())
  240. return dEmptyStr;
  241. int iLast = m_dDelimiters.GetLength()-1;
  242. std::function<void()> fnApply = [this, &iLast, &fnApply]()
  243. {
  244. --iLast;
  245. if ( iLast>=0 )
  246. AppendRawChunk( m_dDelimiters[iLast].RawComma( fnApply ));
  247. };
  248. return m_dDelimiters.Last().RawComma( fnApply );
  249. }
  250. inline void StringBuilder_c::LazyComma_c::Swap ( LazyComma_c & rhs ) noexcept
  251. {
  252. Comma_c::Swap ( rhs );
  253. m_sPrefix.swap ( rhs.m_sPrefix );
  254. m_sSuffix.swap ( rhs.m_sSuffix );
  255. ::Swap ( m_bSkipNext, rhs.m_bSkipNext );
  256. }
  257. template<typename... Params>
  258. StringBuilder_c& StringBuilder_c::Sprint ( Params&&... tValues )
  259. {
  260. (void)std::initializer_list<int> { ( *this << std::forward<Params>(tValues), 0 )... };
  261. return *this;
  262. }
  263. inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, const CSphNamedInt& tValue )
  264. {
  265. tOut.Sprintf ( "%s=%d", tValue.first.cstr(), tValue.second );
  266. return tOut;
  267. }
  268. inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, timespan_t tVal )
  269. {
  270. tOut.Sprintf ( "%t", tVal.m_iVal );
  271. return tOut;
  272. }
  273. inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, timestamp_t tVal )
  274. {
  275. tOut.Sprintf ( "%T", tVal.m_iVal );
  276. return tOut;
  277. }
  278. template<typename INT, int iPrec>
  279. inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, FixedFrac_T<INT, iPrec> tVal )
  280. {
  281. tOut.template IFtoA<INT, iPrec>(tVal);
  282. return tOut;
  283. }
  284. template<typename INT, int iBase, int iWidth, int iPrec, char cFill>
  285. StringBuilder_c& operator<< ( StringBuilder_c& tOut, FixedNum_T<INT, iBase, iWidth, iPrec, cFill> tVal )
  286. {
  287. tOut.template NtoA<INT, iBase, iWidth, iPrec, cFill> ( tVal.m_tVal );
  288. return tOut;
  289. }