• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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