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 | |
30 | using namespace Aws::Utils; |
31 | |
32 | void 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 | |
54 | Aws::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 | |
66 | Aws::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 | |
78 | bool 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 | |
86 | Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn) |
87 | { |
88 | return Split(toSplit, splitOn, SIZE_MAX, SplitOptions::NOT_SET); |
89 | } |
90 | |
91 | Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, SplitOptions option) |
92 | { |
93 | return Split(toSplit, splitOn, SIZE_MAX, option); |
94 | } |
95 | |
96 | Aws::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 | |
101 | Aws::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 | |
141 | Aws::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 | |
159 | Aws::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 | |
183 | Aws::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 | |
207 | Aws::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 | |
220 | Aws::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 | |
295 | static bool IsSpace(int ch) |
296 | { |
297 | if (ch < -1 || ch > 255) |
298 | { |
299 | return false; |
300 | } |
301 | |
302 | return ::isspace(ch) != 0; |
303 | } |
304 | |
305 | Aws::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 |
313 | Aws::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 |
321 | Aws::String StringUtils::Trim(const char* source) |
322 | { |
323 | return LTrim(RTrim(source).c_str()); |
324 | } |
325 | |
326 | long 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 | |
341 | long StringUtils::ConvertToInt32(const char* source) |
342 | { |
343 | if (!source) |
344 | { |
345 | return 0; |
346 | } |
347 | |
348 | return std::atol(source); |
349 | } |
350 | |
351 | |
352 | bool 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 | |
369 | double 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 | |
381 | Aws::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 | |
400 | Aws::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 | |