// // Copyright (c) 2017-2023, Manticore Software LTD (https://manticoresearch.com) // Copyright (c) 2001-2016, Andrew Aksyonoff // Copyright (c) 2008-2016, Sphinx Technologies Inc // All rights reserved // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License. You should have // received a copy of the GPL license along with this program; if you // did not, you can find it at http://www.gnu.org // #include "num_conv.h" #if __has_include( ) #include #endif inline void StringBuilder_c::AppendRawChunk ( Str_t sText ) // append without any commas { if ( !sText.second ) return; GrowEnough ( sText.second + 1 ); // +1 because we'll put trailing \0 also memcpy ( m_szBuffer + m_iUsed, sText.first, sText.second ); m_iUsed += sText.second; m_szBuffer[m_iUsed] = '\0'; } inline StringBuilder_c& StringBuilder_c::SkipNextComma() { if ( !m_dDelimiters.IsEmpty() ) m_dDelimiters.Last().SkipNext(); return *this; } inline StringBuilder_c& StringBuilder_c::AppendName ( const Str_t& sName, bool bQuoted ) { if ( ::IsEmpty ( sName ) ) return *this; AppendChunk ( sName, bQuoted ? '"' : '\0' ); GrowEnough ( 2 ); m_szBuffer[m_iUsed] = ':'; m_szBuffer[m_iUsed + 1] = '\0'; m_iUsed += 1; return SkipNextComma(); } inline StringBuilder_c& StringBuilder_c::AppendName ( const char* szName, bool bQuoted ) { return AppendName ( FromSz ( szName ), bQuoted ); } inline StringBuilder_c& StringBuilder_c::AppendChunk ( const Str_t& sChunk, char cQuote ) { if ( !sChunk.second ) return *this; auto sComma = Delim(); int iQuote = cQuote != 0; GrowEnough ( sChunk.second + sComma.second + iQuote + iQuote + 1 ); // +1 because we'll put trailing \0 also if ( sComma.second ) memcpy ( m_szBuffer + m_iUsed, sComma.first, sComma.second ); if ( iQuote ) m_szBuffer[m_iUsed + sComma.second] = cQuote; memcpy ( m_szBuffer + m_iUsed + sComma.second + iQuote, sChunk.first, sChunk.second ); m_iUsed += sChunk.second + sComma.second + iQuote + iQuote; if ( iQuote ) m_szBuffer[m_iUsed - 1] = cQuote; m_szBuffer[m_iUsed] = '\0'; return *this; } inline StringBuilder_c& StringBuilder_c::AppendString ( const CSphString& sText, char cQuote ) { return AppendChunk ( FromStr ( sText ), cQuote ); } inline StringBuilder_c& StringBuilder_c::AppendString ( const char* szText, char cQuote ) { return AppendChunk ( FromSz ( szText ), cQuote ); } inline StringBuilder_c& StringBuilder_c::AppendString ( Str_t sText, char cQuote ) { return AppendChunk ( sText, cQuote ); } inline StringBuilder_c& StringBuilder_c::operator+= ( const char* sText ) { if ( !sText || *sText == '\0' ) return *this; return AppendChunk ( { sText, (int)strlen ( sText ) } ); } inline StringBuilder_c& StringBuilder_c::operator<< ( const Str_t& sChunk ) { return AppendChunk ( sChunk ); } inline StringBuilder_c& StringBuilder_c::operator<< ( const VecTraits_T& sText ) { if ( sText.IsEmpty() ) return *this; return AppendChunk ( { sText.begin(), sText.GetLength() } ); } inline StringBuilder_c& StringBuilder_c::operator<< ( int iVal ) { InitAddPrefix(); GrowEnough ( 32 ); #if __has_include( ) SetPos ( std::to_chars ( end(), AfterEnd(), iVal ).ptr ); #else m_iUsed += sph::NtoA ( end(), iVal ); #endif m_szBuffer[m_iUsed] = '\0'; return *this; } inline StringBuilder_c& StringBuilder_c::operator<< ( long iVal ) { InitAddPrefix(); GrowEnough ( 32 ); #if __has_include( ) SetPos ( std::to_chars ( end(), AfterEnd(), iVal ).ptr ); #else m_iUsed += sph::NtoA ( end(), iVal ); #endif m_szBuffer[m_iUsed] = '\0'; return *this; } inline StringBuilder_c& StringBuilder_c::operator<< ( long long iVal ) { InitAddPrefix(); GrowEnough ( 32 ); #if __has_include( ) SetPos ( std::to_chars ( end(), AfterEnd(), iVal ).ptr ); #else m_iUsed += sph::NtoA ( end(), iVal ); #endif m_szBuffer[m_iUsed] = '\0'; return *this; } inline StringBuilder_c& StringBuilder_c::operator<< ( unsigned int uVal ) { InitAddPrefix(); GrowEnough ( 32 ); #if __has_include( ) SetPos ( std::to_chars ( end(), AfterEnd(), uVal ).ptr ); #else m_iUsed += sph::NtoA ( end(), uVal ); #endif m_szBuffer[m_iUsed] = '\0'; return *this; } inline StringBuilder_c& StringBuilder_c::operator<< ( unsigned long uVal ) { InitAddPrefix(); GrowEnough ( 32 ); #if __has_include( ) SetPos ( std::to_chars ( end(), AfterEnd(), uVal ).ptr ); #else m_iUsed += sph::NtoA ( end(), uVal ); #endif m_szBuffer[m_iUsed] = '\0'; return *this; } inline StringBuilder_c& StringBuilder_c::operator<< ( unsigned long long uVal ) { InitAddPrefix(); GrowEnough ( 32 ); #if __has_include( ) SetPos ( std::to_chars ( end(), AfterEnd(), uVal ).ptr ); #else m_iUsed += sph::NtoA ( end(), uVal ); #endif m_szBuffer[m_iUsed] = '\0'; return *this; } template StringBuilder_c& StringBuilder_c::operator+= ( const T* pVal ) { InitAddPrefix(); AppendRawChunk ( FROMS ( "0x" ) ); GrowEnough ( sizeof ( void* ) * 2 ); m_iUsed += sph::NtoA ( end(), reinterpret_cast ( pVal ), 16, sizeof ( void* ) * 2, 0, '0' ); m_szBuffer[m_iUsed] = '\0'; return *this; } inline StringBuilder_c& StringBuilder_c::operator<< ( bool bVal ) { if ( bVal ) return *this << "true"; return *this << "false"; } inline void StringBuilder_c::Clear() { Rewind(); m_dDelimiters.Reset(); } inline void StringBuilder_c::Rewind() { if ( m_szBuffer ) m_szBuffer[0] = '\0'; m_iUsed = 0; } template inline void StringBuilder_c::NtoA ( INT uVal ) { InitAddPrefix(); const int MAX_NUMERIC_STR = 22; GrowEnough ( MAX_NUMERIC_STR + 1 ); #if __has_include( ) if constexpr ( iWidth == 0 && iPrec == 0 && cFill == ' ' ) SetPos ( std::to_chars ( end(), AfterEnd(), uVal, iBase ).ptr ); else m_iUsed += sph::NtoA ( end(), uVal, iBase, iWidth, iPrec, cFill ); #else m_iUsed += sph::NtoA ( end(), uVal, iBase, iWidth, iPrec, cFill ); #endif m_szBuffer[m_iUsed] = '\0'; } template inline void StringBuilder_c::IFtoA ( FixedFrac_T tVal ) { InitAddPrefix(); const int MAX_NUMERIC_STR = 22; GrowEnough ( MAX_NUMERIC_STR + 1 ); int iLen = sph::IFtoA ( end(), tVal.m_tVal, iPrec ); m_iUsed += iLen; m_szBuffer[m_iUsed] = '\0'; } // shrink, if necessary, to be able to fit at least iLen more chars inline void StringBuilder_c::GrowEnough ( int iLen ) { if ( m_iUsed + iLen fnApply = [this, &iLast, &fnApply]() { --iLast; if ( iLast>=0 ) AppendRawChunk( m_dDelimiters[iLast].RawComma( fnApply )); }; return m_dDelimiters.Last().RawComma( fnApply ); } inline void StringBuilder_c::LazyComma_c::Swap ( LazyComma_c & rhs ) noexcept { Comma_c::Swap ( rhs ); m_sPrefix.swap ( rhs.m_sPrefix ); m_sSuffix.swap ( rhs.m_sSuffix ); ::Swap ( m_bSkipNext, rhs.m_bSkipNext ); } template StringBuilder_c& StringBuilder_c::Sprint ( Params&&... tValues ) { (void)std::initializer_list { ( *this << std::forward(tValues), 0 )... }; return *this; } inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, const CSphNamedInt& tValue ) { tOut.Sprintf ( "%s=%d", tValue.first.cstr(), tValue.second ); return tOut; } inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, timespan_t tVal ) { tOut.Sprintf ( "%t", tVal.m_iVal ); return tOut; } inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, timestamp_t tVal ) { tOut.Sprintf ( "%T", tVal.m_iVal ); return tOut; } template inline StringBuilder_c& operator<< ( StringBuilder_c& tOut, FixedFrac_T tVal ) { tOut.template IFtoA(tVal); return tOut; } template StringBuilder_c& operator<< ( StringBuilder_c& tOut, FixedNum_T tVal ) { tOut.template NtoA ( tVal.m_tVal ); return tOut; }