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 "android/base/misc/StringUtils.h"
16
17 #include <stdlib.h>
18 #include <string.h>
19
20 #ifdef _MSC_VER
21 #include "msvc-posix.h"
22 #endif
23
24 #include <algorithm>
25 #include <string>
26 #include <vector>
27
28 #ifdef _WIN32
memmem(const void * haystack,size_t haystackLen,const void * needle,size_t needleLen)29 const void* memmem(const void* haystack, size_t haystackLen,
30 const void* needle, size_t needleLen) {
31 if (!haystack || !needle) {
32 return nullptr;
33 }
34
35 const auto it = std::search(
36 static_cast<const char*>(haystack),
37 static_cast<const char*>(haystack) + haystackLen,
38 static_cast<const char*>(needle),
39 static_cast<const char*>(needle) + needleLen);
40 return it == static_cast<const char*>(haystack) + haystackLen
41 ? nullptr
42 : it;
43 }
44 #endif // _WIN32
45
46 namespace android {
47 namespace base {
48
strDup(StringView view)49 char* strDup(StringView view) {
50 // Same as strdup(str.c_str()) but avoids a strlen() call.
51 char* ret = static_cast<char*>(malloc(view.size() + 1u));
52 ::memcpy(ret, view.data(), view.size());
53 ret[view.size()] = '\0';
54 return ret;
55 }
56
strContains(StringView haystack,const char * needle)57 bool strContains(StringView haystack, const char* needle) {
58 return ::memmem(haystack.data(), haystack.size(), needle,
59 ::strlen(needle)) != nullptr;
60 }
61
Trim(const std::string & s)62 std::string Trim(const std::string& s) {
63 std::string result;
64
65 if (s.size() == 0) {
66 return result;
67 }
68
69 size_t start_index = 0;
70 size_t end_index = s.size() - 1;
71
72 // Skip initial whitespace.
73 while (start_index < s.size()) {
74 if (!isspace(s[start_index])) {
75 break;
76 }
77 start_index++;
78 }
79
80 // Skip terminating whitespace.
81 while (end_index >= start_index) {
82 if (!isspace(s[end_index])) {
83 break;
84 }
85 end_index--;
86 }
87
88 // All spaces, no beef.
89 if (end_index < start_index) {
90 return "";
91 }
92 // Start_index is the first non-space, end_index is the last one.
93 return s.substr(start_index, end_index - start_index + 1);
94 }
95
trim(const std::string & in)96 std::string trim(const std::string& in) {
97 return Trim(in);
98 }
99
StartsWith(std::string_view s,std::string_view prefix)100 bool StartsWith(std::string_view s, std::string_view prefix) {
101 return s.substr(0, prefix.size()) == prefix;
102 }
103
startsWith(StringView string,StringView prefix)104 bool startsWith(StringView string, StringView prefix) {
105 return string.size() >= prefix.size() &&
106 memcmp(string.data(), prefix.data(), prefix.size()) == 0;
107 }
108
endsWith(StringView string,StringView suffix)109 bool endsWith(StringView string, StringView suffix) {
110 return string.size() >= suffix.size() &&
111 memcmp(string.data() + string.size() - suffix.size(),
112 suffix.data(), suffix.size()) == 0;
113 }
114
splitTokens(const std::string & input,std::vector<std::string> * out,StringView splitBy)115 void splitTokens(const std::string& input,
116 std::vector<std::string>* out,
117 StringView splitBy) {
118 auto removeWhiteSpace = [out](StringView strView) {
119 std::string s = strView.str();
120 s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
121 out->push_back(s);
122 };
123 out->clear();
124 split(input, splitBy, removeWhiteSpace);
125 }
126
127 #define CHECK_NE(a, b) \
128 if ((a) == (b)) \
129 abort();
130
Split(const std::string & s,const std::string & delimiters)131 std::vector<std::string> Split(const std::string& s,
132 const std::string& delimiters) {
133 CHECK_NE(delimiters.size(), 0U);
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