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
3 // found in the LICENSE file.
4
5 #include "base/strings/string_util.h"
6
7 #include <ctype.h>
8 #include <errno.h>
9 #include <math.h>
10 #include <stdarg.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <time.h>
16 #include <wchar.h>
17 #include <wctype.h>
18
19 #include <algorithm>
20 #include <limits>
21 #include <vector>
22
23 #include "base/logging.h"
24 #include "base/macros.h"
25 #include "base/memory/singleton.h"
26 #include "base/strings/string_split.h"
27 #include "base/strings/utf_string_conversion_utils.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/third_party/icu/icu_utf.h"
30 #include "build/build_config.h"
31
32 namespace base {
33
34 namespace {
35
36 // Force the singleton used by EmptyString[16] to be a unique type. This
37 // prevents other code that might accidentally use Singleton<string> from
38 // getting our internal one.
39 struct EmptyStrings {
EmptyStringsbase::__anon2d9711ec0111::EmptyStrings40 EmptyStrings() {}
41 const std::string s;
42 const string16 s16;
43
GetInstancebase::__anon2d9711ec0111::EmptyStrings44 static EmptyStrings* GetInstance() {
45 return Singleton<EmptyStrings>::get();
46 }
47 };
48
49 // Used by ReplaceStringPlaceholders to track the position in the string of
50 // replaced parameters.
51 struct ReplacementOffset {
ReplacementOffsetbase::__anon2d9711ec0111::ReplacementOffset52 ReplacementOffset(uintptr_t parameter, size_t offset)
53 : parameter(parameter),
54 offset(offset) {}
55
56 // Index of the parameter.
57 uintptr_t parameter;
58
59 // Starting position in the string.
60 size_t offset;
61 };
62
CompareParameter(const ReplacementOffset & elem1,const ReplacementOffset & elem2)63 static bool CompareParameter(const ReplacementOffset& elem1,
64 const ReplacementOffset& elem2) {
65 return elem1.parameter < elem2.parameter;
66 }
67
68 // Assuming that a pointer is the size of a "machine word", then
69 // uintptr_t is an integer type that is also a machine word.
70 typedef uintptr_t MachineWord;
71 const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
72
IsAlignedToMachineWord(const void * pointer)73 inline bool IsAlignedToMachineWord(const void* pointer) {
74 return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask);
75 }
76
AlignToMachineWord(T * pointer)77 template<typename T> inline T* AlignToMachineWord(T* pointer) {
78 return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) &
79 ~kMachineWordAlignmentMask);
80 }
81
82 template<size_t size, typename CharacterType> struct NonASCIIMask;
83 template<> struct NonASCIIMask<4, char16> {
valuebase::__anon2d9711ec0111::NonASCIIMask84 static inline uint32_t value() { return 0xFF80FF80U; }
85 };
86 template<> struct NonASCIIMask<4, char> {
valuebase::__anon2d9711ec0111::NonASCIIMask87 static inline uint32_t value() { return 0x80808080U; }
88 };
89 template<> struct NonASCIIMask<8, char16> {
valuebase::__anon2d9711ec0111::NonASCIIMask90 static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; }
91 };
92 template<> struct NonASCIIMask<8, char> {
valuebase::__anon2d9711ec0111::NonASCIIMask93 static inline uint64_t value() { return 0x8080808080808080ULL; }
94 };
95 #if defined(WCHAR_T_IS_UTF32)
96 template<> struct NonASCIIMask<4, wchar_t> {
valuebase::__anon2d9711ec0111::NonASCIIMask97 static inline uint32_t value() { return 0xFFFFFF80U; }
98 };
99 template<> struct NonASCIIMask<8, wchar_t> {
valuebase::__anon2d9711ec0111::NonASCIIMask100 static inline uint64_t value() { return 0xFFFFFF80FFFFFF80ULL; }
101 };
102 #endif // WCHAR_T_IS_UTF32
103
104 } // namespace
105
IsWprintfFormatPortable(const wchar_t * format)106 bool IsWprintfFormatPortable(const wchar_t* format) {
107 for (const wchar_t* position = format; *position != '\0'; ++position) {
108 if (*position == '%') {
109 bool in_specification = true;
110 bool modifier_l = false;
111 while (in_specification) {
112 // Eat up characters until reaching a known specifier.
113 if (*++position == '\0') {
114 // The format string ended in the middle of a specification. Call
115 // it portable because no unportable specifications were found. The
116 // string is equally broken on all platforms.
117 return true;
118 }
119
120 if (*position == 'l') {
121 // 'l' is the only thing that can save the 's' and 'c' specifiers.
122 modifier_l = true;
123 } else if (((*position == 's' || *position == 'c') && !modifier_l) ||
124 *position == 'S' || *position == 'C' || *position == 'F' ||
125 *position == 'D' || *position == 'O' || *position == 'U') {
126 // Not portable.
127 return false;
128 }
129
130 if (wcschr(L"diouxXeEfgGaAcspn%", *position)) {
131 // Portable, keep scanning the rest of the format string.
132 in_specification = false;
133 }
134 }
135 }
136 }
137
138 return true;
139 }
140
141 namespace {
142
143 template<typename StringType>
ToLowerASCIIImpl(BasicStringPiece<StringType> str)144 StringType ToLowerASCIIImpl(BasicStringPiece<StringType> str) {
145 StringType ret;
146 ret.reserve(str.size());
147 for (size_t i = 0; i < str.size(); i++)
148 ret.push_back(ToLowerASCII(str[i]));
149 return ret;
150 }
151
152 template<typename StringType>
ToUpperASCIIImpl(BasicStringPiece<StringType> str)153 StringType ToUpperASCIIImpl(BasicStringPiece<StringType> str) {
154 StringType ret;
155 ret.reserve(str.size());
156 for (size_t i = 0; i < str.size(); i++)
157 ret.push_back(ToUpperASCII(str[i]));
158 return ret;
159 }
160
161 } // namespace
162
ToLowerASCII(StringPiece str)163 std::string ToLowerASCII(StringPiece str) {
164 return ToLowerASCIIImpl<std::string>(str);
165 }
166
ToLowerASCII(StringPiece16 str)167 string16 ToLowerASCII(StringPiece16 str) {
168 return ToLowerASCIIImpl<string16>(str);
169 }
170
ToUpperASCII(StringPiece str)171 std::string ToUpperASCII(StringPiece str) {
172 return ToUpperASCIIImpl<std::string>(str);
173 }
174
ToUpperASCII(StringPiece16 str)175 string16 ToUpperASCII(StringPiece16 str) {
176 return ToUpperASCIIImpl<string16>(str);
177 }
178
179 template<class StringType>
CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,BasicStringPiece<StringType> b)180 int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,
181 BasicStringPiece<StringType> b) {
182 // Find the first characters that aren't equal and compare them. If the end
183 // of one of the strings is found before a nonequal character, the lengths
184 // of the strings are compared.
185 size_t i = 0;
186 while (i < a.length() && i < b.length()) {
187 typename StringType::value_type lower_a = ToLowerASCII(a[i]);
188 typename StringType::value_type lower_b = ToLowerASCII(b[i]);
189 if (lower_a < lower_b)
190 return -1;
191 if (lower_a > lower_b)
192 return 1;
193 i++;
194 }
195
196 // End of one string hit before finding a different character. Expect the
197 // common case to be "strings equal" at this point so check that first.
198 if (a.length() == b.length())
199 return 0;
200
201 if (a.length() < b.length())
202 return -1;
203 return 1;
204 }
205
CompareCaseInsensitiveASCII(StringPiece a,StringPiece b)206 int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) {
207 return CompareCaseInsensitiveASCIIT<std::string>(a, b);
208 }
209
CompareCaseInsensitiveASCII(StringPiece16 a,StringPiece16 b)210 int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
211 return CompareCaseInsensitiveASCIIT<string16>(a, b);
212 }
213
EqualsCaseInsensitiveASCII(StringPiece a,StringPiece b)214 bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
215 if (a.length() != b.length())
216 return false;
217 return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
218 }
219
EqualsCaseInsensitiveASCII(StringPiece16 a,StringPiece16 b)220 bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
221 if (a.length() != b.length())
222 return false;
223 return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0;
224 }
225
EmptyString()226 const std::string& EmptyString() {
227 return EmptyStrings::GetInstance()->s;
228 }
229
EmptyString16()230 const string16& EmptyString16() {
231 return EmptyStrings::GetInstance()->s16;
232 }
233
234 template<typename STR>
ReplaceCharsT(const STR & input,const STR & replace_chars,const STR & replace_with,STR * output)235 bool ReplaceCharsT(const STR& input,
236 const STR& replace_chars,
237 const STR& replace_with,
238 STR* output) {
239 bool removed = false;
240 size_t replace_length = replace_with.length();
241
242 *output = input;
243
244 size_t found = output->find_first_of(replace_chars);
245 while (found != STR::npos) {
246 removed = true;
247 output->replace(found, 1, replace_with);
248 found = output->find_first_of(replace_chars, found + replace_length);
249 }
250
251 return removed;
252 }
253
ReplaceChars(const string16 & input,const StringPiece16 & replace_chars,const string16 & replace_with,string16 * output)254 bool ReplaceChars(const string16& input,
255 const StringPiece16& replace_chars,
256 const string16& replace_with,
257 string16* output) {
258 return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
259 }
260
ReplaceChars(const std::string & input,const StringPiece & replace_chars,const std::string & replace_with,std::string * output)261 bool ReplaceChars(const std::string& input,
262 const StringPiece& replace_chars,
263 const std::string& replace_with,
264 std::string* output) {
265 return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
266 }
267
RemoveChars(const string16 & input,const StringPiece16 & remove_chars,string16 * output)268 bool RemoveChars(const string16& input,
269 const StringPiece16& remove_chars,
270 string16* output) {
271 return ReplaceChars(input, remove_chars.as_string(), string16(), output);
272 }
273
RemoveChars(const std::string & input,const StringPiece & remove_chars,std::string * output)274 bool RemoveChars(const std::string& input,
275 const StringPiece& remove_chars,
276 std::string* output) {
277 return ReplaceChars(input, remove_chars.as_string(), std::string(), output);
278 }
279
280 template<typename Str>
TrimStringT(const Str & input,BasicStringPiece<Str> trim_chars,TrimPositions positions,Str * output)281 TrimPositions TrimStringT(const Str& input,
282 BasicStringPiece<Str> trim_chars,
283 TrimPositions positions,
284 Str* output) {
285 // Find the edges of leading/trailing whitespace as desired. Need to use
286 // a StringPiece version of input to be able to call find* on it with the
287 // StringPiece version of trim_chars (normally the trim_chars will be a
288 // constant so avoid making a copy).
289 BasicStringPiece<Str> input_piece(input);
290 const size_t last_char = input.length() - 1;
291 const size_t first_good_char = (positions & TRIM_LEADING) ?
292 input_piece.find_first_not_of(trim_chars) : 0;
293 const size_t last_good_char = (positions & TRIM_TRAILING) ?
294 input_piece.find_last_not_of(trim_chars) : last_char;
295
296 // When the string was all trimmed, report that we stripped off characters
297 // from whichever position the caller was interested in. For empty input, we
298 // stripped no characters, but we still need to clear |output|.
299 if (input.empty() ||
300 (first_good_char == Str::npos) || (last_good_char == Str::npos)) {
301 bool input_was_empty = input.empty(); // in case output == &input
302 output->clear();
303 return input_was_empty ? TRIM_NONE : positions;
304 }
305
306 // Trim.
307 *output =
308 input.substr(first_good_char, last_good_char - first_good_char + 1);
309
310 // Return where we trimmed from.
311 return static_cast<TrimPositions>(
312 ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
313 ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
314 }
315
TrimString(const string16 & input,StringPiece16 trim_chars,string16 * output)316 bool TrimString(const string16& input,
317 StringPiece16 trim_chars,
318 string16* output) {
319 return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
320 }
321
TrimString(const std::string & input,StringPiece trim_chars,std::string * output)322 bool TrimString(const std::string& input,
323 StringPiece trim_chars,
324 std::string* output) {
325 return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
326 }
327
328 template<typename Str>
TrimStringPieceT(BasicStringPiece<Str> input,BasicStringPiece<Str> trim_chars,TrimPositions positions)329 BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input,
330 BasicStringPiece<Str> trim_chars,
331 TrimPositions positions) {
332 size_t begin = (positions & TRIM_LEADING) ?
333 input.find_first_not_of(trim_chars) : 0;
334 size_t end = (positions & TRIM_TRAILING) ?
335 input.find_last_not_of(trim_chars) + 1 : input.size();
336 return input.substr(begin, end - begin);
337 }
338
TrimString(StringPiece16 input,const StringPiece16 & trim_chars,TrimPositions positions)339 StringPiece16 TrimString(StringPiece16 input,
340 const StringPiece16& trim_chars,
341 TrimPositions positions) {
342 return TrimStringPieceT(input, trim_chars, positions);
343 }
344
TrimString(StringPiece input,const StringPiece & trim_chars,TrimPositions positions)345 StringPiece TrimString(StringPiece input,
346 const StringPiece& trim_chars,
347 TrimPositions positions) {
348 return TrimStringPieceT(input, trim_chars, positions);
349 }
350
TruncateUTF8ToByteSize(const std::string & input,const size_t byte_size,std::string * output)351 void TruncateUTF8ToByteSize(const std::string& input,
352 const size_t byte_size,
353 std::string* output) {
354 DCHECK(output);
355 if (byte_size > input.length()) {
356 *output = input;
357 return;
358 }
359 DCHECK_LE(byte_size,
360 static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
361 // Note: This cast is necessary because CBU8_NEXT uses int32_ts.
362 int32_t truncation_length = static_cast<int32_t>(byte_size);
363 int32_t char_index = truncation_length - 1;
364 const char* data = input.data();
365
366 // Using CBU8, we will move backwards from the truncation point
367 // to the beginning of the string looking for a valid UTF8
368 // character. Once a full UTF8 character is found, we will
369 // truncate the string to the end of that character.
370 while (char_index >= 0) {
371 int32_t prev = char_index;
372 base_icu::UChar32 code_point = 0;
373 CBU8_NEXT(data, char_index, truncation_length, code_point);
374 if (!IsValidCharacter(code_point) ||
375 !IsValidCodepoint(code_point)) {
376 char_index = prev - 1;
377 } else {
378 break;
379 }
380 }
381
382 if (char_index >= 0 )
383 *output = input.substr(0, char_index);
384 else
385 output->clear();
386 }
387
TrimWhitespace(const string16 & input,TrimPositions positions,string16 * output)388 TrimPositions TrimWhitespace(const string16& input,
389 TrimPositions positions,
390 string16* output) {
391 return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
392 }
393
TrimWhitespace(StringPiece16 input,TrimPositions positions)394 StringPiece16 TrimWhitespace(StringPiece16 input,
395 TrimPositions positions) {
396 return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
397 }
398
TrimWhitespaceASCII(const std::string & input,TrimPositions positions,std::string * output)399 TrimPositions TrimWhitespaceASCII(const std::string& input,
400 TrimPositions positions,
401 std::string* output) {
402 return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
403 }
404
TrimWhitespaceASCII(StringPiece input,TrimPositions positions)405 StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) {
406 return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions);
407 }
408
409 template<typename STR>
CollapseWhitespaceT(const STR & text,bool trim_sequences_with_line_breaks)410 STR CollapseWhitespaceT(const STR& text,
411 bool trim_sequences_with_line_breaks) {
412 STR result;
413 result.resize(text.size());
414
415 // Set flags to pretend we're already in a trimmed whitespace sequence, so we
416 // will trim any leading whitespace.
417 bool in_whitespace = true;
418 bool already_trimmed = true;
419
420 int chars_written = 0;
421 for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
422 if (IsUnicodeWhitespace(*i)) {
423 if (!in_whitespace) {
424 // Reduce all whitespace sequences to a single space.
425 in_whitespace = true;
426 result[chars_written++] = L' ';
427 }
428 if (trim_sequences_with_line_breaks && !already_trimmed &&
429 ((*i == '\n') || (*i == '\r'))) {
430 // Whitespace sequences containing CR or LF are eliminated entirely.
431 already_trimmed = true;
432 --chars_written;
433 }
434 } else {
435 // Non-whitespace chracters are copied straight across.
436 in_whitespace = false;
437 already_trimmed = false;
438 result[chars_written++] = *i;
439 }
440 }
441
442 if (in_whitespace && !already_trimmed) {
443 // Any trailing whitespace is eliminated.
444 --chars_written;
445 }
446
447 result.resize(chars_written);
448 return result;
449 }
450
CollapseWhitespace(const string16 & text,bool trim_sequences_with_line_breaks)451 string16 CollapseWhitespace(const string16& text,
452 bool trim_sequences_with_line_breaks) {
453 return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
454 }
455
CollapseWhitespaceASCII(const std::string & text,bool trim_sequences_with_line_breaks)456 std::string CollapseWhitespaceASCII(const std::string& text,
457 bool trim_sequences_with_line_breaks) {
458 return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
459 }
460
ContainsOnlyChars(const StringPiece & input,const StringPiece & characters)461 bool ContainsOnlyChars(const StringPiece& input,
462 const StringPiece& characters) {
463 return input.find_first_not_of(characters) == StringPiece::npos;
464 }
465
ContainsOnlyChars(const StringPiece16 & input,const StringPiece16 & characters)466 bool ContainsOnlyChars(const StringPiece16& input,
467 const StringPiece16& characters) {
468 return input.find_first_not_of(characters) == StringPiece16::npos;
469 }
470
471 template <class Char>
DoIsStringASCII(const Char * characters,size_t length)472 inline bool DoIsStringASCII(const Char* characters, size_t length) {
473 MachineWord all_char_bits = 0;
474 const Char* end = characters + length;
475
476 // Prologue: align the input.
477 while (!IsAlignedToMachineWord(characters) && characters != end) {
478 all_char_bits |= *characters;
479 ++characters;
480 }
481
482 // Compare the values of CPU word size.
483 const Char* word_end = AlignToMachineWord(end);
484 const size_t loop_increment = sizeof(MachineWord) / sizeof(Char);
485 while (characters < word_end) {
486 all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
487 characters += loop_increment;
488 }
489
490 // Process the remaining bytes.
491 while (characters != end) {
492 all_char_bits |= *characters;
493 ++characters;
494 }
495
496 MachineWord non_ascii_bit_mask =
497 NonASCIIMask<sizeof(MachineWord), Char>::value();
498 return !(all_char_bits & non_ascii_bit_mask);
499 }
500
IsStringASCII(const StringPiece & str)501 bool IsStringASCII(const StringPiece& str) {
502 return DoIsStringASCII(str.data(), str.length());
503 }
504
IsStringASCII(const StringPiece16 & str)505 bool IsStringASCII(const StringPiece16& str) {
506 return DoIsStringASCII(str.data(), str.length());
507 }
508
IsStringASCII(const string16 & str)509 bool IsStringASCII(const string16& str) {
510 return DoIsStringASCII(str.data(), str.length());
511 }
512
513 #if defined(WCHAR_T_IS_UTF32)
IsStringASCII(const std::wstring & str)514 bool IsStringASCII(const std::wstring& str) {
515 return DoIsStringASCII(str.data(), str.length());
516 }
517 #endif
518
IsStringUTF8(const StringPiece & str)519 bool IsStringUTF8(const StringPiece& str) {
520 const char *src = str.data();
521 int32_t src_len = static_cast<int32_t>(str.length());
522 int32_t char_index = 0;
523
524 while (char_index < src_len) {
525 int32_t code_point;
526 CBU8_NEXT(src, char_index, src_len, code_point);
527 if (!IsValidCharacter(code_point))
528 return false;
529 }
530 return true;
531 }
532
533 // Implementation note: Normally this function will be called with a hardcoded
534 // constant for the lowercase_ascii parameter. Constructing a StringPiece from
535 // a C constant requires running strlen, so the result will be two passes
536 // through the buffers, one to file the length of lowercase_ascii, and one to
537 // compare each letter.
538 //
539 // This function could have taken a const char* to avoid this and only do one
540 // pass through the string. But the strlen is faster than the case-insensitive
541 // compares and lets us early-exit in the case that the strings are different
542 // lengths (will often be the case for non-matches). So whether one approach or
543 // the other will be faster depends on the case.
544 //
545 // The hardcoded strings are typically very short so it doesn't matter, and the
546 // string piece gives additional flexibility for the caller (doesn't have to be
547 // null terminated) so we choose the StringPiece route.
548 template<typename Str>
DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,StringPiece lowercase_ascii)549 static inline bool DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,
550 StringPiece lowercase_ascii) {
551 if (str.size() != lowercase_ascii.size())
552 return false;
553 for (size_t i = 0; i < str.size(); i++) {
554 if (ToLowerASCII(str[i]) != lowercase_ascii[i])
555 return false;
556 }
557 return true;
558 }
559
LowerCaseEqualsASCII(StringPiece str,StringPiece lowercase_ascii)560 bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowercase_ascii) {
561 return DoLowerCaseEqualsASCII<std::string>(str, lowercase_ascii);
562 }
563
LowerCaseEqualsASCII(StringPiece16 str,StringPiece lowercase_ascii)564 bool LowerCaseEqualsASCII(StringPiece16 str, StringPiece lowercase_ascii) {
565 return DoLowerCaseEqualsASCII<string16>(str, lowercase_ascii);
566 }
567
EqualsASCII(StringPiece16 str,StringPiece ascii)568 bool EqualsASCII(StringPiece16 str, StringPiece ascii) {
569 if (str.length() != ascii.length())
570 return false;
571 return std::equal(ascii.begin(), ascii.end(), str.begin());
572 }
573
574 template<typename Str>
StartsWithT(BasicStringPiece<Str> str,BasicStringPiece<Str> search_for,CompareCase case_sensitivity)575 bool StartsWithT(BasicStringPiece<Str> str,
576 BasicStringPiece<Str> search_for,
577 CompareCase case_sensitivity) {
578 if (search_for.size() > str.size())
579 return false;
580
581 BasicStringPiece<Str> source = str.substr(0, search_for.size());
582
583 switch (case_sensitivity) {
584 case CompareCase::SENSITIVE:
585 return source == search_for;
586
587 case CompareCase::INSENSITIVE_ASCII:
588 return std::equal(
589 search_for.begin(), search_for.end(),
590 source.begin(),
591 CaseInsensitiveCompareASCII<typename Str::value_type>());
592
593 default:
594 NOTREACHED();
595 return false;
596 }
597 }
598
StartsWith(StringPiece str,StringPiece search_for,CompareCase case_sensitivity)599 bool StartsWith(StringPiece str,
600 StringPiece search_for,
601 CompareCase case_sensitivity) {
602 return StartsWithT<std::string>(str, search_for, case_sensitivity);
603 }
604
StartsWith(StringPiece16 str,StringPiece16 search_for,CompareCase case_sensitivity)605 bool StartsWith(StringPiece16 str,
606 StringPiece16 search_for,
607 CompareCase case_sensitivity) {
608 return StartsWithT<string16>(str, search_for, case_sensitivity);
609 }
610
611 template <typename Str>
EndsWithT(BasicStringPiece<Str> str,BasicStringPiece<Str> search_for,CompareCase case_sensitivity)612 bool EndsWithT(BasicStringPiece<Str> str,
613 BasicStringPiece<Str> search_for,
614 CompareCase case_sensitivity) {
615 if (search_for.size() > str.size())
616 return false;
617
618 BasicStringPiece<Str> source = str.substr(str.size() - search_for.size(),
619 search_for.size());
620
621 switch (case_sensitivity) {
622 case CompareCase::SENSITIVE:
623 return source == search_for;
624
625 case CompareCase::INSENSITIVE_ASCII:
626 return std::equal(
627 source.begin(), source.end(),
628 search_for.begin(),
629 CaseInsensitiveCompareASCII<typename Str::value_type>());
630
631 default:
632 NOTREACHED();
633 return false;
634 }
635 }
636
EndsWith(StringPiece str,StringPiece search_for,CompareCase case_sensitivity)637 bool EndsWith(StringPiece str,
638 StringPiece search_for,
639 CompareCase case_sensitivity) {
640 return EndsWithT<std::string>(str, search_for, case_sensitivity);
641 }
642
EndsWith(StringPiece16 str,StringPiece16 search_for,CompareCase case_sensitivity)643 bool EndsWith(StringPiece16 str,
644 StringPiece16 search_for,
645 CompareCase case_sensitivity) {
646 return EndsWithT<string16>(str, search_for, case_sensitivity);
647 }
648
HexDigitToInt(wchar_t c)649 char HexDigitToInt(wchar_t c) {
650 DCHECK(IsHexDigit(c));
651 if (c >= '0' && c <= '9')
652 return static_cast<char>(c - '0');
653 if (c >= 'A' && c <= 'F')
654 return static_cast<char>(c - 'A' + 10);
655 if (c >= 'a' && c <= 'f')
656 return static_cast<char>(c - 'a' + 10);
657 return 0;
658 }
659
IsUnicodeWhitespace(wchar_t c)660 bool IsUnicodeWhitespace(wchar_t c) {
661 // kWhitespaceWide is a NULL-terminated string
662 for (const wchar_t* cur = kWhitespaceWide; *cur; ++cur) {
663 if (*cur == c)
664 return true;
665 }
666 return false;
667 }
668
669 static const char* const kByteStringsUnlocalized[] = {
670 " B",
671 " kB",
672 " MB",
673 " GB",
674 " TB",
675 " PB"
676 };
677
FormatBytesUnlocalized(int64_t bytes)678 string16 FormatBytesUnlocalized(int64_t bytes) {
679 double unit_amount = static_cast<double>(bytes);
680 size_t dimension = 0;
681 const int kKilo = 1024;
682 while (unit_amount >= kKilo &&
683 dimension < arraysize(kByteStringsUnlocalized) - 1) {
684 unit_amount /= kKilo;
685 dimension++;
686 }
687
688 char buf[64];
689 if (bytes != 0 && dimension > 0 && unit_amount < 100) {
690 base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount,
691 kByteStringsUnlocalized[dimension]);
692 } else {
693 base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount,
694 kByteStringsUnlocalized[dimension]);
695 }
696
697 return ASCIIToUTF16(buf);
698 }
699
700 // Runs in O(n) time in the length of |str|.
701 template<class StringType>
DoReplaceSubstringsAfterOffset(StringType * str,size_t offset,BasicStringPiece<StringType> find_this,BasicStringPiece<StringType> replace_with,bool replace_all)702 void DoReplaceSubstringsAfterOffset(StringType* str,
703 size_t offset,
704 BasicStringPiece<StringType> find_this,
705 BasicStringPiece<StringType> replace_with,
706 bool replace_all) {
707 DCHECK(!find_this.empty());
708
709 // If the find string doesn't appear, there's nothing to do.
710 offset = str->find(find_this.data(), offset, find_this.size());
711 if (offset == StringType::npos)
712 return;
713
714 // If we're only replacing one instance, there's no need to do anything
715 // complicated.
716 size_t find_length = find_this.length();
717 if (!replace_all) {
718 str->replace(offset, find_length, replace_with.data(), replace_with.size());
719 return;
720 }
721
722 // If the find and replace strings are the same length, we can simply use
723 // replace() on each instance, and finish the entire operation in O(n) time.
724 size_t replace_length = replace_with.length();
725 if (find_length == replace_length) {
726 do {
727 str->replace(offset, find_length,
728 replace_with.data(), replace_with.size());
729 offset = str->find(find_this.data(), offset + replace_length,
730 find_this.size());
731 } while (offset != StringType::npos);
732 return;
733 }
734
735 // Since the find and replace strings aren't the same length, a loop like the
736 // one above would be O(n^2) in the worst case, as replace() will shift the
737 // entire remaining string each time. We need to be more clever to keep
738 // things O(n).
739 //
740 // If we're shortening the string, we can alternate replacements with shifting
741 // forward the intervening characters using memmove().
742 size_t str_length = str->length();
743 if (find_length > replace_length) {
744 size_t write_offset = offset;
745 do {
746 if (replace_length) {
747 str->replace(write_offset, replace_length,
748 replace_with.data(), replace_with.size());
749 write_offset += replace_length;
750 }
751 size_t read_offset = offset + find_length;
752 offset = std::min(
753 str->find(find_this.data(), read_offset, find_this.size()),
754 str_length);
755 size_t length = offset - read_offset;
756 if (length) {
757 memmove(&(*str)[write_offset], &(*str)[read_offset],
758 length * sizeof(typename StringType::value_type));
759 write_offset += length;
760 }
761 } while (offset < str_length);
762 str->resize(write_offset);
763 return;
764 }
765
766 // We're lengthening the string. We can use alternating replacements and
767 // memmove() calls like above, but we need to precalculate the final string
768 // length and then expand from back-to-front to avoid overwriting the string
769 // as we're reading it, needing to shift, or having to copy to a second string
770 // temporarily.
771 size_t first_match = offset;
772
773 // First, calculate the final length and resize the string.
774 size_t final_length = str_length;
775 size_t expansion = replace_length - find_length;
776 size_t current_match;
777 do {
778 final_length += expansion;
779 // Minor optimization: save this offset into |current_match|, so that on
780 // exit from the loop, |current_match| will point at the last instance of
781 // the find string, and we won't need to find() it again immediately.
782 current_match = offset;
783 offset = str->find(find_this.data(), offset + find_length,
784 find_this.size());
785 } while (offset != StringType::npos);
786 str->resize(final_length);
787
788 // Now do the replacement loop, working backwards through the string.
789 for (size_t prev_match = str_length, write_offset = final_length; ;
790 current_match = str->rfind(find_this.data(), current_match - 1,
791 find_this.size())) {
792 size_t read_offset = current_match + find_length;
793 size_t length = prev_match - read_offset;
794 if (length) {
795 write_offset -= length;
796 memmove(&(*str)[write_offset], &(*str)[read_offset],
797 length * sizeof(typename StringType::value_type));
798 }
799 write_offset -= replace_length;
800 str->replace(write_offset, replace_length,
801 replace_with.data(), replace_with.size());
802 if (current_match == first_match)
803 return;
804 prev_match = current_match;
805 }
806 }
807
ReplaceFirstSubstringAfterOffset(string16 * str,size_t start_offset,StringPiece16 find_this,StringPiece16 replace_with)808 void ReplaceFirstSubstringAfterOffset(string16* str,
809 size_t start_offset,
810 StringPiece16 find_this,
811 StringPiece16 replace_with) {
812 DoReplaceSubstringsAfterOffset<string16>(
813 str, start_offset, find_this, replace_with, false); // Replace first.
814 }
815
ReplaceFirstSubstringAfterOffset(std::string * str,size_t start_offset,StringPiece find_this,StringPiece replace_with)816 void ReplaceFirstSubstringAfterOffset(std::string* str,
817 size_t start_offset,
818 StringPiece find_this,
819 StringPiece replace_with) {
820 DoReplaceSubstringsAfterOffset<std::string>(
821 str, start_offset, find_this, replace_with, false); // Replace first.
822 }
823
ReplaceSubstringsAfterOffset(string16 * str,size_t start_offset,StringPiece16 find_this,StringPiece16 replace_with)824 void ReplaceSubstringsAfterOffset(string16* str,
825 size_t start_offset,
826 StringPiece16 find_this,
827 StringPiece16 replace_with) {
828 DoReplaceSubstringsAfterOffset<string16>(
829 str, start_offset, find_this, replace_with, true); // Replace all.
830 }
831
ReplaceSubstringsAfterOffset(std::string * str,size_t start_offset,StringPiece find_this,StringPiece replace_with)832 void ReplaceSubstringsAfterOffset(std::string* str,
833 size_t start_offset,
834 StringPiece find_this,
835 StringPiece replace_with) {
836 DoReplaceSubstringsAfterOffset<std::string>(
837 str, start_offset, find_this, replace_with, true); // Replace all.
838 }
839
840 template <class string_type>
WriteIntoT(string_type * str,size_t length_with_null)841 inline typename string_type::value_type* WriteIntoT(string_type* str,
842 size_t length_with_null) {
843 DCHECK_GT(length_with_null, 1u);
844 str->reserve(length_with_null);
845 str->resize(length_with_null - 1);
846 return &((*str)[0]);
847 }
848
WriteInto(std::string * str,size_t length_with_null)849 char* WriteInto(std::string* str, size_t length_with_null) {
850 return WriteIntoT(str, length_with_null);
851 }
852
WriteInto(string16 * str,size_t length_with_null)853 char16* WriteInto(string16* str, size_t length_with_null) {
854 return WriteIntoT(str, length_with_null);
855 }
856
857 template<typename STR>
JoinStringT(const std::vector<STR> & parts,BasicStringPiece<STR> sep)858 static STR JoinStringT(const std::vector<STR>& parts,
859 BasicStringPiece<STR> sep) {
860 if (parts.empty())
861 return STR();
862
863 STR result(parts[0]);
864 auto iter = parts.begin();
865 ++iter;
866
867 for (; iter != parts.end(); ++iter) {
868 sep.AppendToString(&result);
869 result += *iter;
870 }
871
872 return result;
873 }
874
JoinString(const std::vector<std::string> & parts,StringPiece separator)875 std::string JoinString(const std::vector<std::string>& parts,
876 StringPiece separator) {
877 return JoinStringT(parts, separator);
878 }
879
JoinString(const std::vector<string16> & parts,StringPiece16 separator)880 string16 JoinString(const std::vector<string16>& parts,
881 StringPiece16 separator) {
882 return JoinStringT(parts, separator);
883 }
884
885 template<class FormatStringType, class OutStringType>
DoReplaceStringPlaceholders(const FormatStringType & format_string,const std::vector<OutStringType> & subst,std::vector<size_t> * offsets)886 OutStringType DoReplaceStringPlaceholders(
887 const FormatStringType& format_string,
888 const std::vector<OutStringType>& subst,
889 std::vector<size_t>* offsets) {
890 size_t substitutions = subst.size();
891
892 size_t sub_length = 0;
893 for (const auto& cur : subst)
894 sub_length += cur.length();
895
896 OutStringType formatted;
897 formatted.reserve(format_string.length() + sub_length);
898
899 std::vector<ReplacementOffset> r_offsets;
900 for (auto i = format_string.begin(); i != format_string.end(); ++i) {
901 if ('$' == *i) {
902 if (i + 1 != format_string.end()) {
903 ++i;
904 DCHECK('$' == *i || '1' <= *i) << "Invalid placeholder: " << *i;
905 if ('$' == *i) {
906 while (i != format_string.end() && '$' == *i) {
907 formatted.push_back('$');
908 ++i;
909 }
910 --i;
911 } else {
912 uintptr_t index = 0;
913 while (i != format_string.end() && '0' <= *i && *i <= '9') {
914 index *= 10;
915 index += *i - '0';
916 ++i;
917 }
918 --i;
919 index -= 1;
920 if (offsets) {
921 ReplacementOffset r_offset(index,
922 static_cast<int>(formatted.size()));
923 r_offsets.insert(std::lower_bound(r_offsets.begin(),
924 r_offsets.end(),
925 r_offset,
926 &CompareParameter),
927 r_offset);
928 }
929 if (index < substitutions)
930 formatted.append(subst.at(index));
931 }
932 }
933 } else {
934 formatted.push_back(*i);
935 }
936 }
937 if (offsets) {
938 for (const auto& cur : r_offsets)
939 offsets->push_back(cur.offset);
940 }
941 return formatted;
942 }
943
ReplaceStringPlaceholders(const string16 & format_string,const std::vector<string16> & subst,std::vector<size_t> * offsets)944 string16 ReplaceStringPlaceholders(const string16& format_string,
945 const std::vector<string16>& subst,
946 std::vector<size_t>* offsets) {
947 return DoReplaceStringPlaceholders(format_string, subst, offsets);
948 }
949
ReplaceStringPlaceholders(const StringPiece & format_string,const std::vector<std::string> & subst,std::vector<size_t> * offsets)950 std::string ReplaceStringPlaceholders(const StringPiece& format_string,
951 const std::vector<std::string>& subst,
952 std::vector<size_t>* offsets) {
953 return DoReplaceStringPlaceholders(format_string, subst, offsets);
954 }
955
ReplaceStringPlaceholders(const string16 & format_string,const string16 & a,size_t * offset)956 string16 ReplaceStringPlaceholders(const string16& format_string,
957 const string16& a,
958 size_t* offset) {
959 std::vector<size_t> offsets;
960 std::vector<string16> subst;
961 subst.push_back(a);
962 string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
963
964 DCHECK_EQ(1U, offsets.size());
965 if (offset)
966 *offset = offsets[0];
967 return result;
968 }
969
970 // The following code is compatible with the OpenBSD lcpy interface. See:
971 // http://www.gratisoft.us/todd/papers/strlcpy.html
972 // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
973
974 namespace {
975
976 template <typename CHAR>
lcpyT(CHAR * dst,const CHAR * src,size_t dst_size)977 size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
978 for (size_t i = 0; i < dst_size; ++i) {
979 if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL.
980 return i;
981 }
982
983 // We were left off at dst_size. We over copied 1 byte. Null terminate.
984 if (dst_size != 0)
985 dst[dst_size - 1] = 0;
986
987 // Count the rest of the |src|, and return it's length in characters.
988 while (src[dst_size]) ++dst_size;
989 return dst_size;
990 }
991
992 } // namespace
993
strlcpy(char * dst,const char * src,size_t dst_size)994 size_t strlcpy(char* dst, const char* src, size_t dst_size) {
995 return lcpyT<char>(dst, src, dst_size);
996 }
wcslcpy(wchar_t * dst,const wchar_t * src,size_t dst_size)997 size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
998 return lcpyT<wchar_t>(dst, src, dst_size);
999 }
1000
1001 } // namespace base
1002