• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
31 namespace base {
32 
33 namespace {
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.
38 struct EmptyStrings {
EmptyStringsbase::__anonaa7f929d0111::EmptyStrings39   EmptyStrings() {}
40   const std::string s;
41   const string16 s16;
42 
GetInstancebase::__anonaa7f929d0111::EmptyStrings43   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.
50 struct ReplacementOffset {
ReplacementOffsetbase::__anonaa7f929d0111::ReplacementOffset51   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 
CompareParameter(const ReplacementOffset & elem1,const ReplacementOffset & elem2)62 static 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).
71 template <typename string_type>
AppendToString(string_type * target,const string_type & source)72 inline void AppendToString(string_type* target, const string_type& source) {
73   target->append(source);
74 }
75 
76 template <typename string_type>
AppendToString(string_type * target,const BasicStringPiece<string_type> & source)77 inline 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.
84 typedef uintptr_t MachineWord;
85 const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
86 
IsAlignedToMachineWord(const void * pointer)87 inline bool IsAlignedToMachineWord(const void* pointer) {
88   return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask);
89 }
90 
AlignToMachineWord(T * pointer)91 template<typename T> inline T* AlignToMachineWord(T* pointer) {
92   return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) &
93                               ~kMachineWordAlignmentMask);
94 }
95 
96 template<size_t size, typename CharacterType> struct NonASCIIMask;
97 template<> struct NonASCIIMask<4, char16> {
valuebase::__anonaa7f929d0111::NonASCIIMask98     static inline uint32_t value() { return 0xFF80FF80U; }
99 };
100 template<> struct NonASCIIMask<4, char> {
valuebase::__anonaa7f929d0111::NonASCIIMask101     static inline uint32_t value() { return 0x80808080U; }
102 };
103 template<> struct NonASCIIMask<8, char16> {
valuebase::__anonaa7f929d0111::NonASCIIMask104     static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; }
105 };
106 template<> struct NonASCIIMask<8, char> {
valuebase::__anonaa7f929d0111::NonASCIIMask107     static inline uint64_t value() { return 0x8080808080808080ULL; }
108 };
109 #if defined(WCHAR_T_IS_UTF32)
110 template<> struct NonASCIIMask<4, wchar_t> {
valuebase::__anonaa7f929d0111::NonASCIIMask111     static inline uint32_t value() { return 0xFFFFFF80U; }
112 };
113 template<> struct NonASCIIMask<8, wchar_t> {
valuebase::__anonaa7f929d0111::NonASCIIMask114     static inline uint64_t value() { return 0xFFFFFF80FFFFFF80ULL; }
115 };
116 #endif  // WCHAR_T_IS_UTF32
117 
118 }  // namespace
119 
IsWprintfFormatPortable(const wchar_t * format)120 bool 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 
155 namespace {
156 
157 template<typename StringType>
ToLowerASCIIImpl(BasicStringPiece<StringType> str)158 StringType 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 
166 template<typename StringType>
ToUpperASCIIImpl(BasicStringPiece<StringType> str)167 StringType 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 
ToLowerASCII(StringPiece str)177 std::string ToLowerASCII(StringPiece str) {
178   return ToLowerASCIIImpl<std::string>(str);
179 }
180 
ToLowerASCII(StringPiece16 str)181 string16 ToLowerASCII(StringPiece16 str) {
182   return ToLowerASCIIImpl<string16>(str);
183 }
184 
ToUpperASCII(StringPiece str)185 std::string ToUpperASCII(StringPiece str) {
186   return ToUpperASCIIImpl<std::string>(str);
187 }
188 
ToUpperASCII(StringPiece16 str)189 string16 ToUpperASCII(StringPiece16 str) {
190   return ToUpperASCIIImpl<string16>(str);
191 }
192 
193 template<class StringType>
CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,BasicStringPiece<StringType> b)194 int 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 
CompareCaseInsensitiveASCII(StringPiece a,StringPiece b)220 int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) {
221   return CompareCaseInsensitiveASCIIT<std::string>(a, b);
222 }
223 
CompareCaseInsensitiveASCII(StringPiece16 a,StringPiece16 b)224 int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
225   return CompareCaseInsensitiveASCIIT<string16>(a, b);
226 }
227 
EqualsCaseInsensitiveASCII(StringPiece a,StringPiece b)228 bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
229   if (a.length() != b.length())
230     return false;
231   return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
232 }
233 
EqualsCaseInsensitiveASCII(StringPiece16 a,StringPiece16 b)234 bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
235   if (a.length() != b.length())
236     return false;
237   return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0;
238 }
239 
EmptyString()240 const std::string& EmptyString() {
241   return EmptyStrings::GetInstance()->s;
242 }
243 
EmptyString16()244 const string16& EmptyString16() {
245   return EmptyStrings::GetInstance()->s16;
246 }
247 
248 template<typename STR>
ReplaceCharsT(const STR & input,const STR & replace_chars,const STR & replace_with,STR * output)249 bool 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 
ReplaceChars(const string16 & input,const StringPiece16 & replace_chars,const string16 & replace_with,string16 * output)268 bool 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 
ReplaceChars(const std::string & input,const StringPiece & replace_chars,const std::string & replace_with,std::string * output)275 bool 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 
RemoveChars(const string16 & input,const StringPiece16 & remove_chars,string16 * output)282 bool RemoveChars(const string16& input,
283                  const StringPiece16& remove_chars,
284                  string16* output) {
285   return ReplaceChars(input, remove_chars.as_string(), string16(), output);
286 }
287 
RemoveChars(const std::string & input,const StringPiece & remove_chars,std::string * output)288 bool 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 
294 template<typename Str>
TrimStringT(const Str & input,BasicStringPiece<Str> trim_chars,TrimPositions positions,Str * output)295 TrimPositions 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 
TrimString(const string16 & input,StringPiece16 trim_chars,string16 * output)330 bool TrimString(const string16& input,
331                 StringPiece16 trim_chars,
332                 string16* output) {
333   return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
334 }
335 
TrimString(const std::string & input,StringPiece trim_chars,std::string * output)336 bool 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 
342 template<typename Str>
TrimStringPieceT(BasicStringPiece<Str> input,BasicStringPiece<Str> trim_chars,TrimPositions positions)343 BasicStringPiece<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 
TrimString(StringPiece16 input,const StringPiece16 & trim_chars,TrimPositions positions)353 StringPiece16 TrimString(StringPiece16 input,
354                          const StringPiece16& trim_chars,
355                          TrimPositions positions) {
356   return TrimStringPieceT(input, trim_chars, positions);
357 }
358 
TrimString(StringPiece input,const StringPiece & trim_chars,TrimPositions positions)359 StringPiece TrimString(StringPiece input,
360                        const StringPiece& trim_chars,
361                        TrimPositions positions) {
362   return TrimStringPieceT(input, trim_chars, positions);
363 }
364 
TruncateUTF8ToByteSize(const std::string & input,const size_t byte_size,std::string * output)365 void 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 
TrimWhitespace(const string16 & input,TrimPositions positions,string16 * output)402 TrimPositions TrimWhitespace(const string16& input,
403                              TrimPositions positions,
404                              string16* output) {
405   return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
406 }
407 
TrimWhitespace(StringPiece16 input,TrimPositions positions)408 StringPiece16 TrimWhitespace(StringPiece16 input,
409                              TrimPositions positions) {
410   return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
411 }
412 
TrimWhitespaceASCII(const std::string & input,TrimPositions positions,std::string * output)413 TrimPositions TrimWhitespaceASCII(const std::string& input,
414                                   TrimPositions positions,
415                                   std::string* output) {
416   return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
417 }
418 
TrimWhitespaceASCII(StringPiece input,TrimPositions positions)419 StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) {
420   return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions);
421 }
422 
423 template<typename STR>
CollapseWhitespaceT(const STR & text,bool trim_sequences_with_line_breaks)424 STR 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 
CollapseWhitespace(const string16 & text,bool trim_sequences_with_line_breaks)465 string16 CollapseWhitespace(const string16& text,
466                             bool trim_sequences_with_line_breaks) {
467   return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
468 }
469 
CollapseWhitespaceASCII(const std::string & text,bool trim_sequences_with_line_breaks)470 std::string CollapseWhitespaceASCII(const std::string& text,
471                                     bool trim_sequences_with_line_breaks) {
472   return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
473 }
474 
ContainsOnlyChars(const StringPiece & input,const StringPiece & characters)475 bool ContainsOnlyChars(const StringPiece& input,
476                        const StringPiece& characters) {
477   return input.find_first_not_of(characters) == StringPiece::npos;
478 }
479 
ContainsOnlyChars(const StringPiece16 & input,const StringPiece16 & characters)480 bool ContainsOnlyChars(const StringPiece16& input,
481                        const StringPiece16& characters) {
482   return input.find_first_not_of(characters) == StringPiece16::npos;
483 }
484 
485 template <class Char>
DoIsStringASCII(const Char * characters,size_t length)486 inline 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 
IsStringASCII(const StringPiece & str)515 bool IsStringASCII(const StringPiece& str) {
516   return DoIsStringASCII(str.data(), str.length());
517 }
518 
IsStringASCII(const StringPiece16 & str)519 bool IsStringASCII(const StringPiece16& str) {
520   return DoIsStringASCII(str.data(), str.length());
521 }
522 
IsStringASCII(const string16 & str)523 bool IsStringASCII(const string16& str) {
524   return DoIsStringASCII(str.data(), str.length());
525 }
526 
527 #if defined(WCHAR_T_IS_UTF32)
IsStringASCII(const std::wstring & str)528 bool IsStringASCII(const std::wstring& str) {
529   return DoIsStringASCII(str.data(), str.length());
530 }
531 #endif
532 
IsStringUTF8(const StringPiece & str)533 bool 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.
562 template<typename Str>
DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,StringPiece lowercase_ascii)563 static 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 
LowerCaseEqualsASCII(StringPiece str,StringPiece lowercase_ascii)574 bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowercase_ascii) {
575   return DoLowerCaseEqualsASCII<std::string>(str, lowercase_ascii);
576 }
577 
LowerCaseEqualsASCII(StringPiece16 str,StringPiece lowercase_ascii)578 bool LowerCaseEqualsASCII(StringPiece16 str, StringPiece lowercase_ascii) {
579   return DoLowerCaseEqualsASCII<string16>(str, lowercase_ascii);
580 }
581 
EqualsASCII(StringPiece16 str,StringPiece ascii)582 bool 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 
588 template<typename Str>
StartsWithT(BasicStringPiece<Str> str,BasicStringPiece<Str> search_for,CompareCase case_sensitivity)589 bool 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 
StartsWith(StringPiece str,StringPiece search_for,CompareCase case_sensitivity)613 bool StartsWith(StringPiece str,
614                 StringPiece search_for,
615                 CompareCase case_sensitivity) {
616   return StartsWithT<std::string>(str, search_for, case_sensitivity);
617 }
618 
StartsWith(StringPiece16 str,StringPiece16 search_for,CompareCase case_sensitivity)619 bool StartsWith(StringPiece16 str,
620                 StringPiece16 search_for,
621                 CompareCase case_sensitivity) {
622   return StartsWithT<string16>(str, search_for, case_sensitivity);
623 }
624 
625 template <typename Str>
EndsWithT(BasicStringPiece<Str> str,BasicStringPiece<Str> search_for,CompareCase case_sensitivity)626 bool 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 
EndsWith(StringPiece str,StringPiece search_for,CompareCase case_sensitivity)651 bool EndsWith(StringPiece str,
652               StringPiece search_for,
653               CompareCase case_sensitivity) {
654   return EndsWithT<std::string>(str, search_for, case_sensitivity);
655 }
656 
EndsWith(StringPiece16 str,StringPiece16 search_for,CompareCase case_sensitivity)657 bool EndsWith(StringPiece16 str,
658               StringPiece16 search_for,
659               CompareCase case_sensitivity) {
660   return EndsWithT<string16>(str, search_for, case_sensitivity);
661 }
662 
HexDigitToInt(wchar_t c)663 char 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 
IsUnicodeWhitespace(wchar_t c)674 bool 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 
683 static const char* const kByteStringsUnlocalized[] = {
684   " B",
685   " kB",
686   " MB",
687   " GB",
688   " TB",
689   " PB"
690 };
691 
FormatBytesUnlocalized(int64_t bytes)692 string16 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|.
715 template<class StringType>
DoReplaceSubstringsAfterOffset(StringType * str,size_t offset,BasicStringPiece<StringType> find_this,BasicStringPiece<StringType> replace_with,bool replace_all)716 void 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 
ReplaceFirstSubstringAfterOffset(string16 * str,size_t start_offset,StringPiece16 find_this,StringPiece16 replace_with)822 void 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 
ReplaceFirstSubstringAfterOffset(std::string * str,size_t start_offset,StringPiece find_this,StringPiece replace_with)830 void 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 
ReplaceSubstringsAfterOffset(string16 * str,size_t start_offset,StringPiece16 find_this,StringPiece16 replace_with)838 void 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 
ReplaceSubstringsAfterOffset(std::string * str,size_t start_offset,StringPiece find_this,StringPiece replace_with)846 void 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 
854 template <class string_type>
WriteIntoT(string_type * str,size_t length_with_null)855 inline 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 
WriteInto(std::string * str,size_t length_with_null)863 char* WriteInto(std::string* str, size_t length_with_null) {
864   return WriteIntoT(str, length_with_null);
865 }
866 
WriteInto(string16 * str,size_t length_with_null)867 char16* 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.
875 template <typename list_type, typename string_type>
JoinStringT(const list_type & parts,BasicStringPiece<string_type> sep)876 static 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 
JoinString(const std::vector<std::string> & parts,StringPiece separator)908 std::string JoinString(const std::vector<std::string>& parts,
909                        StringPiece separator) {
910   return JoinStringT(parts, separator);
911 }
912 
JoinString(const std::vector<string16> & parts,StringPiece16 separator)913 string16 JoinString(const std::vector<string16>& parts,
914                     StringPiece16 separator) {
915   return JoinStringT(parts, separator);
916 }
917 
JoinString(const std::vector<StringPiece> & parts,StringPiece separator)918 std::string JoinString(const std::vector<StringPiece>& parts,
919                        StringPiece separator) {
920   return JoinStringT(parts, separator);
921 }
922 
JoinString(const std::vector<StringPiece16> & parts,StringPiece16 separator)923 string16 JoinString(const std::vector<StringPiece16>& parts,
924                     StringPiece16 separator) {
925   return JoinStringT(parts, separator);
926 }
927 
JoinString(std::initializer_list<StringPiece> parts,StringPiece separator)928 std::string JoinString(std::initializer_list<StringPiece> parts,
929                        StringPiece separator) {
930   return JoinStringT(parts, separator);
931 }
932 
JoinString(std::initializer_list<StringPiece16> parts,StringPiece16 separator)933 string16 JoinString(std::initializer_list<StringPiece16> parts,
934                     StringPiece16 separator) {
935   return JoinStringT(parts, separator);
936 }
937 
938 template<class FormatStringType, class OutStringType>
DoReplaceStringPlaceholders(const FormatStringType & format_string,const std::vector<OutStringType> & subst,std::vector<size_t> * offsets)939 OutStringType 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 
ReplaceStringPlaceholders(const string16 & format_string,const std::vector<string16> & subst,std::vector<size_t> * offsets)994 string16 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 
ReplaceStringPlaceholders(const StringPiece & format_string,const std::vector<std::string> & subst,std::vector<size_t> * offsets)1000 std::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 
ReplaceStringPlaceholders(const string16 & format_string,const string16 & a,size_t * offset)1006 string16 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 
1024 namespace {
1025 
1026 template <typename CHAR>
lcpyT(CHAR * dst,const CHAR * src,size_t dst_size)1027 size_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 
strlcpy(char * dst,const char * src,size_t dst_size)1044 size_t strlcpy(char* dst, const char* src, size_t dst_size) {
1045   return lcpyT<char>(dst, src, dst_size);
1046 }
wcslcpy(wchar_t * dst,const wchar_t * src,size_t dst_size)1047 size_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