xref: /aosp_15_r20/external/perfetto/src/base/string_utils.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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