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
StripPrefix(const std::string & str,const std::string & prefix)123 std::string StripPrefix(const std::string& str, const std::string& prefix) {
124 return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
125 }
126
StripSuffix(const std::string & str,const std::string & suffix)127 std::string StripSuffix(const std::string& str, const std::string& suffix) {
128 return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
129 : str;
130 }
131
ToUpper(const std::string & str)132 std::string ToUpper(const std::string& str) {
133 // Don't use toupper(), it depends on the locale.
134 std::string res(str);
135 auto end = res.end();
136 for (auto c = res.begin(); c != end; ++c)
137 *c = Uppercase(*c);
138 return res;
139 }
140
ToLower(const std::string & str)141 std::string ToLower(const std::string& str) {
142 // Don't use tolower(), it depends on the locale.
143 std::string res(str);
144 auto end = res.end();
145 for (auto c = res.begin(); c != end; ++c)
146 *c = Lowercase(*c);
147 return res;
148 }
149
ToHex(const char * data,size_t size)150 std::string ToHex(const char* data, size_t size) {
151 std::string hex(2 * size + 1, 'x');
152 for (size_t i = 0; i < size; ++i) {
153 // snprintf prints 3 characters, the two hex digits and a null byte. As we
154 // write left to right, we keep overwriting the nullbytes, except for the
155 // last call to snprintf.
156 snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
157 }
158 // Remove the trailing nullbyte produced by the last snprintf.
159 hex.resize(2 * size);
160 return hex;
161 }
162
IntToHexString(uint32_t number)163 std::string IntToHexString(uint32_t number) {
164 size_t max_size = 11; // Max uint32 is 0xFFFFFFFF + 1 for null byte.
165 std::string buf;
166 buf.resize(max_size);
167 size_t final_len = SprintfTrunc(&buf[0], max_size, "0x%02x", number);
168 buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
169 return buf;
170 }
171
Uint64ToHexString(uint64_t number)172 std::string Uint64ToHexString(uint64_t number) {
173 return "0x" + Uint64ToHexStringNoPrefix(number);
174 }
175
Uint64ToHexStringNoPrefix(uint64_t number)176 std::string Uint64ToHexStringNoPrefix(uint64_t number) {
177 size_t max_size = 17; // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
178 std::string buf;
179 buf.resize(max_size);
180 size_t final_len = SprintfTrunc(&buf[0], max_size, "%" PRIx64 "", number);
181 buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
182 return buf;
183 }
184
StripChars(const std::string & str,const std::string & chars,char replacement)185 std::string StripChars(const std::string& str,
186 const std::string& chars,
187 char replacement) {
188 std::string res(str);
189 const char* start = res.c_str();
190 const char* remove = chars.c_str();
191 for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
192 res[static_cast<uintptr_t>(c - start)] = replacement;
193 return res;
194 }
195
ReplaceAll(std::string str,const std::string & to_replace,const std::string & replacement)196 std::string ReplaceAll(std::string str,
197 const std::string& to_replace,
198 const std::string& replacement) {
199 PERFETTO_CHECK(!to_replace.empty());
200 size_t pos = 0;
201 while ((pos = str.find(to_replace, pos)) != std::string::npos) {
202 str.replace(pos, to_replace.length(), replacement);
203 pos += replacement.length();
204 }
205 return str;
206 }
207
SprintfTrunc(char * dst,size_t dst_size,const char * fmt,...)208 size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
209 if (PERFETTO_UNLIKELY(dst_size) == 0)
210 return 0;
211
212 va_list args;
213 va_start(args, fmt);
214 int src_size = vsnprintf(dst, dst_size, fmt, args);
215 va_end(args);
216
217 if (PERFETTO_UNLIKELY(src_size) <= 0) {
218 dst[0] = '\0';
219 return 0;
220 }
221
222 size_t res;
223 if (PERFETTO_LIKELY(src_size < static_cast<int>(dst_size))) {
224 // Most common case.
225 res = static_cast<size_t>(src_size);
226 } else {
227 // Truncation case.
228 res = dst_size - 1;
229 }
230
231 PERFETTO_DCHECK(res < dst_size);
232 PERFETTO_DCHECK(dst[res] == '\0');
233 return res;
234 }
235
236 } // namespace base
237 } // namespace perfetto
238