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 |