string_utils.cpp (9200B)
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 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 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 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 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 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 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 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 159 bool ReadFileToString(const std::string &path, std::string *stringOut) 160 { 161 std::ifstream inFile(path.c_str()); 162 if (inFile.fail()) 163 { 164 return false; 165 } 166 167 inFile.seekg(0, std::ios::end); 168 stringOut->reserve(static_cast<std::string::size_type>(inFile.tellg())); 169 inFile.seekg(0, std::ios::beg); 170 171 stringOut->assign(std::istreambuf_iterator<char>(inFile), std::istreambuf_iterator<char>()); 172 return !inFile.fail(); 173 } 174 175 bool BeginsWith(const std::string &str, const std::string &prefix) 176 { 177 return strncmp(str.c_str(), prefix.c_str(), prefix.length()) == 0; 178 } 179 180 bool BeginsWith(const std::string &str, const char *prefix) 181 { 182 return strncmp(str.c_str(), prefix, strlen(prefix)) == 0; 183 } 184 185 bool BeginsWith(const char *str, const char *prefix) 186 { 187 return strncmp(str, prefix, strlen(prefix)) == 0; 188 } 189 190 bool BeginsWith(const std::string &str, const std::string &prefix, const size_t prefixLength) 191 { 192 return strncmp(str.c_str(), prefix.c_str(), prefixLength) == 0; 193 } 194 195 bool EndsWith(const std::string &str, const std::string &suffix) 196 { 197 return EndsWithSuffix(str.c_str(), str.length(), suffix.c_str(), suffix.length()); 198 } 199 200 bool EndsWith(const std::string &str, const char *suffix) 201 { 202 return EndsWithSuffix(str.c_str(), str.length(), suffix, strlen(suffix)); 203 } 204 205 bool EndsWith(const char *str, const char *suffix) 206 { 207 return EndsWithSuffix(str, strlen(str), suffix, strlen(suffix)); 208 } 209 210 bool ContainsToken(const std::string &tokenStr, char delimiter, const std::string &token) 211 { 212 if (token.empty()) 213 { 214 return false; 215 } 216 // Compare token with all sub-strings terminated by delimiter or end of string 217 std::string::size_type start = 0u; 218 do 219 { 220 std::string::size_type end = tokenStr.find(delimiter, start); 221 if (end == std::string::npos) 222 { 223 end = tokenStr.length(); 224 } 225 const std::string::size_type length = end - start; 226 if (length == token.length() && tokenStr.compare(start, length, token) == 0) 227 { 228 return true; 229 } 230 start = end + 1u; 231 } while (start < tokenStr.size()); 232 return false; 233 } 234 235 void ToLower(std::string *str) 236 { 237 for (char &ch : *str) 238 { 239 ch = static_cast<char>(::tolower(ch)); 240 } 241 } 242 243 void ToUpper(std::string *str) 244 { 245 for (char &ch : *str) 246 { 247 ch = static_cast<char>(::toupper(ch)); 248 } 249 } 250 251 bool ReplaceSubstring(std::string *str, 252 const std::string &substring, 253 const std::string &replacement) 254 { 255 size_t replacePos = str->find(substring); 256 if (replacePos == std::string::npos) 257 { 258 return false; 259 } 260 str->replace(replacePos, substring.size(), replacement); 261 return true; 262 } 263 264 int ReplaceAllSubstrings(std::string *str, 265 const std::string &substring, 266 const std::string &replacement) 267 { 268 int count = 0; 269 while (ReplaceSubstring(str, substring, replacement)) 270 { 271 count++; 272 } 273 return count; 274 } 275 276 std::string ToCamelCase(const std::string &str) 277 { 278 std::string result; 279 280 bool lastWasUnderscore = false; 281 for (char c : str) 282 { 283 if (c == '_') 284 { 285 lastWasUnderscore = true; 286 continue; 287 } 288 289 if (lastWasUnderscore) 290 { 291 c = static_cast<char>(std::toupper(c)); 292 lastWasUnderscore = false; 293 } 294 result += c; 295 } 296 297 return result; 298 } 299 300 std::vector<std::string> GetStringsFromEnvironmentVarOrAndroidProperty(const char *varName, 301 const char *propertyName, 302 const char *separator) 303 { 304 std::string environment = GetEnvironmentVarOrAndroidProperty(varName, propertyName); 305 return SplitString(environment, separator, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); 306 } 307 308 std::vector<std::string> GetCachedStringsFromEnvironmentVarOrAndroidProperty( 309 const char *varName, 310 const char *propertyName, 311 const char *separator) 312 { 313 std::string environment = GetEnvironmentVarOrAndroidProperty(varName, propertyName); 314 return SplitString(environment, separator, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); 315 } 316 317 // glob can have * as wildcard 318 bool NamesMatchWithWildcard(const char *glob, const char *name) 319 { 320 // Find the first * in glob. 321 const char *firstWildcard = strchr(glob, '*'); 322 323 // If there are no wildcards, match the strings precisely. 324 if (firstWildcard == nullptr) 325 { 326 return strcmp(glob, name) == 0; 327 } 328 329 // Otherwise, match up to the wildcard first. 330 size_t preWildcardLen = firstWildcard - glob; 331 if (strncmp(glob, name, preWildcardLen) != 0) 332 { 333 return false; 334 } 335 336 const char *postWildcardRef = glob + preWildcardLen + 1; 337 338 // As a small optimization, if the wildcard is the last character in glob, accept the match 339 // already. 340 if (postWildcardRef[0] == '\0') 341 { 342 return true; 343 } 344 345 // Try to match the wildcard with a number of characters. 346 for (size_t matchSize = 0; name[matchSize] != '\0'; ++matchSize) 347 { 348 if (NamesMatchWithWildcard(postWildcardRef, name + matchSize)) 349 { 350 return true; 351 } 352 } 353 354 return false; 355 } 356 357 } // namespace angle