• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #853

19 Dec 2022 01:01AM UTC coverage: 86.287% (+0.4%) from 85.912%
#853

push

StellarBot
Merge #6109

6109: Modernize serialization module r=hkaiser a=hkaiser

- flyby separate serialization of Boost types

working towards https://github.com/STEllAR-GROUP/hpx/issues/5497

Co-authored-by: Hartmut Kaiser <hartmut.kaiser@gmail.com>

53 of 53 new or added lines in 6 files covered. (100.0%)

173939 of 201582 relevant lines covered (86.29%)

1931657.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.91
/libs/core/ini/src/ini.cpp
1
//  Copyright (c) 2005-2007 Andre Merzky
2
//  Copyright (c) 2005-2018 Hartmut Kaiser
3
//  Copyright (c)      2011 Bryce Lelbach
4
//
5
//  SPDX-License-Identifier: BSL-1.0
6
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
7
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8

9
#include <hpx/config.hpp>
10

11
// System Header Files
12
#include <cerrno>
13
#include <cstddef>
14
#include <cstdint>
15
#include <cstdio>
16
#include <cstdlib>
17
#include <cstring>
18
#include <fstream>
19
#include <iostream>
20
#include <list>
21
#include <mutex>
22
#include <regex>
23
#include <string>
24
#include <type_traits>
25
#include <utility>
26
#include <vector>
27

28
#include <hpx/assert.hpp>
29
#include <hpx/ini/ini.hpp>
30
#include <hpx/modules/errors.hpp>
31
#include <hpx/serialization/map.hpp>
32
#include <hpx/serialization/serialize.hpp>
33
#include <hpx/string_util/classification.hpp>
34
#include <hpx/string_util/split.hpp>
35
#include <hpx/thread_support/unlock_guard.hpp>
36

37
#ifdef __APPLE__
38
#include <crt_externs.h>
39
#define environ (*_NSGetEnviron())
40
#elif !defined(HPX_WINDOWS)
41
extern char** environ;
42
#endif
43

44
///////////////////////////////////////////////////////////////////////////////
45
namespace hpx { namespace util {
46

47
    ///////////////////////////////////////////////////////////////////////////////
48
    // example ini line: line # comment
49
    const char pattern_comment[] = "^([^#]*)(#.*)$";
50
    ///////////////////////////////////////////////////////////////////////////////
51

52
    namespace detail {
53
        ///////////////////////////////////////////////////////////////////////////
54
        inline std::string trim_whitespace(std::string const& s)
2,915,983✔
55
        {
56
            typedef std::string::size_type size_type;
57

58
            size_type first = s.find_first_not_of(" \t\r\n");
2,915,983✔
59
            if (std::string::npos == first)
2,915,983✔
60
                return (std::string());
7,071✔
61

62
            size_type last = s.find_last_not_of(" \t\r\n");
2,908,912✔
63
            return s.substr(first, last - first + 1);
2,908,912✔
64
        }
2,915,983✔
65

66
        // MSVC: using std::string::replace triggers ASAN reports
67
        std::string replace_substr(std::string const& str,
299,049✔
68
            std::size_t start_pos, std::size_t oldlen, char const* newstr)
69
        {
70
            std::string result(str.substr(0, start_pos));
299,049✔
71
            result += newstr;
299,049✔
72
            result += str.substr(start_pos + oldlen);
299,049✔
73
            return result;
299,049✔
74
        }
299,049✔
75

76
        std::string replace_substr(std::string const& str,
291,735✔
77
            std::size_t start_pos, std::size_t oldlen,
78
            std::string const& newstr)
79
        {
80
            return replace_substr(str, start_pos, oldlen, newstr.c_str());
291,735✔
81
        }
82
    }    // namespace detail
83

84
    ///////////////////////////////////////////////////////////////////////////////
85
    section::section()
1,126,996✔
86
      : root_(this_())
1,126,996✔
87
    {
88
    }
1,126,996✔
89

90
    section::section(std::string const& filename, section* root)
×
91
      : root_(nullptr != root ? root : this_())
×
92
      , name_(filename)
×
93
    {
94
        read(filename);
×
95
    }
×
96

97
    section::section(const section& in)
2,042,126✔
98
      : root_(this_())
1,021,063✔
99
      , name_(in.get_name())
1,021,063✔
100
      , parent_name_(in.get_parent_name())
1,021,063✔
101
    {
102
        entry_map const& e = in.get_entries();
1,021,063✔
103
        entry_map::const_iterator end = e.end();
1,021,063✔
104
        for (entry_map::const_iterator i = e.begin(); i != end; ++i)
5,482,612✔
105
            add_entry(i->first, i->second);
4,461,549✔
106

107
        section_map s = in.get_sections();
1,021,063✔
108
        section_map::iterator send = s.end();
1,021,063✔
109
        for (section_map::iterator si = s.begin(); si != send; ++si)
1,537,675✔
110
            add_section(si->first, si->second, get_root());
516,612✔
111
    }
1,021,063✔
112

113
    section& section::operator=(section const& rhs)
1,799✔
114
    {
115
        if (this != &rhs)
1,799✔
116
        {
117
            std::unique_lock<mutex_type> l(mtx_);
1,799✔
118

119
            root_ = this;
1,799✔
120
            parent_name_ = rhs.get_parent_name();
1,799✔
121
            name_ = rhs.get_name();
1,799✔
122

123
            entry_map const& e = rhs.get_entries();
1,799✔
124
            entry_map::const_iterator end = e.end();
1,799✔
125
            for (entry_map::const_iterator i = e.begin(); i != end; ++i)
5,279✔
126
                add_entry(l, i->first, i->first, i->second);
3,480✔
127

128
            section_map s = rhs.get_sections();
1,799✔
129
            section_map::iterator send = s.end();
1,799✔
130
            for (section_map::iterator si = s.begin(); si != send; ++si)
5,456✔
131
                add_section(l, si->first, si->second, get_root());
3,657✔
132
        }
1,799✔
133
        return *this;
1,799✔
134
    }
×
135

136
    section& section::clone_from(section const& rhs, section* root)
1,068,276✔
137
    {
138
        if (this != &rhs)
1,068,276✔
139
        {
140
            std::unique_lock<mutex_type> l(mtx_);
1,068,276✔
141

142
            root_ = root ? root : this;
1,068,276✔
143
            parent_name_ = rhs.get_parent_name();
1,068,276✔
144
            name_ = rhs.get_name();
1,068,276✔
145

146
            entry_map const& e = rhs.get_entries();
1,068,276✔
147
            entry_map::const_iterator end = e.end();
1,068,276✔
148
            for (entry_map::const_iterator i = e.begin(); i != end; ++i)
5,529,825✔
149
                add_entry(l, i->first, i->first, i->second);
4,461,549✔
150

151
            section_map s = rhs.get_sections();
1,068,276✔
152
            section_map::iterator send = s.end();
1,068,276✔
153
            for (section_map::iterator si = s.begin(); si != send; ++si)
1,561,161✔
154
                add_section(l, si->first, si->second, get_root());
492,885✔
155
        }
1,068,276✔
156
        return *this;
1,068,276✔
157
    }
×
158

159
    void section::read(std::string const& filename)
×
160
    {
161
#if defined(__AIX__) && defined(__GNUC__)
162
        // NEVER ask why... seems to be some weird stdlib initialization problem
163
        // If you don't call getline() here the while(getline...) loop below will
164
        // crash with a bad_cast exception. Stupid AIX...
165
        std::string l1;
166
        std::ifstream i1;
167
        i1.open(filename.c_str(), std::ios::in);
168
        std::getline(i1, l1);
169
        i1.close();
170
#endif
171

172
        // build ini - open file and parse each line
173
        std::ifstream input(filename.c_str(), std::ios::in);
×
174
        if (!input.is_open())
×
175
            line_msg("Cannot open file: ", filename);
×
176

177
        // read file
178
        std::string line;
×
179
        std::vector<std::string> lines;
×
180
        while (std::getline(input, line))
×
181
            lines.push_back(line);
×
182

183
        // parse file
184
        parse(filename, lines, false);
×
185
    }
×
186

187
    bool force_entry(std::string& str)
914,378✔
188
    {
189
        std::string::size_type p = str.find_last_of('!');
914,378✔
190
        if (p != std::string::npos &&
914,378✔
191
            str.find_first_not_of(" \t", p + 1) == std::string::npos)
24,891✔
192
        {
193
            str = str.substr(0, p);    // remove forcing modifier ('!')
24,891✔
194
            return true;
24,891✔
195
        }
196
        return false;
889,487✔
197
    }
914,378✔
198

199
    // parse file
200
    void section::parse(std::string const& sourcename,
19,199✔
201
        std::vector<std::string> const& lines, bool verify_existing,
202
        bool weed_out_comments, bool replace_existing)
203
    {
204
        int linenum = 0;
19,199✔
205
        section* current = this;
19,199✔
206

207
        std::regex regex_comment(pattern_comment, std::regex_constants::icase);
19,199✔
208

209
        std::vector<std::string>::const_iterator end = lines.end();
19,199✔
210
        for (std::vector<std::string>::const_iterator it = lines.begin();
1,106,426✔
211
             it != end; ++it)
1,106,426✔
212
        {
213
            ++linenum;
1,087,227✔
214

215
            // remove trailing new lines and white spaces
216
            std::string line(detail::trim_whitespace(*it));
1,087,227✔
217

218
            // skip if empty line
219
            if (line.empty())
1,087,227✔
220
                continue;
2,976✔
221

222
            // weed out comments
223
            if (weed_out_comments)
1,084,251✔
224
            {
225
                std::smatch what_comment;
592✔
226
                if (std::regex_match(line, what_comment, regex_comment))
592✔
227
                {
228
                    HPX_ASSERT(3 == what_comment.size());
×
229

230
                    line = detail::trim_whitespace(what_comment[1]);
×
231
                    if (line.empty())
×
232
                        continue;
×
233
                }
×
234
            }
592✔
235
            // no comments anymore: line is either section, key=val,
236
            // or garbage/empty
237

238
            // Check if we have a section.
239
            // Example: [sec.ssec]
240
            if (line.front() == '[' && line.back() == ']')
1,084,251✔
241
            {
242
                current = this;    // start adding sections at the root
169,873✔
243

244
                // got the section name. It might be hierarchical, so split it up, and
245
                // for each elem, check if we have it.  If not, create it, and add
246
                std::string sec_name(line.substr(1, line.size() - 2));
169,873✔
247
                std::string::size_type pos = 0;
169,873✔
248
                for (std::string::size_type pos1 = sec_name.find_first_of('.');
458,193✔
249
                     std::string::npos != pos1;
458,193✔
250
                     pos1 = sec_name.find_first_of('.', pos = pos1 + 1))
288,320✔
251
                {
252
                    current = current->add_section_if_new(
288,320✔
253
                        sec_name.substr(pos, pos1 - pos));
288,320✔
254
                }
288,320✔
255

256
                current = current->add_section_if_new(sec_name.substr(pos));
169,873✔
257
                continue;
258
            }
169,873✔
259

260
            // Check if we have a key=val entry...
261
            std::string::size_type assign_pos = line.find('=');
914,378✔
262
            if (assign_pos != std::string::npos)
914,378✔
263
            {
264
                std::string sec_key =
265
                    detail::trim_whitespace(line.substr(0, assign_pos));
914,378✔
266
                std::string value = detail::trim_whitespace(
914,378✔
267
                    line.substr(assign_pos + 1, line.size() - assign_pos - 1));
914,378✔
268

269
                section* s = current;    // save the section we're in
914,378✔
270
                current = this;          // start adding sections at the root
914,378✔
271

272
                std::string::size_type pos = 0;
914,378✔
273
                // Check if we have a qualified key name
274
                // Example: hpx.commandline.allow_unknown
275
                for (std::string::size_type dot_pos =
1,065,876✔
276
                         sec_key.find_first_of('.');
914,378✔
277
                     std::string::npos != dot_pos;
1,065,876✔
278
                     dot_pos = sec_key.find_first_of('.', pos = dot_pos + 1))
151,498✔
279
                {
280
                    current = current->add_section_if_new(
151,498✔
281
                        sec_key.substr(pos, dot_pos - pos));
151,498✔
282
                }
151,498✔
283
                // if we don't have section qualifiers, restore current...
284
                if (current == this)
914,378✔
285
                {
286
                    current = s;
825,106✔
287
                }
825,106✔
288

289
                std::string key = sec_key.substr(pos);
914,378✔
290

291
                // add key/val to this section
292
                std::unique_lock<mutex_type> l(current->mtx_);
914,378✔
293

294
                if (!force_entry(key) && verify_existing &&
978,759✔
295
                    !current->has_entry(l, key))
64,381✔
296
                {
297
                    line_msg("Attempt to initialize unknown entry: ",
×
298
                        sourcename, linenum, line);
×
299
                }
×
300

301
                if (replace_existing || !current->has_entry(l, key))
914,378✔
302
                {
303
                    current->add_entry(l, key, key, value);
467,008✔
304
                }
467,008✔
305

306
                // restore the old section
307
                current = s;
914,378✔
308
            }
914,378✔
309
            else
310
            {
311
                // Hmm, is not a section, is not an entry, is not empty - must be
312
                // an error!
313
                line_msg("Cannot parse line at: ", sourcename, linenum, line);
×
314
            }
315
        }
1,087,227✔
316
    }
19,199✔
317

318
    ///////////////////////////////////////////////////////////////////////////////
319
    void section::add_section(std::unique_lock<mutex_type>& /* l */,
1,068,276✔
320
        std::string const& sec_name, section& sec, section* root)
321
    {
322
        // setting name and root
323
        sec.name_ = sec_name;
1,068,276✔
324
        sec.parent_name_ = get_full_name();
1,068,276✔
325

326
        section& newsec = sections_[sec_name];
1,068,276✔
327
        newsec.clone_from(sec, (nullptr != root) ? root : get_root());
1,068,276✔
328
    }
1,068,276✔
329

330
    ///////////////////////////////////////////////////////////////////////////
331
    section* section::add_section_if_new(
611,445✔
332
        std::unique_lock<mutex_type>& l, std::string const& sec_name)
333
    {
334
        // do we know this one?
335
        if (!has_section(l, sec_name))
611,445✔
336
        {
337
            // no - add it!
338
            section sec;
55,122✔
339
            add_section(l, sec_name, sec, get_root());
55,122✔
340
        }
55,122✔
341

342
        return get_section(l, sec_name);
611,445✔
343
    }
×
344

345
    bool section::has_section(
737,516✔
346
        std::unique_lock<mutex_type>& l, std::string const& sec_name) const
347
    {
348
        std::string::size_type i = sec_name.find('.');
737,516✔
349
        if (i != std::string::npos)
737,516✔
350
        {
351
            std::string cor_sec_name = sec_name.substr(0, i);
74,791✔
352

353
            section_map::const_iterator it = sections_.find(cor_sec_name);
74,791✔
354
            if (it != sections_.end())
74,791✔
355
            {
356
                std::string sub_sec_name = sec_name.substr(i + 1);
74,791✔
357
                hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
74,791✔
358
                return (*it).second.has_section(sub_sec_name);
74,791✔
359
            }
74,791✔
360
            return false;
×
361
        }
74,791✔
362
        return sections_.find(sec_name) != sections_.end();
662,725✔
363
    }
737,516✔
364

365
    section* section::get_section(
671,804✔
366
        std::unique_lock<mutex_type>& l, std::string const& sec_name)
367
    {
368
        std::string::size_type i = sec_name.find('.');
671,804✔
369
        if (i != std::string::npos)
671,804✔
370
        {
371
            std::string cor_sec_name = sec_name.substr(0, i);
39,358✔
372
            section_map::iterator it = sections_.find(cor_sec_name);
39,358✔
373
            if (it != sections_.end())
39,358✔
374
            {
375
                std::string sub_sec_name = sec_name.substr(i + 1);
39,358✔
376
                hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
39,358✔
377
                return (*it).second.get_section(sub_sec_name);
39,358✔
378
            }
39,358✔
379

380
            std::string name(get_name());
×
381
            if (name.empty())
×
382
                name = "<root>";
×
383

384
            HPX_THROW_EXCEPTION(hpx::error::bad_parameter,
×
385
                "section::get_section", "No such section ({}) in section: {}",
386
                sec_name, name);
387
            return nullptr;
388
        }
39,358✔
389

390
        section_map::iterator it = sections_.find(sec_name);
632,446✔
391
        if (it != sections_.end())
632,446✔
392
            return &((*it).second);
632,446✔
393

394
        HPX_THROW_EXCEPTION(hpx::error::bad_parameter, "section::get_section",
×
395
            "No such section ({}) in section: {}", sec_name, get_name());
396
        return nullptr;
397
    }
671,804✔
398

399
    section const* section::get_section(
166,008✔
400
        std::unique_lock<mutex_type>& l, std::string const& sec_name) const
401
    {
402
        std::string::size_type i = sec_name.find('.');
166,008✔
403
        if (i != std::string::npos)
166,008✔
404
        {
405
            std::string cor_sec_name = sec_name.substr(0, i);
90,924✔
406
            section_map::const_iterator it = sections_.find(cor_sec_name);
90,924✔
407
            if (it != sections_.end())
90,924✔
408
            {
409
                std::string sub_sec_name = sec_name.substr(i + 1);
90,924✔
410
                hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
90,924✔
411
                return (*it).second.get_section(sub_sec_name);
90,924✔
412
            }
90,924✔
413

414
            std::string name(get_name());
×
415
            if (name.empty())
×
416
                name = "<root>";
×
417

418
            HPX_THROW_EXCEPTION(hpx::error::bad_parameter,
×
419
                "section::get_section", "No such section ({}) in section: {}",
420
                sec_name, name);
421
            return nullptr;
422
        }
90,924✔
423

424
        section_map::const_iterator it = sections_.find(sec_name);
75,084✔
425
        if (it != sections_.end())
75,084✔
426
            return &((*it).second);
75,084✔
427

428
        HPX_THROW_EXCEPTION(hpx::error::bad_parameter, "section::get_section",
×
429
            "No such section ({}) in section: {}", sec_name, get_name());
430
        return nullptr;
431
    }
166,008✔
432

433
    void section::add_entry(std::unique_lock<mutex_type>& l,
467,784✔
434
        std::string const& fullkey, std::string const& key, std::string val)
435
    {
436
        // first expand the full property name in the value (avoids infinite recursion)
437
        expand_only(
467,784✔
438
            l, val, std::string::size_type(-1), get_full_name() + "." + key);
467,784✔
439

440
        std::string::size_type i = key.find_last_of('.');
467,784✔
441
        if (i != std::string::npos)
467,784✔
442
        {
443
            section* current = root_;
20✔
444

445
            // make sure all sections in key exist
446
            std::string sec_name = key.substr(0, i);
20✔
447

448
            std::string::size_type pos = 0;
20✔
449
            for (std::string::size_type pos1 = sec_name.find_first_of('.');
44✔
450
                 std::string::npos != pos1;
44✔
451
                 pos1 = sec_name.find_first_of('.', pos = pos1 + 1))
24✔
452
            {
453
                current = current->add_section_if_new(
24✔
454
                    l, sec_name.substr(pos, pos1 - pos));
24✔
455
            }
24✔
456

457
            current = current->add_section_if_new(l, sec_name.substr(pos));
20✔
458

459
            // now add this entry to the section
460
            current->add_entry(l, fullkey, key.substr(i + 1), val);
20✔
461
        }
20✔
462
        else
463
        {
464
            entry_map::iterator it = entries_.find(key);
467,764✔
465
            if (it != entries_.end())
467,764✔
466
            {
467
                auto& e = it->second;
215,075✔
468
                e.first = HPX_MOVE(val);
215,075✔
469
                if (!e.second.empty())
215,075✔
470
                {
471
                    std::string value = e.first;
2✔
472
                    entry_changed_func f = e.second;
2✔
473

474
                    hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
2✔
475
                    f(fullkey, value);
2✔
476
                }
2✔
477
            }
215,075✔
478
            else
479
            {
480
                // just add this entry to the section
481
                entries_[key] = entry_type(val, entry_changed_func());
252,689✔
482
            }
483
        }
484
    }
467,784✔
485

486
    void section::add_entry(std::unique_lock<mutex_type>& l,
8,926,578✔
487
        std::string const& fullkey, std::string const& key,
488
        entry_type const& val)
489
    {
490
        std::string::size_type i = key.find_last_of('.');
8,926,578✔
491
        if (i != std::string::npos)
8,926,578✔
492
        {
493
            section* current = root_;
×
494

495
            // make sure all sections in key exist
496
            std::string sec_name = key.substr(0, i);
×
497

498
            std::string::size_type pos = 0;
×
499
            for (std::string::size_type pos1 = sec_name.find_first_of('.');
×
500
                 std::string::npos != pos1;
×
501
                 pos1 = sec_name.find_first_of('.', pos = pos1 + 1))
×
502
            {
503
                current = current->add_section_if_new(
×
504
                    l, sec_name.substr(pos, pos1 - pos));
×
505
            }
×
506

507
            current = current->add_section_if_new(l, sec_name.substr(pos));
×
508

509
            // now add this entry to the section
510
            current->add_entry(l, fullkey, key.substr(i + 1), val);
×
511
        }
×
512
        else
513
        {
514
            entry_map::iterator it = entries_.find(key);
8,926,578✔
515
            if (it != entries_.end())
8,926,578✔
516
            {
517
                it->second = val;
×
518
                if (!it->second.second.empty())
×
519
                {
520
                    std::string value = it->second.first;
×
521
                    entry_changed_func f = it->second.second;
×
522

523
                    hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
×
524
                    f(fullkey, value);
×
525
                }
×
526
            }
×
527
            else
528
            {
529
                // just add this entry to the section
530
                std::pair<entry_map::iterator, bool> p =
531
                    entries_.insert(entry_map::value_type(key, val));
8,926,578✔
532
                HPX_ASSERT(p.second);
8,926,578✔
533

534
                if (!p.first->second.second.empty())
8,926,578✔
535
                {
536
                    std::string key = p.first->first;
×
537
                    std::string value = p.first->second.first;
×
538
                    entry_changed_func f = p.first->second.second;
×
539

540
                    hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
×
541
                    f(key, value);
×
542
                }
×
543
            }
544
        }
545
    }
8,926,578✔
546

547
    ///////////////////////////////////////////////////////////////////////////////
548
    template <typename F1, typename F2>
549
    class compose_callback_impl
34✔
550
    {
551
    public:
552
        template <typename A1, typename A2>
553
        compose_callback_impl(A1&& f1, A2&& f2)
10✔
554
          : f1_(HPX_FORWARD(A1, f1))
10✔
555
          , f2_(HPX_FORWARD(A2, f2))
10✔
556
        {
557
        }
10✔
558

559
        void operator()(std::string const& k, std::string const& v) const
×
560
        {
561
            f1_(k, v);
×
562
            f2_(k, v);
×
563
        }
×
564

565
    private:
566
        F1 f1_;
567
        F2 f2_;
568
    };
569

570
    template <typename F1, typename F2>
571
    static HPX_FORCEINLINE
572
        hpx::function<void(std::string const&, std::string const&)>
573
        compose_callback(F1&& f1, F2&& f2)
570✔
574
    {
575
        if (!f1)
570✔
576
            return HPX_FORWARD(F2, f2);
×
577
        else if (!f2)
570✔
578
            return HPX_FORWARD(F1, f1);
560✔
579

580
        // otherwise create a combined callback
581
        typedef compose_callback_impl<typename std::decay<F1>::type,
582
            typename std::decay<F2>::type>
583
            result_type;
584
        return result_type(HPX_FORWARD(F1, f1), HPX_FORWARD(F2, f2));
10✔
585
    }
570✔
586

587
    void section::add_notification_callback(std::unique_lock<mutex_type>& l,
1,140✔
588
        std::string const& key, entry_changed_func const& callback)
589
    {
590
        std::string::size_type i = key.find_last_of('.');
1,140✔
591
        if (i != std::string::npos)
1,140✔
592
        {
593
            section* current = root_;
570✔
594

595
            // make sure all sections in key exist
596
            std::string sec_name = key.substr(0, i);
570✔
597

598
            std::string::size_type pos = 0;
570✔
599
            for (std::string::size_type pos1 = sec_name.find_first_of('.');
1,710✔
600
                 std::string::npos != pos1;
1,710✔
601
                 pos1 = sec_name.find_first_of('.', pos = pos1 + 1))
1,140✔
602
            {
603
                current = current->add_section_if_new(
1,140✔
604
                    l, sec_name.substr(pos, pos1 - pos));
1,140✔
605
            }
1,140✔
606

607
            current = current->add_section_if_new(l, sec_name.substr(pos));
570✔
608

609
            // now add this entry to the section
610
            current->add_notification_callback(l, key.substr(i + 1), callback);
570✔
611
        }
570✔
612
        else
613
        {
614
            // just add this entry to the section
615
            entry_map::iterator it = entries_.find(key);
570✔
616
            if (it != entries_.end())
570✔
617
            {
618
                it->second.second =
570✔
619
                    compose_callback(callback, it->second.second);
570✔
620
            }
570✔
621
            else
622
            {
623
                entries_[key] = entry_type("", callback);
×
624
            }
625
        }
626
    }
1,140✔
627

628
    bool section::has_entry(
732,228✔
629
        std::unique_lock<mutex_type>& l, std::string const& key) const
630
    {
631
        std::string::size_type i = key.find('.');
732,228✔
632
        if (i != std::string::npos)
732,228✔
633
        {
634
            std::string sub_sec = key.substr(0, i);
1✔
635
            std::string sub_key = key.substr(i + 1, key.size() - i);
1✔
636
            if (has_section(l, sub_sec))
1✔
637
            {
638
                section_map::const_iterator cit = sections_.find(sub_sec);
1✔
639
                HPX_ASSERT(cit != sections_.end());
1✔
640
                hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
1✔
641
                return (*cit).second.has_entry(sub_key);
1✔
642
            }
1✔
643
            return false;
×
644
        }
1✔
645
        return entries_.find(key) != entries_.end();
732,227✔
646
    }
732,228✔
647

648
    std::string section::get_entry(
71,257✔
649
        std::unique_lock<mutex_type>& l, std::string const& key) const
650
    {
651
        std::string::size_type i = key.find('.');
71,257✔
652
        if (i != std::string::npos)
71,257✔
653
        {
654
            std::string sub_sec = key.substr(0, i);
14,505✔
655
            std::string sub_key = key.substr(i + 1, key.size() - i);
14,505✔
656
            if (has_section(l, sub_sec))
14,505✔
657
            {
658
                section_map::const_iterator cit = sections_.find(sub_sec);
14,505✔
659
                HPX_ASSERT(cit != sections_.end());
14,505✔
660
                hpx::unlock_guard<std::unique_lock<mutex_type>> ul(l);
14,505✔
661
                return (*cit).second.get_entry(sub_key);
14,505✔
662
            }
14,505✔
663

664
            HPX_THROW_EXCEPTION(hpx::error::bad_parameter, "section::get_entry",
×
665
                "No such key ({}) in section: {}", key, get_name());
666
            return "";
667
        }
14,505✔
668

669
        if (entries_.find(key) != entries_.end())
56,752✔
670
        {
671
            entry_map::const_iterator cit = entries_.find(key);
56,752✔
672
            HPX_ASSERT(cit != entries_.end());
56,752✔
673
            return expand(l, (*cit).second.first);
56,752✔
674
        }
675

676
        HPX_THROW_EXCEPTION(hpx::error::bad_parameter, "section::get_entry",
×
677
            "No such section ({}) in section: {}", key, get_name());
678
        return "";
679
    }
71,257✔
680

681
    std::string section::get_entry(std::unique_lock<mutex_type>& l,
338,153✔
682
        std::string const& key, std::string const& default_val) const
683
    {
684
        typedef std::vector<std::string> string_vector;
685

686
        string_vector split_key;
338,152✔
687
        hpx::string_util::split(
338,152✔
688
            split_key, key, hpx::string_util::is_any_of("."));
338,152✔
689

690
        std::string sk = split_key.back();
338,153✔
691
        split_key.pop_back();
338,151✔
692

693
        section const* cur_section = this;
338,151✔
694
        for (string_vector::const_iterator iter = split_key.begin(),
1,006,221✔
695
                                           end = split_key.end();
338,151✔
696
             iter != end; ++iter)
668,066✔
697
        {
698
            section_map::const_iterator next =
699
                cur_section->sections_.find(*iter);
333,254✔
700
            if (cur_section->sections_.end() == next)
333,256✔
701
                return expand(l, default_val);
3,341✔
702
            cur_section = &next->second;
329,915✔
703
        }
329,915✔
704

705
        entry_map::const_iterator entry = cur_section->entries_.find(sk);
334,812✔
706
        if (cur_section->entries_.end() == entry)
334,810✔
707
            return expand(l, default_val);
38,054✔
708

709
        return expand(l, entry->second.first);
296,756✔
710
    }
338,153✔
711

712
    inline void indent(int ind, std::ostream& strm)
×
713
    {
714
        for (int i = 0; i < ind; ++i)
×
715
            strm << "  ";
×
716
    }
×
717

718
    void section::dump(int ind) const
×
719
    {
720
        return dump(ind, std::cout);
×
721
    }
722

723
    void section::dump(int ind, std::ostream& strm) const
×
724
    {
725
        std::unique_lock<mutex_type> l(mtx_);
×
726

727
        bool header = false;
×
728
        if (0 == ind)
×
729
            header = true;
×
730

731
        ++ind;
×
732
        if (header)
×
733
        {
734
            if (get_root() == this)
×
735
            {
736
                strm << "============================\n";
×
737
            }
×
738
            else
739
            {
740
                strm << "============================[\n"
×
741
                     << get_name() << "\n"
×
742
                     << "]\n";
×
743
            }
744
        }
×
745

746
        entry_map::const_iterator eend = entries_.end();
×
747
        for (entry_map::const_iterator i = entries_.begin(); i != eend; ++i)
×
748
        {
749
            indent(ind, strm);
×
750

751
            const std::string expansion = expand(l, i->second.first);
×
752

753
            // Check if the expanded entry is different from the actual entry.
754
            if (expansion != i->second.first)
×
755
                // If the expansion is different from the real entry, then print
756
                // it out.
757
                strm << "'" << i->first << "' : '" << i->second.first
×
758
                     << "' -> '" << expansion << "'\n";
×
759
            else
760
                strm << "'" << i->first << "' : '" << i->second.first << "'\n";
×
761
        }
×
762

763
        section_map::const_iterator send = sections_.end();
×
764
        for (section_map::const_iterator i = sections_.begin(); i != send; ++i)
×
765
        {
766
            indent(ind, strm);
×
767
            strm << "[" << i->first << "]\n";
×
768
            (*i).second.dump(ind, strm);
×
769
        }
×
770

771
        if (header)
×
772
            strm << "============================\n";
×
773

774
        strm << std::flush;
×
775
    }
×
776

777
    void section::merge(std::string const& filename)
×
778
    {
779
        section tmp(filename, root_);
×
780
        merge(tmp);
×
781
    }
×
782

783
    void section::merge(section& second)
×
784
    {
785
        std::unique_lock<mutex_type> l(mtx_);
×
786

787
        // merge entries: keep own entries, and add other entries
788
        entry_map const& s_entries = second.get_entries();
×
789
        entry_map::const_iterator end = s_entries.end();
×
790
        for (entry_map::const_iterator i = s_entries.begin(); i != end; ++i)
×
791
            entries_[i->first] = i->second;
×
792

793
        // merge subsection known in first section
794
        section_map::iterator send = sections_.end();
×
795
        for (section_map::iterator i = sections_.begin(); i != send; ++i)
×
796
        {
797
            // is there something to merge with?
798
            if (second.has_section(l, i->first))
×
799
                i->second.merge(second.sections_[i->first]);
×
800
        }
×
801

802
        // merge subsection known in second section
803
        section_map s = second.get_sections();
×
804
        section_map::iterator secend = s.end();
×
805
        for (section_map::iterator i = s.begin(); i != secend; ++i)
×
806
        {
807
            // if THIS knows the section, we already merged it above
808
            if (!has_section(l, i->first))
×
809
            {
810
                // it is not known here, so we can't merge, but have to add it.
811
                add_section(l, i->first, i->second, get_root());
×
812
            }
×
813
        }
×
814
    }
×
815

816
    /////////////////////////////////////////////////////////////////////////////////
817
    void section::line_msg(std::string msg, std::string const& file, int lnum,
×
818
        std::string const& line)
819
    {
820
        msg += " " + file;
×
821
        if (lnum > 0)
×
822
            msg += ": line " + std::to_string(lnum);
×
823
        if (!line.empty())
×
824
            msg += " (offending entry: " + line + ")";
×
825

826
        HPX_THROW_EXCEPTION(hpx::error::no_success, "section::line_msg", msg);
×
827
    }
×
828

829
    ///////////////////////////////////////////////////////////////////////////////
830
    // find the matching closing brace starting from 'begin', escaped braces will
831
    // be un-escaped
832
    inline std::string::size_type find_next(char const* ch, std::string& value,
863,360✔
833
        std::string::size_type begin = static_cast<std::string::size_type>(-1))
834
    {
835
        std::string::size_type end = value.find_first_of(ch, begin + 1);
863,360✔
836
        while (end != std::string::npos)
863,360✔
837
        {
838
            if (end != 0 && value[end - 1] != '\\')
673,417✔
839
                break;
673,417✔
840
            value = detail::replace_substr(value, end - 1, 2, ch);
×
841
            end = value.find_first_of(ch, end);
×
842
        }
843
        return end;
863,360✔
844
    }
845

846
    ///////////////////////////////////////////////////////////////////////////////
847
    void section::expand(std::unique_lock<mutex_type>& l, std::string& value,
444,903✔
848
        std::string::size_type begin) const
849
    {
850
        std::string::size_type p = value.find_first_of('$', begin + 1);
444,903✔
851
        while (p != std::string::npos && value.size() - 1 != p)
524,157✔
852
        {
853
            if ('[' == value[p + 1])
79,254✔
854
                expand_bracket(l, value, p);
49,998✔
855
            else if ('{' == value[p + 1])
29,256✔
856
                expand_brace(l, value, p);
×
857
            p = value.find_first_of('$', p + 1);
79,254✔
858
        }
859
    }
444,903✔
860

861
    void section::expand_bracket(std::unique_lock<mutex_type>& l,
49,998✔
862
        std::string& value, std::string::size_type begin) const
863
    {
864
        // expand all keys embedded inside this key
865
        expand(l, value, begin);
49,998✔
866

867
        // now expand the key itself
868
        std::string::size_type end = find_next("]", value, begin + 1);
49,998✔
869
        if (end != std::string::npos)
49,998✔
870
        {
871
            std::string to_expand = value.substr(begin + 2, end - begin - 2);
49,998✔
872
            std::string::size_type colon = find_next(":", to_expand);
49,998✔
873
            if (colon == std::string::npos)
49,998✔
874
            {
875
                value = detail::replace_substr(value, begin, end - begin + 1,
99,996✔
876
                    root_->get_entry(l, to_expand, std::string("")));
49,998✔
877
            }
49,998✔
878
            else
879
            {
880
                value = detail::replace_substr(value, begin, end - begin + 1,
×
881
                    root_->get_entry(l, to_expand.substr(0, colon),
×
882
                        to_expand.substr(colon + 1)));
×
883
            }
884
        }
49,998✔
885
    }
49,998✔
886

887
    void section::expand_brace(std::unique_lock<mutex_type>& l,
×
888
        std::string& value, std::string::size_type begin) const
889
    {
890
        // expand all keys embedded inside this key
891
        expand(l, value, begin);
×
892

893
        // now expand the key itself
894
        std::string::size_type end = find_next("}", value, begin + 1);
×
895
        if (end != std::string::npos)
×
896
        {
897
            std::string to_expand = value.substr(begin + 2, end - begin - 2);
×
898
            std::string::size_type colon = find_next(":", to_expand);
×
899
            if (colon == std::string::npos)
×
900
            {
901
                char* env = getenv(to_expand.c_str());
×
902
                value = detail::replace_substr(
×
903
                    value, begin, end - begin + 1, nullptr != env ? env : "");
×
904
            }
×
905
            else
906
            {
907
                char* env = getenv(to_expand.substr(0, colon).c_str());
×
908
                value = detail::replace_substr(value, begin, end - begin + 1,
×
909
                    nullptr != env ? std::string(env) :
×
910
                                     to_expand.substr(colon + 1));
×
911
            }
912
        }
×
913
    }
×
914

915
    std::string section::expand(
394,903✔
916
        std::unique_lock<mutex_type>& l, std::string value) const
917
    {
918
        expand(l, value, std::string::size_type(-1));
394,903✔
919
        return value;
394,903✔
920
    }
921

922
    ///////////////////////////////////////////////////////////////////////////////
923
    void section::expand_only(std::unique_lock<mutex_type>& l,
849,466✔
924
        std::string& value, std::string::size_type begin,
925
        std::string const& expand_this) const
926
    {
927
        std::string::size_type p = value.find_first_of('$', begin + 1);
849,466✔
928
        while (p != std::string::npos && value.size() - 1 != p)
1,463,708✔
929
        {
930
            if ('[' == value[p + 1])
614,242✔
931
                expand_bracket_only(l, value, p, expand_this);
132,631✔
932
            else if ('{' == value[p + 1])
481,611✔
933
                expand_brace_only(l, value, p, expand_this);
249,051✔
934
            p = value.find_first_of('$', p + 1);
614,242✔
935
        }
936
    }
849,466✔
937

938
    void section::expand_bracket_only(std::unique_lock<mutex_type>& l,
132,631✔
939
        std::string& value, std::string::size_type begin,
940
        std::string const& expand_this) const
941
    {
942
        // expand all keys embedded inside this key
943
        expand_only(l, value, begin, expand_this);
132,631✔
944

945
        // now expand the key itself
946
        std::string::size_type end = find_next("]", value, begin + 1);
132,631✔
947
        if (end != std::string::npos)
132,631✔
948
        {
949
            std::string to_expand = value.substr(begin + 2, end - begin - 2);
132,631✔
950
            std::string::size_type colon = find_next(":", to_expand);
132,631✔
951
            if (colon == std::string::npos)
132,631✔
952
            {
953
                if (to_expand == expand_this)
132,631✔
954
                {
955
                    value =
×
956
                        detail::replace_substr(value, begin, end - begin + 1,
×
957
                            root_->get_entry(l, to_expand, std::string("")));
×
958
                }
×
959
            }
132,631✔
960
            else if (to_expand.substr(0, colon) == expand_this)
×
961
            {
962
                value = detail::replace_substr(value, begin, end - begin + 1,
×
963
                    root_->get_entry(l, to_expand.substr(0, colon),
×
964
                        to_expand.substr(colon + 1)));
×
965
            }
×
966
        }
132,631✔
967
    }
132,631✔
968

969
    void section::expand_brace_only(std::unique_lock<mutex_type>& l,
249,051✔
970
        std::string& value, std::string::size_type begin,
971
        std::string const& expand_this) const
972
    {
973
        // expand all keys embedded inside this key
974
        expand_only(l, value, begin, expand_this);
249,051✔
975

976
        // now expand the key itself
977
        std::string::size_type end = find_next("}", value, begin + 1);
249,051✔
978
        if (end != std::string::npos)
249,051✔
979
        {
980
            std::string to_expand = value.substr(begin + 2, end - begin - 2);
249,051✔
981
            std::string::size_type colon = find_next(":", to_expand);
249,051✔
982
            if (colon == std::string::npos)
249,051✔
983
            {
984
                char* env = getenv(to_expand.c_str());
7,314✔
985
                value = detail::replace_substr(
7,314✔
986
                    value, begin, end - begin + 1, nullptr != env ? env : "");
7,314✔
987
            }
7,314✔
988
            else
989
            {
990
                char* env = getenv(to_expand.substr(0, colon).c_str());
241,737✔
991
                value = detail::replace_substr(value, begin, end - begin + 1,
483,474✔
992
                    nullptr != env ? std::string(env) :
483,471✔
993
                                     to_expand.substr(colon + 1));
241,734✔
994
            }
995
        }
249,051✔
996
    }
249,051✔
997

998
    std::string section::expand_only(std::unique_lock<mutex_type>& l,
×
999
        std::string value, std::string const& expand_this) const
1000
    {
1001
        expand_only(l, value, std::string::size_type(-1), expand_this);
×
1002
        return value;
×
1003
    }
1004

1005
    ///////////////////////////////////////////////////////////////////////////////
1006
    template <typename Archive>
1007
    void section::save(Archive& ar, const unsigned int /* version */) const
×
1008
    {
1009
        ar << name_;
×
1010
        ar << parent_name_;
×
1011

1012
        std::uint64_t size = entries_.size();
×
1013
        ar << size;
×
1014
        for (auto const& val : entries_)
×
1015
        {
1016
            ar << val.first;
×
1017
        }
1018

1019
        ar << sections_;
×
1020
    }
×
1021

1022
    template <typename Archive>
1023
    void section::load(Archive& ar, const unsigned int /* version */)
×
1024
    {
1025
        ar >> name_;
×
1026
        ar >> parent_name_;
×
1027

1028
        std::uint64_t size;
1029
        ar >> size;    //-V128
×
1030

1031
        entries_.clear();
×
1032
        for (std::size_t i = 0; i < size; ++i)
×
1033
        {
1034
            using value_type = typename entry_map::value_type;
1035

1036
            value_type v;
×
1037
            ar >> const_cast<std::string&>(v.first);
×
1038
            entries_.insert(entries_.end(), HPX_MOVE(v));
×
1039
        }
×
1040

1041
        ar >> sections_;
×
1042

1043
        set_root(this, true);    // make this the current root
×
1044
    }
×
1045

1046
    // explicit instantiation for the correct archive types
1047
    template HPX_CORE_EXPORT void section::save(
1048
        serialization::output_archive&, const unsigned int version) const;
1049

1050
    template HPX_CORE_EXPORT void section::load(
1051
        serialization::input_archive&, const unsigned int version);
1052

1053
}}    // namespace hpx::util
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc