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