/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 | } |