| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/strings/string_util.h" | 5 #include "base/strings/string_util.h" |
| 6 | 6 |
| 7 #include <ctype.h> | 7 #include <ctype.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <math.h> | 9 #include <math.h> |
| 10 #include <stdarg.h> | 10 #include <stdarg.h> |
| 11 #include <stdio.h> | 11 #include <stdio.h> |
| 12 #include <stdlib.h> | 12 #include <stdlib.h> |
| 13 #include <string.h> | 13 #include <string.h> |
| 14 #include <time.h> | 14 #include <time.h> |
| 15 #include <wchar.h> | 15 #include <wchar.h> |
| 16 #include <wctype.h> | 16 #include <wctype.h> |
| 17 | 17 |
| 18 #include <algorithm> | 18 #include <algorithm> |
| 19 #include <vector> | 19 #include <vector> |
| 20 | 20 |
| 21 #include "base/basictypes.h" | 21 #include "base/basictypes.h" |
| 22 #include "base/logging.h" | 22 #include "base/logging.h" |
| 23 #include "base/memory/singleton.h" | 23 #include "base/memory/singleton.h" |
| 24 #include "base/strings/string_split.h" |
| 24 #include "base/strings/utf_string_conversion_utils.h" | 25 #include "base/strings/utf_string_conversion_utils.h" |
| 25 #include "base/strings/utf_string_conversions.h" | 26 #include "base/strings/utf_string_conversions.h" |
| 26 #include "base/third_party/icu/icu_utf.h" | 27 #include "base/third_party/icu/icu_utf.h" |
| 27 #include "build/build_config.h" | 28 #include "build/build_config.h" |
| 28 | 29 |
| 29 // Remove when this entire file is in the base namespace. | 30 namespace base { |
| 30 using base::char16; | |
| 31 using base::string16; | |
| 32 | 31 |
| 33 namespace { | 32 namespace { |
| 34 | 33 |
| 35 // Force the singleton used by EmptyString[16] to be a unique type. This | 34 // Force the singleton used by EmptyString[16] to be a unique type. This |
| 36 // prevents other code that might accidentally use Singleton<string> from | 35 // prevents other code that might accidentally use Singleton<string> from |
| 37 // getting our internal one. | 36 // getting our internal one. |
| 38 struct EmptyStrings { | 37 struct EmptyStrings { |
| 39 EmptyStrings() {} | 38 EmptyStrings() {} |
| 40 const std::string s; | 39 const std::string s; |
| 41 const string16 s16; | 40 const string16 s16; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 72 inline bool IsAlignedToMachineWord(const void* pointer) { | 71 inline bool IsAlignedToMachineWord(const void* pointer) { |
| 73 return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask); | 72 return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask); |
| 74 } | 73 } |
| 75 | 74 |
| 76 template<typename T> inline T* AlignToMachineWord(T* pointer) { | 75 template<typename T> inline T* AlignToMachineWord(T* pointer) { |
| 77 return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) & | 76 return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) & |
| 78 ~kMachineWordAlignmentMask); | 77 ~kMachineWordAlignmentMask); |
| 79 } | 78 } |
| 80 | 79 |
| 81 template<size_t size, typename CharacterType> struct NonASCIIMask; | 80 template<size_t size, typename CharacterType> struct NonASCIIMask; |
| 82 template<> struct NonASCIIMask<4, base::char16> { | 81 template <> |
| 83 static inline uint32_t value() { return 0xFF80FF80U; } | 82 struct NonASCIIMask<4, char16> { |
| 83 static inline uint32_t value() { return 0xFF80FF80U; } |
| 84 }; | 84 }; |
| 85 template<> struct NonASCIIMask<4, char> { | 85 template<> struct NonASCIIMask<4, char> { |
| 86 static inline uint32_t value() { return 0x80808080U; } | 86 static inline uint32_t value() { return 0x80808080U; } |
| 87 }; | 87 }; |
| 88 template<> struct NonASCIIMask<8, base::char16> { | 88 template <> |
| 89 static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; } | 89 struct NonASCIIMask<8, char16> { |
| 90 static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; } |
| 90 }; | 91 }; |
| 91 template<> struct NonASCIIMask<8, char> { | 92 template<> struct NonASCIIMask<8, char> { |
| 92 static inline uint64_t value() { return 0x8080808080808080ULL; } | 93 static inline uint64_t value() { return 0x8080808080808080ULL; } |
| 93 }; | 94 }; |
| 94 #if defined(WCHAR_T_IS_UTF32) | 95 #if defined(WCHAR_T_IS_UTF32) |
| 95 template<> struct NonASCIIMask<4, wchar_t> { | 96 template<> struct NonASCIIMask<4, wchar_t> { |
| 96 static inline uint32_t value() { return 0xFFFFFF80U; } | 97 static inline uint32_t value() { return 0xFFFFFF80U; } |
| 97 }; | 98 }; |
| 98 template<> struct NonASCIIMask<8, wchar_t> { | 99 template<> struct NonASCIIMask<8, wchar_t> { |
| 99 static inline uint64_t value() { return 0xFFFFFF80FFFFFF80ULL; } | 100 static inline uint64_t value() { return 0xFFFFFF80FFFFFF80ULL; } |
| 100 }; | 101 }; |
| 101 #endif // WCHAR_T_IS_UTF32 | 102 #endif // WCHAR_T_IS_UTF32 |
| 102 | 103 |
| 104 // DO NOT USE. http://crbug.com/24917 |
| 105 // |
| 106 // tolower() will given incorrect results for non-ASCII characters. Use the |
| 107 // ASCII version, base::i18n::ToLower, or base::i18n::FoldCase. This is here |
| 108 // for backwards-compat for StartsWith until such calls can be updated. |
| 109 struct CaseInsensitiveCompareDeprecated { |
| 110 public: |
| 111 bool operator()(char16 x, char16 y) const { return tolower(x) == tolower(y); } |
| 112 }; |
| 113 |
| 103 } // namespace | 114 } // namespace |
| 104 | 115 |
| 105 namespace base { | |
| 106 | |
| 107 bool IsWprintfFormatPortable(const wchar_t* format) { | 116 bool IsWprintfFormatPortable(const wchar_t* format) { |
| 108 for (const wchar_t* position = format; *position != '\0'; ++position) { | 117 for (const wchar_t* position = format; *position != '\0'; ++position) { |
| 109 if (*position == '%') { | 118 if (*position == '%') { |
| 110 bool in_specification = true; | 119 bool in_specification = true; |
| 111 bool modifier_l = false; | 120 bool modifier_l = false; |
| 112 while (in_specification) { | 121 while (in_specification) { |
| 113 // Eat up characters until reaching a known specifier. | 122 // Eat up characters until reaching a known specifier. |
| 114 if (*++position == '\0') { | 123 if (*++position == '\0') { |
| 115 // The format string ended in the middle of a specification. Call | 124 // The format string ended in the middle of a specification. Call |
| 116 // it portable because no unportable specifications were found. The | 125 // it portable because no unportable specifications were found. The |
| (...skipping 15 matching lines...) Expand all Loading... |
| 132 // Portable, keep scanning the rest of the format string. | 141 // Portable, keep scanning the rest of the format string. |
| 133 in_specification = false; | 142 in_specification = false; |
| 134 } | 143 } |
| 135 } | 144 } |
| 136 } | 145 } |
| 137 } | 146 } |
| 138 | 147 |
| 139 return true; | 148 return true; |
| 140 } | 149 } |
| 141 | 150 |
| 151 template <class StringType> |
| 152 int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a, |
| 153 BasicStringPiece<StringType> b) { |
| 154 // Find the first characters that aren't equal and compare them. If the end |
| 155 // of one of the strings is found before a nonequal character, the lengths |
| 156 // of the strings are compared. |
| 157 size_t i = 0; |
| 158 while (i < a.length() && i < b.length()) { |
| 159 typename StringType::value_type lower_a = ToLowerASCII(a[i]); |
| 160 typename StringType::value_type lower_b = ToLowerASCII(b[i]); |
| 161 if (lower_a < lower_b) |
| 162 return -1; |
| 163 if (lower_a > lower_b) |
| 164 return 1; |
| 165 i++; |
| 166 } |
| 167 |
| 168 // End of one string hit before finding a different character. Expect the |
| 169 // common case to be "strings equal" at this point so check that first. |
| 170 if (a.length() == b.length()) |
| 171 return 0; |
| 172 |
| 173 if (a.length() < b.length()) |
| 174 return -1; |
| 175 return 1; |
| 176 } |
| 177 |
| 178 int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) { |
| 179 return CompareCaseInsensitiveASCIIT<std::string>(a, b); |
| 180 } |
| 181 |
| 182 int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) { |
| 183 return CompareCaseInsensitiveASCIIT<string16>(a, b); |
| 184 } |
| 185 |
| 186 bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) { |
| 187 if (a.length() != b.length()) |
| 188 return false; |
| 189 return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0; |
| 190 } |
| 191 |
| 192 bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) { |
| 193 if (a.length() != b.length()) |
| 194 return false; |
| 195 return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0; |
| 196 } |
| 197 |
| 142 const std::string& EmptyString() { | 198 const std::string& EmptyString() { |
| 143 return EmptyStrings::GetInstance()->s; | 199 return EmptyStrings::GetInstance()->s; |
| 144 } | 200 } |
| 145 | 201 |
| 146 const string16& EmptyString16() { | 202 const string16& EmptyString16() { |
| 147 return EmptyStrings::GetInstance()->s16; | 203 return EmptyStrings::GetInstance()->s16; |
| 148 } | 204 } |
| 149 | 205 |
| 150 template<typename STR> | 206 template<typename STR> |
| 151 bool ReplaceCharsT(const STR& input, | 207 bool ReplaceCharsT(const STR& input, |
| 152 const STR& replace_chars, | 208 const STR& replace_chars, |
| 153 const STR& replace_with, | 209 const STR& replace_with, |
| 154 STR* output) { | 210 STR* output) { |
| 155 bool removed = false; | 211 bool removed = false; |
| 156 size_t replace_length = replace_with.length(); | 212 size_t replace_length = replace_with.length(); |
| 157 | 213 |
| 158 *output = input; | 214 *output = input; |
| 159 | 215 |
| 160 size_t found = output->find_first_of(replace_chars); | 216 size_t found = output->find_first_of(replace_chars); |
| 161 while (found != STR::npos) { | 217 while (found != STR::npos) { |
| 162 removed = true; | 218 removed = true; |
| 163 output->replace(found, 1, replace_with); | 219 output->replace(found, 1, replace_with); |
| 164 found = output->find_first_of(replace_chars, found + replace_length); | 220 found = output->find_first_of(replace_chars, found + replace_length); |
| 165 } | 221 } |
| 166 | 222 |
| 167 return removed; | 223 return removed; |
| 168 } | 224 } |
| 169 | 225 |
| 170 bool ReplaceChars(const string16& input, | 226 bool ReplaceChars(const string16& input, |
| 171 const base::StringPiece16& replace_chars, | 227 const StringPiece16& replace_chars, |
| 172 const string16& replace_with, | 228 const string16& replace_with, |
| 173 string16* output) { | 229 string16* output) { |
| 174 return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); | 230 return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); |
| 175 } | 231 } |
| 176 | 232 |
| 177 bool ReplaceChars(const std::string& input, | 233 bool ReplaceChars(const std::string& input, |
| 178 const base::StringPiece& replace_chars, | 234 const StringPiece& replace_chars, |
| 179 const std::string& replace_with, | 235 const std::string& replace_with, |
| 180 std::string* output) { | 236 std::string* output) { |
| 181 return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); | 237 return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); |
| 182 } | 238 } |
| 183 | 239 |
| 184 bool RemoveChars(const string16& input, | 240 bool RemoveChars(const string16& input, |
| 185 const base::StringPiece16& remove_chars, | 241 const StringPiece16& remove_chars, |
| 186 string16* output) { | 242 string16* output) { |
| 187 return ReplaceChars(input, remove_chars.as_string(), string16(), output); | 243 return ReplaceChars(input, remove_chars.as_string(), string16(), output); |
| 188 } | 244 } |
| 189 | 245 |
| 190 bool RemoveChars(const std::string& input, | 246 bool RemoveChars(const std::string& input, |
| 191 const base::StringPiece& remove_chars, | 247 const StringPiece& remove_chars, |
| 192 std::string* output) { | 248 std::string* output) { |
| 193 return ReplaceChars(input, remove_chars.as_string(), std::string(), output); | 249 return ReplaceChars(input, remove_chars.as_string(), std::string(), output); |
| 194 } | 250 } |
| 195 | 251 |
| 196 template<typename STR> | 252 template <typename Str> |
| 197 TrimPositions TrimStringT(const STR& input, | 253 TrimPositions TrimStringT(const Str& input, |
| 198 const STR& trim_chars, | 254 BasicStringPiece<Str> trim_chars, |
| 199 TrimPositions positions, | 255 TrimPositions positions, |
| 200 STR* output) { | 256 Str* output) { |
| 201 // Find the edges of leading/trailing whitespace as desired. | 257 // Find the edges of leading/trailing whitespace as desired. Need to use |
| 258 // a StringPiece version of input to be able to call find* on it with the |
| 259 // StringPiece version of trim_chars (normally the trim_chars will be a |
| 260 // constant so avoid making a copy). |
| 261 BasicStringPiece<Str> input_piece(input); |
| 202 const size_t last_char = input.length() - 1; | 262 const size_t last_char = input.length() - 1; |
| 203 const size_t first_good_char = (positions & TRIM_LEADING) ? | 263 const size_t first_good_char = (positions & TRIM_LEADING) |
| 204 input.find_first_not_of(trim_chars) : 0; | 264 ? input_piece.find_first_not_of(trim_chars) |
| 205 const size_t last_good_char = (positions & TRIM_TRAILING) ? | 265 : 0; |
| 206 input.find_last_not_of(trim_chars) : last_char; | 266 const size_t last_good_char = (positions & TRIM_TRAILING) |
| 267 ? input_piece.find_last_not_of(trim_chars) |
| 268 : last_char; |
| 207 | 269 |
| 208 // When the string was all whitespace, report that we stripped off whitespace | 270 // When the string was all trimmed, report that we stripped off characters |
| 209 // from whichever position the caller was interested in. For empty input, we | 271 // from whichever position the caller was interested in. For empty input, we |
| 210 // stripped no whitespace, but we still need to clear |output|. | 272 // stripped no characters, but we still need to clear |output|. |
| 211 if (input.empty() || | 273 if (input.empty() || (first_good_char == Str::npos) || |
| 212 (first_good_char == STR::npos) || (last_good_char == STR::npos)) { | 274 (last_good_char == Str::npos)) { |
| 213 bool input_was_empty = input.empty(); // in case output == &input | 275 bool input_was_empty = input.empty(); // in case output == &input |
| 214 output->clear(); | 276 output->clear(); |
| 215 return input_was_empty ? TRIM_NONE : positions; | 277 return input_was_empty ? TRIM_NONE : positions; |
| 216 } | 278 } |
| 217 | 279 |
| 218 // Trim the whitespace. | 280 // Trim. |
| 219 *output = | 281 *output = |
| 220 input.substr(first_good_char, last_good_char - first_good_char + 1); | 282 input.substr(first_good_char, last_good_char - first_good_char + 1); |
| 221 | 283 |
| 222 // Return where we trimmed from. | 284 // Return where we trimmed from. |
| 223 return static_cast<TrimPositions>( | 285 return static_cast<TrimPositions>( |
| 224 ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | | 286 ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | |
| 225 ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); | 287 ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); |
| 226 } | 288 } |
| 227 | 289 |
| 228 bool TrimString(const string16& input, | 290 bool TrimString(const string16& input, |
| 229 const base::StringPiece16& trim_chars, | 291 StringPiece16 trim_chars, |
| 230 string16* output) { | 292 string16* output) { |
| 231 return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) != | 293 return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; |
| 232 TRIM_NONE; | |
| 233 } | 294 } |
| 234 | 295 |
| 235 bool TrimString(const std::string& input, | 296 bool TrimString(const std::string& input, |
| 236 const base::StringPiece& trim_chars, | 297 StringPiece trim_chars, |
| 237 std::string* output) { | 298 std::string* output) { |
| 238 return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) != | 299 return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; |
| 239 TRIM_NONE; | 300 } |
| 301 |
| 302 template <typename Str> |
| 303 BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input, |
| 304 BasicStringPiece<Str> trim_chars, |
| 305 TrimPositions positions) { |
| 306 size_t begin = |
| 307 (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; |
| 308 size_t end = (positions & TRIM_TRAILING) |
| 309 ? input.find_last_not_of(trim_chars) + 1 |
| 310 : input.size(); |
| 311 return input.substr(begin, end - begin); |
| 312 } |
| 313 |
| 314 StringPiece16 TrimString(StringPiece16 input, |
| 315 const StringPiece16& trim_chars, |
| 316 TrimPositions positions) { |
| 317 return TrimStringPieceT(input, trim_chars, positions); |
| 318 } |
| 319 |
| 320 StringPiece TrimString(StringPiece input, |
| 321 const StringPiece& trim_chars, |
| 322 TrimPositions positions) { |
| 323 return TrimStringPieceT(input, trim_chars, positions); |
| 240 } | 324 } |
| 241 | 325 |
| 242 void TruncateUTF8ToByteSize(const std::string& input, | 326 void TruncateUTF8ToByteSize(const std::string& input, |
| 243 const size_t byte_size, | 327 const size_t byte_size, |
| 244 std::string* output) { | 328 std::string* output) { |
| 245 DCHECK(output); | 329 DCHECK(output); |
| 246 if (byte_size > input.length()) { | 330 if (byte_size > input.length()) { |
| 247 *output = input; | 331 *output = input; |
| 248 return; | 332 return; |
| 249 } | 333 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 271 | 355 |
| 272 if (char_index >= 0 ) | 356 if (char_index >= 0 ) |
| 273 *output = input.substr(0, char_index); | 357 *output = input.substr(0, char_index); |
| 274 else | 358 else |
| 275 output->clear(); | 359 output->clear(); |
| 276 } | 360 } |
| 277 | 361 |
| 278 TrimPositions TrimWhitespace(const string16& input, | 362 TrimPositions TrimWhitespace(const string16& input, |
| 279 TrimPositions positions, | 363 TrimPositions positions, |
| 280 string16* output) { | 364 string16* output) { |
| 281 return TrimStringT(input, base::string16(kWhitespaceUTF16), positions, | 365 return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output); |
| 282 output); | 366 } |
| 367 |
| 368 StringPiece16 TrimWhitespaceASCII(StringPiece16 input, |
| 369 TrimPositions positions) { |
| 370 return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions); |
| 283 } | 371 } |
| 284 | 372 |
| 285 TrimPositions TrimWhitespaceASCII(const std::string& input, | 373 TrimPositions TrimWhitespaceASCII(const std::string& input, |
| 286 TrimPositions positions, | 374 TrimPositions positions, |
| 287 std::string* output) { | 375 std::string* output) { |
| 288 return TrimStringT(input, std::string(kWhitespaceASCII), positions, output); | 376 return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output); |
| 377 } |
| 378 |
| 379 StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) { |
| 380 return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions); |
| 289 } | 381 } |
| 290 | 382 |
| 291 // This function is only for backward-compatibility. | 383 // This function is only for backward-compatibility. |
| 292 // To be removed when all callers are updated. | 384 // To be removed when all callers are updated. |
| 293 TrimPositions TrimWhitespace(const std::string& input, | 385 TrimPositions TrimWhitespace(const std::string& input, |
| 294 TrimPositions positions, | 386 TrimPositions positions, |
| 295 std::string* output) { | 387 std::string* output) { |
| 296 return TrimWhitespaceASCII(input, positions, output); | 388 return TrimWhitespaceASCII(input, positions, output); |
| 297 } | 389 } |
| 298 | 390 |
| 299 template<typename STR> | 391 template<typename STR> |
| 300 STR CollapseWhitespaceT(const STR& text, | 392 STR CollapseWhitespaceT(const STR& text, |
| 301 bool trim_sequences_with_line_breaks) { | 393 bool trim_sequences_with_line_breaks) { |
| 302 STR result; | 394 STR result; |
| 303 result.resize(text.size()); | 395 result.resize(text.size()); |
| 304 | 396 |
| 305 // Set flags to pretend we're already in a trimmed whitespace sequence, so we | 397 // Set flags to pretend we're already in a trimmed whitespace sequence, so we |
| 306 // will trim any leading whitespace. | 398 // will trim any leading whitespace. |
| 307 bool in_whitespace = true; | 399 bool in_whitespace = true; |
| 308 bool already_trimmed = true; | 400 bool already_trimmed = true; |
| 309 | 401 |
| 310 int chars_written = 0; | 402 int chars_written = 0; |
| 311 for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) { | 403 for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) { |
| 312 if (IsWhitespace(*i)) { | 404 if (IsUnicodeWhitespace(*i)) { |
| 313 if (!in_whitespace) { | 405 if (!in_whitespace) { |
| 314 // Reduce all whitespace sequences to a single space. | 406 // Reduce all whitespace sequences to a single space. |
| 315 in_whitespace = true; | 407 in_whitespace = true; |
| 316 result[chars_written++] = L' '; | 408 result[chars_written++] = L' '; |
| 317 } | 409 } |
| 318 if (trim_sequences_with_line_breaks && !already_trimmed && | 410 if (trim_sequences_with_line_breaks && !already_trimmed && |
| 319 ((*i == '\n') || (*i == '\r'))) { | 411 ((*i == '\n') || (*i == '\r'))) { |
| 320 // Whitespace sequences containing CR or LF are eliminated entirely. | 412 // Whitespace sequences containing CR or LF are eliminated entirely. |
| 321 already_trimmed = true; | 413 already_trimmed = true; |
| 322 --chars_written; | 414 --chars_written; |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 475 const char* b) { | 567 const char* b) { |
| 476 return DoLowerCaseEqualsASCII(a_begin, a_end, b); | 568 return DoLowerCaseEqualsASCII(a_begin, a_end, b); |
| 477 } | 569 } |
| 478 | 570 |
| 479 bool EqualsASCII(const string16& a, const StringPiece& b) { | 571 bool EqualsASCII(const string16& a, const StringPiece& b) { |
| 480 if (a.length() != b.length()) | 572 if (a.length() != b.length()) |
| 481 return false; | 573 return false; |
| 482 return std::equal(b.begin(), b.end(), a.begin()); | 574 return std::equal(b.begin(), b.end(), a.begin()); |
| 483 } | 575 } |
| 484 | 576 |
| 485 } // namespace base | 577 template <typename Str> |
| 578 bool StartsWithT(BasicStringPiece<Str> str, |
| 579 BasicStringPiece<Str> search_for, |
| 580 CompareCase case_sensitivity) { |
| 581 if (search_for.size() > str.size()) |
| 582 return false; |
| 486 | 583 |
| 487 bool StartsWithASCII(const std::string& str, | 584 BasicStringPiece<Str> source = str.substr(0, search_for.size()); |
| 488 const std::string& search, | 585 |
| 489 bool case_sensitive) { | 586 switch (case_sensitivity) { |
| 490 if (case_sensitive) | 587 case CompareCase::SENSITIVE: |
| 491 return str.compare(0, search.length(), search) == 0; | 588 return source == search_for; |
| 492 else | 589 |
| 493 return base::strncasecmp(str.c_str(), search.c_str(), search.length()) == 0; | 590 case CompareCase::INSENSITIVE_ASCII: |
| 591 return std::equal( |
| 592 search_for.begin(), search_for.end(), source.begin(), |
| 593 CaseInsensitiveCompareASCII<typename Str::value_type>()); |
| 594 |
| 595 default: |
| 596 NOTREACHED(); |
| 597 return false; |
| 598 } |
| 494 } | 599 } |
| 495 | 600 |
| 496 template <typename STR> | 601 bool StartsWith(StringPiece str, |
| 497 bool StartsWithT(const STR& str, const STR& search, bool case_sensitive) { | 602 StringPiece search_for, |
| 498 if (case_sensitive) { | 603 CompareCase case_sensitivity) { |
| 499 return str.compare(0, search.length(), search) == 0; | 604 return StartsWithT<std::string>(str, search_for, case_sensitivity); |
| 500 } else { | 605 } |
| 606 |
| 607 bool StartsWith(StringPiece16 str, |
| 608 StringPiece16 search_for, |
| 609 CompareCase case_sensitivity) { |
| 610 return StartsWithT<string16>(str, search_for, case_sensitivity); |
| 611 } |
| 612 |
| 613 bool StartsWith(const string16& str, |
| 614 const string16& search, |
| 615 bool case_sensitive) { |
| 616 if (!case_sensitive) { |
| 617 // This function was originally written using the current locale functions |
| 618 // for case-insensitive comparisons. Emulate this behavior until callers |
| 619 // can be converted either to use the case-insensitive ASCII one (most |
| 620 // callers) or ICU functions in base_i18n. |
| 501 if (search.size() > str.size()) | 621 if (search.size() > str.size()) |
| 502 return false; | 622 return false; |
| 503 return std::equal(search.begin(), search.end(), str.begin(), | 623 return std::equal(search.begin(), search.end(), str.begin(), |
| 504 base::CaseInsensitiveCompare<typename STR::value_type>()); | 624 CaseInsensitiveCompareDeprecated()); |
| 625 } |
| 626 return StartsWith(StringPiece16(str), StringPiece16(search), |
| 627 CompareCase::SENSITIVE); |
| 628 } |
| 629 |
| 630 template <typename Str> |
| 631 bool EndsWithT(BasicStringPiece<Str> str, |
| 632 BasicStringPiece<Str> search_for, |
| 633 CompareCase case_sensitivity) { |
| 634 if (search_for.size() > str.size()) |
| 635 return false; |
| 636 |
| 637 BasicStringPiece<Str> source = |
| 638 str.substr(str.size() - search_for.size(), search_for.size()); |
| 639 |
| 640 switch (case_sensitivity) { |
| 641 case CompareCase::SENSITIVE: |
| 642 return source == search_for; |
| 643 |
| 644 case CompareCase::INSENSITIVE_ASCII: |
| 645 return std::equal( |
| 646 source.begin(), source.end(), search_for.begin(), |
| 647 CaseInsensitiveCompareASCII<typename Str::value_type>()); |
| 648 |
| 649 default: |
| 650 NOTREACHED(); |
| 651 return false; |
| 505 } | 652 } |
| 506 } | 653 } |
| 507 | 654 |
| 508 bool StartsWith(const string16& str, const string16& search, | 655 bool EndsWith(StringPiece str, |
| 509 bool case_sensitive) { | 656 StringPiece search_for, |
| 510 return StartsWithT(str, search, case_sensitive); | 657 CompareCase case_sensitivity) { |
| 658 return EndsWithT<std::string>(str, search_for, case_sensitivity); |
| 511 } | 659 } |
| 512 | 660 |
| 513 template <typename STR> | 661 bool EndsWith(StringPiece16 str, |
| 514 bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) { | 662 StringPiece16 search_for, |
| 515 size_t str_length = str.length(); | 663 CompareCase case_sensitivity) { |
| 516 size_t search_length = search.length(); | 664 return EndsWithT<string16>(str, search_for, case_sensitivity); |
| 517 if (search_length > str_length) | |
| 518 return false; | |
| 519 if (case_sensitive) | |
| 520 return str.compare(str_length - search_length, search_length, search) == 0; | |
| 521 return std::equal(search.begin(), search.end(), | |
| 522 str.begin() + (str_length - search_length), | |
| 523 base::CaseInsensitiveCompare<typename STR::value_type>()); | |
| 524 } | 665 } |
| 525 | 666 |
| 526 bool EndsWith(const std::string& str, const std::string& search, | 667 bool EndsWith(const string16& str, |
| 668 const string16& search, |
| 527 bool case_sensitive) { | 669 bool case_sensitive) { |
| 528 return EndsWithT(str, search, case_sensitive); | 670 if (!case_sensitive) { |
| 671 // This function was originally written using the current locale functions |
| 672 // for case-insensitive comparisons. Emulate this behavior until callers |
| 673 // can be converted either to use the case-insensitive ASCII one (most |
| 674 // callers) or ICU functions in base_i18n. |
| 675 if (search.size() > str.size()) |
| 676 return false; |
| 677 return std::equal(search.begin(), search.end(), |
| 678 str.begin() + (str.size() - search.size()), |
| 679 CaseInsensitiveCompareDeprecated()); |
| 680 } |
| 681 return EndsWith(StringPiece16(str), StringPiece16(search), |
| 682 CompareCase::SENSITIVE); |
| 529 } | 683 } |
| 530 | 684 |
| 531 bool EndsWith(const string16& str, const string16& search, | 685 char HexDigitToInt(wchar_t c) { |
| 532 bool case_sensitive) { | 686 DCHECK(IsHexDigit(c)); |
| 533 return EndsWithT(str, search, case_sensitive); | 687 if (c >= '0' && c <= '9') |
| 688 return static_cast<char>(c - '0'); |
| 689 if (c >= 'A' && c <= 'F') |
| 690 return static_cast<char>(c - 'A' + 10); |
| 691 if (c >= 'a' && c <= 'f') |
| 692 return static_cast<char>(c - 'a' + 10); |
| 693 return 0; |
| 534 } | 694 } |
| 535 | 695 |
| 536 static const char* const kByteStringsUnlocalized[] = { | 696 static const char* const kByteStringsUnlocalized[] = { |
| 537 " B", | 697 " B", |
| 538 " kB", | 698 " kB", |
| 539 " MB", | 699 " MB", |
| 540 " GB", | 700 " GB", |
| 541 " TB", | 701 " TB", |
| 542 " PB" | 702 " PB" |
| 543 }; | 703 }; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 554 | 714 |
| 555 char buf[64]; | 715 char buf[64]; |
| 556 if (bytes != 0 && dimension > 0 && unit_amount < 100) { | 716 if (bytes != 0 && dimension > 0 && unit_amount < 100) { |
| 557 base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount, | 717 base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount, |
| 558 kByteStringsUnlocalized[dimension]); | 718 kByteStringsUnlocalized[dimension]); |
| 559 } else { | 719 } else { |
| 560 base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount, | 720 base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount, |
| 561 kByteStringsUnlocalized[dimension]); | 721 kByteStringsUnlocalized[dimension]); |
| 562 } | 722 } |
| 563 | 723 |
| 564 return base::ASCIIToUTF16(buf); | 724 return ASCIIToUTF16(buf); |
| 565 } | 725 } |
| 566 | 726 |
| 567 // Runs in O(n) time in the length of |str|. | 727 // Runs in O(n) time in the length of |str|. |
| 568 template<class StringType> | 728 template <class StringType> |
| 569 void DoReplaceSubstringsAfterOffset(StringType* str, | 729 void DoReplaceSubstringsAfterOffset(StringType* str, |
| 570 size_t offset, | 730 size_t offset, |
| 571 const StringType& find_this, | 731 BasicStringPiece<StringType> find_this, |
| 572 const StringType& replace_with, | 732 BasicStringPiece<StringType> replace_with, |
| 573 bool replace_all) { | 733 bool replace_all) { |
| 574 DCHECK(!find_this.empty()); | 734 DCHECK(!find_this.empty()); |
| 575 | 735 |
| 576 // If the find string doesn't appear, there's nothing to do. | 736 // If the find string doesn't appear, there's nothing to do. |
| 577 offset = str->find(find_this, offset); | 737 offset = str->find(find_this.data(), offset, find_this.size()); |
| 578 if (offset == StringType::npos) | 738 if (offset == StringType::npos) |
| 579 return; | 739 return; |
| 580 | 740 |
| 581 // If we're only replacing one instance, there's no need to do anything | 741 // If we're only replacing one instance, there's no need to do anything |
| 582 // complicated. | 742 // complicated. |
| 583 size_t find_length = find_this.length(); | 743 size_t find_length = find_this.length(); |
| 584 if (!replace_all) { | 744 if (!replace_all) { |
| 585 str->replace(offset, find_length, replace_with); | 745 str->replace(offset, find_length, replace_with.data(), replace_with.size()); |
| 586 return; | 746 return; |
| 587 } | 747 } |
| 588 | 748 |
| 589 // If the find and replace strings are the same length, we can simply use | 749 // If the find and replace strings are the same length, we can simply use |
| 590 // replace() on each instance, and finish the entire operation in O(n) time. | 750 // replace() on each instance, and finish the entire operation in O(n) time. |
| 591 size_t replace_length = replace_with.length(); | 751 size_t replace_length = replace_with.length(); |
| 592 if (find_length == replace_length) { | 752 if (find_length == replace_length) { |
| 593 do { | 753 do { |
| 594 str->replace(offset, find_length, replace_with); | 754 str->replace(offset, find_length, replace_with.data(), |
| 595 offset = str->find(find_this, offset + replace_length); | 755 replace_with.size()); |
| 756 offset = str->find(find_this.data(), offset + replace_length, |
| 757 find_this.size()); |
| 596 } while (offset != StringType::npos); | 758 } while (offset != StringType::npos); |
| 597 return; | 759 return; |
| 598 } | 760 } |
| 599 | 761 |
| 600 // Since the find and replace strings aren't the same length, a loop like the | 762 // Since the find and replace strings aren't the same length, a loop like the |
| 601 // one above would be O(n^2) in the worst case, as replace() will shift the | 763 // one above would be O(n^2) in the worst case, as replace() will shift the |
| 602 // entire remaining string each time. We need to be more clever to keep | 764 // entire remaining string each time. We need to be more clever to keep |
| 603 // things O(n). | 765 // things O(n). |
| 604 // | 766 // |
| 605 // If we're shortening the string, we can alternate replacements with shifting | 767 // If we're shortening the string, we can alternate replacements with shifting |
| 606 // forward the intervening characters using memmove(). | 768 // forward the intervening characters using memmove(). |
| 607 size_t str_length = str->length(); | 769 size_t str_length = str->length(); |
| 608 if (find_length > replace_length) { | 770 if (find_length > replace_length) { |
| 609 size_t write_offset = offset; | 771 size_t write_offset = offset; |
| 610 do { | 772 do { |
| 611 if (replace_length) { | 773 if (replace_length) { |
| 612 str->replace(write_offset, replace_length, replace_with); | 774 str->replace(write_offset, replace_length, replace_with.data(), |
| 775 replace_with.size()); |
| 613 write_offset += replace_length; | 776 write_offset += replace_length; |
| 614 } | 777 } |
| 615 size_t read_offset = offset + find_length; | 778 size_t read_offset = offset + find_length; |
| 616 offset = std::min(str->find(find_this, read_offset), str_length); | 779 offset = |
| 780 std::min(str->find(find_this.data(), read_offset, find_this.size()), |
| 781 str_length); |
| 617 size_t length = offset - read_offset; | 782 size_t length = offset - read_offset; |
| 618 if (length) { | 783 if (length) { |
| 619 memmove(&(*str)[write_offset], &(*str)[read_offset], | 784 memmove(&(*str)[write_offset], &(*str)[read_offset], |
| 620 length * sizeof(typename StringType::value_type)); | 785 length * sizeof(typename StringType::value_type)); |
| 621 write_offset += length; | 786 write_offset += length; |
| 622 } | 787 } |
| 623 } while (offset < str_length); | 788 } while (offset < str_length); |
| 624 str->resize(write_offset); | 789 str->resize(write_offset); |
| 625 return; | 790 return; |
| 626 } | 791 } |
| 627 | 792 |
| 628 // We're lengthening the string. We can use alternating replacements and | 793 // We're lengthening the string. We can use alternating replacements and |
| 629 // memmove() calls like above, but we need to precalculate the final string | 794 // memmove() calls like above, but we need to precalculate the final string |
| 630 // length and then expand from back-to-front to avoid overwriting the string | 795 // length and then expand from back-to-front to avoid overwriting the string |
| 631 // as we're reading it, needing to shift, or having to copy to a second string | 796 // as we're reading it, needing to shift, or having to copy to a second string |
| 632 // temporarily. | 797 // temporarily. |
| 633 size_t first_match = offset; | 798 size_t first_match = offset; |
| 634 | 799 |
| 635 // First, calculate the final length and resize the string. | 800 // First, calculate the final length and resize the string. |
| 636 size_t final_length = str_length; | 801 size_t final_length = str_length; |
| 637 size_t expansion = replace_length - find_length; | 802 size_t expansion = replace_length - find_length; |
| 638 size_t current_match; | 803 size_t current_match; |
| 639 do { | 804 do { |
| 640 final_length += expansion; | 805 final_length += expansion; |
| 641 // Minor optimization: save this offset into |current_match|, so that on | 806 // Minor optimization: save this offset into |current_match|, so that on |
| 642 // exit from the loop, |current_match| will point at the last instance of | 807 // exit from the loop, |current_match| will point at the last instance of |
| 643 // the find string, and we won't need to find() it again immediately. | 808 // the find string, and we won't need to find() it again immediately. |
| 644 current_match = offset; | 809 current_match = offset; |
| 645 offset = str->find(find_this, offset + find_length); | 810 offset = |
| 811 str->find(find_this.data(), offset + find_length, find_this.size()); |
| 646 } while (offset != StringType::npos); | 812 } while (offset != StringType::npos); |
| 647 str->resize(final_length); | 813 str->resize(final_length); |
| 648 | 814 |
| 649 // Now do the replacement loop, working backwards through the string. | 815 // Now do the replacement loop, working backwards through the string. |
| 650 for (size_t prev_match = str_length, write_offset = final_length; ; | 816 for (size_t prev_match = str_length, write_offset = final_length;; |
| 651 current_match = str->rfind(find_this, current_match - 1)) { | 817 current_match = |
| 818 str->rfind(find_this.data(), current_match - 1, find_this.size())) { |
| 652 size_t read_offset = current_match + find_length; | 819 size_t read_offset = current_match + find_length; |
| 653 size_t length = prev_match - read_offset; | 820 size_t length = prev_match - read_offset; |
| 654 if (length) { | 821 if (length) { |
| 655 write_offset -= length; | 822 write_offset -= length; |
| 656 memmove(&(*str)[write_offset], &(*str)[read_offset], | 823 memmove(&(*str)[write_offset], &(*str)[read_offset], |
| 657 length * sizeof(typename StringType::value_type)); | 824 length * sizeof(typename StringType::value_type)); |
| 658 } | 825 } |
| 659 write_offset -= replace_length; | 826 write_offset -= replace_length; |
| 660 str->replace(write_offset, replace_length, replace_with); | 827 str->replace(write_offset, replace_length, replace_with.data(), |
| 828 replace_with.size()); |
| 661 if (current_match == first_match) | 829 if (current_match == first_match) |
| 662 return; | 830 return; |
| 663 prev_match = current_match; | 831 prev_match = current_match; |
| 664 } | 832 } |
| 665 } | 833 } |
| 666 | 834 |
| 667 void ReplaceFirstSubstringAfterOffset(string16* str, | 835 void ReplaceFirstSubstringAfterOffset(string16* str, |
| 668 size_t start_offset, | 836 size_t start_offset, |
| 669 const string16& find_this, | 837 StringPiece16 find_this, |
| 670 const string16& replace_with) { | 838 StringPiece16 replace_with) { |
| 671 DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, | 839 DoReplaceSubstringsAfterOffset<string16>( |
| 672 false); // replace first instance | 840 str, start_offset, find_this, replace_with, false); // Replace first. |
| 673 } | 841 } |
| 674 | 842 |
| 675 void ReplaceFirstSubstringAfterOffset(std::string* str, | 843 void ReplaceFirstSubstringAfterOffset(std::string* str, |
| 676 size_t start_offset, | 844 size_t start_offset, |
| 677 const std::string& find_this, | 845 StringPiece find_this, |
| 678 const std::string& replace_with) { | 846 StringPiece replace_with) { |
| 679 DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, | 847 DoReplaceSubstringsAfterOffset<std::string>( |
| 680 false); // replace first instance | 848 str, start_offset, find_this, replace_with, false); // Replace first. |
| 681 } | 849 } |
| 682 | 850 |
| 683 void ReplaceSubstringsAfterOffset(string16* str, | 851 void ReplaceSubstringsAfterOffset(string16* str, |
| 684 size_t start_offset, | 852 size_t start_offset, |
| 685 const string16& find_this, | 853 StringPiece16 find_this, |
| 686 const string16& replace_with) { | 854 StringPiece16 replace_with) { |
| 687 DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, | 855 DoReplaceSubstringsAfterOffset<string16>(str, start_offset, find_this, |
| 688 true); // replace all instances | 856 replace_with, true); // Replace all. |
| 689 } | 857 } |
| 690 | 858 |
| 691 void ReplaceSubstringsAfterOffset(std::string* str, | 859 void ReplaceSubstringsAfterOffset(std::string* str, |
| 692 size_t start_offset, | 860 size_t start_offset, |
| 693 const std::string& find_this, | 861 StringPiece find_this, |
| 694 const std::string& replace_with) { | 862 StringPiece replace_with) { |
| 695 DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, | 863 DoReplaceSubstringsAfterOffset<std::string>( |
| 696 true); // replace all instances | 864 str, start_offset, find_this, replace_with, true); // Replace all. |
| 697 } | 865 } |
| 698 | 866 |
| 699 | 867 template <class string_type> |
| 700 template<typename STR> | 868 inline typename string_type::value_type* WriteIntoT(string_type* str, |
| 701 static size_t TokenizeT(const STR& str, | 869 size_t length_with_null) { |
| 702 const STR& delimiters, | 870 DCHECK_GT(length_with_null, 1u); |
| 703 std::vector<STR>* tokens) { | 871 str->reserve(length_with_null); |
| 704 tokens->clear(); | 872 str->resize(length_with_null - 1); |
| 705 | 873 return &((*str)[0]); |
| 706 size_t start = str.find_first_not_of(delimiters); | |
| 707 while (start != STR::npos) { | |
| 708 size_t end = str.find_first_of(delimiters, start + 1); | |
| 709 if (end == STR::npos) { | |
| 710 tokens->push_back(str.substr(start)); | |
| 711 break; | |
| 712 } else { | |
| 713 tokens->push_back(str.substr(start, end - start)); | |
| 714 start = str.find_first_not_of(delimiters, end + 1); | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 return tokens->size(); | |
| 719 } | 874 } |
| 720 | 875 |
| 721 size_t Tokenize(const string16& str, | 876 char* WriteInto(std::string* str, size_t length_with_null) { |
| 722 const string16& delimiters, | 877 return WriteIntoT(str, length_with_null); |
| 723 std::vector<string16>* tokens) { | |
| 724 return TokenizeT(str, delimiters, tokens); | |
| 725 } | 878 } |
| 726 | 879 |
| 727 size_t Tokenize(const std::string& str, | 880 char16* WriteInto(string16* str, size_t length_with_null) { |
| 728 const std::string& delimiters, | 881 return WriteIntoT(str, length_with_null); |
| 729 std::vector<std::string>* tokens) { | |
| 730 return TokenizeT(str, delimiters, tokens); | |
| 731 } | 882 } |
| 732 | 883 |
| 733 size_t Tokenize(const base::StringPiece& str, | 884 template <typename STR> |
| 734 const base::StringPiece& delimiters, | 885 static STR JoinStringT(const std::vector<STR>& parts, |
| 735 std::vector<base::StringPiece>* tokens) { | 886 BasicStringPiece<STR> sep) { |
| 736 return TokenizeT(str, delimiters, tokens); | |
| 737 } | |
| 738 | |
| 739 template<typename STR> | |
| 740 static STR JoinStringT(const std::vector<STR>& parts, const STR& sep) { | |
| 741 if (parts.empty()) | 887 if (parts.empty()) |
| 742 return STR(); | 888 return STR(); |
| 743 | 889 |
| 744 STR result(parts[0]); | 890 STR result(parts[0]); |
| 745 typename std::vector<STR>::const_iterator iter = parts.begin(); | 891 auto iter = parts.begin(); |
| 746 ++iter; | 892 ++iter; |
| 747 | 893 |
| 748 for (; iter != parts.end(); ++iter) { | 894 for (; iter != parts.end(); ++iter) { |
| 749 result += sep; | 895 sep.AppendToString(&result); |
| 750 result += *iter; | 896 result += *iter; |
| 751 } | 897 } |
| 752 | 898 |
| 753 return result; | 899 return result; |
| 754 } | 900 } |
| 755 | 901 |
| 756 std::string JoinString(const std::vector<std::string>& parts, char sep) { | |
| 757 return JoinStringT(parts, std::string(1, sep)); | |
| 758 } | |
| 759 | |
| 760 string16 JoinString(const std::vector<string16>& parts, char16 sep) { | |
| 761 return JoinStringT(parts, string16(1, sep)); | |
| 762 } | |
| 763 | |
| 764 std::string JoinString(const std::vector<std::string>& parts, | 902 std::string JoinString(const std::vector<std::string>& parts, |
| 765 const std::string& separator) { | 903 StringPiece separator) { |
| 766 return JoinStringT(parts, separator); | 904 return JoinStringT(parts, separator); |
| 767 } | 905 } |
| 768 | 906 |
| 769 string16 JoinString(const std::vector<string16>& parts, | 907 string16 JoinString(const std::vector<string16>& parts, |
| 770 const string16& separator) { | 908 StringPiece16 separator) { |
| 771 return JoinStringT(parts, separator); | 909 return JoinStringT(parts, separator); |
| 772 } | 910 } |
| 773 | 911 |
| 774 template<class FormatStringType, class OutStringType> | 912 template <class FormatStringType, class OutStringType> |
| 775 OutStringType DoReplaceStringPlaceholders(const FormatStringType& format_string, | 913 OutStringType DoReplaceStringPlaceholders( |
| 776 const std::vector<OutStringType>& subst, std::vector<size_t>* offsets) { | 914 const FormatStringType& format_string, |
| 915 const std::vector<OutStringType>& subst, |
| 916 std::vector<size_t>* offsets) { |
| 777 size_t substitutions = subst.size(); | 917 size_t substitutions = subst.size(); |
| 778 | 918 |
| 779 size_t sub_length = 0; | 919 size_t sub_length = 0; |
| 780 for (typename std::vector<OutStringType>::const_iterator iter = subst.begin(); | 920 for (const auto& cur : subst) |
| 781 iter != subst.end(); ++iter) { | 921 sub_length += cur.length(); |
| 782 sub_length += iter->length(); | |
| 783 } | |
| 784 | 922 |
| 785 OutStringType formatted; | 923 OutStringType formatted; |
| 786 formatted.reserve(format_string.length() + sub_length); | 924 formatted.reserve(format_string.length() + sub_length); |
| 787 | 925 |
| 788 std::vector<ReplacementOffset> r_offsets; | 926 std::vector<ReplacementOffset> r_offsets; |
| 789 for (typename FormatStringType::const_iterator i = format_string.begin(); | 927 for (auto i = format_string.begin(); i != format_string.end(); ++i) { |
| 790 i != format_string.end(); ++i) { | |
| 791 if ('$' == *i) { | 928 if ('$' == *i) { |
| 792 if (i + 1 != format_string.end()) { | 929 if (i + 1 != format_string.end()) { |
| 793 ++i; | 930 ++i; |
| 794 DCHECK('$' == *i || '1' <= *i) << "Invalid placeholder: " << *i; | 931 DCHECK('$' == *i || '1' <= *i) << "Invalid placeholder: " << *i; |
| 795 if ('$' == *i) { | 932 if ('$' == *i) { |
| 796 while (i != format_string.end() && '$' == *i) { | 933 while (i != format_string.end() && '$' == *i) { |
| 797 formatted.push_back('$'); | 934 formatted.push_back('$'); |
| 798 ++i; | 935 ++i; |
| 799 } | 936 } |
| 800 --i; | 937 --i; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 818 } | 955 } |
| 819 if (index < substitutions) | 956 if (index < substitutions) |
| 820 formatted.append(subst.at(index)); | 957 formatted.append(subst.at(index)); |
| 821 } | 958 } |
| 822 } | 959 } |
| 823 } else { | 960 } else { |
| 824 formatted.push_back(*i); | 961 formatted.push_back(*i); |
| 825 } | 962 } |
| 826 } | 963 } |
| 827 if (offsets) { | 964 if (offsets) { |
| 828 for (std::vector<ReplacementOffset>::const_iterator i = r_offsets.begin(); | 965 for (const auto& cur : r_offsets) |
| 829 i != r_offsets.end(); ++i) { | 966 offsets->push_back(cur.offset); |
| 830 offsets->push_back(i->offset); | |
| 831 } | |
| 832 } | 967 } |
| 833 return formatted; | 968 return formatted; |
| 834 } | 969 } |
| 835 | 970 |
| 836 string16 ReplaceStringPlaceholders(const string16& format_string, | 971 string16 ReplaceStringPlaceholders(const string16& format_string, |
| 837 const std::vector<string16>& subst, | 972 const std::vector<string16>& subst, |
| 838 std::vector<size_t>* offsets) { | 973 std::vector<size_t>* offsets) { |
| 839 return DoReplaceStringPlaceholders(format_string, subst, offsets); | 974 return DoReplaceStringPlaceholders(format_string, subst, offsets); |
| 840 } | 975 } |
| 841 | 976 |
| 842 std::string ReplaceStringPlaceholders(const base::StringPiece& format_string, | 977 std::string ReplaceStringPlaceholders(const StringPiece& format_string, |
| 843 const std::vector<std::string>& subst, | 978 const std::vector<std::string>& subst, |
| 844 std::vector<size_t>* offsets) { | 979 std::vector<size_t>* offsets) { |
| 845 return DoReplaceStringPlaceholders(format_string, subst, offsets); | 980 return DoReplaceStringPlaceholders(format_string, subst, offsets); |
| 846 } | 981 } |
| 847 | 982 |
| 848 string16 ReplaceStringPlaceholders(const string16& format_string, | 983 string16 ReplaceStringPlaceholders(const string16& format_string, |
| 849 const string16& a, | 984 const string16& a, |
| 850 size_t* offset) { | 985 size_t* offset) { |
| 851 std::vector<size_t> offsets; | 986 std::vector<size_t> offsets; |
| 852 std::vector<string16> subst; | 987 std::vector<string16> subst; |
| 853 subst.push_back(a); | 988 subst.push_back(a); |
| 854 string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets); | 989 string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets); |
| 855 | 990 |
| 856 DCHECK_EQ(1U, offsets.size()); | 991 DCHECK_EQ(1U, offsets.size()); |
| 857 if (offset) | 992 if (offset) |
| 858 *offset = offsets[0]; | 993 *offset = offsets[0]; |
| 859 return result; | 994 return result; |
| 860 } | 995 } |
| 861 | 996 |
| 862 static bool IsWildcard(base_icu::UChar32 character) { | |
| 863 return character == '*' || character == '?'; | |
| 864 } | |
| 865 | |
| 866 // Move the strings pointers to the point where they start to differ. | |
| 867 template <typename CHAR, typename NEXT> | |
| 868 static void EatSameChars(const CHAR** pattern, const CHAR* pattern_end, | |
| 869 const CHAR** string, const CHAR* string_end, | |
| 870 NEXT next) { | |
| 871 const CHAR* escape = NULL; | |
| 872 while (*pattern != pattern_end && *string != string_end) { | |
| 873 if (!escape && IsWildcard(**pattern)) { | |
| 874 // We don't want to match wildcard here, except if it's escaped. | |
| 875 return; | |
| 876 } | |
| 877 | |
| 878 // Check if the escapement char is found. If so, skip it and move to the | |
| 879 // next character. | |
| 880 if (!escape && **pattern == '\\') { | |
| 881 escape = *pattern; | |
| 882 next(pattern, pattern_end); | |
| 883 continue; | |
| 884 } | |
| 885 | |
| 886 // Check if the chars match, if so, increment the ptrs. | |
| 887 const CHAR* pattern_next = *pattern; | |
| 888 const CHAR* string_next = *string; | |
| 889 base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end); | |
| 890 if (pattern_char == next(&string_next, string_end) && | |
| 891 pattern_char != CBU_SENTINEL) { | |
| 892 *pattern = pattern_next; | |
| 893 *string = string_next; | |
| 894 } else { | |
| 895 // Uh oh, it did not match, we are done. If the last char was an | |
| 896 // escapement, that means that it was an error to advance the ptr here, | |
| 897 // let's put it back where it was. This also mean that the MatchPattern | |
| 898 // function will return false because if we can't match an escape char | |
| 899 // here, then no one will. | |
| 900 if (escape) { | |
| 901 *pattern = escape; | |
| 902 } | |
| 903 return; | |
| 904 } | |
| 905 | |
| 906 escape = NULL; | |
| 907 } | |
| 908 } | |
| 909 | |
| 910 template <typename CHAR, typename NEXT> | |
| 911 static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) { | |
| 912 while (*pattern != end) { | |
| 913 if (!IsWildcard(**pattern)) | |
| 914 return; | |
| 915 next(pattern, end); | |
| 916 } | |
| 917 } | |
| 918 | |
| 919 template <typename CHAR, typename NEXT> | |
| 920 static bool MatchPatternT(const CHAR* eval, const CHAR* eval_end, | |
| 921 const CHAR* pattern, const CHAR* pattern_end, | |
| 922 int depth, | |
| 923 NEXT next) { | |
| 924 const int kMaxDepth = 16; | |
| 925 if (depth > kMaxDepth) | |
| 926 return false; | |
| 927 | |
| 928 // Eat all the matching chars. | |
| 929 EatSameChars(&pattern, pattern_end, &eval, eval_end, next); | |
| 930 | |
| 931 // If the string is empty, then the pattern must be empty too, or contains | |
| 932 // only wildcards. | |
| 933 if (eval == eval_end) { | |
| 934 EatWildcard(&pattern, pattern_end, next); | |
| 935 return pattern == pattern_end; | |
| 936 } | |
| 937 | |
| 938 // Pattern is empty but not string, this is not a match. | |
| 939 if (pattern == pattern_end) | |
| 940 return false; | |
| 941 | |
| 942 // If this is a question mark, then we need to compare the rest with | |
| 943 // the current string or the string with one character eaten. | |
| 944 const CHAR* next_pattern = pattern; | |
| 945 next(&next_pattern, pattern_end); | |
| 946 if (pattern[0] == '?') { | |
| 947 if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, | |
| 948 depth + 1, next)) | |
| 949 return true; | |
| 950 const CHAR* next_eval = eval; | |
| 951 next(&next_eval, eval_end); | |
| 952 if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end, | |
| 953 depth + 1, next)) | |
| 954 return true; | |
| 955 } | |
| 956 | |
| 957 // This is a *, try to match all the possible substrings with the remainder | |
| 958 // of the pattern. | |
| 959 if (pattern[0] == '*') { | |
| 960 // Collapse duplicate wild cards (********** into *) so that the | |
| 961 // method does not recurse unnecessarily. http://crbug.com/52839 | |
| 962 EatWildcard(&next_pattern, pattern_end, next); | |
| 963 | |
| 964 while (eval != eval_end) { | |
| 965 if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, | |
| 966 depth + 1, next)) | |
| 967 return true; | |
| 968 eval++; | |
| 969 } | |
| 970 | |
| 971 // We reached the end of the string, let see if the pattern contains only | |
| 972 // wildcards. | |
| 973 if (eval == eval_end) { | |
| 974 EatWildcard(&pattern, pattern_end, next); | |
| 975 if (pattern != pattern_end) | |
| 976 return false; | |
| 977 return true; | |
| 978 } | |
| 979 } | |
| 980 | |
| 981 return false; | |
| 982 } | |
| 983 | |
| 984 struct NextCharUTF8 { | |
| 985 base_icu::UChar32 operator()(const char** p, const char* end) { | |
| 986 base_icu::UChar32 c; | |
| 987 int offset = 0; | |
| 988 CBU8_NEXT(*p, offset, end - *p, c); | |
| 989 *p += offset; | |
| 990 return c; | |
| 991 } | |
| 992 }; | |
| 993 | |
| 994 struct NextCharUTF16 { | |
| 995 base_icu::UChar32 operator()(const char16** p, const char16* end) { | |
| 996 base_icu::UChar32 c; | |
| 997 int offset = 0; | |
| 998 CBU16_NEXT(*p, offset, end - *p, c); | |
| 999 *p += offset; | |
| 1000 return c; | |
| 1001 } | |
| 1002 }; | |
| 1003 | |
| 1004 bool MatchPattern(const base::StringPiece& eval, | |
| 1005 const base::StringPiece& pattern) { | |
| 1006 return MatchPatternT(eval.data(), eval.data() + eval.size(), | |
| 1007 pattern.data(), pattern.data() + pattern.size(), | |
| 1008 0, NextCharUTF8()); | |
| 1009 } | |
| 1010 | |
| 1011 bool MatchPattern(const string16& eval, const string16& pattern) { | |
| 1012 return MatchPatternT(eval.c_str(), eval.c_str() + eval.size(), | |
| 1013 pattern.c_str(), pattern.c_str() + pattern.size(), | |
| 1014 0, NextCharUTF16()); | |
| 1015 } | |
| 1016 | |
| 1017 // The following code is compatible with the OpenBSD lcpy interface. See: | 997 // The following code is compatible with the OpenBSD lcpy interface. See: |
| 1018 // http://www.gratisoft.us/todd/papers/strlcpy.html | 998 // http://www.gratisoft.us/todd/papers/strlcpy.html |
| 1019 // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c | 999 // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c |
| 1020 | 1000 |
| 1021 namespace { | 1001 namespace { |
| 1022 | 1002 |
| 1023 template <typename CHAR> | 1003 template <typename CHAR> |
| 1024 size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { | 1004 size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { |
| 1025 for (size_t i = 0; i < dst_size; ++i) { | 1005 for (size_t i = 0; i < dst_size; ++i) { |
| 1026 if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. | 1006 if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. |
| 1027 return i; | 1007 return i; |
| 1028 } | 1008 } |
| 1029 | 1009 |
| 1030 // We were left off at dst_size. We over copied 1 byte. Null terminate. | 1010 // We were left off at dst_size. We over copied 1 byte. Null terminate. |
| 1031 if (dst_size != 0) | 1011 if (dst_size != 0) |
| 1032 dst[dst_size - 1] = 0; | 1012 dst[dst_size - 1] = 0; |
| 1033 | 1013 |
| 1034 // Count the rest of the |src|, and return it's length in characters. | 1014 // Count the rest of the |src|, and return it's length in characters. |
| 1035 while (src[dst_size]) ++dst_size; | 1015 while (src[dst_size]) ++dst_size; |
| 1036 return dst_size; | 1016 return dst_size; |
| 1037 } | 1017 } |
| 1038 | 1018 |
| 1039 } // namespace | 1019 } // namespace |
| 1040 | 1020 |
| 1041 size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { | 1021 size_t strlcpy(char* dst, const char* src, size_t dst_size) { |
| 1042 return lcpyT<char>(dst, src, dst_size); | 1022 return lcpyT<char>(dst, src, dst_size); |
| 1043 } | 1023 } |
| 1044 size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { | 1024 size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { |
| 1045 return lcpyT<wchar_t>(dst, src, dst_size); | 1025 return lcpyT<wchar_t>(dst, src, dst_size); |
| 1046 } | 1026 } |
| 1027 |
| 1028 } // namespace base |
| OLD | NEW |