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 <string.h>
21 
22 #include <algorithm>
23 
24 #include "perfetto/base/logging.h"
25 
26 namespace perfetto {
27 namespace base {
28 
StartsWith(const std::string & str,const std::string & prefix)29 bool StartsWith(const std::string& str, const std::string& prefix) {
30   return str.compare(0, prefix.length(), prefix) == 0;
31 }
32 
EndsWith(const std::string & str,const std::string & suffix)33 bool EndsWith(const std::string& str, const std::string& suffix) {
34   if (suffix.size() > str.size())
35     return false;
36   return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
37 }
38 
Contains(const std::string & haystack,const std::string & needle)39 bool Contains(const std::string& haystack, const std::string& needle) {
40   return haystack.find(needle) != std::string::npos;
41 }
42 
Find(const StringView & needle,const StringView & haystack)43 size_t Find(const StringView& needle, const StringView& haystack) {
44   if (needle.empty())
45     return 0;
46   if (needle.size() > haystack.size())
47     return std::string::npos;
48   for (size_t i = 0; i < haystack.size() - (needle.size() - 1); ++i) {
49     if (strncmp(haystack.data() + i, needle.data(), needle.size()) == 0)
50       return i;
51   }
52   return std::string::npos;
53 }
54 
CaseInsensitiveEqual(const std::string & first,const std::string & second)55 bool CaseInsensitiveEqual(const std::string& first, const std::string& second) {
56   return first.size() == second.size() &&
57          std::equal(
58              first.begin(), first.end(), second.begin(),
59              [](char a, char b) { return Lowercase(a) == Lowercase(b); });
60 }
61 
Join(const std::vector<std::string> & parts,const std::string & delim)62 std::string Join(const std::vector<std::string>& parts,
63                  const std::string& delim) {
64   std::string acc;
65   for (size_t i = 0; i < parts.size(); ++i) {
66     acc += parts[i];
67     if (i + 1 != parts.size()) {
68       acc += delim;
69     }
70   }
71   return acc;
72 }
73 
SplitString(const std::string & text,const std::string & delimiter)74 std::vector<std::string> SplitString(const std::string& text,
75                                      const std::string& delimiter) {
76   PERFETTO_CHECK(!delimiter.empty());
77 
78   std::vector<std::string> output;
79   size_t start = 0;
80   size_t next;
81   for (;;) {
82     next = std::min(text.find(delimiter, start), text.size());
83     if (next > start)
84       output.emplace_back(&text[start], next - start);
85     start = next + delimiter.size();
86     if (start >= text.size())
87       break;
88   }
89   return output;
90 }
91 
StripPrefix(const std::string & str,const std::string & prefix)92 std::string StripPrefix(const std::string& str, const std::string& prefix) {
93   return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
94 }
95 
StripSuffix(const std::string & str,const std::string & suffix)96 std::string StripSuffix(const std::string& str, const std::string& suffix) {
97   return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
98                                : str;
99 }
100 
ToUpper(const std::string & str)101 std::string ToUpper(const std::string& str) {
102   // Don't use toupper(), it depends on the locale.
103   std::string res(str);
104   auto end = res.end();
105   for (auto c = res.begin(); c != end; ++c)
106     *c = Uppercase(*c);
107   return res;
108 }
109 
ToLower(const std::string & str)110 std::string ToLower(const std::string& str) {
111   // Don't use tolower(), it depends on the locale.
112   std::string res(str);
113   auto end = res.end();
114   for (auto c = res.begin(); c != end; ++c)
115     *c = Lowercase(*c);
116   return res;
117 }
118 
ToHex(const char * data,size_t size)119 std::string ToHex(const char* data, size_t size) {
120   std::string hex(2 * size + 1, 'x');
121   for (size_t i = 0; i < size; ++i) {
122     // snprintf prints 3 characters, the two hex digits and a null byte. As we
123     // write left to right, we keep overwriting the nullbytes, except for the
124     // last call to snprintf.
125     snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
126   }
127   // Remove the trailing nullbyte produced by the last snprintf.
128   hex.resize(2 * size);
129   return hex;
130 }
131 
IntToHexString(uint32_t number)132 std::string IntToHexString(uint32_t number) {
133   size_t max_size = 11;  // Max uint32 is 0xFFFFFFFF + 1 for null byte.
134   std::string buf;
135   buf.resize(max_size);
136   auto final_size = snprintf(&buf[0], max_size, "0x%02x", number);
137   PERFETTO_DCHECK(final_size >= 0);
138   buf.resize(static_cast<size_t>(final_size));  // Cuts off the final null byte.
139   return buf;
140 }
141 
Uint64ToHexString(uint64_t number)142 std::string Uint64ToHexString(uint64_t number) {
143   return "0x" + Uint64ToHexStringNoPrefix(number);
144 }
145 
Uint64ToHexStringNoPrefix(uint64_t number)146 std::string Uint64ToHexStringNoPrefix(uint64_t number) {
147   size_t max_size = 17;  // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
148   std::string buf;
149   buf.resize(max_size);
150   auto final_size = snprintf(&buf[0], max_size, "%" PRIx64 "", number);
151   PERFETTO_DCHECK(final_size >= 0);
152   buf.resize(static_cast<size_t>(final_size));  // Cuts off the final null byte.
153   return buf;
154 }
155 
StripChars(const std::string & str,const std::string & chars,char replacement)156 std::string StripChars(const std::string& str,
157                        const std::string& chars,
158                        char replacement) {
159   std::string res(str);
160   const char* start = res.c_str();
161   const char* remove = chars.c_str();
162   for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
163     res[static_cast<uintptr_t>(c - start)] = replacement;
164   return res;
165 }
166 
ReplaceAll(std::string str,const std::string & to_replace,const std::string & replacement)167 std::string ReplaceAll(std::string str,
168                        const std::string& to_replace,
169                        const std::string& replacement) {
170   PERFETTO_CHECK(!to_replace.empty());
171   size_t pos = 0;
172   while ((pos = str.find(to_replace, pos)) != std::string::npos) {
173     str.replace(pos, to_replace.length(), replacement);
174     pos += replacement.length();
175   }
176   return str;
177 }
178 
TrimLeading(const std::string & str)179 std::string TrimLeading(const std::string& str) {
180   size_t idx = str.find_first_not_of(' ');
181   return idx == std::string::npos ? str : str.substr(idx);
182 }
183 
184 }  // namespace base
185 }  // namespace perfetto
186