/src/log4cplus/src/stringhelper.cxx
Line | Count | Source |
1 | | // Module: Log4CPLUS |
2 | | // File: stringhelper.cxx |
3 | | // Created: 4/2003 |
4 | | // Author: Tad E. Smith |
5 | | // |
6 | | // |
7 | | // Copyright 2003-2017 Tad E. Smith |
8 | | // |
9 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
10 | | // you may not use this file except in compliance with the License. |
11 | | // You may obtain a copy of the License at |
12 | | // |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // |
15 | | // Unless required by applicable law or agreed to in writing, software |
16 | | // distributed under the License is distributed on an "AS IS" BASIS, |
17 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
18 | | // See the License for the specific language governing permissions and |
19 | | // limitations under the License. |
20 | | |
21 | | #include <log4cplus/helpers/stringhelper.h> |
22 | | #include <log4cplus/streams.h> |
23 | | #include <log4cplus/internal/internal.h> |
24 | | |
25 | | #include <iterator> |
26 | | #include <algorithm> |
27 | | #include <cstring> |
28 | | #include <cwchar> |
29 | | #include <cwctype> |
30 | | #include <cctype> |
31 | | #include <cassert> |
32 | | |
33 | | #if defined (LOG4CPLUS_WITH_UNIT_TESTS) |
34 | | #include <catch.hpp> |
35 | | #include <limits> |
36 | | #endif |
37 | | |
38 | | |
39 | | namespace log4cplus |
40 | | { |
41 | | |
42 | | namespace internal |
43 | | { |
44 | | |
45 | | log4cplus::tstring const empty_str; |
46 | | |
47 | | } // namespace internal |
48 | | |
49 | | } // namespace log4cplus |
50 | | |
51 | | |
52 | | ////////////////////////////////////////////////////////////////////////////// |
53 | | // Global Methods |
54 | | ////////////////////////////////////////////////////////////////////////////// |
55 | | |
56 | | #if defined (UNICODE) && defined (LOG4CPLUS_ENABLE_GLOBAL_C_STRING_STREAM_INSERTER) |
57 | | |
58 | | log4cplus::tostream& |
59 | | operator <<(log4cplus::tostream& stream, const char* str) |
60 | | { |
61 | | return (stream << log4cplus::helpers::towstring(str)); |
62 | | } |
63 | | |
64 | | #endif |
65 | | |
66 | | |
67 | | namespace log4cplus |
68 | | { |
69 | | |
70 | | namespace helpers |
71 | | { |
72 | | |
73 | | |
74 | | void |
75 | | clear_mbstate (std::mbstate_t & mbs) |
76 | 0 | { |
77 | | // Initialize/clear mbstate_t type. |
78 | | // XXX: This is just a hack that works. The shape of mbstate_t varies |
79 | | // from single unsigned to char[128]. Without some sort of initialization |
80 | | // the codecvt::in/out methods randomly fail because the initial state is |
81 | | // random/invalid. |
82 | 0 | std::memset (&mbs, 0, sizeof (std::mbstate_t)); |
83 | 0 | } |
84 | | |
85 | | |
86 | | #if defined (LOG4CPLUS_POOR_MANS_CHCONV) |
87 | | |
88 | | static |
89 | | void |
90 | | tostring_internal (std::string & ret, wchar_t const * src, std::size_t size) |
91 | 0 | { |
92 | 0 | ret.resize(size); |
93 | 0 | for (std::size_t i = 0; i < size; ++i) |
94 | 0 | { |
95 | 0 | std::char_traits<wchar_t>::int_type src_int |
96 | 0 | = std::char_traits<wchar_t>::to_int_type (src[i]); |
97 | 0 | ret[i] = src_int <= 127 |
98 | 0 | ? std::char_traits<char>::to_char_type (src_int) |
99 | 0 | : '?'; |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | |
104 | | std::string |
105 | | tostring(const std::wstring& src) |
106 | 0 | { |
107 | 0 | std::string ret; |
108 | 0 | tostring_internal (ret, src.c_str (), src.size ()); |
109 | 0 | return ret; |
110 | 0 | } |
111 | | |
112 | | |
113 | | std::string |
114 | | tostring(wchar_t const * src) |
115 | 0 | { |
116 | 0 | assert (src); |
117 | 0 | std::string ret; |
118 | 0 | tostring_internal (ret, src, std::wcslen (src)); |
119 | 0 | return ret; |
120 | 0 | } |
121 | | |
122 | | |
123 | | static |
124 | | void |
125 | | towstring_internal (std::wstring & ret, char const * src, std::size_t size) |
126 | 0 | { |
127 | 0 | ret.resize(size); |
128 | 0 | for (std::size_t i = 0; i < size; ++i) |
129 | 0 | { |
130 | 0 | std::char_traits<char>::int_type src_int |
131 | 0 | = std::char_traits<char>::to_int_type (src[i]); |
132 | 0 | ret[i] = src_int <= 127 |
133 | 0 | ? std::char_traits<wchar_t>::to_char_type (src_int) |
134 | 0 | : L'?'; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | |
139 | | std::wstring |
140 | | towstring(const std::string& src) |
141 | 0 | { |
142 | 0 | std::wstring ret; |
143 | 0 | towstring_internal (ret, src.c_str (), src.size ()); |
144 | 0 | return ret; |
145 | 0 | } |
146 | | |
147 | | |
148 | | std::wstring |
149 | | towstring(char const * src) |
150 | 0 | { |
151 | 0 | assert (src); |
152 | 0 | std::wstring ret; |
153 | 0 | towstring_internal (ret, src, std::strlen (src)); |
154 | 0 | return ret; |
155 | 0 | } |
156 | | |
157 | | #endif // LOG4CPLUS_POOR_MANS_CHCONV |
158 | | |
159 | | |
160 | | namespace |
161 | | { |
162 | | |
163 | | |
164 | | struct toupper_func |
165 | | { |
166 | | tchar |
167 | | operator () (tchar ch) const |
168 | 762k | { |
169 | 762k | return std::char_traits<tchar>::to_char_type ( |
170 | | #ifdef UNICODE |
171 | | std::towupper |
172 | | #else |
173 | 762k | std::toupper |
174 | 762k | #endif |
175 | 762k | (std::char_traits<tchar>::to_int_type (ch))); |
176 | 762k | } |
177 | | }; |
178 | | |
179 | | |
180 | | struct tolower_func |
181 | | { |
182 | | tchar |
183 | | operator () (tchar ch) const |
184 | 152k | { |
185 | 152k | return std::char_traits<tchar>::to_char_type ( |
186 | | #ifdef UNICODE |
187 | | std::towlower |
188 | | #else |
189 | 152k | std::tolower |
190 | 152k | #endif |
191 | 152k | (std::char_traits<tchar>::to_int_type (ch))); |
192 | 152k | } |
193 | | }; |
194 | | |
195 | | |
196 | | } // namespace |
197 | | |
198 | | |
199 | | tchar |
200 | | toUpper (tchar ch) |
201 | 0 | { |
202 | 0 | return toupper_func () (ch); |
203 | 0 | } |
204 | | |
205 | | |
206 | | tstring |
207 | | toUpper(const tstring& s) |
208 | 152k | { |
209 | 152k | tstring ret; |
210 | 152k | std::transform(s.begin(), s.end(), std::back_inserter (ret), |
211 | 152k | toupper_func ()); |
212 | 152k | return ret; |
213 | 152k | } |
214 | | |
215 | | |
216 | | tchar |
217 | | toLower (tchar ch) |
218 | 0 | { |
219 | 0 | return tolower_func () (ch); |
220 | 0 | } |
221 | | |
222 | | |
223 | | tstring |
224 | | toLower(const tstring& s) |
225 | 152k | { |
226 | 152k | tstring ret; |
227 | 152k | std::transform(s.begin(), s.end(), std::back_inserter (ret), |
228 | 152k | tolower_func ()); |
229 | 152k | return ret; |
230 | 152k | } |
231 | | |
232 | | |
233 | | #if defined (LOG4CPLUS_WITH_UNIT_TESTS) |
234 | | |
235 | | namespace |
236 | | { |
237 | | |
238 | | template <typename IntType> |
239 | | struct test |
240 | | { |
241 | | using limits = std::numeric_limits<IntType>; |
242 | | |
243 | | static void run_one (IntType value = 123) |
244 | | { |
245 | | tostringstream oss; |
246 | | oss.imbue (std::locale ("C")); |
247 | | oss << +value; |
248 | | |
249 | | CATCH_REQUIRE (convertIntegerToString (value) == oss.str ()); |
250 | | } |
251 | | |
252 | | static void run () |
253 | | { |
254 | | test<IntType>::run_one (); |
255 | | test<IntType>::run_one (limits::min ()); |
256 | | test<IntType>::run_one (limits::max ()); |
257 | | } |
258 | | }; |
259 | | |
260 | | } // namespace |
261 | | |
262 | | |
263 | | CATCH_TEST_CASE( "Strings helpers", "[strings]" ) |
264 | | { |
265 | | CATCH_SECTION ("empty_str is empty") |
266 | | { |
267 | | CATCH_REQUIRE (internal::empty_str.empty ()); |
268 | | } |
269 | | |
270 | | CATCH_SECTION ("tokenize") |
271 | | { |
272 | | std::vector<tstring> tokens; |
273 | | |
274 | | CATCH_SECTION ("collapse tokens") |
275 | | { |
276 | | CATCH_SECTION ("1") |
277 | | { |
278 | | tstring const str = LOG4CPLUS_TEXT ("1"); |
279 | | std::vector<tstring> const expected_tokens = { |
280 | | { LOG4CPLUS_TEXT ("1") } }; |
281 | | tokenize (str, LOG4CPLUS_TEXT (','), |
282 | | std::back_inserter (tokens)); |
283 | | CATCH_REQUIRE (tokens == expected_tokens); |
284 | | } |
285 | | |
286 | | CATCH_SECTION ("1,2") |
287 | | { |
288 | | tstring const str = LOG4CPLUS_TEXT ("1,2"); |
289 | | std::vector<tstring> const expected_tokens = { |
290 | | { LOG4CPLUS_TEXT ("1") }, |
291 | | { LOG4CPLUS_TEXT ("2") } }; |
292 | | tokenize (str, LOG4CPLUS_TEXT (','), |
293 | | std::back_inserter (tokens), false); |
294 | | CATCH_REQUIRE (tokens == expected_tokens); |
295 | | } |
296 | | |
297 | | CATCH_SECTION ("1,2,3") |
298 | | { |
299 | | tstring const str = LOG4CPLUS_TEXT ("1,2,3"); |
300 | | std::vector<tstring> const expected_tokens = { |
301 | | { LOG4CPLUS_TEXT ("1") }, |
302 | | { LOG4CPLUS_TEXT ("2") }, |
303 | | { LOG4CPLUS_TEXT ("3") } }; |
304 | | tokenize (str, LOG4CPLUS_TEXT (','), |
305 | | std::back_inserter (tokens)); |
306 | | CATCH_REQUIRE (tokens == expected_tokens); |
307 | | } |
308 | | |
309 | | CATCH_SECTION ("1,,2,,,3") |
310 | | { |
311 | | tstring const str = LOG4CPLUS_TEXT ("1,,2,,,3"); |
312 | | std::vector<tstring> const expected_tokens = { |
313 | | { LOG4CPLUS_TEXT ("1") }, |
314 | | { LOG4CPLUS_TEXT ("2") }, |
315 | | { LOG4CPLUS_TEXT ("3") } }; |
316 | | tokenize (str, LOG4CPLUS_TEXT (','), |
317 | | std::back_inserter (tokens)); |
318 | | CATCH_REQUIRE (tokens == expected_tokens); |
319 | | } |
320 | | |
321 | | CATCH_SECTION ("1,,2,,,3,") |
322 | | { |
323 | | tstring const str = LOG4CPLUS_TEXT ("1,,2,,,3,"); |
324 | | std::vector<tstring> const expected_tokens = { |
325 | | { LOG4CPLUS_TEXT ("1") }, |
326 | | { LOG4CPLUS_TEXT ("2") }, |
327 | | { LOG4CPLUS_TEXT ("3") } }; |
328 | | tokenize (str, LOG4CPLUS_TEXT (','), std::back_inserter (tokens)); |
329 | | CATCH_REQUIRE (tokens == expected_tokens); |
330 | | } |
331 | | |
332 | | CATCH_SECTION ("1,,2,,,3,,") |
333 | | { |
334 | | tstring const str = LOG4CPLUS_TEXT ("1,,2,,,3,,"); |
335 | | std::vector<tstring> const expected_tokens = { |
336 | | { LOG4CPLUS_TEXT ("1") }, |
337 | | { LOG4CPLUS_TEXT ("2") }, |
338 | | { LOG4CPLUS_TEXT ("3") } }; |
339 | | tokenize (str, LOG4CPLUS_TEXT (','), std::back_inserter (tokens)); |
340 | | CATCH_REQUIRE (tokens == expected_tokens); |
341 | | } |
342 | | } |
343 | | |
344 | | CATCH_SECTION ("do not collapse tokens") |
345 | | { |
346 | | CATCH_SECTION ("1") |
347 | | { |
348 | | tstring const str = LOG4CPLUS_TEXT ("1"); |
349 | | std::vector<tstring> const expected_tokens = { |
350 | | { LOG4CPLUS_TEXT ("1") } }; |
351 | | tokenize (str, LOG4CPLUS_TEXT (','), |
352 | | std::back_inserter (tokens)); |
353 | | CATCH_REQUIRE (tokens == expected_tokens); |
354 | | } |
355 | | |
356 | | CATCH_SECTION ("1,2") |
357 | | { |
358 | | tstring const str = LOG4CPLUS_TEXT ("1,2"); |
359 | | std::vector<tstring> const expected_tokens = { |
360 | | { LOG4CPLUS_TEXT ("1") }, |
361 | | { LOG4CPLUS_TEXT ("2") } }; |
362 | | tokenize (str, LOG4CPLUS_TEXT (','), |
363 | | std::back_inserter (tokens), false); |
364 | | CATCH_REQUIRE (tokens == expected_tokens); |
365 | | } |
366 | | |
367 | | CATCH_SECTION ("1,2,3") |
368 | | { |
369 | | tstring const str = LOG4CPLUS_TEXT ("1,2,3"); |
370 | | std::vector<tstring> const expected_tokens = { |
371 | | { LOG4CPLUS_TEXT ("1") }, |
372 | | { LOG4CPLUS_TEXT ("2") }, |
373 | | { LOG4CPLUS_TEXT ("3") } }; |
374 | | tokenize (str, LOG4CPLUS_TEXT (','), |
375 | | std::back_inserter (tokens), false); |
376 | | CATCH_REQUIRE (tokens == expected_tokens); |
377 | | } |
378 | | |
379 | | CATCH_SECTION ("1,,2,,,3") |
380 | | { |
381 | | tstring const str = LOG4CPLUS_TEXT ("1,,2,,,3"); |
382 | | std::vector<tstring> const expected_tokens = { |
383 | | { LOG4CPLUS_TEXT ("1") }, |
384 | | { LOG4CPLUS_TEXT ("") }, |
385 | | { LOG4CPLUS_TEXT ("2") }, |
386 | | { LOG4CPLUS_TEXT ("") }, |
387 | | { LOG4CPLUS_TEXT ("") }, |
388 | | { LOG4CPLUS_TEXT ("3") } }; |
389 | | tokenize (str, LOG4CPLUS_TEXT (','), |
390 | | std::back_inserter (tokens), false); |
391 | | CATCH_REQUIRE (tokens == expected_tokens); |
392 | | } |
393 | | |
394 | | |
395 | | CATCH_SECTION (",1,,2,,,3,") |
396 | | { |
397 | | tstring const str = LOG4CPLUS_TEXT (",1,,2,,,3,"); |
398 | | std::vector<tstring> const expected_tokens = { |
399 | | { LOG4CPLUS_TEXT ("") }, |
400 | | { LOG4CPLUS_TEXT ("1") }, |
401 | | { LOG4CPLUS_TEXT ("") }, |
402 | | { LOG4CPLUS_TEXT ("2") }, |
403 | | { LOG4CPLUS_TEXT ("") }, |
404 | | { LOG4CPLUS_TEXT ("") }, |
405 | | { LOG4CPLUS_TEXT ("3") }, |
406 | | { LOG4CPLUS_TEXT ("") } }; |
407 | | tokenize (str, LOG4CPLUS_TEXT (','), |
408 | | std::back_inserter (tokens), false); |
409 | | CATCH_REQUIRE (tokens == expected_tokens); |
410 | | } |
411 | | |
412 | | CATCH_SECTION (",1,,2,,,3,,") |
413 | | { |
414 | | tstring const str = LOG4CPLUS_TEXT (",1,,2,,,3,,"); |
415 | | std::vector<tstring> const expected_tokens = { |
416 | | { LOG4CPLUS_TEXT ("") }, |
417 | | { LOG4CPLUS_TEXT ("1") }, |
418 | | { LOG4CPLUS_TEXT ("") }, |
419 | | { LOG4CPLUS_TEXT ("2") }, |
420 | | { LOG4CPLUS_TEXT ("") }, |
421 | | { LOG4CPLUS_TEXT ("") }, |
422 | | { LOG4CPLUS_TEXT ("3") }, |
423 | | { LOG4CPLUS_TEXT ("") }, |
424 | | { LOG4CPLUS_TEXT ("") } }; |
425 | | tokenize (str, LOG4CPLUS_TEXT (','), |
426 | | std::back_inserter (tokens), false); |
427 | | CATCH_REQUIRE (tokens == expected_tokens); |
428 | | } |
429 | | |
430 | | CATCH_SECTION (",,1,,2,,,3,,") |
431 | | { |
432 | | tstring const str = LOG4CPLUS_TEXT (",,1,,2,,,3,,"); |
433 | | std::vector<tstring> const expected_tokens = { |
434 | | { LOG4CPLUS_TEXT ("") }, |
435 | | { LOG4CPLUS_TEXT ("") }, |
436 | | { LOG4CPLUS_TEXT ("1") }, |
437 | | { LOG4CPLUS_TEXT ("") }, |
438 | | { LOG4CPLUS_TEXT ("2") }, |
439 | | { LOG4CPLUS_TEXT ("") }, |
440 | | { LOG4CPLUS_TEXT ("") }, |
441 | | { LOG4CPLUS_TEXT ("3") }, |
442 | | { LOG4CPLUS_TEXT ("") }, |
443 | | { LOG4CPLUS_TEXT ("") } }; |
444 | | tokenize (str, LOG4CPLUS_TEXT (','), |
445 | | std::back_inserter (tokens), false); |
446 | | CATCH_REQUIRE (tokens == expected_tokens); |
447 | | } |
448 | | } |
449 | | } |
450 | | |
451 | | CATCH_SECTION ("convert integer to string") |
452 | | { |
453 | | |
454 | | #define LOG4CPLUS_GEN_TEST(TYPE) \ |
455 | | CATCH_SECTION (#TYPE) \ |
456 | | { \ |
457 | | test<TYPE>::run (); \ |
458 | | } |
459 | | |
460 | | LOG4CPLUS_GEN_TEST (char) |
461 | | LOG4CPLUS_GEN_TEST (unsigned char) |
462 | | LOG4CPLUS_GEN_TEST (signed char) |
463 | | LOG4CPLUS_GEN_TEST (short) |
464 | | LOG4CPLUS_GEN_TEST (unsigned short) |
465 | | LOG4CPLUS_GEN_TEST (int) |
466 | | LOG4CPLUS_GEN_TEST (unsigned int) |
467 | | LOG4CPLUS_GEN_TEST (long) |
468 | | LOG4CPLUS_GEN_TEST (unsigned long) |
469 | | LOG4CPLUS_GEN_TEST (long long) |
470 | | LOG4CPLUS_GEN_TEST (unsigned long long) |
471 | | #undef LOG4CPLUS_GEN_TEST |
472 | | } |
473 | | |
474 | | CATCH_SECTION ("join strings") |
475 | | { |
476 | | tstring const items[] = { |
477 | | { LOG4CPLUS_TEXT ("1") }, |
478 | | { LOG4CPLUS_TEXT ("2") }, |
479 | | { LOG4CPLUS_TEXT ("3") } }; |
480 | | tstring result; |
481 | | |
482 | | CATCH_SECTION ("1") |
483 | | { |
484 | | helpers::join (result, std::begin (items), items + 1, |
485 | | LOG4CPLUS_TEXT (',')); |
486 | | CATCH_REQUIRE (result == LOG4CPLUS_TEXT ("1")); |
487 | | } |
488 | | |
489 | | CATCH_SECTION ("1,2") |
490 | | { |
491 | | helpers::join (result, std::begin (items), items + 2, |
492 | | LOG4CPLUS_TEXT (",")); |
493 | | CATCH_REQUIRE (result == LOG4CPLUS_TEXT ("1,2")); |
494 | | } |
495 | | |
496 | | CATCH_SECTION ("1,2,3") |
497 | | { |
498 | | helpers::join (result, std::begin (items), items + 3, |
499 | | LOG4CPLUS_TEXT (',')); |
500 | | CATCH_REQUIRE (result == LOG4CPLUS_TEXT ("1,2,3")); |
501 | | } |
502 | | } |
503 | | } |
504 | | #endif |
505 | | |
506 | | |
507 | | |
508 | | } // namespace helpers |
509 | | |
510 | | } // namespace log4cplus |