Coverage Report

Created: 2026-06-30 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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