1/*
2 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16
17#include <aws/core/utils/StringUtils.h>
18#include <aws/core/utils/memory/stl/AWSStringStream.h>
19#include <algorithm>
20#include <iomanip>
21#include <cstdlib>
22#include <cstdio>
23#include <cstring>
24#include <functional>
25
26#ifdef _WIN32
27#include <Windows.h>
28#endif
29
30using namespace Aws::Utils;
31
32void StringUtils::Replace(Aws::String& s, const char* search, const char* replace)
33{
34 if(!search || !replace)
35 {
36 return;
37 }
38
39 size_t replaceLength = strlen(replace);
40 size_t searchLength = strlen(search);
41
42 for (std::size_t pos = 0;; pos += replaceLength)
43 {
44 pos = s.find(search, pos);
45 if (pos == Aws::String::npos)
46 break;
47
48 s.erase(pos, searchLength);
49 s.insert(pos, replace);
50 }
51}
52
53
54Aws::String StringUtils::ToLower(const char* source)
55{
56 Aws::String copy;
57 size_t sourceLength = strlen(source);
58 copy.resize(sourceLength);
59 //appease the latest whims of the VC++ 2017 gods
60 std::transform(source, source + sourceLength, copy.begin(), [](unsigned char c) { return (char)::tolower(c); });
61
62 return copy;
63}
64
65
66Aws::String StringUtils::ToUpper(const char* source)
67{
68 Aws::String copy;
69 size_t sourceLength = strlen(source);
70 copy.resize(sourceLength);
71 //appease the latest whims of the VC++ 2017 gods
72 std::transform(source, source + sourceLength, copy.begin(), [](unsigned char c) { return (char)::toupper(c); });
73
74 return copy;
75}
76
77
78bool StringUtils::CaselessCompare(const char* value1, const char* value2)
79{
80 Aws::String value1Lower = ToLower(value1);
81 Aws::String value2Lower = ToLower(value2);
82
83 return value1Lower == value2Lower;
84}
85
86Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn)
87{
88 return Split(toSplit, splitOn, SIZE_MAX, SplitOptions::NOT_SET);
89}
90
91Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, SplitOptions option)
92{
93 return Split(toSplit, splitOn, SIZE_MAX, option);
94}
95
96Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts)
97{
98 return Split(toSplit, splitOn, numOfTargetParts, SplitOptions::NOT_SET);
99}
100
101Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts, SplitOptions option)
102{
103 Aws::Vector<Aws::String> returnValues;
104 Aws::StringStream input(toSplit);
105 Aws::String item;
106
107 while(returnValues.size() < numOfTargetParts - 1 && std::getline(input, item, splitOn))
108 {
109 if (!item.empty() || option == SplitOptions::INCLUDE_EMPTY_ENTRIES)
110 {
111 returnValues.emplace_back(std::move(item));
112 }
113 }
114
115 if (std::getline(input, item, static_cast<char>(EOF)))
116 {
117 if (option != SplitOptions::INCLUDE_EMPTY_ENTRIES)
118 {
119 // Trim all leading delimiters.
120 item.erase(item.begin(), std::find_if(item.begin(), item.end(), [splitOn](int ch) { return ch != splitOn; }));
121 if (!item.empty())
122 {
123 returnValues.emplace_back(std::move(item));
124 }
125 }
126 else
127 {
128 returnValues.emplace_back(std::move(item));
129 }
130
131 }
132 // To handle the case when there are trailing delimiters.
133 else if (!toSplit.empty() && toSplit.back() == splitOn && option == SplitOptions::INCLUDE_EMPTY_ENTRIES)
134 {
135 returnValues.emplace_back();
136 }
137
138 return returnValues;
139}
140
141Aws::Vector<Aws::String> StringUtils::SplitOnLine(const Aws::String& toSplit)
142{
143 Aws::StringStream input(toSplit);
144 Aws::Vector<Aws::String> returnValues;
145 Aws::String item;
146
147 while (std::getline(input, item))
148 {
149 if (item.size() > 0)
150 {
151 returnValues.push_back(item);
152 }
153 }
154
155 return returnValues;
156}
157
158
159Aws::String StringUtils::URLEncode(const char* unsafe)
160{
161 Aws::StringStream escaped;
162 escaped.fill('0');
163 escaped << std::hex << std::uppercase;
164
165 size_t unsafeLength = strlen(unsafe);
166 for (auto i = unsafe, n = unsafe + unsafeLength; i != n; ++i)
167 {
168 char c = *i;
169 if (IsAlnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
170 {
171 escaped << (char)c;
172 }
173 else
174 {
175 //this unsigned char cast allows us to handle unicode characters.
176 escaped << '%' << std::setw(2) << int((unsigned char)c) << std::setw(0);
177 }
178 }
179
180 return escaped.str();
181}
182
183Aws::String StringUtils::UTF8Escape(const char* unicodeString, const char* delimiter)
184{
185 Aws::StringStream escaped;
186 escaped.fill('0');
187 escaped << std::hex << std::uppercase;
188
189 size_t unsafeLength = strlen(unicodeString);
190 for (auto i = unicodeString, n = unicodeString + unsafeLength; i != n; ++i)
191 {
192 int c = *i;
193 if (c >= ' ' && c < 127 )
194 {
195 escaped << (char)c;
196 }
197 else
198 {
199 //this unsigned char cast allows us to handle unicode characters.
200 escaped << delimiter << std::setw(2) << int((unsigned char)c) << std::setw(0);
201 }
202 }
203
204 return escaped.str();
205}
206
207Aws::String StringUtils::URLEncode(double unsafe)
208{
209 char buffer[32];
210#if defined(_MSC_VER) && _MSC_VER < 1900
211 _snprintf_s(buffer, sizeof(buffer), _TRUNCATE, "%g", unsafe);
212#else
213 snprintf(buffer, sizeof(buffer), "%g", unsafe);
214#endif
215
216 return StringUtils::URLEncode(buffer);
217}
218
219
220Aws::String StringUtils::URLDecode(const char* safe)
221{
222 Aws::String unescaped;
223
224 for (; *safe; safe++)
225 {
226 switch(*safe)
227 {
228 case '%':
229 {
230 int hex = 0;
231 auto ch = *++safe;
232 if (ch >= '0' && ch <= '9')
233 {
234 hex = (ch - '0') * 16;
235 }
236 else if (ch >= 'A' && ch <= 'F')
237 {
238 hex = (ch - 'A' + 10) * 16;
239 }
240 else if (ch >= 'a' && ch <= 'f')
241 {
242 hex = (ch - 'a' + 10) * 16;
243 }
244 else
245 {
246 unescaped.push_back('%');
247 if (ch == 0)
248 {
249 return unescaped;
250 }
251 unescaped.push_back(ch);
252 break;
253 }
254
255 ch = *++safe;
256 if (ch >= '0' && ch <= '9')
257 {
258 hex += (ch - '0');
259 }
260 else if (ch >= 'A' && ch <= 'F')
261 {
262 hex += (ch - 'A' + 10);
263 }
264 else if (ch >= 'a' && ch <= 'f')
265 {
266 hex += (ch - 'a' + 10);
267 }
268 else
269 {
270 unescaped.push_back('%');
271 unescaped.push_back(*(safe - 1));
272 if (ch == 0)
273 {
274 return unescaped;
275 }
276 unescaped.push_back(ch);
277 break;
278 }
279
280 unescaped.push_back(char(hex));
281 break;
282 }
283 case '+':
284 unescaped.push_back(' ');
285 break;
286 default:
287 unescaped.push_back(*safe);
288 break;
289 }
290 }
291
292 return unescaped;
293}
294
295static bool IsSpace(int ch)
296{
297 if (ch < -1 || ch > 255)
298 {
299 return false;
300 }
301
302 return ::isspace(ch) != 0;
303}
304
305Aws::String StringUtils::LTrim(const char* source)
306{
307 Aws::String copy(source);
308 copy.erase(copy.begin(), std::find_if(copy.begin(), copy.end(), [](int ch) { return !IsSpace(ch); }));
309 return copy;
310}
311
312// trim from end
313Aws::String StringUtils::RTrim(const char* source)
314{
315 Aws::String copy(source);
316 copy.erase(std::find_if(copy.rbegin(), copy.rend(), [](int ch) { return !IsSpace(ch); }).base(), copy.end());
317 return copy;
318}
319
320// trim from both ends
321Aws::String StringUtils::Trim(const char* source)
322{
323 return LTrim(RTrim(source).c_str());
324}
325
326long long StringUtils::ConvertToInt64(const char* source)
327{
328 if(!source)
329 {
330 return 0;
331 }
332
333#ifdef __ANDROID__
334 return atoll(source);
335#else
336 return std::atoll(source);
337#endif // __ANDROID__
338}
339
340
341long StringUtils::ConvertToInt32(const char* source)
342{
343 if (!source)
344 {
345 return 0;
346 }
347
348 return std::atol(source);
349}
350
351
352bool StringUtils::ConvertToBool(const char* source)
353{
354 if(!source)
355 {
356 return false;
357 }
358
359 Aws::String strValue = ToLower(source);
360 if(strValue == "true" || strValue == "1")
361 {
362 return true;
363 }
364
365 return false;
366}
367
368
369double StringUtils::ConvertToDouble(const char* source)
370{
371 if(!source)
372 {
373 return 0.0;
374 }
375
376 return std::strtod(source, NULL);
377}
378
379#ifdef _WIN32
380
381Aws::WString StringUtils::ToWString(const char* source)
382{
383 const auto len = static_cast<int>(std::strlen(source));
384 Aws::WString outString;
385 outString.resize(len); // there is no way UTF-16 would require _more_ code-points than UTF-8 for the _same_ string
386 const auto result = MultiByteToWideChar(CP_UTF8 /*CodePage*/,
387 0 /*dwFlags*/,
388 source /*lpMultiByteStr*/,
389 len /*cbMultiByte*/,
390 &outString[0] /*lpWideCharStr*/,
391 static_cast<int>(outString.length())/*cchWideChar*/);
392 if (!result)
393 {
394 return L"";
395 }
396 outString.resize(result);
397 return outString;
398}
399
400Aws::String StringUtils::FromWString(const wchar_t* source)
401{
402 const auto len = static_cast<int>(std::wcslen(source));
403 Aws::String output;
404 if (int requiredSizeInBytes = WideCharToMultiByte(CP_UTF8 /*CodePage*/,
405 0 /*dwFlags*/,
406 source /*lpWideCharStr*/,
407 len /*cchWideChar*/,
408 nullptr /*lpMultiByteStr*/,
409 0 /*cbMultiByte*/,
410 nullptr /*lpDefaultChar*/,
411 nullptr /*lpUsedDefaultChar*/))
412 {
413 output.resize(requiredSizeInBytes);
414 }
415 const auto result = WideCharToMultiByte(CP_UTF8 /*CodePage*/,
416 0 /*dwFlags*/,
417 source /*lpWideCharStr*/,
418 len /*cchWideChar*/,
419 &output[0] /*lpMultiByteStr*/,
420 static_cast<int>(output.length()) /*cbMultiByte*/,
421 nullptr /*lpDefaultChar*/,
422 nullptr /*lpUsedDefaultChar*/);
423 if (!result)
424 {
425 return "";
426 }
427 output.resize(result);
428 return output;
429}
430
431#endif
432