1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // string_utils:
7 // String helper functions.
8 //
9
10 #include "common/string_utils.h"
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <algorithm>
15 #include <fstream>
16 #include <sstream>
17
18 #include "common/platform.h"
19 #include "common/system_utils.h"
20
21 namespace
22 {
23
EndsWithSuffix(const char * str,const size_t strLen,const char * suffix,const size_t suffixLen)24 bool EndsWithSuffix(const char *str,
25 const size_t strLen,
26 const char *suffix,
27 const size_t suffixLen)
28 {
29 return suffixLen <= strLen && strncmp(str + strLen - suffixLen, suffix, suffixLen) == 0;
30 }
31
32 } // anonymous namespace
33
34 namespace angle
35 {
36
37 const char kWhitespaceASCII[] = " \f\n\r\t\v";
38
SplitString(const std::string & input,const std::string & delimiters,WhitespaceHandling whitespace,SplitResult resultType)39 std::vector<std::string> SplitString(const std::string &input,
40 const std::string &delimiters,
41 WhitespaceHandling whitespace,
42 SplitResult resultType)
43 {
44 std::vector<std::string> result;
45 if (input.empty())
46 {
47 return result;
48 }
49
50 std::string::size_type start = 0;
51 while (start != std::string::npos)
52 {
53 auto end = input.find_first_of(delimiters, start);
54
55 std::string piece;
56 if (end == std::string::npos)
57 {
58 piece = input.substr(start);
59 start = std::string::npos;
60 }
61 else
62 {
63 piece = input.substr(start, end - start);
64 start = end + 1;
65 }
66
67 if (whitespace == TRIM_WHITESPACE)
68 {
69 piece = TrimString(piece, kWhitespaceASCII);
70 }
71
72 if (resultType == SPLIT_WANT_ALL || !piece.empty())
73 {
74 result.push_back(std::move(piece));
75 }
76 }
77
78 return result;
79 }
80
SplitStringAlongWhitespace(const std::string & input,std::vector<std::string> * tokensOut)81 void SplitStringAlongWhitespace(const std::string &input, std::vector<std::string> *tokensOut)
82 {
83
84 std::istringstream stream(input);
85 std::string line;
86
87 while (std::getline(stream, line))
88 {
89 size_t prev = 0, pos;
90 while ((pos = line.find_first_of(kWhitespaceASCII, prev)) != std::string::npos)
91 {
92 if (pos > prev)
93 tokensOut->push_back(line.substr(prev, pos - prev));
94 prev = pos + 1;
95 }
96 if (prev < line.length())
97 tokensOut->push_back(line.substr(prev, std::string::npos));
98 }
99 }
100
TrimString(const std::string & input,const std::string & trimChars)101 std::string TrimString(const std::string &input, const std::string &trimChars)
102 {
103 auto begin = input.find_first_not_of(trimChars);
104 if (begin == std::string::npos)
105 {
106 return "";
107 }
108
109 std::string::size_type end = input.find_last_not_of(trimChars);
110 if (end == std::string::npos)
111 {
112 return input.substr(begin);
113 }
114
115 return input.substr(begin, end - begin + 1);
116 }
117
GetPrefix(const std::string & input,size_t offset,const char * delimiter)118 std::string GetPrefix(const std::string &input, size_t offset, const char *delimiter)
119 {
120 size_t match = input.find(delimiter, offset);
121 if (match == std::string::npos)
122 {
123 return input.substr(offset);
124 }
125 return input.substr(offset, match - offset);
126 }
127
GetPrefix(const std::string & input,size_t offset,char delimiter)128 std::string GetPrefix(const std::string &input, size_t offset, char delimiter)
129 {
130 size_t match = input.find(delimiter, offset);
131 if (match == std::string::npos)
132 {
133 return input.substr(offset);
134 }
135 return input.substr(offset, match - offset);
136 }
137
HexStringToUInt(const std::string & input,unsigned int * uintOut)138 bool HexStringToUInt(const std::string &input, unsigned int *uintOut)
139 {
140 unsigned int offset = 0;
141
142 if (input.size() >= 2 && input[0] == '0' && input[1] == 'x')
143 {
144 offset = 2u;
145 }
146
147 // Simple validity check
148 if (input.find_first_not_of("0123456789ABCDEFabcdef", offset) != std::string::npos)
149 {
150 return false;
151 }
152
153 std::stringstream inStream(input);
154 inStream >> std::hex >> *uintOut;
155 return !inStream.fail();
156 }
157
ReadFileToString(const std::string & path,std::string * stringOut)158 bool ReadFileToString(const std::string &path, std::string *stringOut)
159 {
160 std::ifstream inFile(path.c_str());
161 if (inFile.fail())
162 {
163 return false;
164 }
165
166 inFile.seekg(0, std::ios::end);
167 stringOut->reserve(static_cast<std::string::size_type>(inFile.tellg()));
168 inFile.seekg(0, std::ios::beg);
169
170 stringOut->assign(std::istreambuf_iterator<char>(inFile), std::istreambuf_iterator<char>());
171 return !inFile.fail();
172 }
173
BeginsWith(const std::string & str,const std::string & prefix)174 bool BeginsWith(const std::string &str, const std::string &prefix)
175 {
176 return strncmp(str.c_str(), prefix.c_str(), prefix.length()) == 0;
177 }
178
BeginsWith(const std::string & str,const char * prefix)179 bool BeginsWith(const std::string &str, const char *prefix)
180 {
181 return strncmp(str.c_str(), prefix, strlen(prefix)) == 0;
182 }
183
BeginsWith(const char * str,const char * prefix)184 bool BeginsWith(const char *str, const char *prefix)
185 {
186 return strncmp(str, prefix, strlen(prefix)) == 0;
187 }
188
BeginsWith(const std::string & str,const std::string & prefix,const size_t prefixLength)189 bool BeginsWith(const std::string &str, const std::string &prefix, const size_t prefixLength)
190 {
191 return strncmp(str.c_str(), prefix.c_str(), prefixLength) == 0;
192 }
193
EndsWith(const std::string & str,const std::string & suffix)194 bool EndsWith(const std::string &str, const std::string &suffix)
195 {
196 return EndsWithSuffix(str.c_str(), str.length(), suffix.c_str(), suffix.length());
197 }
198
EndsWith(const std::string & str,const char * suffix)199 bool EndsWith(const std::string &str, const char *suffix)
200 {
201 return EndsWithSuffix(str.c_str(), str.length(), suffix, strlen(suffix));
202 }
203
EndsWith(const char * str,const char * suffix)204 bool EndsWith(const char *str, const char *suffix)
205 {
206 return EndsWithSuffix(str, strlen(str), suffix, strlen(suffix));
207 }
208
ContainsToken(const std::string & tokenStr,char delimiter,const std::string & token)209 bool ContainsToken(const std::string &tokenStr, char delimiter, const std::string &token)
210 {
211 if (token.empty())
212 {
213 return false;
214 }
215 // Compare token with all sub-strings terminated by delimiter or end of string
216 std::string::size_type start = 0u;
217 do
218 {
219 std::string::size_type end = tokenStr.find(delimiter, start);
220 if (end == std::string::npos)
221 {
222 end = tokenStr.length();
223 }
224 const std::string::size_type length = end - start;
225 if (length == token.length() && tokenStr.compare(start, length, token) == 0)
226 {
227 return true;
228 }
229 start = end + 1u;
230 } while (start < tokenStr.size());
231 return false;
232 }
233
ToLower(std::string * str)234 void ToLower(std::string *str)
235 {
236 for (char &ch : *str)
237 {
238 ch = static_cast<char>(::tolower(ch));
239 }
240 }
241
ToUpper(std::string * str)242 void ToUpper(std::string *str)
243 {
244 for (char &ch : *str)
245 {
246 ch = static_cast<char>(::toupper(ch));
247 }
248 }
249
ReplaceSubstring(std::string * str,const std::string & substring,const std::string & replacement)250 bool ReplaceSubstring(std::string *str,
251 const std::string &substring,
252 const std::string &replacement)
253 {
254 size_t replacePos = str->find(substring);
255 if (replacePos == std::string::npos)
256 {
257 return false;
258 }
259 str->replace(replacePos, substring.size(), replacement);
260 return true;
261 }
262
GetStringsFromEnvironmentVarOrAndroidProperty(const char * varName,const char * propertyName,const char * separator)263 std::vector<std::string> GetStringsFromEnvironmentVarOrAndroidProperty(const char *varName,
264 const char *propertyName,
265 const char *separator)
266 {
267 std::string environment = GetEnvironmentVarOrAndroidProperty(varName, propertyName);
268 return SplitString(environment, separator, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
269 }
270
GetCachedStringsFromEnvironmentVarOrAndroidProperty(const char * varName,const char * propertyName,const char * separator)271 std::vector<std::string> GetCachedStringsFromEnvironmentVarOrAndroidProperty(
272 const char *varName,
273 const char *propertyName,
274 const char *separator)
275 {
276 std::string environment = GetEnvironmentVarOrAndroidProperty(varName, propertyName);
277 return SplitString(environment, separator, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
278 }
279
280 // reference name can have *.
NamesMatchWithWildcard(const char * ref,const char * testName)281 bool NamesMatchWithWildcard(const char *ref, const char *testName)
282 {
283 // Find the first * in ref.
284 const char *firstWildcard = strchr(ref, '*');
285
286 // If there are no wildcards, match the strings precisely.
287 if (firstWildcard == nullptr)
288 {
289 return strcmp(ref, testName) == 0;
290 }
291
292 // Otherwise, match up to the wildcard first.
293 size_t preWildcardLen = firstWildcard - ref;
294 if (strncmp(ref, testName, preWildcardLen) != 0)
295 {
296 return false;
297 }
298
299 const char *postWildcardRef = ref + preWildcardLen + 1;
300
301 // As a small optimization, if the wildcard is the last character in ref, accept the match
302 // already.
303 if (postWildcardRef[0] == '\0')
304 {
305 return true;
306 }
307
308 // Try to match the wildcard with a number of characters.
309 for (size_t matchSize = 0; testName[matchSize] != '\0'; ++matchSize)
310 {
311 if (NamesMatchWithWildcard(postWildcardRef, testName + matchSize))
312 {
313 return true;
314 }
315 }
316
317 return false;
318 }
319
320 } // namespace angle
321