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> |
(...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
447 const char* b) { | 447 const char* b) { |
448 return DoLowerCaseEqualsASCII(a_begin, a_end, b); | 448 return DoLowerCaseEqualsASCII(a_begin, a_end, b); |
449 } | 449 } |
450 | 450 |
451 bool LowerCaseEqualsASCII(string16::const_iterator a_begin, | 451 bool LowerCaseEqualsASCII(string16::const_iterator a_begin, |
452 string16::const_iterator a_end, | 452 string16::const_iterator a_end, |
453 const char* b) { | 453 const char* b) { |
454 return DoLowerCaseEqualsASCII(a_begin, a_end, b); | 454 return DoLowerCaseEqualsASCII(a_begin, a_end, b); |
455 } | 455 } |
456 | 456 |
457 // TODO(jdduke): Remove guards after complete adoption of libc++ on Android. | 457 // TODO(port): Resolve wchar_t/iterator issues that require OS_ANDROID here. |
458 #if !defined(OS_ANDROID) || !defined(USE_STLPORT) | 458 #if !defined(OS_ANDROID) |
459 bool LowerCaseEqualsASCII(const char* a_begin, | 459 bool LowerCaseEqualsASCII(const char* a_begin, |
460 const char* a_end, | 460 const char* a_end, |
461 const char* b) { | 461 const char* b) { |
462 return DoLowerCaseEqualsASCII(a_begin, a_end, b); | 462 return DoLowerCaseEqualsASCII(a_begin, a_end, b); |
463 } | 463 } |
464 | 464 |
465 bool LowerCaseEqualsASCII(const char16* a_begin, | 465 bool LowerCaseEqualsASCII(const char16* a_begin, |
466 const char16* a_end, | 466 const char16* a_end, |
467 const char* b) { | 467 const char* b) { |
468 return DoLowerCaseEqualsASCII(a_begin, a_end, b); | 468 return DoLowerCaseEqualsASCII(a_begin, a_end, b); |
469 } | 469 } |
470 #endif // !defined(OS_ANDROID) || !defined(USE_STLPORT) | 470 |
| 471 #endif // !defined(OS_ANDROID) |
471 | 472 |
472 bool EqualsASCII(const string16& a, const base::StringPiece& b) { | 473 bool EqualsASCII(const string16& a, const base::StringPiece& b) { |
473 if (a.length() != b.length()) | 474 if (a.length() != b.length()) |
474 return false; | 475 return false; |
475 return std::equal(b.begin(), b.end(), a.begin()); | 476 return std::equal(b.begin(), b.end(), a.begin()); |
476 } | 477 } |
477 | 478 |
478 bool StartsWithASCII(const std::string& str, | 479 bool StartsWithASCII(const std::string& str, |
479 const std::string& search, | 480 const std::string& search, |
480 bool case_sensitive) { | 481 bool case_sensitive) { |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
548 base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount, | 549 base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount, |
549 kByteStringsUnlocalized[dimension]); | 550 kByteStringsUnlocalized[dimension]); |
550 } else { | 551 } else { |
551 base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount, | 552 base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount, |
552 kByteStringsUnlocalized[dimension]); | 553 kByteStringsUnlocalized[dimension]); |
553 } | 554 } |
554 | 555 |
555 return base::ASCIIToUTF16(buf); | 556 return base::ASCIIToUTF16(buf); |
556 } | 557 } |
557 | 558 |
| 559 // Runs in O(n) time in the length of |str|. |
558 template<class StringType> | 560 template<class StringType> |
559 void DoReplaceSubstringsAfterOffset(StringType* str, | 561 void DoReplaceSubstringsAfterOffset(StringType* str, |
560 size_t start_offset, | 562 size_t offset, |
561 const StringType& find_this, | 563 const StringType& find_this, |
562 const StringType& replace_with, | 564 const StringType& replace_with, |
563 bool replace_all) { | 565 bool replace_all) { |
564 if ((start_offset == StringType::npos) || (start_offset >= str->length())) | 566 DCHECK(!find_this.empty()); |
| 567 |
| 568 // If the find string doesn't appear, there's nothing to do. |
| 569 offset = str->find(find_this, offset); |
| 570 if (offset == StringType::npos) |
565 return; | 571 return; |
566 | 572 |
567 DCHECK(!find_this.empty()); | 573 // If we're only replacing one instance, there's no need to do anything |
568 for (size_t offs(str->find(find_this, start_offset)); | 574 // complicated. |
569 offs != StringType::npos; offs = str->find(find_this, offs)) { | 575 size_t find_length = find_this.length(); |
570 str->replace(offs, find_this.length(), replace_with); | 576 if (!replace_all) { |
571 offs += replace_with.length(); | 577 str->replace(offset, find_length, replace_with); |
| 578 return; |
| 579 } |
572 | 580 |
573 if (!replace_all) | 581 // If the find and replace strings are the same length, we can simply use |
574 break; | 582 // replace() on each instance, and finish the entire operation in O(n) time. |
| 583 size_t replace_length = replace_with.length(); |
| 584 if (find_length == replace_length) { |
| 585 do { |
| 586 str->replace(offset, find_length, replace_with); |
| 587 offset = str->find(find_this, offset + replace_length); |
| 588 } while (offset != StringType::npos); |
| 589 return; |
| 590 } |
| 591 |
| 592 // Since the find and replace strings aren't the same length, a loop like the |
| 593 // one above would be O(n^2) in the worst case, as replace() will shift the |
| 594 // entire remaining string each time. We need to be more clever to keep |
| 595 // things O(n). |
| 596 // |
| 597 // If we're shortening the string, we can alternate replacements with shifting |
| 598 // forward the intervening characters using memmove(). |
| 599 size_t str_length = str->length(); |
| 600 if (find_length > replace_length) { |
| 601 size_t write_offset = offset; |
| 602 do { |
| 603 if (replace_length) { |
| 604 str->replace(write_offset, replace_length, replace_with); |
| 605 write_offset += replace_length; |
| 606 } |
| 607 size_t read_offset = offset + find_length; |
| 608 offset = std::min(str->find(find_this, read_offset), str_length); |
| 609 size_t length = offset - read_offset; |
| 610 if (length) { |
| 611 memmove(&(*str)[write_offset], &(*str)[read_offset], |
| 612 length * sizeof(typename StringType::value_type)); |
| 613 write_offset += length; |
| 614 } |
| 615 } while (offset < str_length); |
| 616 str->resize(write_offset); |
| 617 return; |
| 618 } |
| 619 |
| 620 // We're lengthening the string. We can use alternating replacements and |
| 621 // memmove() calls like above, but we need to precalculate the final string |
| 622 // length and then expand from back-to-front to avoid overwriting the string |
| 623 // as we're reading it, needing to shift, or having to copy to a second string |
| 624 // temporarily. |
| 625 size_t first_match = offset; |
| 626 |
| 627 // First, calculate the final length and resize the string. |
| 628 size_t final_length = str_length; |
| 629 size_t expansion = replace_length - find_length; |
| 630 size_t current_match; |
| 631 do { |
| 632 final_length += expansion; |
| 633 // Minor optimization: save this offset into |current_match|, so that on |
| 634 // exit from the loop, |current_match| will point at the last instance of |
| 635 // the find string, and we won't need to find() it again immediately. |
| 636 current_match = offset; |
| 637 offset = str->find(find_this, offset + find_length); |
| 638 } while (offset != StringType::npos); |
| 639 str->resize(final_length); |
| 640 |
| 641 // Now do the replacement loop, working backwards through the string. |
| 642 for (size_t prev_match = str_length, write_offset = final_length; ; |
| 643 current_match = str->rfind(find_this, current_match - 1)) { |
| 644 size_t read_offset = current_match + find_length; |
| 645 size_t length = prev_match - read_offset; |
| 646 if (length) { |
| 647 write_offset -= length; |
| 648 memmove(&(*str)[write_offset], &(*str)[read_offset], |
| 649 length * sizeof(typename StringType::value_type)); |
| 650 } |
| 651 write_offset -= replace_length; |
| 652 str->replace(write_offset, replace_length, replace_with); |
| 653 if (current_match == first_match) |
| 654 return; |
| 655 prev_match = current_match; |
575 } | 656 } |
576 } | 657 } |
577 | 658 |
578 void ReplaceFirstSubstringAfterOffset(string16* str, | 659 void ReplaceFirstSubstringAfterOffset(string16* str, |
579 size_t start_offset, | 660 size_t start_offset, |
580 const string16& find_this, | 661 const string16& find_this, |
581 const string16& replace_with) { | 662 const string16& replace_with) { |
582 DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, | 663 DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, |
583 false); // replace first instance | 664 false); // replace first instance |
584 } | 665 } |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
948 } | 1029 } |
949 | 1030 |
950 } // namespace | 1031 } // namespace |
951 | 1032 |
952 size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { | 1033 size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { |
953 return lcpyT<char>(dst, src, dst_size); | 1034 return lcpyT<char>(dst, src, dst_size); |
954 } | 1035 } |
955 size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { | 1036 size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { |
956 return lcpyT<wchar_t>(dst, src, dst_size); | 1037 return lcpyT<wchar_t>(dst, src, dst_size); |
957 } | 1038 } |
OLD | NEW |