You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Editor/Util/StringHelpers.cpp

936 lines
22 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <platform.h>
#include "StringHelpers.h"
#include "Util.h"
#include "StringUtils.h" // From CryCommon
#include "UnicodeFunctions.h"
#include <cctype>
#include <algorithm>
#include <AzCore/PlatformIncl.h> // WideCharToMultibyte(), CP_UTF8, etc.
#include <AzCore/Casting/numeric_cast.h>
static inline char ToLower(const char c)
{
return tolower(c);
}
static inline wchar_t ToLower(const wchar_t c)
{
return towlower(c);
}
static inline char ToUpper(const char c)
{
return toupper(c);
}
static inline wchar_t ToUpper(const wchar_t c)
{
return towupper(c);
}
static inline int Vscprintf(const char* format, va_list argList)
{
#if defined(AZ_PLATFORM_WINDOWS)
return _vscprintf(format, argList);
#elif AZ_TRAIT_OS_PLATFORM_APPLE || defined(AZ_PLATFORM_LINUX)
int retval;
va_list argcopy;
va_copy(argcopy, argList);
retval = azvsnprintf(NULL, 0, format, argcopy);
va_end(argcopy);
return retval;
#else
#error Not supported!
#endif
}
static inline int Vscprintf(const wchar_t* format, va_list argList)
{
#if defined(AZ_PLATFORM_WINDOWS)
return _vscwprintf(format, argList);
#elif AZ_TRAIT_OS_PLATFORM_APPLE || defined(AZ_PLATFORM_LINUX)
int retval;
va_list argcopy;
va_copy(argcopy, argList);
retval = azvsnwprintf(NULL, 0, format, argcopy);
va_end(argcopy);
return retval;
#else
#error Not supported!
#endif
}
static inline int Vsnprintf_s(char* str, size_t sizeInBytes, [[maybe_unused]] size_t count, const char* format, va_list argList)
{
return azvsnprintf(str, sizeInBytes, format, argList);
}
static inline int Vsnprintf_s(wchar_t* str, size_t sizeInBytes, [[maybe_unused]] size_t count, const wchar_t* format, va_list argList)
{
return azvsnwprintf(str, sizeInBytes, format, argList);
}
int StringHelpers::Compare(const string& str0, const string& str1)
{
const size_t minLength = Util::getMin(str0.length(), str1.length());
const int result = std::memcmp(str0.c_str(), str1.c_str(), minLength);
if (result)
{
return result;
}
else
{
return (str0.length() == str1.length())
? 0
: ((str0.length() < str1.length()) ? -1 : +1);
}
}
int StringHelpers::Compare(const wstring& str0, const wstring& str1)
{
const size_t minLength = Util::getMin(str0.length(), str1.length());
for (size_t i = 0; i < minLength; ++i)
{
const wchar_t c0 = str0[i];
const wchar_t c1 = str1[i];
if (c0 != c1)
{
return (c0 < c1) ? -1 : 1;
}
}
return (str0.length() == str1.length())
? 0
: ((str0.length() < str1.length()) ? -1 : +1);
}
int StringHelpers::CompareIgnoreCase(const string& str0, const string& str1)
{
const size_t minLength = Util::getMin(str0.length(), str1.length());
const int result = azmemicmp(str0.c_str(), str1.c_str(), minLength);
if (result)
{
return result;
}
else
{
return (str0.length() == str1.length())
? 0
: ((str0.length() < str1.length()) ? -1 : +1);
}
}
int StringHelpers::CompareIgnoreCase(const wstring& str0, const wstring& str1)
{
const size_t minLength = Util::getMin(str0.length(), str1.length());
for (size_t i = 0; i < minLength; ++i)
{
const wchar_t c0 = towlower(str0[i]);
const wchar_t c1 = towlower(str1[i]);
if (c0 != c1)
{
return (c0 < c1) ? -1 : 1;
}
}
return (str0.length() == str1.length())
? 0
: ((str0.length() < str1.length()) ? -1 : +1);
}
bool StringHelpers::Equals(const string& str0, const string& str1)
{
if (str0.length() != str1.length())
{
return false;
}
return std::memcmp(str0.c_str(), str1.c_str(), str1.length()) == 0;
}
bool StringHelpers::Equals(const wstring& str0, const wstring& str1)
{
if (str0.length() != str1.length())
{
return false;
}
return std::memcmp(str0.c_str(), str1.c_str(), str1.length() * sizeof(wstring::value_type)) == 0;
}
bool StringHelpers::EqualsIgnoreCase(const string& str0, const string& str1)
{
if (str0.length() != str1.length())
{
return false;
}
return azmemicmp(str0.c_str(), str1.c_str(), str1.length()) == 0;
}
bool StringHelpers::EqualsIgnoreCase(const wstring& str0, const wstring& str1)
{
const size_t str1Length = str1.length();
if (str0.length() != str1Length)
{
return false;
}
for (size_t i = 0; i < str1Length; ++i)
{
if (towlower(str0[i]) != towlower(str1[i]))
{
return false;
}
}
return true;
}
bool StringHelpers::StartsWith(const string& str, const string& pattern)
{
if (str.length() < pattern.length())
{
return false;
}
return std::memcmp(str.c_str(), pattern.c_str(), pattern.length()) == 0;
}
bool StringHelpers::StartsWith(const wstring& str, const wstring& pattern)
{
if (str.length() < pattern.length())
{
return false;
}
return std::memcmp(str.c_str(), pattern.c_str(), pattern.length() * sizeof(wstring::value_type)) == 0;
}
bool StringHelpers::StartsWithIgnoreCase(const string& str, const string& pattern)
{
if (str.length() < pattern.length())
{
return false;
}
return azmemicmp(str.c_str(), pattern.c_str(), pattern.length()) == 0;
}
bool StringHelpers::StartsWithIgnoreCase(const wstring& str, const wstring& pattern)
{
const size_t patternLength = pattern.length();
if (str.length() < patternLength)
{
return false;
}
for (size_t i = 0; i < patternLength; ++i)
{
if (towlower(str[i]) != towlower(pattern[i]))
{
return false;
}
}
return true;
}
bool StringHelpers::EndsWith(const string& str, const string& pattern)
{
if (str.length() < pattern.length())
{
return false;
}
return std::memcmp(str.c_str() + str.length() - pattern.length(), pattern.c_str(), pattern.length()) == 0;
}
bool StringHelpers::EndsWith(const wstring& str, const wstring& pattern)
{
if (str.length() < pattern.length())
{
return false;
}
return std::memcmp(str.c_str() + str.length() - pattern.length(), pattern.c_str(), pattern.length() * sizeof(wstring::value_type)) == 0;
}
bool StringHelpers::EndsWithIgnoreCase(const string& str, const string& pattern)
{
if (str.length() < pattern.length())
{
return false;
}
return azmemicmp(str.c_str() + str.length() - pattern.length(), pattern.c_str(), pattern.length()) == 0;
}
bool StringHelpers::EndsWithIgnoreCase(const wstring& str, const wstring& pattern)
{
const size_t patternLength = pattern.length();
if (str.length() < patternLength)
{
return false;
}
for (size_t i = str.length() - patternLength, j = 0; i < patternLength; ++i, ++j)
{
if (towlower(str[i]) != towlower(pattern[j]))
{
return false;
}
}
return true;
}
bool StringHelpers::Contains(const string& str, const string& pattern)
{
const size_t patternLength = pattern.length();
if (str.length() < patternLength)
{
return false;
}
const size_t n = str.length() - patternLength + 1;
for (size_t i = 0; i < n; ++i)
{
if (std::memcmp(str.c_str() + i, pattern.c_str(), patternLength) == 0)
{
return true;
}
}
return false;
}
bool StringHelpers::Contains(const wstring& str, const wstring& pattern)
{
const size_t patternLength = pattern.length();
if (str.length() < patternLength)
{
return false;
}
const size_t n = str.length() - patternLength + 1;
for (size_t i = 0; i < n; ++i)
{
if (std::memcmp(str.c_str() + i, pattern.c_str(), patternLength * sizeof(wstring::value_type)) == 0)
{
return true;
}
}
return false;
}
bool StringHelpers::ContainsIgnoreCase(const string& str, const string& pattern)
{
const size_t patternLength = pattern.length();
if (str.length() < patternLength)
{
return false;
}
const size_t n = str.length() - patternLength + 1;
for (size_t i = 0; i < n; ++i)
{
if (azmemicmp(str.c_str() + i, pattern.c_str(), patternLength) == 0)
{
return true;
}
}
return false;
}
bool StringHelpers::ContainsIgnoreCase(const wstring& str, const wstring& pattern)
{
const size_t patternLength = pattern.length();
if (patternLength == 0)
{
return true;
}
if (str.length() < patternLength)
{
return false;
}
const wstring::value_type firstPatternLetter = towlower(pattern[0]);
const size_t n = str.length() - patternLength + 1;
for (size_t i = 0; i < n; ++i)
{
bool match = true;
for (size_t j = 0; j < patternLength; ++j)
{
if (towlower(str[i + j]) != towlower(pattern[j]))
{
match = false;
break;
}
}
if (match)
{
return true;
}
}
return false;
}
string StringHelpers::TrimLeft(const string& s)
{
const size_t first = s.find_first_not_of(" \r\t");
return (first == s.npos) ? string() : s.substr(first);
}
wstring StringHelpers::TrimLeft(const wstring& s)
{
const size_t first = s.find_first_not_of(L" \r\t");
return (first == s.npos) ? wstring() : s.substr(first);
}
string StringHelpers::TrimRight(const string& s)
{
const size_t last = s.find_last_not_of(" \r\t");
return (last == s.npos) ? s : s.substr(0, last + 1);
}
wstring StringHelpers::TrimRight(const wstring& s)
{
const size_t last = s.find_last_not_of(L" \r\t");
return (last == s.npos) ? s : s.substr(0, last + 1);
}
string StringHelpers::Trim(const string& s)
{
return TrimLeft(TrimRight(s));
}
wstring StringHelpers::Trim(const wstring& s)
{
return TrimLeft(TrimRight(s));
}
template <class TS>
static inline TS RemoveDuplicateSpaces_Tpl(const TS& s)
{
TS res;
bool spaceFound = false;
for (size_t i = 0, n = s.length(); i < n; ++i)
{
if ((s[i] == ' ') || (s[i] == '\r') || (s[i] == '\t'))
{
spaceFound = true;
}
else
{
if (spaceFound)
{
res += ' ';
spaceFound = false;
}
res += s[i];
}
}
if (spaceFound)
{
res += ' ';
}
return res;
}
string StringHelpers::RemoveDuplicateSpaces(const string& s)
{
return RemoveDuplicateSpaces_Tpl(s);
}
wstring StringHelpers::RemoveDuplicateSpaces(const wstring& s)
{
return RemoveDuplicateSpaces_Tpl(s);
}
template <class TS>
static inline TS MakeLowerCase_Tpl(const TS& s)
{
TS copy;
copy.reserve(s.length());
for (typename TS::const_iterator it = s.begin(), end = s.end(); it != end; ++it)
{
copy.append(1, ToLower(*it));
}
return copy;
}
string StringHelpers::MakeLowerCase(const string& s)
{
return MakeLowerCase_Tpl(s);
}
wstring StringHelpers::MakeLowerCase(const wstring& s)
{
return MakeLowerCase_Tpl(s);
}
template <class TS>
static inline TS MakeUpperCase_Tpl(const TS& s)
{
TS copy;
copy.reserve(s.length());
for (typename TS::const_iterator it = s.begin(), end = s.end(); it != end; ++it)
{
copy.append(1, ToUpper(*it));
}
return copy;
}
string StringHelpers::MakeUpperCase(const string& s)
{
return MakeUpperCase_Tpl(s);
}
wstring StringHelpers::MakeUpperCase(const wstring& s)
{
return MakeUpperCase_Tpl(s);
}
template <class TS>
static inline TS Replace_Tpl(const TS& s, const typename TS::value_type oldChar, const typename TS::value_type newChar)
{
TS copy;
copy.reserve(s.length());
for (typename TS::const_iterator it = s.begin(), end = s.end(); it != end; ++it)
{
const typename TS::value_type c = (*it);
copy.append(1, ((c == oldChar) ? newChar : c));
}
return copy;
}
string StringHelpers::Replace(const string& s, char oldChar, char newChar)
{
return Replace_Tpl(s, oldChar, newChar);
}
wstring StringHelpers::Replace(const wstring& s, wchar_t oldChar, wchar_t newChar)
{
return Replace_Tpl(s, oldChar, newChar);
}
void StringHelpers::ConvertStringByRef(string& out, const string& in)
{
out = in;
}
void StringHelpers::ConvertStringByRef(wstring& out, const string& in)
{
Unicode::Convert(out, in);
}
void StringHelpers::ConvertStringByRef(string& out, const wstring& in)
{
Unicode::Convert(out, in);
}
void StringHelpers::ConvertStringByRef(wstring& out, const wstring& in)
{
out = in;
}
template <class TS>
static inline void Split_Tpl(const TS& str, const TS& separator, bool bReturnEmptyPartsToo, std::vector<TS>& outParts)
{
if (str.empty())
{
return;
}
if (separator.empty())
{
for (size_t i = 0; i < str.length(); ++i)
{
outParts.push_back(str.substr(i, 1));
}
return;
}
size_t partStart = 0;
for (;; )
{
const size_t pos = str.find(separator, partStart);
if (pos == TS::npos)
{
break;
}
if (bReturnEmptyPartsToo || (pos > partStart))
{
outParts.push_back(str.substr(partStart, pos - partStart));
}
partStart = pos + separator.length();
}
if (bReturnEmptyPartsToo || (partStart < str.length()))
{
outParts.push_back(str.substr(partStart, str.length() - partStart));
}
}
template <class TS>
static inline void SplitByAnyOf_Tpl(const TS& str, const TS& separators, bool bReturnEmptyPartsToo, std::vector<TS>& outParts)
{
if (str.empty())
{
return;
}
if (separators.empty())
{
for (size_t i = 0; i < str.length(); ++i)
{
outParts.push_back(str.substr(i, 1));
}
return;
}
size_t partStart = 0;
for (;; )
{
const size_t pos = str.find_first_of(separators, partStart);
if (pos == TS::npos)
{
break;
}
if (bReturnEmptyPartsToo || (pos > partStart))
{
outParts.push_back(str.substr(partStart, pos - partStart));
}
partStart = pos + 1;
}
if (bReturnEmptyPartsToo || (partStart < str.length()))
{
outParts.push_back(str.substr(partStart, str.length() - partStart));
}
}
void StringHelpers::Split(const string& str, const string& separator, bool bReturnEmptyPartsToo, std::vector<string>& outParts)
{
Split_Tpl(str, separator, bReturnEmptyPartsToo, outParts);
}
void StringHelpers::Split(const wstring& str, const wstring& separator, bool bReturnEmptyPartsToo, std::vector<wstring>& outParts)
{
Split_Tpl(str, separator, bReturnEmptyPartsToo, outParts);
}
void StringHelpers::SplitByAnyOf(const string& str, const string& separators, bool bReturnEmptyPartsToo, std::vector<string>& outParts)
{
SplitByAnyOf_Tpl(str, separators, bReturnEmptyPartsToo, outParts);
}
void StringHelpers::SplitByAnyOf(const wstring& str, const wstring& separators, bool bReturnEmptyPartsToo, std::vector<wstring>& outParts)
{
SplitByAnyOf_Tpl(str, separators, bReturnEmptyPartsToo, outParts);
}
template <class TS>
static inline TS FormatVA_Tpl(const typename TS::value_type* const format, va_list parg)
{
if ((format == 0) || (format[0] == 0))
{
return TS();
}
std::vector<typename TS::value_type> bf;
size_t capacity = 0;
size_t wantedCapacity = Vscprintf(format, parg);
wantedCapacity += 2; // '+ 2' to prevent uncertainty when Vsnprintf_s() returns 'size - 1'
for (;; )
{
if (wantedCapacity > capacity)
{
capacity = wantedCapacity;
bf.resize(capacity + 1);
}
const int countWritten = Vsnprintf_s(&bf[0], capacity + 1, _TRUNCATE, format, parg);
if ((countWritten >= 0) && (capacity > (size_t)countWritten + 1))
{
bf[countWritten] = 0;
return TS(&bf[0]);
}
wantedCapacity = capacity * 2;
}
}
string StringHelpers::FormatVA(const char* const format, va_list parg)
{
return FormatVA_Tpl<string>(format, parg);
}
wstring StringHelpers::FormatVA(const wchar_t* const format, va_list parg)
{
return FormatVA_Tpl<wstring>(format, parg);
}
//////////////////////////////////////////////////////////////////////////
template <class TC>
static inline void SafeCopy_Tpl(TC* const pDstBuffer, const size_t dstBufferSizeInBytes, const TC* const pSrc)
{
if (dstBufferSizeInBytes >= sizeof(TC))
{
const size_t n = dstBufferSizeInBytes / sizeof(TC) - 1;
size_t i;
for (i = 0; i < n && pSrc[i]; ++i)
{
pDstBuffer[i] = pSrc[i];
}
pDstBuffer[i] = 0;
}
}
template <class TC>
static inline void SafeCopyPadZeros_Tpl(TC* const pDstBuffer, const size_t dstBufferSizeInBytes, const TC* const pSrc)
{
if (dstBufferSizeInBytes > 0)
{
const size_t n = (dstBufferSizeInBytes < sizeof(TC)) ? 0 : dstBufferSizeInBytes / sizeof(TC) - 1;
size_t i;
for (i = 0; i < n && pSrc[i]; ++i)
{
pDstBuffer[i] = pSrc[i];
}
memset(&pDstBuffer[i], 0, dstBufferSizeInBytes - i * sizeof(TC));
}
}
void StringHelpers::SafeCopy(char* const pDstBuffer, const size_t dstBufferSizeInBytes, const char* const pSrc)
{
SafeCopy_Tpl<char>(pDstBuffer, dstBufferSizeInBytes, pSrc);
}
void StringHelpers::SafeCopy(wchar_t* const pDstBuffer, const size_t dstBufferSizeInBytes, const wchar_t* const pSrc)
{
SafeCopy_Tpl<wchar_t>(pDstBuffer, dstBufferSizeInBytes, pSrc);
}
void StringHelpers::SafeCopyPadZeros(char* const pDstBuffer, const size_t dstBufferSizeInBytes, const char* const pSrc)
{
SafeCopyPadZeros_Tpl<char>(pDstBuffer, dstBufferSizeInBytes, pSrc);
}
void StringHelpers::SafeCopyPadZeros(wchar_t* const pDstBuffer, const size_t dstBufferSizeInBytes, const wchar_t* const pSrc)
{
SafeCopyPadZeros_Tpl<wchar_t>(pDstBuffer, dstBufferSizeInBytes, pSrc);
}
//////////////////////////////////////////////////////////////////////////
bool StringHelpers::Utf16ContainsAsciiOnly(const wchar_t* wstr)
{
while (*wstr)
{
if (*wstr > 127 || *wstr < 0)
{
return false;
}
++wstr;
}
return true;
}
string StringHelpers::ConvertAsciiUtf16ToAscii(const wchar_t* wstr)
{
const size_t len = wcslen(wstr);
string result;
result.reserve(len);
for (size_t i = 0; i < len; ++i)
{
result.push_back(wstr[i] & 0x7F);
}
return result;
}
wstring StringHelpers::ConvertAsciiToUtf16(const char* str)
{
const size_t len = strlen(str);
wstring result;
result.reserve(len);
for (size_t i = 0; i < len; ++i)
{
result.push_back(str[i] & 0x7F);
}
return result;
}
#if defined(AZ_PLATFORM_WINDOWS)
static string ConvertUtf16ToMultibyte(const wchar_t* wstr, uint codePage, char badChar)
{
if (wstr[0] == 0)
{
return string();
}
const int len = aznumeric_caster(wcslen(wstr));
// Request needed buffer size, in bytes
int neededByteCount = WideCharToMultiByte(
codePage,
0,
wstr,
len,
0,
0,
((badChar && codePage != CP_UTF8) ? &badChar : NULL),
NULL);
if (neededByteCount <= 0)
{
return string();
}
++neededByteCount; // extra space for terminating zero
std::vector<char> buffer(neededByteCount);
const int byteCount = WideCharToMultiByte(
codePage,
0,
wstr,
len,
&buffer[0], // output buffer
neededByteCount - 1, // size of the output buffer in bytes
((badChar && codePage != CP_UTF8) ? &badChar : NULL),
NULL);
if (byteCount != neededByteCount - 1)
{
return string();
}
buffer[byteCount] = 0;
return string(&buffer[0]);
}
static wstring ConvertMultibyteToUtf16(const char* str, uint codePage)
{
if (str[0] == 0)
{
return wstring();
}
const int len = aznumeric_caster(strlen(str));
// Request needed buffer size, in characters
int neededCharCount = MultiByteToWideChar(
codePage,
0,
str,
len,
0,
0);
if (neededCharCount <= 0)
{
return wstring();
}
++neededCharCount; // extra space for terminating zero
std::vector<wchar_t> wbuffer(neededCharCount);
const int charCount = MultiByteToWideChar(
codePage,
0,
str,
len,
&wbuffer[0], // output buffer
neededCharCount - 1); // size of the output buffer in characters
if (charCount != neededCharCount - 1)
{
return wstring();
}
wbuffer[charCount] = 0;
return wstring(&wbuffer[0]);
}
wstring StringHelpers::ConvertUtf8ToUtf16(const char* str)
{
return ConvertMultibyteToUtf16(str, CP_UTF8);
}
string StringHelpers::ConvertUtf16ToUtf8(const wchar_t* wstr)
{
return ConvertUtf16ToMultibyte(wstr, CP_UTF8, 0);
}
wstring StringHelpers::ConvertAnsiToUtf16(const char* str)
{
return ConvertMultibyteToUtf16(str, CP_ACP);
}
string StringHelpers::ConvertUtf16ToAnsi(const wchar_t* wstr, char badChar)
{
return ConvertUtf16ToMultibyte(wstr, CP_ACP, badChar);
}
#endif //AZ_PLATFORM_WINDOWS
string StringHelpers::ConvertAnsiToAscii(const char* str, char badChar)
{
const size_t len = strlen(str);
string result;
result.reserve(len);
for (size_t i = 0; i < len; ++i)
{
char c = str[i];
if (c < 0 || c > 127)
{
c = badChar;
}
result.push_back(c);
}
return result;
}
// eof