#include <vrcore/strtools_public.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <codecvt>
#include <iostream>
#include <functional>
#include <locale>
#include <codecvt>
#include <cstdarg>
#if !defined( VRCORE_NO_PLATFORM )
#include <vrcore/assert.h>
#else
#define AssertMsg( cond, ... )
#endif
#if defined( _WIN32 )
#include <windows.h>
#endif
#if defined( OSX ) || defined( LINUX )
int stricmp( const char *pStr1, const char *pStr2 )
{
return strcasecmp( pStr1, pStr2 );
}
int strnicmp( const char *pStr1, const char *pStr2, size_t unBufferLen )
{
return strncasecmp( pStr1, pStr2, unBufferLen );
}
#endif
bool StringHasPrefix( const std::string & sString, const std::string & sPrefix )
{
return 0 == strnicmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
}
bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix )
{
return 0 == strncmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
}
bool StringHasSuffix( const std::string &sString, const std::string &sSuffix )
{
size_t cStrLen = sString.length();
size_t cSuffixLen = sSuffix.length();
if ( cSuffixLen > cStrLen )
return false;
std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
return 0 == stricmp( sStringSuffix.c_str(), sSuffix.c_str() );
}
bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix )
{
size_t cStrLen = sString.length();
size_t cSuffixLen = sSuffix.length();
if ( cSuffixLen > cStrLen )
return false;
std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
return 0 == strncmp( sStringSuffix.c_str(), sSuffix.c_str(),cSuffixLen );
}
std::string StringReplace( const std::string &sString, const std::string &sFind, const std::string &sReplace )
{
size_t cStartPos = 0;
size_t cFindLen = sFind.length();
size_t cReplaceLen = sReplace.length();
if ( cFindLen == 0 )
return sString;
std::string sResult = sString;
while ( ( cStartPos = sResult.find( sFind, cStartPos ) ) != std::string::npos )
{
sResult.replace( cStartPos, cFindLen, sReplace );
cStartPos += cReplaceLen;
}
return sResult;
}
std::string UTF16to8( const std::wstring &in )
{
static std::wstring_convert< std::codecvt_utf8_utf16< wchar_t >, wchar_t > s_convert;
try
{
return s_convert.to_bytes( in );
}
catch ( ... )
{
return std::string();
}
}
std::string UTF16to8( const wchar_t * in )
{
if (in == nullptr)
{
return std::string();
}
std::wstring wstr( in );
return UTF16to8( wstr );
}
std::wstring UTF8to16( const std::string &in )
{
static std::wstring_convert< std::codecvt_utf8_utf16< wchar_t >, wchar_t > s_convert;
try
{
return s_convert.from_bytes( in );
}
catch ( ... )
{
return std::wstring();
}
}
std::wstring UTF8to16( const char * in )
{
if ( in == nullptr )
{
return std::wstring();
}
std::string str( in );
return UTF8to16( str );
}
std::string Format( const char *pchFormat, ... )
{
static constexpr size_t k_ulMaxStackString = 4096;
va_list args;
char pchBuffer[k_ulMaxStackString];
va_start( args, pchFormat );
int unSize = vsnprintf( pchBuffer, sizeof( pchBuffer ), pchFormat, args );
va_end( args );
if ( unSize < 0 )
{
AssertMsg( false, "Format string parse failure" );
return "";
}
if ( unSize < k_ulMaxStackString )
{
return pchBuffer;
}
std::vector< char > vecChar{};
vecChar.resize( unSize + 1 );
va_start( args, pchFormat );
unSize = vsnprintf( vecChar.data(), vecChar.size(), pchFormat, args );
va_end( args );
if ( unSize < 0 )
{
AssertMsg( false, "Format string parse failure" );
return "";
}
return vecChar.data();
}
#if defined( _WIN32 )
std::string DefaultACPtoUTF8( const char *pszStr )
{
if ( GetACP() == CP_UTF8 )
{
return pszStr;
}
else
{
std::vector<wchar_t> vecBuf( strlen( pszStr ) + 1 ); MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszStr, -1, vecBuf.data(), (int) vecBuf.size() );
return UTF16to8( vecBuf.data() );
}
}
#endif
void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource )
{
strncpy( pchBuffer, pchSource, unBufferSizeBytes - 1 );
pchBuffer[unBufferSizeBytes - 1] = '\0';
}
std::string StringToUpper( const std::string & sString )
{
std::string sOut;
sOut.reserve( sString.size() + 1 );
for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
{
sOut.push_back( (char)toupper( *i ) );
}
return sOut;
}
std::string StringToLower( const std::string & sString )
{
std::string sOut;
sOut.reserve( sString.size() + 1 );
for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
{
sOut.push_back( (char)tolower( *i ) );
}
return sOut;
}
uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen )
{
uint32_t unLen = (uint32_t)sValue.length() + 1;
if( !pchBuffer || !unBufferLen )
return unLen;
if( unBufferLen < unLen )
{
pchBuffer[0] = '\0';
}
else
{
memcpy( pchBuffer, sValue.c_str(), unLen );
}
return unLen;
}
std::string Uint64ToString( uint64_t ulValue )
{
char buf[ 22 ];
#if defined( _WIN32 )
sprintf_s( buf, "%llu", ulValue );
#else
snprintf( buf, sizeof( buf ), "%llu", (long long unsigned int ) ulValue );
#endif
return buf;
}
uint64_t StringToUint64( const std::string & sValue )
{
return strtoull( sValue.c_str(), NULL, 0 );
}
char cIntToHexDigit( int nValue )
{
return "0123456789ABCDEF"[ nValue & 15 ];
}
int iHexCharToInt( char cValue )
{
int32_t iValue = cValue;
if ( (uint32_t)( iValue - '0' ) < 10 )
return iValue - '0';
iValue |= 0x20;
if ( (uint32_t)( iValue - 'a' ) < 6 )
return iValue - 'a' + 10;
return -1;
}
static bool CharNeedsEscape_Component( const char c )
{
return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
&& c != '-' && c != '_' && c != '.');
}
static bool CharNeedsEscape_FullPath( const char c )
{
return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
&& c != '-' && c != '_' && c != '.' && c != '/' && c != ':' );
}
void V_URLEncodeInternal( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen,
bool bUsePlusForSpace, std::function< bool(const char)> fnNeedsEscape )
{
int iDestPos = 0;
for ( int i=0; i < nSourceLen; ++i )
{
if( (iDestPos+3) > nDestLen )
{
pchDest[0] = '\0';
return;
}
if ( fnNeedsEscape( pchSource[i] ) )
{
if ( bUsePlusForSpace && pchSource[i] == ' ' )
{
pchDest[iDestPos++] = '+';
}
else
{
pchDest[iDestPos++] = '%';
uint8_t iValue = pchSource[i];
if ( iValue == 0 )
{
pchDest[iDestPos++] = '0';
pchDest[iDestPos++] = '0';
}
else
{
char cHexDigit1 = cIntToHexDigit( iValue % 16 );
iValue /= 16;
char cHexDigit2 = cIntToHexDigit( iValue );
pchDest[iDestPos++] = cHexDigit2;
pchDest[iDestPos++] = cHexDigit1;
}
}
}
else
{
pchDest[iDestPos++] = pchSource[i];
}
}
if( (iDestPos+1) > nDestLen )
{
pchDest[0] = '\0';
return;
}
pchDest[iDestPos++] = 0;
}
size_t V_URLDecodeInternal( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen, bool bUsePlusForSpace )
{
if ( nDecodeDestLen < nEncodedSourceLen )
{
return 0;
}
int iDestPos = 0;
for( int i=0; i < nEncodedSourceLen; ++i )
{
if ( bUsePlusForSpace && pchEncodedSource[i] == '+' )
{
pchDecodeDest[ iDestPos++ ] = ' ';
}
else if ( pchEncodedSource[i] == '%' )
{
if ( i < nEncodedSourceLen - 2 )
{
char cHexDigit1 = pchEncodedSource[i+1];
char cHexDigit2 = pchEncodedSource[i+2];
bool bValid = false;
int iValue = iHexCharToInt( cHexDigit1 );
if ( iValue != -1 )
{
iValue *= 16;
int iValue2 = iHexCharToInt( cHexDigit2 );
if ( iValue2 != -1 )
{
iValue += iValue2;
pchDecodeDest[ iDestPos++ ] = (char)iValue;
bValid = true;
}
}
if ( !bValid )
{
pchDecodeDest[ iDestPos++ ] = '%';
pchDecodeDest[ iDestPos++ ] = cHexDigit1;
pchDecodeDest[ iDestPos++ ] = cHexDigit2;
}
}
i += 2;
}
else
{
pchDecodeDest[ iDestPos++ ] = pchEncodedSource[i];
}
}
if ( iDestPos < nDecodeDestLen )
{
pchDecodeDest[iDestPos] = 0;
}
return (size_t)iDestPos;
}
void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, true, CharNeedsEscape_Component );
}
void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_Component );
}
void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_FullPath );
}
size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
{
return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, true );
}
size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
{
return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, false );
}
void V_StripExtension( std::string &in )
{
std::string::size_type test = in.rfind( '.' );
if ( test != std::string::npos )
{
if ( in.rfind( '\\' ) < test && in.rfind( '/' ) < test )
{
in.resize( test );
}
}
}
std::vector<std::string> TokenizeString( const std::string & sString, char cToken )
{
std::vector<std::string> vecStrings;
std::istringstream stream( sString );
std::string s;
while ( std::getline( stream, s, cToken ) )
{
vecStrings.push_back( s );
}
if ( !sString.empty() && sString.back() == cToken )
{
vecStrings.push_back( "" );
}
return vecStrings;
}
bool RepairUTF8( const char *pbegin, const char *pend, std::string & sOutputUtf8 )
{
typedef std::codecvt_utf8<char32_t> facet_type;
facet_type myfacet;
std::mbstate_t mystate = std::mbstate_t();
sOutputUtf8.clear();
sOutputUtf8.reserve( pend - pbegin );
bool bSqueakyClean = true;
const char *pmid = pbegin;
while ( pmid != pend )
{
bool bHasError = false;
bool bHasValidData = false;
char32_t out = 0xdeadbeef, *pout;
pbegin = pmid;
switch ( myfacet.in( mystate, pbegin, pend, pmid, &out, &out + 1, pout ) )
{
case facet_type::ok:
bHasValidData = true;
break;
case facet_type::noconv:
bSqueakyClean = false;
break;
case facet_type::partial:
bHasError = pbegin == pmid;
if ( bHasError )
{
bSqueakyClean = false;
}
else
{
bHasValidData = true;
}
break;
case facet_type::error:
bHasError = true;
bSqueakyClean = false;
break;
}
if ( bHasValidData )
{
for ( const char *p = pbegin; p != pmid; ++p )
{
sOutputUtf8 += *p;
}
}
if ( bHasError )
{
sOutputUtf8 += '?';
}
if ( pmid == pbegin )
{
pmid++;
}
}
return bSqueakyClean;
}
bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 )
{
return RepairUTF8( sInputUtf8.data(), sInputUtf8.data() + sInputUtf8.size(), sOutputUtf8 );
}
std::string TrimTrailingWhitespace( const std::string& in )
{
size_t endpos = in.find_last_not_of( " \t\n\r" );
if ( std::string::npos != endpos )
{
return in.substr( 0, endpos + 1 );
}
else
{
return in;
}
}
std::string IpToString( uint32_t unIpH )
{
uint8_t *ip = ( uint8_t * )&unIpH;
return Format( "%d.%d.%d.%d", ip[ 3 ], ip[ 2 ], ip[ 1 ], ip[ 0 ] );
}
std::string IpAndPortToString( uint32_t unIpH, uint16_t usPortH )
{
uint8_t *ip = ( uint8_t * )&unIpH;
return Format( "%d.%d.%d.%d:%u", ip[ 3 ], ip[ 2 ], ip[ 1 ], ip[ 0 ], usPortH );
}