Coverage Report

Created: 2025-11-11 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/butil/strings/string_util.cc
Line
Count
Source
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 "butil/strings/string_util.h"
6
7
#include <ctype.h>
8
#include <errno.h>
9
#include <math.h>
10
#include <stdarg.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <time.h>
15
#include <wchar.h>
16
#include <wctype.h>
17
18
#include <algorithm>
19
#include <vector>
20
21
#include "butil/basictypes.h"
22
#include "butil/logging.h"
23
#include "butil/memory/singleton.h"
24
#include "butil/strings/utf_string_conversion_utils.h"
25
#include "butil/strings/utf_string_conversions.h"
26
#include "butil/third_party/icu/icu_utf.h"
27
#include "butil/build_config.h"
28
29
// Remove when this entire file is in namespace butil.
30
using butil::char16;
31
using butil::string16;
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 {
39
0
  EmptyStrings() {}
40
  const std::string s;
41
  const string16 s16;
42
43
0
  static EmptyStrings* GetInstance() {
44
0
    return Singleton<EmptyStrings>::get();
45
0
  }
46
};
47
48
// Used by ReplaceStringPlaceholders to track the position in the string of
49
// replaced parameters.
50
struct ReplacementOffset {
51
  ReplacementOffset(uintptr_t parameter, size_t offset)
52
0
      : parameter(parameter),
53
0
        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
62
static bool CompareParameter(const ReplacementOffset& elem1,
63
0
                             const ReplacementOffset& elem2) {
64
0
  return elem1.parameter < elem2.parameter;
65
0
}
66
67
}  // namespace
68
69
namespace butil {
70
71
0
bool IsWprintfFormatPortable(const wchar_t* format) {
72
0
  for (const wchar_t* position = format; *position != '\0'; ++position) {
73
0
    if (*position == '%') {
74
0
      bool in_specification = true;
75
0
      bool modifier_l = false;
76
0
      while (in_specification) {
77
        // Eat up characters until reaching a known specifier.
78
0
        if (*++position == '\0') {
79
          // The format string ended in the middle of a specification.  Call
80
          // it portable because no unportable specifications were found.  The
81
          // string is equally broken on all platforms.
82
0
          return true;
83
0
        }
84
85
0
        if (*position == 'l') {
86
          // 'l' is the only thing that can save the 's' and 'c' specifiers.
87
0
          modifier_l = true;
88
0
        } else if (((*position == 's' || *position == 'c') && !modifier_l) ||
89
0
                   *position == 'S' || *position == 'C' || *position == 'F' ||
90
0
                   *position == 'D' || *position == 'O' || *position == 'U') {
91
          // Not portable.
92
0
          return false;
93
0
        }
94
95
0
        if (wcschr(L"diouxXeEfgGaAcspn%", *position)) {
96
          // Portable, keep scanning the rest of the format string.
97
0
          in_specification = false;
98
0
        }
99
0
      }
100
0
    }
101
0
  }
102
103
0
  return true;
104
0
}
105
106
0
const std::string& EmptyString() {
107
0
  return EmptyStrings::GetInstance()->s;
108
0
}
109
110
0
const string16& EmptyString16() {
111
0
  return EmptyStrings::GetInstance()->s16;
112
0
}
113
114
template<typename STR>
115
bool ReplaceCharsT(const STR& input,
116
                   const STR& replace_chars,
117
                   const STR& replace_with,
118
0
                   STR* output) {
119
0
  bool removed = false;
120
0
  size_t replace_length = replace_with.length();
121
122
0
  *output = input;
123
124
0
  size_t found = output->find_first_of(replace_chars);
125
0
  while (found != STR::npos) {
126
0
    removed = true;
127
0
    output->replace(found, 1, replace_with);
128
0
    found = output->find_first_of(replace_chars, found + replace_length);
129
0
  }
130
131
0
  return removed;
132
0
}
Unexecuted instantiation: bool butil::ReplaceCharsT<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> >*)
Unexecuted instantiation: bool butil::ReplaceCharsT<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)
133
134
bool ReplaceChars(const string16& input,
135
                  const butil::StringPiece16& replace_chars,
136
                  const string16& replace_with,
137
0
                  string16* output) {
138
0
  return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
139
0
}
140
141
bool ReplaceChars(const std::string& input,
142
                  const butil::StringPiece& replace_chars,
143
                  const std::string& replace_with,
144
0
                  std::string* output) {
145
0
  return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
146
0
}
147
148
bool RemoveChars(const string16& input,
149
                 const butil::StringPiece16& remove_chars,
150
0
                 string16* output) {
151
0
  return ReplaceChars(input, remove_chars.as_string(), string16(), output);
152
0
}
153
154
bool RemoveChars(const std::string& input,
155
                 const butil::StringPiece& remove_chars,
156
0
                 std::string* output) {
157
0
  return ReplaceChars(input, remove_chars.as_string(), std::string(), output);
158
0
}
159
160
template<typename STR>
161
TrimPositions TrimStringT(const STR& input,
162
                          const STR& trim_chars,
163
                          TrimPositions positions,
164
0
                          STR* output) {
165
  // Find the edges of leading/trailing whitespace as desired.
166
0
  const size_t last_char = input.length() - 1;
167
0
  const size_t first_good_char = (positions & TRIM_LEADING) ?
168
0
      input.find_first_not_of(trim_chars) : 0;
169
0
  const size_t last_good_char = (positions & TRIM_TRAILING) ?
170
0
      input.find_last_not_of(trim_chars) : last_char;
171
172
  // When the string was all whitespace, report that we stripped off whitespace
173
  // from whichever position the caller was interested in.  For empty input, we
174
  // stripped no whitespace, but we still need to clear |output|.
175
0
  if (input.empty() ||
176
0
      (first_good_char == STR::npos) || (last_good_char == STR::npos)) {
177
0
    bool input_was_empty = input.empty();  // in case output == &input
178
0
    output->clear();
179
0
    return input_was_empty ? TRIM_NONE : positions;
180
0
  }
181
182
  // Trim the whitespace.
183
0
  *output =
184
0
      input.substr(first_good_char, last_good_char - first_good_char + 1);
185
186
  // Return where we trimmed from.
187
0
  return static_cast<TrimPositions>(
188
0
      ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
189
0
      ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
190
0
}
Unexecuted instantiation: butil::TrimPositions butil::TrimStringT<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, butil::TrimPositions, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> >*)
Unexecuted instantiation: butil::TrimPositions butil::TrimStringT<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, butil::TrimPositions, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)
Unexecuted instantiation: butil::TrimPositions butil::TrimStringT<butil::BasicStringPiece<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > > >(butil::BasicStringPiece<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > > const&, butil::BasicStringPiece<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > > const&, butil::TrimPositions, butil::BasicStringPiece<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >*)
Unexecuted instantiation: butil::TrimPositions butil::TrimStringT<butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >(butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, butil::TrimPositions, butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >*)
191
192
bool TrimString(const string16& input,
193
                const butil::StringPiece16& trim_chars,
194
0
                string16* output) {
195
0
  return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) !=
196
0
      TRIM_NONE;
197
0
}
198
199
bool TrimString(const std::string& input,
200
                const butil::StringPiece& trim_chars,
201
0
                std::string* output) {
202
0
  return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) !=
203
0
      TRIM_NONE;
204
0
}
205
206
void TruncateUTF8ToByteSize(const std::string& input,
207
                            const size_t byte_size,
208
0
                            std::string* output) {
209
0
  DCHECK(output);
210
0
  if (byte_size > input.length()) {
211
0
    *output = input;
212
0
    return;
213
0
  }
214
0
  DCHECK_LE(byte_size, static_cast<uint32_t>(kint32max));
215
  // Note: This cast is necessary because CBU8_NEXT uses int32s.
216
0
  int32_t truncation_length = static_cast<int32_t>(byte_size);
217
0
  int32_t char_index = truncation_length - 1;
218
0
  const char* data = input.data();
219
220
  // Using CBU8, we will move backwards from the truncation point
221
  // to the beginning of the string looking for a valid UTF8
222
  // character.  Once a full UTF8 character is found, we will
223
  // truncate the string to the end of that character.
224
0
  while (char_index >= 0) {
225
0
    int32_t prev = char_index;
226
0
    uint32_t code_point = 0;
227
0
    CBU8_NEXT(data, char_index, truncation_length, code_point);
228
0
    if (!IsValidCharacter(code_point) ||
229
0
        !IsValidCodepoint(code_point)) {
230
0
      char_index = prev - 1;
231
0
    } else {
232
0
      break;
233
0
    }
234
0
  }
235
236
0
  if (char_index >= 0 )
237
0
    *output = input.substr(0, char_index);
238
0
  else
239
0
    output->clear();
240
0
}
241
242
TrimPositions TrimWhitespace(const string16& input,
243
                             TrimPositions positions,
244
0
                             string16* output) {
245
0
  return TrimStringT(input, butil::string16(kWhitespaceUTF16), positions,
246
0
                     output);
247
0
}
248
249
TrimPositions TrimWhitespace(const butil::StringPiece16& input,
250
                             TrimPositions positions,
251
0
                             butil::StringPiece16* output) {
252
0
  return TrimStringT(input, butil::StringPiece16(kWhitespaceUTF16), positions,
253
0
                     output);
254
0
}
255
256
TrimPositions TrimWhitespaceASCII(const std::string& input,
257
                                  TrimPositions positions,
258
0
                                  std::string* output) {
259
0
  return TrimStringT(input, std::string(kWhitespaceASCII), positions, output);
260
0
}
261
262
TrimPositions TrimWhitespaceASCII(const butil::StringPiece& input,
263
                                  TrimPositions positions,
264
0
                                  butil::StringPiece* output) {
265
0
  return TrimStringT(input, butil::StringPiece(kWhitespaceASCII), positions, output);
266
0
}
267
268
// This function is only for backward-compatibility.
269
// To be removed when all callers are updated.
270
TrimPositions TrimWhitespace(const std::string& input,
271
                             TrimPositions positions,
272
0
                             std::string* output) {
273
0
  return TrimWhitespaceASCII(input, positions, output);
274
0
}
275
276
TrimPositions TrimWhitespace(const butil::StringPiece& input,
277
                             TrimPositions positions,
278
0
                             butil::StringPiece* output) {
279
0
  return TrimWhitespaceASCII(input, positions, output);
280
0
}
281
282
template<typename STR>
283
STR CollapseWhitespaceT(const STR& text,
284
0
                        bool trim_sequences_with_line_breaks) {
285
0
  STR result;
286
0
  result.resize(text.size());
287
288
  // Set flags to pretend we're already in a trimmed whitespace sequence, so we
289
  // will trim any leading whitespace.
290
0
  bool in_whitespace = true;
291
0
  bool already_trimmed = true;
292
293
0
  int chars_written = 0;
294
0
  for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
295
0
    if (IsWhitespace(*i)) {
296
0
      if (!in_whitespace) {
297
        // Reduce all whitespace sequences to a single space.
298
0
        in_whitespace = true;
299
0
        result[chars_written++] = L' ';
300
0
      }
301
0
      if (trim_sequences_with_line_breaks && !already_trimmed &&
302
0
          ((*i == '\n') || (*i == '\r'))) {
303
        // Whitespace sequences containing CR or LF are eliminated entirely.
304
0
        already_trimmed = true;
305
0
        --chars_written;
306
0
      }
307
0
    } else {
308
      // Non-whitespace chracters are copied straight across.
309
0
      in_whitespace = false;
310
0
      already_trimmed = false;
311
0
      result[chars_written++] = *i;
312
0
    }
313
0
  }
314
315
0
  if (in_whitespace && !already_trimmed) {
316
    // Any trailing whitespace is eliminated.
317
0
    --chars_written;
318
0
  }
319
320
0
  result.resize(chars_written);
321
0
  return result;
322
0
}
Unexecuted instantiation: std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > butil::CollapseWhitespaceT<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, bool)
Unexecuted instantiation: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > butil::CollapseWhitespaceT<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)
323
324
string16 CollapseWhitespace(const string16& text,
325
0
                            bool trim_sequences_with_line_breaks) {
326
0
  return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
327
0
}
328
329
std::string CollapseWhitespaceASCII(const std::string& text,
330
0
                                    bool trim_sequences_with_line_breaks) {
331
0
  return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
332
0
}
333
334
bool ContainsOnlyChars(const StringPiece& input,
335
0
                       const StringPiece& characters) {
336
0
  return input.find_first_not_of(characters) == StringPiece::npos;
337
0
}
338
339
bool ContainsOnlyChars(const StringPiece16& input,
340
0
                       const StringPiece16& characters) {
341
0
  return input.find_first_not_of(characters) == StringPiece16::npos;
342
0
}
343
344
template<class STR>
345
0
static bool DoIsStringASCII(const STR& str) {
346
0
  for (size_t i = 0; i < str.length(); i++) {
347
0
    typename ToUnsigned<typename STR::value_type>::Unsigned c = str[i];
348
0
    if (c > 0x7F)
349
0
      return false;
350
0
  }
351
0
  return true;
352
0
}
Unexecuted instantiation: string_util.cc:bool butil::DoIsStringASCII<butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >(butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&)
Unexecuted instantiation: string_util.cc:bool butil::DoIsStringASCII<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&)
353
354
0
bool IsStringASCII(const StringPiece& str) {
355
0
  return DoIsStringASCII(str);
356
0
}
357
358
0
bool IsStringASCII(const string16& str) {
359
0
  return DoIsStringASCII(str);
360
0
}
361
362
0
bool IsStringUTF8(const StringPiece& str) {
363
0
  const char *src = str.data();
364
0
  int32_t src_len = static_cast<int32_t>(str.length());
365
0
  int32_t char_index = 0;
366
367
0
  while (char_index < src_len) {
368
0
    int32_t code_point;
369
0
    CBU8_NEXT(src, char_index, src_len, code_point);
370
0
    if (!IsValidCharacter(code_point))
371
0
      return false;
372
0
  }
373
0
  return true;
374
0
}
375
376
}  // namespace butil
377
378
template<typename Iter>
379
static inline bool DoLowerCaseEqualsASCII(Iter a_begin,
380
                                          Iter a_end,
381
0
                                          const char* b) {
382
0
  for (Iter it = a_begin; it != a_end; ++it, ++b) {
383
0
    if (!*b || butil::ToLowerASCII(*it) != *b)
384
0
      return false;
385
0
  }
386
0
  return *b == 0;
387
0
}
Unexecuted instantiation: string_util.cc:bool DoLowerCaseEqualsASCII<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >(__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, char const*)
Unexecuted instantiation: string_util.cc:bool DoLowerCaseEqualsASCII<__gnu_cxx::__normal_iterator<unsigned short const*, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > > >(__gnu_cxx::__normal_iterator<unsigned short const*, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >, __gnu_cxx::__normal_iterator<unsigned short const*, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >, char const*)
Unexecuted instantiation: string_util.cc:bool DoLowerCaseEqualsASCII<char const*>(char const*, char const*, char const*)
Unexecuted instantiation: string_util.cc:bool DoLowerCaseEqualsASCII<unsigned short const*>(unsigned short const*, unsigned short const*, char const*)
388
389
// Front-ends for LowerCaseEqualsASCII.
390
0
bool LowerCaseEqualsASCII(const std::string& a, const char* b) {
391
0
  return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
392
0
}
393
394
0
bool LowerCaseEqualsASCII(const string16& a, const char* b) {
395
0
  return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
396
0
}
397
398
bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
399
                          std::string::const_iterator a_end,
400
0
                          const char* b) {
401
0
  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
402
0
}
403
404
bool LowerCaseEqualsASCII(string16::const_iterator a_begin,
405
                          string16::const_iterator a_end,
406
0
                          const char* b) {
407
0
  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
408
0
}
409
410
// TODO(port): Resolve wchar_t/iterator issues that require OS_ANDROID here.
411
#if !defined(OS_ANDROID)
412
bool LowerCaseEqualsASCII(const char* a_begin,
413
                          const char* a_end,
414
0
                          const char* b) {
415
0
  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
416
0
}
417
418
bool LowerCaseEqualsASCII(const char16* a_begin,
419
                          const char16* a_end,
420
0
                          const char* b) {
421
0
  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
422
0
}
423
424
#endif  // !defined(OS_ANDROID)
425
426
0
bool EqualsASCII(const string16& a, const butil::StringPiece& b) {
427
0
  if (a.length() != b.length())
428
0
    return false;
429
0
  return std::equal(b.begin(), b.end(), a.begin());
430
0
}
431
432
bool StartsWithASCII(const std::string& str,
433
                     const std::string& search,
434
0
                     bool case_sensitive) {
435
0
  if (case_sensitive)
436
0
    return str.compare(0, search.length(), search) == 0;
437
0
  else
438
0
    return butil::strncasecmp(str.c_str(), search.c_str(), search.length()) == 0;
439
0
}
440
441
template <typename STR>
442
0
bool StartsWithT(const STR& str, const STR& search, bool case_sensitive) {
443
0
  if (case_sensitive) {
444
0
    return str.compare(0, search.length(), search) == 0;
445
0
  } else {
446
0
    if (search.size() > str.size())
447
0
      return false;
448
0
    return std::equal(search.begin(), search.end(), str.begin(),
449
0
                      butil::CaseInsensitiveCompare<typename STR::value_type>());
450
0
  }
451
0
}
452
453
bool StartsWith(const string16& str, const string16& search,
454
0
                bool case_sensitive) {
455
0
  return StartsWithT(str, search, case_sensitive);
456
0
}
457
458
template <typename STR>
459
0
bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) {
460
0
  size_t str_length = str.length();
461
0
  size_t search_length = search.length();
462
0
  if (search_length > str_length)
463
0
    return false;
464
0
  if (case_sensitive)
465
0
    return str.compare(str_length - search_length, search_length, search) == 0;
466
0
  return std::equal(search.begin(), search.end(),
467
0
                    str.begin() + (str_length - search_length),
468
0
                    butil::CaseInsensitiveCompare<typename STR::value_type>());
469
0
}
Unexecuted instantiation: bool EndsWithT<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)
Unexecuted instantiation: bool EndsWithT<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, bool)
470
471
bool EndsWith(const std::string& str, const std::string& search,
472
0
              bool case_sensitive) {
473
0
  return EndsWithT(str, search, case_sensitive);
474
0
}
475
476
bool EndsWith(const string16& str, const string16& search,
477
0
              bool case_sensitive) {
478
0
  return EndsWithT(str, search, case_sensitive);
479
0
}
480
481
static const char* const kByteStringsUnlocalized[] = {
482
  " B",
483
  " kB",
484
  " MB",
485
  " GB",
486
  " TB",
487
  " PB"
488
};
489
490
0
string16 FormatBytesUnlocalized(int64_t bytes) {
491
0
  double unit_amount = static_cast<double>(bytes);
492
0
  size_t dimension = 0;
493
0
  const int kKilo = 1024;
494
0
  while (unit_amount >= kKilo &&
495
0
         dimension < arraysize(kByteStringsUnlocalized) - 1) {
496
0
    unit_amount /= kKilo;
497
0
    dimension++;
498
0
  }
499
500
0
  char buf[64];
501
0
  if (bytes != 0 && dimension > 0 && unit_amount < 100) {
502
0
    butil::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount,
503
0
                   kByteStringsUnlocalized[dimension]);
504
0
  } else {
505
0
    butil::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount,
506
0
                   kByteStringsUnlocalized[dimension]);
507
0
  }
508
509
0
  return butil::ASCIIToUTF16(buf);
510
0
}
511
512
template<class StringType>
513
void DoReplaceSubstringsAfterOffset(StringType* str,
514
                                    size_t start_offset,
515
                                    const StringType& find_this,
516
                                    const StringType& replace_with,
517
0
                                    bool replace_all) {
518
0
  if ((start_offset == StringType::npos) || (start_offset >= str->length()))
519
0
    return;
520
521
0
  DCHECK(!find_this.empty());
522
0
  for (size_t offs(str->find(find_this, start_offset));
523
0
      offs != StringType::npos; offs = str->find(find_this, offs)) {
524
0
    str->replace(offs, find_this.length(), replace_with);
525
0
    offs += replace_with.length();
526
527
0
    if (!replace_all)
528
0
      break;
529
0
  }
530
0
}
Unexecuted instantiation: void DoReplaceSubstringsAfterOffset<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> >*, unsigned long, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, bool)
Unexecuted instantiation: void DoReplaceSubstringsAfterOffset<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)
531
532
void ReplaceFirstSubstringAfterOffset(string16* str,
533
                                      size_t start_offset,
534
                                      const string16& find_this,
535
0
                                      const string16& replace_with) {
536
0
  DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
537
0
                                 false);  // replace first instance
538
0
}
539
540
void ReplaceFirstSubstringAfterOffset(std::string* str,
541
                                      size_t start_offset,
542
                                      const std::string& find_this,
543
0
                                      const std::string& replace_with) {
544
0
  DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
545
0
                                 false);  // replace first instance
546
0
}
547
548
void ReplaceSubstringsAfterOffset(string16* str,
549
                                  size_t start_offset,
550
                                  const string16& find_this,
551
0
                                  const string16& replace_with) {
552
0
  DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
553
0
                                 true);  // replace all instances
554
0
}
555
556
void ReplaceSubstringsAfterOffset(std::string* str,
557
                                  size_t start_offset,
558
                                  const std::string& find_this,
559
0
                                  const std::string& replace_with) {
560
0
  DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
561
0
                                 true);  // replace all instances
562
0
}
563
564
565
template<typename STR>
566
static size_t TokenizeT(const STR& str,
567
                        const STR& delimiters,
568
0
                        std::vector<STR>* tokens) {
569
0
  tokens->clear();
570
571
0
  size_t start = str.find_first_not_of(delimiters);
572
0
  while (start != STR::npos) {
573
0
    size_t end = str.find_first_of(delimiters, start + 1);
574
0
    if (end == STR::npos) {
575
0
      tokens->push_back(str.substr(start));
576
0
      break;
577
0
    } else {
578
0
      tokens->push_back(str.substr(start, end - start));
579
0
      start = str.find_first_not_of(delimiters, end + 1);
580
0
    }
581
0
  }
582
583
0
  return tokens->size();
584
0
}
Unexecuted instantiation: string_util.cc:unsigned long TokenizeT<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::vector<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> >, std::allocator<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > > >*)
Unexecuted instantiation: string_util.cc:unsigned long TokenizeT<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*)
Unexecuted instantiation: string_util.cc:unsigned long TokenizeT<butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >(butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, std::vector<butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >*)
585
586
size_t Tokenize(const string16& str,
587
                const string16& delimiters,
588
0
                std::vector<string16>* tokens) {
589
0
  return TokenizeT(str, delimiters, tokens);
590
0
}
591
592
size_t Tokenize(const std::string& str,
593
                const std::string& delimiters,
594
0
                std::vector<std::string>* tokens) {
595
0
  return TokenizeT(str, delimiters, tokens);
596
0
}
597
598
size_t Tokenize(const butil::StringPiece& str,
599
                const butil::StringPiece& delimiters,
600
0
                std::vector<butil::StringPiece>* tokens) {
601
0
  return TokenizeT(str, delimiters, tokens);
602
0
}
603
604
template<typename STR>
605
0
static STR JoinStringT(const std::vector<STR>& parts, const STR& sep) {
606
0
  if (parts.empty())
607
0
    return STR();
608
609
0
  STR result(parts[0]);
610
0
  typename std::vector<STR>::const_iterator iter = parts.begin();
611
0
  ++iter;
612
613
0
  for (; iter != parts.end(); ++iter) {
614
0
    result += sep;
615
0
    result += *iter;
616
0
  }
617
618
0
  return result;
619
0
}
Unexecuted instantiation: string_util.cc:std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > JoinStringT<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
Unexecuted instantiation: string_util.cc:std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > JoinStringT<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::vector<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> >, std::allocator<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > > > const&, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&)
620
621
0
std::string JoinString(const std::vector<std::string>& parts, char sep) {
622
0
  return JoinStringT(parts, std::string(1, sep));
623
0
}
624
625
0
string16 JoinString(const std::vector<string16>& parts, char16 sep) {
626
0
  return JoinStringT(parts, string16(1, sep));
627
0
}
628
629
std::string JoinString(const std::vector<std::string>& parts,
630
0
                       const std::string& separator) {
631
0
  return JoinStringT(parts, separator);
632
0
}
633
634
string16 JoinString(const std::vector<string16>& parts,
635
0
                    const string16& separator) {
636
0
  return JoinStringT(parts, separator);
637
0
}
638
639
template<class FormatStringType, class OutStringType>
640
OutStringType DoReplaceStringPlaceholders(const FormatStringType& format_string,
641
0
    const std::vector<OutStringType>& subst, std::vector<size_t>* offsets) {
642
0
  size_t substitutions = subst.size();
643
644
0
  size_t sub_length = 0;
645
0
  for (typename std::vector<OutStringType>::const_iterator iter = subst.begin();
646
0
       iter != subst.end(); ++iter) {
647
0
    sub_length += iter->length();
648
0
  }
649
650
0
  OutStringType formatted;
651
0
  formatted.reserve(format_string.length() + sub_length);
652
653
0
  std::vector<ReplacementOffset> r_offsets;
654
0
  for (typename FormatStringType::const_iterator i = format_string.begin();
655
0
       i != format_string.end(); ++i) {
656
0
    if ('$' == *i) {
657
0
      if (i + 1 != format_string.end()) {
658
0
        ++i;
659
0
        DCHECK('$' == *i || '1' <= *i) << "Invalid placeholder: " << *i;
660
0
        if ('$' == *i) {
661
0
          while (i != format_string.end() && '$' == *i) {
662
0
            formatted.push_back('$');
663
0
            ++i;
664
0
          }
665
0
          --i;
666
0
        } else {
667
0
          uintptr_t index = 0;
668
0
          while (i != format_string.end() && '0' <= *i && *i <= '9') {
669
0
            index *= 10;
670
0
            index += *i - '0';
671
0
            ++i;
672
0
          }
673
0
          --i;
674
0
          index -= 1;
675
0
          if (offsets) {
676
0
            ReplacementOffset r_offset(index,
677
0
                static_cast<int>(formatted.size()));
678
0
            r_offsets.insert(std::lower_bound(r_offsets.begin(),
679
0
                                              r_offsets.end(),
680
0
                                              r_offset,
681
0
                                              &CompareParameter),
682
0
                             r_offset);
683
0
          }
684
0
          if (index < substitutions)
685
0
            formatted.append(subst.at(index));
686
0
        }
687
0
      }
688
0
    } else {
689
0
      formatted.push_back(*i);
690
0
    }
691
0
  }
692
0
  if (offsets) {
693
0
    for (std::vector<ReplacementOffset>::const_iterator i = r_offsets.begin();
694
0
         i != r_offsets.end(); ++i) {
695
0
      offsets->push_back(i->offset);
696
0
    }
697
0
  }
698
0
  return formatted;
699
0
}
Unexecuted instantiation: std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > DoReplaceStringPlaceholders<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> >, std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > >(std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > const&, std::vector<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> >, std::allocator<std::__cxx11::basic_string<unsigned short, butil::string16_char_traits, std::allocator<unsigned short> > > > const&, std::vector<unsigned long, std::allocator<unsigned long> >*)
Unexecuted instantiation: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DoReplaceStringPlaceholders<butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(butil::BasicStringPiece<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<unsigned long, std::allocator<unsigned long> >*)
700
701
string16 ReplaceStringPlaceholders(const string16& format_string,
702
                                   const std::vector<string16>& subst,
703
0
                                   std::vector<size_t>* offsets) {
704
0
  return DoReplaceStringPlaceholders(format_string, subst, offsets);
705
0
}
706
707
std::string ReplaceStringPlaceholders(const butil::StringPiece& format_string,
708
                                      const std::vector<std::string>& subst,
709
0
                                      std::vector<size_t>* offsets) {
710
0
  return DoReplaceStringPlaceholders(format_string, subst, offsets);
711
0
}
712
713
string16 ReplaceStringPlaceholders(const string16& format_string,
714
                                   const string16& a,
715
0
                                   size_t* offset) {
716
0
  std::vector<size_t> offsets;
717
0
  std::vector<string16> subst;
718
0
  subst.push_back(a);
719
0
  string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
720
721
0
  DCHECK_EQ(1U, offsets.size());
722
0
  if (offset)
723
0
    *offset = offsets[0];
724
0
  return result;
725
0
}
726
727
0
static bool IsWildcard(base_icu::UChar32 character) {
728
0
  return character == '*' || character == '?';
729
0
}
730
731
// Move the strings pointers to the point where they start to differ.
732
template <typename CHAR, typename NEXT>
733
static void EatSameChars(const CHAR** pattern, const CHAR* pattern_end,
734
                         const CHAR** string, const CHAR* string_end,
735
0
                         NEXT next) {
736
0
  const CHAR* escape = NULL;
737
0
  while (*pattern != pattern_end && *string != string_end) {
738
0
    if (!escape && IsWildcard(**pattern)) {
739
      // We don't want to match wildcard here, except if it's escaped.
740
0
      return;
741
0
    }
742
743
    // Check if the escapement char is found. If so, skip it and move to the
744
    // next character.
745
0
    if (!escape && **pattern == '\\') {
746
0
      escape = *pattern;
747
0
      next(pattern, pattern_end);
748
0
      continue;
749
0
    }
750
751
    // Check if the chars match, if so, increment the ptrs.
752
0
    const CHAR* pattern_next = *pattern;
753
0
    const CHAR* string_next = *string;
754
0
    base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end);
755
0
    if (pattern_char == next(&string_next, string_end) &&
756
0
        pattern_char != (base_icu::UChar32) CBU_SENTINEL) {
757
0
      *pattern = pattern_next;
758
0
      *string = string_next;
759
0
    } else {
760
      // Uh ho, it did not match, we are done. If the last char was an
761
      // escapement, that means that it was an error to advance the ptr here,
762
      // let's put it back where it was. This also mean that the MatchPattern
763
      // function will return false because if we can't match an escape char
764
      // here, then no one will.
765
0
      if (escape) {
766
0
        *pattern = escape;
767
0
      }
768
0
      return;
769
0
    }
770
771
0
    escape = NULL;
772
0
  }
773
0
}
Unexecuted instantiation: string_util.cc:void EatSameChars<char, NextCharUTF8>(char const**, char const*, char const**, char const*, NextCharUTF8)
Unexecuted instantiation: string_util.cc:void EatSameChars<unsigned short, NextCharUTF16>(unsigned short const**, unsigned short const*, unsigned short const**, unsigned short const*, NextCharUTF16)
774
775
template <typename CHAR, typename NEXT>
776
0
static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) {
777
0
  while (*pattern != end) {
778
0
    if (!IsWildcard(**pattern))
779
0
      return;
780
0
    next(pattern, end);
781
0
  }
782
0
}
Unexecuted instantiation: string_util.cc:void EatWildcard<char, NextCharUTF8>(char const**, char const*, NextCharUTF8)
Unexecuted instantiation: string_util.cc:void EatWildcard<unsigned short, NextCharUTF16>(unsigned short const**, unsigned short const*, NextCharUTF16)
783
784
template <typename CHAR, typename NEXT>
785
static bool MatchPatternT(const CHAR* eval, const CHAR* eval_end,
786
                          const CHAR* pattern, const CHAR* pattern_end,
787
                          int depth,
788
0
                          NEXT next) {
789
0
  const int kMaxDepth = 16;
790
0
  if (depth > kMaxDepth)
791
0
    return false;
792
793
  // Eat all the matching chars.
794
0
  EatSameChars(&pattern, pattern_end, &eval, eval_end, next);
795
796
  // If the string is empty, then the pattern must be empty too, or contains
797
  // only wildcards.
798
0
  if (eval == eval_end) {
799
0
    EatWildcard(&pattern, pattern_end, next);
800
0
    return pattern == pattern_end;
801
0
  }
802
803
  // Pattern is empty but not string, this is not a match.
804
0
  if (pattern == pattern_end)
805
0
    return false;
806
807
  // If this is a question mark, then we need to compare the rest with
808
  // the current string or the string with one character eaten.
809
0
  const CHAR* next_pattern = pattern;
810
0
  next(&next_pattern, pattern_end);
811
0
  if (pattern[0] == '?') {
812
0
    if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
813
0
                      depth + 1, next))
814
0
      return true;
815
0
    const CHAR* next_eval = eval;
816
0
    next(&next_eval, eval_end);
817
0
    if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end,
818
0
                      depth + 1, next))
819
0
      return true;
820
0
  }
821
822
  // This is a *, try to match all the possible substrings with the remainder
823
  // of the pattern.
824
0
  if (pattern[0] == '*') {
825
    // Collapse duplicate wild cards (********** into *) so that the
826
    // method does not recurse unnecessarily. http://crbug.com/52839
827
0
    EatWildcard(&next_pattern, pattern_end, next);
828
829
0
    while (eval != eval_end) {
830
0
      if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
831
0
                        depth + 1, next))
832
0
        return true;
833
0
      eval++;
834
0
    }
835
836
    // We reached the end of the string, let see if the pattern contains only
837
    // wildcards.
838
0
    if (eval == eval_end) {
839
0
      EatWildcard(&pattern, pattern_end, next);
840
0
      if (pattern != pattern_end)
841
0
        return false;
842
0
      return true;
843
0
    }
844
0
  }
845
846
0
  return false;
847
0
}
Unexecuted instantiation: string_util.cc:bool MatchPatternT<char, NextCharUTF8>(char const*, char const*, char const*, char const*, int, NextCharUTF8)
Unexecuted instantiation: string_util.cc:bool MatchPatternT<unsigned short, NextCharUTF16>(unsigned short const*, unsigned short const*, unsigned short const*, unsigned short const*, int, NextCharUTF16)
848
849
struct NextCharUTF8 {
850
0
  base_icu::UChar32 operator()(const char** p, const char* end) {
851
0
    base_icu::UChar32 c;
852
0
    int offset = 0;
853
0
    CBU8_NEXT(*p, offset, end - *p, c);
854
0
    *p += offset;
855
0
    return c;
856
0
  }
857
};
858
859
struct NextCharUTF16 {
860
0
  base_icu::UChar32 operator()(const char16** p, const char16* end) {
861
0
    base_icu::UChar32 c;
862
0
    int offset = 0;
863
0
    CBU16_NEXT(*p, offset, end - *p, c);
864
0
    *p += offset;
865
0
    return c;
866
0
  }
867
};
868
869
bool MatchPattern(const butil::StringPiece& eval,
870
0
                  const butil::StringPiece& pattern) {
871
0
  return MatchPatternT(eval.data(), eval.data() + eval.size(),
872
0
                       pattern.data(), pattern.data() + pattern.size(),
873
0
                       0, NextCharUTF8());
874
0
}
875
876
0
bool MatchPattern(const string16& eval, const string16& pattern) {
877
0
  return MatchPatternT(eval.c_str(), eval.c_str() + eval.size(),
878
0
                       pattern.c_str(), pattern.c_str() + pattern.size(),
879
0
                       0, NextCharUTF16());
880
0
}
881
882
// The following code is compatible with the OpenBSD lcpy interface.  See:
883
//   http://www.gratisoft.us/todd/papers/strlcpy.html
884
//   ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
885
886
namespace {
887
888
template <typename CHAR>
889
0
size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
890
0
  for (size_t i = 0; i < dst_size; ++i) {
891
0
    if ((dst[i] = src[i]) == 0)  // We hit and copied the terminating NULL.
892
0
      return i;
893
0
  }
894
895
  // We were left off at dst_size.  We over copied 1 byte.  Null terminate.
896
0
  if (dst_size != 0)
897
0
    dst[dst_size - 1] = 0;
898
899
  // Count the rest of the |src|, and return it's length in characters.
900
0
  while (src[dst_size]) ++dst_size;
901
0
  return dst_size;
902
0
}
Unexecuted instantiation: string_util.cc:unsigned long (anonymous namespace)::lcpyT<char>(char*, char const*, unsigned long)
Unexecuted instantiation: string_util.cc:unsigned long (anonymous namespace)::lcpyT<wchar_t>(wchar_t*, wchar_t const*, unsigned long)
903
904
}  // namespace
905
906
0
size_t butil::strlcpy(char* dst, const char* src, size_t dst_size) {
907
0
  return lcpyT<char>(dst, src, dst_size);
908
0
}
909
0
size_t butil::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
910
0
  return lcpyT<wchar_t>(dst, src, dst_size);
911
0
}