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 <inttypes.h>
20 #include <locale.h>
21 #include <string.h>
22
23 #if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
24 #include <xlocale.h>
25 #endif
26
27 #include <algorithm>
28
29 #include "perfetto/base/logging.h"
30
31 namespace perfetto {
32 namespace base {
33 namespace {
34 constexpr char kBase64Table[] =
35 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
36 "abcdefghijklmnopqrstuvwxyz0123456789+/";
37 }
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
QuoteAndEscapeControlCodes(const std::string & raw)51 std::string QuoteAndEscapeControlCodes(const std::string& raw) {
52 std::string ret;
53 for (auto it = raw.cbegin(); it != raw.cend(); it++) {
54 switch (*it) {
55 case '\\':
56 ret += "\\\\";
57 break;
58 case '"':
59 ret += "\\\"";
60 break;
61 case '/':
62 ret += "\\/";
63 break;
64 case '\b':
65 ret += "\\b";
66 break;
67 case '\f':
68 ret += "\\f";
69 break;
70 case '\n':
71 ret += "\\n";
72 break;
73 case '\r':
74 ret += "\\r";
75 break;
76 case '\t':
77 ret += "\\t";
78 break;
79 default:
80 ret += *it;
81 break;
82 }
83 }
84 return '"' + ret + '"';
85 }
86
StartsWith(const std::string & str,const std::string & prefix)87 bool StartsWith(const std::string& str, const std::string& prefix) {
88 return str.compare(0, prefix.length(), prefix) == 0;
89 }
90
EndsWith(const std::string & str,const std::string & suffix)91 bool EndsWith(const std::string& str, const std::string& suffix) {
92 if (suffix.size() > str.size())
93 return false;
94 return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
95 }
96
Contains(const std::string & haystack,const std::string & needle)97 bool Contains(const std::string& haystack, const std::string& needle) {
98 return haystack.find(needle) != std::string::npos;
99 }
100
Contains(const std::string & haystack,const char needle)101 bool Contains(const std::string& haystack, const char needle) {
102 return haystack.find(needle) != std::string::npos;
103 }
104
Find(const StringView & needle,const StringView & haystack)105 size_t Find(const StringView& needle, const StringView& haystack) {
106 if (needle.empty())
107 return 0;
108 if (needle.size() > haystack.size())
109 return std::string::npos;
110 for (size_t i = 0; i < haystack.size() - (needle.size() - 1); ++i) {
111 if (strncmp(haystack.data() + i, needle.data(), needle.size()) == 0)
112 return i;
113 }
114 return std::string::npos;
115 }
116
CaseInsensitiveEqual(const std::string & first,const std::string & second)117 bool CaseInsensitiveEqual(const std::string& first, const std::string& second) {
118 return first.size() == second.size() &&
119 std::equal(
120 first.begin(), first.end(), second.begin(),
121 [](char a, char b) { return Lowercase(a) == Lowercase(b); });
122 }
123
Join(const std::vector<std::string> & parts,const std::string & delim)124 std::string Join(const std::vector<std::string>& parts,
125 const std::string& delim) {
126 std::string acc;
127 for (size_t i = 0; i < parts.size(); ++i) {
128 acc += parts[i];
129 if (i + 1 != parts.size()) {
130 acc += delim;
131 }
132 }
133 return acc;
134 }
135
SplitString(const std::string & text,const std::string & delimiter)136 std::vector<std::string> SplitString(const std::string& text,
137 const std::string& delimiter) {
138 PERFETTO_CHECK(!delimiter.empty());
139
140 std::vector<std::string> output;
141 size_t start = 0;
142 size_t next;
143 for (;;) {
144 next = std::min(text.find(delimiter, start), text.size());
145 if (next > start)
146 output.emplace_back(&text[start], next - start);
147 start = next + delimiter.size();
148 if (start >= text.size())
149 break;
150 }
151 return output;
152 }
153
StripPrefix(const std::string & str,const std::string & prefix)154 std::string StripPrefix(const std::string& str, const std::string& prefix) {
155 return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
156 }
157
StripSuffix(const std::string & str,const std::string & suffix)158 std::string StripSuffix(const std::string& str, const std::string& suffix) {
159 return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
160 : str;
161 }
162
ToUpper(const std::string & str)163 std::string ToUpper(const std::string& str) {
164 // Don't use toupper(), it depends on the locale.
165 std::string res(str);
166 auto end = res.end();
167 for (auto c = res.begin(); c != end; ++c)
168 *c = Uppercase(*c);
169 return res;
170 }
171
ToLower(const std::string & str)172 std::string ToLower(const std::string& str) {
173 // Don't use tolower(), it depends on the locale.
174 std::string res(str);
175 auto end = res.end();
176 for (auto c = res.begin(); c != end; ++c)
177 *c = Lowercase(*c);
178 return res;
179 }
180
ToHex(const char * data,size_t size)181 std::string ToHex(const char* data, size_t size) {
182 std::string hex(2 * size + 1, 'x');
183 for (size_t i = 0; i < size; ++i) {
184 // snprintf prints 3 characters, the two hex digits and a null byte. As we
185 // write left to right, we keep overwriting the nullbytes, except for the
186 // last call to snprintf.
187 snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
188 }
189 // Remove the trailing nullbyte produced by the last snprintf.
190 hex.resize(2 * size);
191 return hex;
192 }
193
IntToHexString(uint32_t number)194 std::string IntToHexString(uint32_t number) {
195 size_t max_size = 11; // Max uint32 is 0xFFFFFFFF + 1 for null byte.
196 std::string buf;
197 buf.resize(max_size);
198 auto final_size = snprintf(&buf[0], max_size, "0x%02x", number);
199 PERFETTO_DCHECK(final_size >= 0);
200 buf.resize(static_cast<size_t>(final_size)); // Cuts off the final null byte.
201 return buf;
202 }
203
Uint64ToHexString(uint64_t number)204 std::string Uint64ToHexString(uint64_t number) {
205 return "0x" + Uint64ToHexStringNoPrefix(number);
206 }
207
Uint64ToHexStringNoPrefix(uint64_t number)208 std::string Uint64ToHexStringNoPrefix(uint64_t number) {
209 size_t max_size = 17; // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
210 std::string buf;
211 buf.resize(max_size);
212 auto final_size = snprintf(&buf[0], max_size, "%" PRIx64 "", number);
213 PERFETTO_DCHECK(final_size >= 0);
214 buf.resize(static_cast<size_t>(final_size)); // Cuts off the final null byte.
215 return buf;
216 }
217
StripChars(const std::string & str,const std::string & chars,char replacement)218 std::string StripChars(const std::string& str,
219 const std::string& chars,
220 char replacement) {
221 std::string res(str);
222 const char* start = res.c_str();
223 const char* remove = chars.c_str();
224 for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
225 res[static_cast<uintptr_t>(c - start)] = replacement;
226 return res;
227 }
228
ReplaceAll(std::string str,const std::string & to_replace,const std::string & replacement)229 std::string ReplaceAll(std::string str,
230 const std::string& to_replace,
231 const std::string& replacement) {
232 PERFETTO_CHECK(!to_replace.empty());
233 size_t pos = 0;
234 while ((pos = str.find(to_replace, pos)) != std::string::npos) {
235 str.replace(pos, to_replace.length(), replacement);
236 pos += replacement.length();
237 }
238 return str;
239 }
240
TrimLeading(const std::string & str)241 std::string TrimLeading(const std::string& str) {
242 size_t idx = str.find_first_not_of(' ');
243 return idx == std::string::npos ? str : str.substr(idx);
244 }
245
Base64Encode(const void * raw,size_t size)246 std::string Base64Encode(const void* raw, size_t size) {
247 // The following three cases are based on the tables in the example
248 // section in https://en.wikipedia.org/wiki/Base64. We process three
249 // input bytes at a time, emitting 4 output bytes at a time.
250 const uint8_t* ptr = static_cast<const uint8_t*>(raw);
251 size_t ii = 0;
252
253 std::string out;
254 out.reserve((size + 2) * 4 / 3);
255
256 // While possible, process three input bytes.
257 for (; ii + 3 <= size; ii += 3) {
258 uint32_t twentyfour_bits =
259 (uint32_t(ptr[ii]) << 16) | (uint32_t(ptr[ii + 1]) << 8) | ptr[ii + 2];
260 out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
261 out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
262 out.push_back(kBase64Table[(twentyfour_bits >> 6) & 0x3f]);
263 out.push_back(kBase64Table[twentyfour_bits & 0x3f]);
264 }
265 if (ii + 2 <= size) { // Process two input bytes.
266 uint32_t twentyfour_bits =
267 (uint32_t(ptr[ii]) << 16) | (uint32_t(ptr[ii + 1]) << 8);
268 out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
269 out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
270 out.push_back(kBase64Table[(twentyfour_bits >> 6) & 0x3f]);
271 out.push_back('='); // Emit padding.
272 return out;
273 }
274 if (ii + 1 <= size) { // Process a single input byte.
275 uint32_t twentyfour_bits = (uint32_t(ptr[ii]) << 16);
276 out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
277 out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
278 out.push_back('='); // Emit padding.
279 out.push_back('='); // Emit padding.
280 }
281 return out;
282 }
283
284 } // namespace base
285 } // namespace perfetto
286