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 #endif
28
29 #include <cinttypes>
30
31 #include "perfetto/base/compiler.h"
32 #include "perfetto/base/logging.h"
33
34 namespace perfetto {
35 namespace base {
36
37 // Locale-independant as possible version of strtod.
StrToD(const char * nptr,char ** endptr)38 double StrToD(const char* nptr, char** endptr) {
39 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
40 PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
41 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
42 static auto c_locale = newlocale(LC_ALL, "C", nullptr);
43 return strtod_l(nptr, endptr, c_locale);
44 #else
45 return strtod(nptr, endptr);
46 #endif
47 }
48
StartsWith(const std::string & str,const std::string & prefix)49 bool StartsWith(const std::string& str, const std::string& prefix) {
50 return str.compare(0, prefix.length(), prefix) == 0;
51 }
52
StartsWithAny(const std::string & str,const std::vector<std::string> & prefixes)53 bool StartsWithAny(const std::string& str,
54 const std::vector<std::string>& prefixes) {
55 return std::any_of(
56 prefixes.begin(), prefixes.end(),
57 [&str](const std::string& prefix) { return StartsWith(str, prefix); });
58 }
59
EndsWith(const std::string & str,const std::string & suffix)60 bool EndsWith(const std::string& str, const std::string& suffix) {
61 if (suffix.size() > str.size())
62 return false;
63 return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
64 }
65
Contains(const std::string & haystack,const std::string & needle)66 bool Contains(const std::string& haystack, const std::string& needle) {
67 return haystack.find(needle) != std::string::npos;
68 }
69
Contains(const std::string & haystack,const char needle)70 bool Contains(const std::string& haystack, const char needle) {
71 return haystack.find(needle) != std::string::npos;
72 }
73
Find(const StringView & needle,const StringView & haystack)74 size_t Find(const StringView& needle, const StringView& haystack) {
75 if (needle.empty())
76 return 0;
77 if (needle.size() > haystack.size())
78 return std::string::npos;
79 for (size_t i = 0; i < haystack.size() - (needle.size() - 1); ++i) {
80 if (strncmp(haystack.data() + i, needle.data(), needle.size()) == 0)
81 return i;
82 }
83 return std::string::npos;
84 }
85
CaseInsensitiveEqual(const std::string & first,const std::string & second)86 bool CaseInsensitiveEqual(const std::string& first, const std::string& second) {
87 return first.size() == second.size() &&
88 std::equal(
89 first.begin(), first.end(), second.begin(),
90 [](char a, char b) { return Lowercase(a) == Lowercase(b); });
91 }
92
Join(const std::vector<std::string> & parts,const std::string & delim)93 std::string Join(const std::vector<std::string>& parts,
94 const std::string& delim) {
95 std::string acc;
96 for (size_t i = 0; i < parts.size(); ++i) {
97 acc += parts[i];
98 if (i + 1 != parts.size()) {
99 acc += delim;
100 }
101 }
102 return acc;
103 }
104
SplitString(const std::string & text,const std::string & delimiter)105 std::vector<std::string> SplitString(const std::string& text,
106 const std::string& delimiter) {
107 PERFETTO_CHECK(!delimiter.empty());
108
109 std::vector<std::string> output;
110 size_t start = 0;
111 size_t next;
112 for (;;) {
113 next = std::min(text.find(delimiter, start), text.size());
114 if (next > start)
115 output.emplace_back(&text[start], next - start);
116 start = next + delimiter.size();
117 if (start >= text.size())
118 break;
119 }
120 return output;
121 }
122
TrimWhitespace(const std::string & str)123 std::string TrimWhitespace(const std::string& str) {
124 std::string whitespaces = "\t\n ";
125
126 size_t front_idx = str.find_first_not_of(whitespaces);
127 std::string front_trimmed =
128 front_idx == std::string::npos ? "" : str.substr(front_idx);
129
130 size_t end_idx = front_trimmed.find_last_not_of(whitespaces);
131 return end_idx == std::string::npos ? ""
132 : front_trimmed.substr(0, end_idx + 1);
133 }
134
StripPrefix(const std::string & str,const std::string & prefix)135 std::string StripPrefix(const std::string& str, const std::string& prefix) {
136 return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
137 }
138
StripSuffix(const std::string & str,const std::string & suffix)139 std::string StripSuffix(const std::string& str, const std::string& suffix) {
140 return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
141 : str;
142 }
143
ToUpper(const std::string & str)144 std::string ToUpper(const std::string& str) {
145 // Don't use toupper(), it depends on the locale.
146 std::string res(str);
147 auto end = res.end();
148 for (auto c = res.begin(); c != end; ++c)
149 *c = Uppercase(*c);
150 return res;
151 }
152
ToLower(const std::string & str)153 std::string ToLower(const std::string& str) {
154 // Don't use tolower(), it depends on the locale.
155 std::string res(str);
156 auto end = res.end();
157 for (auto c = res.begin(); c != end; ++c)
158 *c = Lowercase(*c);
159 return res;
160 }
161
ToHex(const char * data,size_t size)162 std::string ToHex(const char* data, size_t size) {
163 std::string hex(2 * size + 1, 'x');
164 for (size_t i = 0; i < size; ++i) {
165 // snprintf prints 3 characters, the two hex digits and a null byte. As we
166 // write left to right, we keep overwriting the nullbytes, except for the
167 // last call to snprintf.
168 snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
169 }
170 // Remove the trailing nullbyte produced by the last snprintf.
171 hex.resize(2 * size);
172 return hex;
173 }
174
IntToHexString(uint32_t number)175 std::string IntToHexString(uint32_t number) {
176 size_t max_size = 11; // Max uint32 is 0xFFFFFFFF + 1 for null byte.
177 std::string buf;
178 buf.resize(max_size);
179 size_t final_len = SprintfTrunc(&buf[0], max_size, "0x%02x", number);
180 buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
181 return buf;
182 }
183
Uint64ToHexString(uint64_t number)184 std::string Uint64ToHexString(uint64_t number) {
185 return "0x" + Uint64ToHexStringNoPrefix(number);
186 }
187
Uint64ToHexStringNoPrefix(uint64_t number)188 std::string Uint64ToHexStringNoPrefix(uint64_t number) {
189 size_t max_size = 17; // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
190 std::string buf;
191 buf.resize(max_size);
192 size_t final_len = SprintfTrunc(&buf[0], max_size, "%" PRIx64 "", number);
193 buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
194 return buf;
195 }
196
StripChars(const std::string & str,const std::string & chars,char replacement)197 std::string StripChars(const std::string& str,
198 const std::string& chars,
199 char replacement) {
200 std::string res(str);
201 const char* start = res.c_str();
202 const char* remove = chars.c_str();
203 for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
204 res[static_cast<uintptr_t>(c - start)] = replacement;
205 return res;
206 }
207
ReplaceAll(std::string str,const std::string & to_replace,const std::string & replacement)208 std::string ReplaceAll(std::string str,
209 const std::string& to_replace,
210 const std::string& replacement) {
211 PERFETTO_CHECK(!to_replace.empty());
212 size_t pos = 0;
213 while ((pos = str.find(to_replace, pos)) != std::string::npos) {
214 str.replace(pos, to_replace.length(), replacement);
215 pos += replacement.length();
216 }
217 return str;
218 }
219
SprintfTrunc(char * dst,size_t dst_size,const char * fmt,...)220 size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
221 if (PERFETTO_UNLIKELY(dst_size) == 0)
222 return 0;
223
224 va_list args;
225 va_start(args, fmt);
226 int src_size = vsnprintf(dst, dst_size, fmt, args);
227 va_end(args);
228
229 if (PERFETTO_UNLIKELY(src_size) <= 0) {
230 dst[0] = '\0';
231 return 0;
232 }
233
234 size_t res;
235 if (PERFETTO_LIKELY(src_size < static_cast<int>(dst_size))) {
236 // Most common case.
237 res = static_cast<size_t>(src_size);
238 } else {
239 // Truncation case.
240 res = dst_size - 1;
241 }
242
243 PERFETTO_DCHECK(res < dst_size);
244 PERFETTO_DCHECK(dst[res] == '\0');
245 return res;
246 }
247
FindLineWithOffset(base::StringView str,uint32_t offset)248 std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
249 uint32_t offset) {
250 static constexpr char kNewLine = '\n';
251 uint32_t line_offset = 0;
252 uint32_t line_count = 1;
253 for (uint32_t i = 0; i < str.size(); ++i) {
254 if (str.at(i) == kNewLine) {
255 line_offset = i + 1;
256 line_count++;
257 continue;
258 }
259 if (i == offset) {
260 size_t end_offset = str.find(kNewLine, i);
261 if (end_offset == std::string::npos) {
262 end_offset = str.size();
263 }
264 base::StringView line = str.substr(line_offset, end_offset - line_offset);
265 return LineWithOffset{line, offset - line_offset, line_count};
266 }
267 }
268 return std::nullopt;
269 }
270
271 } // namespace base
272 } // namespace perfetto
273