1 // Copyright (C) 2015 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "aemu/base/misc/StringUtils.h"
16 
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #ifdef _MSC_VER
21 #include "aemu/base/msvc.h"
22 #endif
23 
24 #include <algorithm>
25 #include <string>
26 #include <string_view>
27 #include <vector>
28 
29 
30 #ifdef _WIN32
memmem(const void * haystack,size_t haystackLen,const void * needle,size_t needleLen)31 const void* memmem(const void* haystack, size_t haystackLen,
32                    const void* needle, size_t needleLen) {
33     if (!haystack || !needle) {
34         return nullptr;
35     }
36 
37     const auto it = std::search(
38                         static_cast<const char*>(haystack),
39                         static_cast<const char*>(haystack) + haystackLen,
40                         static_cast<const char*>(needle),
41                         static_cast<const char*>(needle) + needleLen);
42     return it == static_cast<const char*>(haystack) + haystackLen
43             ? nullptr
44             : it;
45 }
46 #endif  // _WIN32
47 
48 namespace android {
49 namespace base {
50 
strDup(std::string_view view)51 char* strDup(std::string_view view) {
52     // Same as strdup(str.c_str()) but avoids a strlen() call.
53     char* ret = static_cast<char*>(malloc(view.size() + 1u));
54     ::memcpy(ret, view.data(), view.size());
55     ret[view.size()] = '\0';
56     return ret;
57 }
58 
strContains(std::string_view haystack,const char * needle)59 bool strContains(std::string_view haystack, const char* needle) {
60     return ::memmem(haystack.data(), haystack.size(), needle,
61                     ::strlen(needle)) != nullptr;
62 }
63 
Trim(const std::string & s)64 std::string Trim(const std::string& s) {
65     std::string result;
66 
67     if (s.size() == 0) {
68         return result;
69     }
70 
71     size_t start_index = 0;
72     size_t end_index = s.size() - 1;
73 
74     // Skip initial whitespace.
75     while (start_index < s.size()) {
76         if (!isspace(s[start_index])) {
77             break;
78         }
79         start_index++;
80     }
81 
82     // Skip terminating whitespace.
83     while (end_index >= start_index) {
84         if (!isspace(s[end_index])) {
85             break;
86         }
87         end_index--;
88     }
89 
90     // All spaces, no beef.
91     if (end_index < start_index) {
92         return "";
93     }
94     // Start_index is the first non-space, end_index is the last one.
95     return s.substr(start_index, end_index - start_index + 1);
96 }
97 
trim(const std::string & in)98 std::string trim(const std::string& in) {
99     return Trim(in);
100 }
101 
StartsWith(std::string_view s,std::string_view prefix)102 bool StartsWith(std::string_view s, std::string_view prefix) {
103     return s.substr(0, prefix.size()) == prefix;
104 }
105 
startsWith(std::string_view string,std::string_view prefix)106 bool startsWith(std::string_view string, std::string_view prefix) {
107     return string.size() >= prefix.size() &&
108             memcmp(string.data(), prefix.data(), prefix.size()) == 0;
109 }
110 
endsWith(std::string_view string,std::string_view suffix)111 bool endsWith(std::string_view string, std::string_view suffix) {
112     return string.size() >= suffix.size() &&
113             memcmp(string.data() + string.size() - suffix.size(),
114                    suffix.data(), suffix.size()) == 0;
115 }
116 
splitTokens(const std::string & input,std::vector<std::string> * out,std::string_view splitBy)117 void splitTokens(const std::string& input,
118                  std::vector<std::string>* out,
119                  std::string_view splitBy) {
120     auto removeWhiteSpace = [out](std::string_view strView) {
121         std::string s(strView);
122         s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
123         out->push_back(s);
124     };
125     out->clear();
126     split(input, splitBy, removeWhiteSpace);
127 }
128 
Split(const std::string & s,const std::string & delimiters)129 std::vector<std::string> Split(const std::string& s,
130                                const std::string& delimiters) {
131     if (delimiters.empty()) {
132         return {}
133     }
134 
135     std::vector<std::string> result;
136 
137     size_t base = 0;
138     size_t found;
139     while (true) {
140         found = s.find_first_of(delimiters, base);
141         result.push_back(s.substr(base, found - base));
142         if (found == s.npos)
143             break;
144         base = found + 1;
145     }
146 
147     return result;
148 }
149 
150 // These cases are probably the norm, so we mark them extern in the header to
151 // aid compile time and binary size.
152 template std::string Join(const std::vector<std::string>&, char);
153 template std::string Join(const std::vector<const char*>&, char);
154 template std::string Join(const std::vector<std::string>&, const std::string&);
155 template std::string Join(const std::vector<const char*>&, const std::string&);
156 
StartsWith(std::string_view s,char prefix)157 bool StartsWith(std::string_view s, char prefix) {
158     return !s.empty() && s.front() == prefix;
159 }
160 
StartsWithIgnoreCase(std::string_view s,std::string_view prefix)161 bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
162     return s.size() >= prefix.size() &&
163            strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
164 }
165 
EndsWith(std::string_view s,std::string_view suffix)166 bool EndsWith(std::string_view s, std::string_view suffix) {
167     return s.size() >= suffix.size() &&
168            s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
169 }
170 
EndsWith(std::string_view s,char suffix)171 bool EndsWith(std::string_view s, char suffix) {
172     return !s.empty() && s.back() == suffix;
173 }
174 
EndsWithIgnoreCase(std::string_view s,std::string_view suffix)175 bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
176     return s.size() >= suffix.size() &&
177            strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(),
178                        suffix.size()) == 0;
179 }
180 
EqualsIgnoreCase(std::string_view lhs,std::string_view rhs)181 bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
182     return lhs.size() == rhs.size() &&
183            strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
184 }
185 
StringReplace(std::string_view s,std::string_view from,std::string_view to,bool all)186 std::string StringReplace(std::string_view s,
187                           std::string_view from,
188                           std::string_view to,
189                           bool all) {
190     if (from.empty())
191         return std::string(s);
192 
193     std::string result;
194     std::string_view::size_type start_pos = 0;
195     do {
196         std::string_view::size_type pos = s.find(from, start_pos);
197         if (pos == std::string_view::npos)
198             break;
199 
200         result.append(s.data() + start_pos, pos - start_pos);
201         result.append(to.data(), to.size());
202 
203         start_pos = pos + from.size();
204     } while (all);
205     result.append(s.data() + start_pos, s.size() - start_pos);
206     return result;
207 }
208 
209 }  // namespace base
210 }  // namespace android
211