1 /*
2 * Copyright (C) 2018 The Android Open Source Project
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 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "perfetto/ext/base/string_utils.h"
18
19 #include <locale.h>
20 #include <stdarg.h>
21 #include <string.h>
22
23 #include <algorithm>
24
25 #if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
26 #include <xlocale.h>
27 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
28 #include <Windows.h>
29 #endif
30
31 #include <cinttypes>
32
33 #include "perfetto/base/compiler.h"
34 #include "perfetto/base/logging.h"
35
36 namespace perfetto {
37 namespace base {
38
39 // Locale-independant as possible version of strtod.
StrToD(const char * nptr,char ** endptr)40 double StrToD(const char* nptr, char** endptr) {
41 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
42 PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
43 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
44 static auto c_locale = newlocale(LC_ALL, "C", nullptr);
45 return strtod_l(nptr, endptr, c_locale);
46 #else
47 return strtod(nptr, endptr);
48 #endif
49 }
50
StartsWith(const std::string & str,const std::string & prefix)51 bool StartsWith(const std::string& str, const std::string& prefix) {
52 return str.compare(0, prefix.length(), prefix) == 0;
53 }
54
StartsWithAny(const std::string & str,const std::vector<std::string> & prefixes)55 bool StartsWithAny(const std::string& str,
56 const std::vector<std::string>& prefixes) {
57 return std::any_of(
58 prefixes.begin(), prefixes.end(),
59 [&str](const std::string& prefix) { return StartsWith(str, prefix); });
60 }
61
EndsWith(const std::string & str,const std::string & suffix)62 bool EndsWith(const std::string& str, const std::string& suffix) {
63 if (suffix.size() > str.size())
64 return false;
65 return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
66 }
67
Contains(const std::string & haystack,const std::string & needle)68 bool Contains(const std::string& haystack, const std::string& needle) {
69 return haystack.find(needle) != std::string::npos;
70 }
71
Contains(const std::string & haystack,const char needle)72 bool Contains(const std::string& haystack, const char needle) {
73 return haystack.find(needle) != std::string::npos;
74 }
75
Find(const StringView & needle,const StringView & haystack)76 size_t Find(const StringView& needle, const StringView& haystack) {
77 if (needle.empty())
78 return 0;
79 if (needle.size() > haystack.size())
80 return std::string::npos;
81 for (size_t i = 0; i < haystack.size() - (needle.size() - 1); ++i) {
82 if (strncmp(haystack.data() + i, needle.data(), needle.size()) == 0)
83 return i;
84 }
85 return std::string::npos;
86 }
87
CaseInsensitiveEqual(const std::string & first,const std::string & second)88 bool CaseInsensitiveEqual(const std::string& first, const std::string& second) {
89 return first.size() == second.size() &&
90 std::equal(
91 first.begin(), first.end(), second.begin(),
92 [](char a, char b) { return Lowercase(a) == Lowercase(b); });
93 }
94
Join(const std::vector<std::string> & parts,const std::string & delim)95 std::string Join(const std::vector<std::string>& parts,
96 const std::string& delim) {
97 std::string acc;
98 for (size_t i = 0; i < parts.size(); ++i) {
99 acc += parts[i];
100 if (i + 1 != parts.size()) {
101 acc += delim;
102 }
103 }
104 return acc;
105 }
106
SplitString(const std::string & text,const std::string & delimiter)107 std::vector<std::string> SplitString(const std::string& text,
108 const std::string& delimiter) {
109 PERFETTO_CHECK(!delimiter.empty());
110
111 std::vector<std::string> output;
112 size_t start = 0;
113 size_t next;
114 for (;;) {
115 next = std::min(text.find(delimiter, start), text.size());
116 if (next > start)
117 output.emplace_back(&text[start], next - start);
118 start = next + delimiter.size();
119 if (start >= text.size())
120 break;
121 }
122 return output;
123 }
124
TrimWhitespace(const std::string & str)125 std::string TrimWhitespace(const std::string& str) {
126 std::string whitespaces = "\t\n ";
127
128 size_t front_idx = str.find_first_not_of(whitespaces);
129 std::string front_trimmed =
130 front_idx == std::string::npos ? "" : str.substr(front_idx);
131
132 size_t end_idx = front_trimmed.find_last_not_of(whitespaces);
133 return end_idx == std::string::npos ? ""
134 : front_trimmed.substr(0, end_idx + 1);
135 }
136
StripPrefix(const std::string & str,const std::string & prefix)137 std::string StripPrefix(const std::string& str, const std::string& prefix) {
138 return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
139 }
140
StripSuffix(const std::string & str,const std::string & suffix)141 std::string StripSuffix(const std::string& str, const std::string& suffix) {
142 return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
143 : str;
144 }
145
ToUpper(const std::string & str)146 std::string ToUpper(const std::string& str) {
147 // Don't use toupper(), it depends on the locale.
148 std::string res(str);
149 auto end = res.end();
150 for (auto c = res.begin(); c != end; ++c)
151 *c = Uppercase(*c);
152 return res;
153 }
154
ToLower(const std::string & str)155 std::string ToLower(const std::string& str) {
156 // Don't use tolower(), it depends on the locale.
157 std::string res(str);
158 auto end = res.end();
159 for (auto c = res.begin(); c != end; ++c)
160 *c = Lowercase(*c);
161 return res;
162 }
163
ToHex(const char * data,size_t size)164 std::string ToHex(const char* data, size_t size) {
165 std::string hex(2 * size + 1, 'x');
166 for (size_t i = 0; i < size; ++i) {
167 // snprintf prints 3 characters, the two hex digits and a null byte. As we
168 // write left to right, we keep overwriting the nullbytes, except for the
169 // last call to snprintf.
170 snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
171 }
172 // Remove the trailing nullbyte produced by the last snprintf.
173 hex.resize(2 * size);
174 return hex;
175 }
176
IntToHexString(uint32_t number)177 std::string IntToHexString(uint32_t number) {
178 size_t max_size = 11; // Max uint32 is 0xFFFFFFFF + 1 for null byte.
179 std::string buf;
180 buf.resize(max_size);
181 size_t final_len = SprintfTrunc(&buf[0], max_size, "0x%02x", number);
182 buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
183 return buf;
184 }
185
Uint64ToHexString(uint64_t number)186 std::string Uint64ToHexString(uint64_t number) {
187 return "0x" + Uint64ToHexStringNoPrefix(number);
188 }
189
Uint64ToHexStringNoPrefix(uint64_t number)190 std::string Uint64ToHexStringNoPrefix(uint64_t number) {
191 size_t max_size = 17; // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
192 std::string buf;
193 buf.resize(max_size);
194 size_t final_len = SprintfTrunc(&buf[0], max_size, "%" PRIx64 "", number);
195 buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
196 return buf;
197 }
198
StripChars(const std::string & str,const std::string & chars,char replacement)199 std::string StripChars(const std::string& str,
200 const std::string& chars,
201 char replacement) {
202 std::string res(str);
203 const char* start = res.c_str();
204 const char* remove = chars.c_str();
205 for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
206 res[static_cast<uintptr_t>(c - start)] = replacement;
207 return res;
208 }
209
ReplaceAll(std::string str,const std::string & to_replace,const std::string & replacement)210 std::string ReplaceAll(std::string str,
211 const std::string& to_replace,
212 const std::string& replacement) {
213 PERFETTO_CHECK(!to_replace.empty());
214 size_t pos = 0;
215 while ((pos = str.find(to_replace, pos)) != std::string::npos) {
216 str.replace(pos, to_replace.length(), replacement);
217 pos += replacement.length();
218 }
219 return str;
220 }
221
222 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
WideToUTF8(const std::wstring & source,std::string & output)223 bool WideToUTF8(const std::wstring& source, std::string& output) {
224 if (source.empty() ||
225 source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
226 return false;
227 }
228 int size = ::WideCharToMultiByte(CP_UTF8, 0, &source[0],
229 static_cast<int>(source.size()), nullptr, 0,
230 nullptr, nullptr);
231 output.assign(static_cast<size_t>(size), '\0');
232 if (::WideCharToMultiByte(CP_UTF8, 0, &source[0],
233 static_cast<int>(source.size()), &output[0], size,
234 nullptr, nullptr) != size) {
235 return false;
236 }
237 return true;
238 }
239 #endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
240
241 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
UTF8ToWide(const std::string & source,std::wstring & output)242 bool UTF8ToWide(const std::string& source, std::wstring& output) {
243 if (source.empty() ||
244 source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
245 return false;
246 }
247 int size = ::MultiByteToWideChar(CP_UTF8, 0, &source[0],
248 static_cast<int>(source.size()), nullptr, 0);
249 output.assign(static_cast<size_t>(size), L'\0');
250 if (::MultiByteToWideChar(CP_UTF8, 0, &source[0],
251 static_cast<int>(source.size()), &output[0],
252 size) != size) {
253 return false;
254 }
255 return true;
256 }
257 #endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
258
SprintfTrunc(char * dst,size_t dst_size,const char * fmt,...)259 size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
260 if (PERFETTO_UNLIKELY(dst_size == 0))
261 return 0;
262
263 va_list args;
264 va_start(args, fmt);
265 int src_size = vsnprintf(dst, dst_size, fmt, args);
266 va_end(args);
267
268 if (PERFETTO_UNLIKELY(src_size <= 0)) {
269 dst[0] = '\0';
270 return 0;
271 }
272
273 size_t res;
274 if (PERFETTO_LIKELY(src_size < static_cast<int>(dst_size))) {
275 // Most common case.
276 res = static_cast<size_t>(src_size);
277 } else {
278 // Truncation case.
279 res = dst_size - 1;
280 }
281
282 PERFETTO_DCHECK(res < dst_size);
283 PERFETTO_DCHECK(dst[res] == '\0');
284 return res;
285 }
286
FindLineWithOffset(base::StringView str,uint32_t offset)287 std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
288 uint32_t offset) {
289 static constexpr char kNewLine = '\n';
290 uint32_t line_offset = 0;
291 uint32_t line_count = 1;
292 for (uint32_t i = 0; i < str.size(); ++i) {
293 if (str.at(i) == kNewLine) {
294 line_offset = i + 1;
295 line_count++;
296 continue;
297 }
298 if (i == offset) {
299 size_t end_offset = str.find(kNewLine, i);
300 if (end_offset == std::string::npos) {
301 end_offset = str.size();
302 }
303 base::StringView line = str.substr(line_offset, end_offset - line_offset);
304 return LineWithOffset{line, offset - line_offset, line_count};
305 }
306 }
307 return std::nullopt;
308 }
309
310 } // namespace base
311 } // namespace perfetto
312