/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/core/utils/StringUtils.h>
#include <aws/core/utils/memory/stl/AWSStringStream.h>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <functional>
#ifdef _WIN32
#include <Windows.h>
#endif
using namespace Aws::Utils;
void StringUtils::Replace(Aws::String& s, const char* search, const char* replace)
{
if(!search || !replace)
{
return;
}
size_t replaceLength = strlen(replace);
size_t searchLength = strlen(search);
for (std::size_t pos = 0;; pos += replaceLength)
{
pos = s.find(search, pos);
if (pos == Aws::String::npos)
break;
s.erase(pos, searchLength);
s.insert(pos, replace);
}
}
Aws::String StringUtils::ToLower(const char* source)
{
Aws::String copy;
size_t sourceLength = strlen(source);
copy.resize(sourceLength);
//appease the latest whims of the VC++ 2017 gods
std::transform(source, source + sourceLength, copy.begin(), [](unsigned char c) { return (char)::tolower(c); });
return copy;
}
Aws::String StringUtils::ToUpper(const char* source)
{
Aws::String copy;
size_t sourceLength = strlen(source);
copy.resize(sourceLength);
//appease the latest whims of the VC++ 2017 gods
std::transform(source, source + sourceLength, copy.begin(), [](unsigned char c) { return (char)::toupper(c); });
return copy;
}
bool StringUtils::CaselessCompare(const char* value1, const char* value2)
{
Aws::String value1Lower = ToLower(value1);
Aws::String value2Lower = ToLower(value2);
return value1Lower == value2Lower;
}
Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn)
{
return Split(toSplit, splitOn, SIZE_MAX, SplitOptions::NOT_SET);
}
Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, SplitOptions option)
{
return Split(toSplit, splitOn, SIZE_MAX, option);
}
Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts)
{
return Split(toSplit, splitOn, numOfTargetParts, SplitOptions::NOT_SET);
}
Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts, SplitOptions option)
{
Aws::Vector<Aws::String> returnValues;
Aws::StringStream input(toSplit);
Aws::String item;
while(returnValues.size() < numOfTargetParts - 1 && std::getline(input, item, splitOn))
{
if (!item.empty() || option == SplitOptions::INCLUDE_EMPTY_ENTRIES)
{
returnValues.emplace_back(std::move(item));
}
}
if (std::getline(input, item, static_cast<char>(EOF)))
{
if (option != SplitOptions::INCLUDE_EMPTY_ENTRIES)
{
// Trim all leading delimiters.
item.erase(item.begin(), std::find_if(item.begin(), item.end(), [splitOn](int ch) { return ch != splitOn; }));
if (!item.empty())
{
returnValues.emplace_back(std::move(item));
}
}
else
{
returnValues.emplace_back(std::move(item));
}
}
// To handle the case when there are trailing delimiters.
else if (!toSplit.empty() && toSplit.back() == splitOn && option == SplitOptions::INCLUDE_EMPTY_ENTRIES)
{
returnValues.emplace_back();
}
return returnValues;
}
Aws::Vector<Aws::String> StringUtils::SplitOnLine(const Aws::String& toSplit)
{
Aws::StringStream input(toSplit);
Aws::Vector<Aws::String> returnValues;
Aws::String item;
while (std::getline(input, item))
{
if (item.size() > 0)
{
returnValues.push_back(item);
}
}
return returnValues;
}
Aws::String StringUtils::URLEncode(const char* unsafe)
{
Aws::StringStream escaped;
escaped.fill('0');
escaped << std::hex << std::uppercase;
size_t unsafeLength = strlen(unsafe);
for (auto i = unsafe, n = unsafe + unsafeLength; i != n; ++i)
{
char c = *i;
if (IsAlnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
{
escaped << (char)c;
}
else
{
//this unsigned char cast allows us to handle unicode characters.
escaped << '%' << std::setw(2) << int((unsigned char)c) << std::setw(0);
}
}
return escaped.str();
}
Aws::String StringUtils::UTF8Escape(const char* unicodeString, const char* delimiter)
{
Aws::StringStream escaped;
escaped.fill('0');
escaped << std::hex << std::uppercase;
size_t unsafeLength = strlen(unicodeString);
for (auto i = unicodeString, n = unicodeString + unsafeLength; i != n; ++i)
{
int c = *i;
if (c >= ' ' && c < 127 )
{
escaped << (char)c;
}
else
{
//this unsigned char cast allows us to handle unicode characters.
escaped << delimiter << std::setw(2) << int((unsigned char)c) << std::setw(0);
}
}
return escaped.str();
}
Aws::String StringUtils::URLEncode(double unsafe)
{
char buffer[32];
#if defined(_MSC_VER) && _MSC_VER < 1900
_snprintf_s(buffer, sizeof(buffer), _TRUNCATE, "%g", unsafe);
#else
snprintf(buffer, sizeof(buffer), "%g", unsafe);
#endif
return StringUtils::URLEncode(buffer);
}
Aws::String StringUtils::URLDecode(const char* safe)
{
Aws::String unescaped;
for (; *safe; safe++)
{
switch(*safe)
{
case '%':
{
int hex = 0;
auto ch = *++safe;
if (ch >= '0' && ch <= '9')
{
hex = (ch - '0') * 16;
}
else if (ch >= 'A' && ch <= 'F')
{
hex = (ch - 'A' + 10) * 16;
}
else if (ch >= 'a' && ch <= 'f')
{
hex = (ch - 'a' + 10) * 16;
}
else
{
unescaped.push_back('%');
if (ch == 0)
{
return unescaped;
}
unescaped.push_back(ch);
break;
}
ch = *++safe;
if (ch >= '0' && ch <= '9')
{
hex += (ch - '0');
}
else if (ch >= 'A' && ch <= 'F')
{
hex += (ch - 'A' + 10);
}
else if (ch >= 'a' && ch <= 'f')
{
hex += (ch - 'a' + 10);
}
else
{
unescaped.push_back('%');
unescaped.push_back(*(safe - 1));
if (ch == 0)
{
return unescaped;
}
unescaped.push_back(ch);
break;
}
unescaped.push_back(char(hex));
break;
}
case '+':
unescaped.push_back(' ');
break;
default:
unescaped.push_back(*safe);
break;
}
}
return unescaped;
}
static bool IsSpace(int ch)
{
if (ch < -1 || ch > 255)
{
return false;
}
return ::isspace(ch) != 0;
}
Aws::String StringUtils::LTrim(const char* source)
{
Aws::String copy(source);
copy.erase(copy.begin(), std::find_if(copy.begin(), copy.end(), [](int ch) { return !IsSpace(ch); }));
return copy;
}
// trim from end
Aws::String StringUtils::RTrim(const char* source)
{
Aws::String copy(source);
copy.erase(std::find_if(copy.rbegin(), copy.rend(), [](int ch) { return !IsSpace(ch); }).base(), copy.end());
return copy;
}
// trim from both ends
Aws::String StringUtils::Trim(const char* source)
{
return LTrim(RTrim(source).c_str());
}
long long StringUtils::ConvertToInt64(const char* source)
{
if(!source)
{
return 0;
}
#ifdef __ANDROID__
return atoll(source);
#else
return std::atoll(source);
#endif // __ANDROID__
}
long StringUtils::ConvertToInt32(const char* source)
{
if (!source)
{
return 0;
}
return std::atol(source);
}
bool StringUtils::ConvertToBool(const char* source)
{
if(!source)
{
return false;
}
Aws::String strValue = ToLower(source);
if(strValue == "true" || strValue == "1")
{
return true;
}
return false;
}
double StringUtils::ConvertToDouble(const char* source)
{
if(!source)
{
return 0.0;
}
return std::strtod(source, NULL);
}
#ifdef _WIN32
Aws::WString StringUtils::ToWString(const char* source)
{
const auto len = static_cast<int>(std::strlen(source));
Aws::WString outString;
outString.resize(len); // there is no way UTF-16 would require _more_ code-points than UTF-8 for the _same_ string
const auto result = MultiByteToWideChar(CP_UTF8 /*CodePage*/,
0 /*dwFlags*/,
source /*lpMultiByteStr*/,
len /*cbMultiByte*/,
&outString[0] /*lpWideCharStr*/,
static_cast<int>(outString.length())/*cchWideChar*/);
if (!result)
{
return L"";
}
outString.resize(result);
return outString;
}
Aws::String StringUtils::FromWString(const wchar_t* source)
{
const auto len = static_cast<int>(std::wcslen(source));
Aws::String output;
if (int requiredSizeInBytes = WideCharToMultiByte(CP_UTF8 /*CodePage*/,
0 /*dwFlags*/,
source /*lpWideCharStr*/,
len /*cchWideChar*/,
nullptr /*lpMultiByteStr*/,
0 /*cbMultiByte*/,
nullptr /*lpDefaultChar*/,
nullptr /*lpUsedDefaultChar*/))
{
output.resize(requiredSizeInBytes);
}
const auto result = WideCharToMultiByte(CP_UTF8 /*CodePage*/,
0 /*dwFlags*/,
source /*lpWideCharStr*/,
len /*cchWideChar*/,
&output[0] /*lpMultiByteStr*/,
static_cast<int>(output.length()) /*cbMultiByte*/,
nullptr /*lpDefaultChar*/,
nullptr /*lpUsedDefaultChar*/);
if (!result)
{
return "";
}
output.resize(result);
return output;
}
#endif