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