/bitcoin/src/common/settings.cpp
Line | Count | Source |
1 | | // Copyright (c) 2019-2022 The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <common/settings.h> |
6 | | |
7 | | #include <bitcoin-build-config.h> // IWYU pragma: keep |
8 | | |
9 | | #include <tinyformat.h> |
10 | | #include <univalue.h> |
11 | | #include <util/fs.h> |
12 | | |
13 | | #include <algorithm> |
14 | | #include <fstream> |
15 | | #include <iterator> |
16 | | #include <map> |
17 | | #include <string> |
18 | | #include <utility> |
19 | | #include <vector> |
20 | | |
21 | | namespace common { |
22 | | namespace { |
23 | | |
24 | | enum class Source { |
25 | | FORCED, |
26 | | COMMAND_LINE, |
27 | | RW_SETTINGS, |
28 | | CONFIG_FILE_NETWORK_SECTION, |
29 | | CONFIG_FILE_DEFAULT_SECTION |
30 | | }; |
31 | | |
32 | | // Json object key for the auto-generated warning comment |
33 | | const std::string SETTINGS_WARN_MSG_KEY{"_warning_"}; |
34 | | |
35 | | //! Merge settings from multiple sources in precedence order: |
36 | | //! Forced config > command line > read-write settings file > config file network-specific section > config file default section |
37 | | //! |
38 | | //! This function is provided with a callback function fn that contains |
39 | | //! specific logic for how to merge the sources. |
40 | | template <typename Fn> |
41 | | static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn) |
42 | 19.0M | { |
43 | | // Merge in the forced settings |
44 | 19.0M | if (auto* value = FindKey(settings.forced_settings, name)) { Branch (44:15): [True: 11.0k, False: 18.0M]
Branch (44:15): [True: 0, False: 904k]
Branch (44:15): [True: 0, False: 88.7k]
|
45 | 11.0k | fn(SettingsSpan(*value), Source::FORCED); |
46 | 11.0k | } |
47 | | // Merge in the command-line options |
48 | 19.0M | if (auto* values = FindKey(settings.command_line_options, name)) { Branch (48:15): [True: 529k, False: 17.5M]
Branch (48:15): [True: 110k, False: 793k]
Branch (48:15): [True: 22.1k, False: 66.5k]
|
49 | 662k | fn(SettingsSpan(*values), Source::COMMAND_LINE); |
50 | 662k | } |
51 | | // Merge in the read-write settings |
52 | 19.0M | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { Branch (52:30): [True: 0, False: 18.0M]
Branch (52:30): [True: 0, False: 904k]
Branch (52:30): [True: 0, False: 88.7k]
|
53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); |
54 | 0 | } |
55 | | // Merge in the network-specific section of the config file |
56 | 19.0M | if (!section.empty()) { Branch (56:9): [True: 17.8M, False: 232k]
Branch (56:9): [True: 848k, False: 55.4k]
Branch (56:9): [True: 88.7k, False: 0]
|
57 | 18.7M | if (auto* map = FindKey(settings.ro_config, section)) { Branch (57:19): [True: 0, False: 17.8M]
Branch (57:19): [True: 0, False: 848k]
Branch (57:19): [True: 0, False: 88.7k]
|
58 | 0 | if (auto* values = FindKey(*map, name)) { Branch (58:23): [True: 0, False: 0]
Branch (58:23): [True: 0, False: 0]
Branch (58:23): [True: 0, False: 0]
|
59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); |
60 | 0 | } |
61 | 0 | } |
62 | 18.7M | } |
63 | | // Merge in the default section of the config file |
64 | 19.0M | if (auto* map = FindKey(settings.ro_config, "")) { Branch (64:15): [True: 0, False: 18.0M]
Branch (64:15): [True: 0, False: 904k]
Branch (64:15): [True: 0, False: 88.7k]
|
65 | 0 | if (auto* values = FindKey(*map, name)) { Branch (65:19): [True: 0, False: 0]
Branch (65:19): [True: 0, False: 0]
Branch (65:19): [True: 0, False: 0]
|
66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); |
67 | 0 | } |
68 | 0 | } |
69 | 19.0M | } settings.cpp:void common::(anonymous namespace)::MergeSettings<common::GetSetting(common::Settings 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&, bool, bool, bool)::$_0>(common::Settings 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&, common::GetSetting(common::Settings 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&, bool, bool, bool)::$_0&&) Line | Count | Source | 42 | 18.0M | { | 43 | | // Merge in the forced settings | 44 | 18.0M | if (auto* value = FindKey(settings.forced_settings, name)) { Branch (44:15): [True: 11.0k, False: 18.0M]
| 45 | 11.0k | fn(SettingsSpan(*value), Source::FORCED); | 46 | 11.0k | } | 47 | | // Merge in the command-line options | 48 | 18.0M | if (auto* values = FindKey(settings.command_line_options, name)) { Branch (48:15): [True: 529k, False: 17.5M]
| 49 | 529k | fn(SettingsSpan(*values), Source::COMMAND_LINE); | 50 | 529k | } | 51 | | // Merge in the read-write settings | 52 | 18.0M | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { Branch (52:30): [True: 0, False: 18.0M]
| 53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); | 54 | 0 | } | 55 | | // Merge in the network-specific section of the config file | 56 | 18.0M | if (!section.empty()) { Branch (56:9): [True: 17.8M, False: 232k]
| 57 | 17.8M | if (auto* map = FindKey(settings.ro_config, section)) { Branch (57:19): [True: 0, False: 17.8M]
| 58 | 0 | if (auto* values = FindKey(*map, name)) { Branch (58:23): [True: 0, False: 0]
| 59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); | 60 | 0 | } | 61 | 0 | } | 62 | 17.8M | } | 63 | | // Merge in the default section of the config file | 64 | 18.0M | if (auto* map = FindKey(settings.ro_config, "")) { Branch (64:15): [True: 0, False: 18.0M]
| 65 | 0 | if (auto* values = FindKey(*map, name)) { Branch (65:19): [True: 0, False: 0]
| 66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | 67 | 0 | } | 68 | 0 | } | 69 | 18.0M | } |
settings.cpp:void common::(anonymous namespace)::MergeSettings<common::GetSettingsList(common::Settings 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&, bool)::$_0>(common::Settings 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&, common::GetSettingsList(common::Settings 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&, bool)::$_0&&) Line | Count | Source | 42 | 904k | { | 43 | | // Merge in the forced settings | 44 | 904k | if (auto* value = FindKey(settings.forced_settings, name)) { Branch (44:15): [True: 0, False: 904k]
| 45 | 0 | fn(SettingsSpan(*value), Source::FORCED); | 46 | 0 | } | 47 | | // Merge in the command-line options | 48 | 904k | if (auto* values = FindKey(settings.command_line_options, name)) { Branch (48:15): [True: 110k, False: 793k]
| 49 | 110k | fn(SettingsSpan(*values), Source::COMMAND_LINE); | 50 | 110k | } | 51 | | // Merge in the read-write settings | 52 | 904k | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { Branch (52:30): [True: 0, False: 904k]
| 53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); | 54 | 0 | } | 55 | | // Merge in the network-specific section of the config file | 56 | 904k | if (!section.empty()) { Branch (56:9): [True: 848k, False: 55.4k]
| 57 | 848k | if (auto* map = FindKey(settings.ro_config, section)) { Branch (57:19): [True: 0, False: 848k]
| 58 | 0 | if (auto* values = FindKey(*map, name)) { Branch (58:23): [True: 0, False: 0]
| 59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); | 60 | 0 | } | 61 | 0 | } | 62 | 848k | } | 63 | | // Merge in the default section of the config file | 64 | 904k | if (auto* map = FindKey(settings.ro_config, "")) { Branch (64:15): [True: 0, False: 904k]
| 65 | 0 | if (auto* values = FindKey(*map, name)) { Branch (65:19): [True: 0, False: 0]
| 66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | 67 | 0 | } | 68 | 0 | } | 69 | 904k | } |
settings.cpp:void common::(anonymous namespace)::MergeSettings<common::OnlyHasDefaultSectionSetting(common::Settings 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&)::$_0>(common::Settings 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&, common::OnlyHasDefaultSectionSetting(common::Settings 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&)::$_0&&) Line | Count | Source | 42 | 88.7k | { | 43 | | // Merge in the forced settings | 44 | 88.7k | if (auto* value = FindKey(settings.forced_settings, name)) { Branch (44:15): [True: 0, False: 88.7k]
| 45 | 0 | fn(SettingsSpan(*value), Source::FORCED); | 46 | 0 | } | 47 | | // Merge in the command-line options | 48 | 88.7k | if (auto* values = FindKey(settings.command_line_options, name)) { Branch (48:15): [True: 22.1k, False: 66.5k]
| 49 | 22.1k | fn(SettingsSpan(*values), Source::COMMAND_LINE); | 50 | 22.1k | } | 51 | | // Merge in the read-write settings | 52 | 88.7k | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { Branch (52:30): [True: 0, False: 88.7k]
| 53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); | 54 | 0 | } | 55 | | // Merge in the network-specific section of the config file | 56 | 88.7k | if (!section.empty()) { Branch (56:9): [True: 88.7k, False: 0]
| 57 | 88.7k | if (auto* map = FindKey(settings.ro_config, section)) { Branch (57:19): [True: 0, False: 88.7k]
| 58 | 0 | if (auto* values = FindKey(*map, name)) { Branch (58:23): [True: 0, False: 0]
| 59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); | 60 | 0 | } | 61 | 0 | } | 62 | 88.7k | } | 63 | | // Merge in the default section of the config file | 64 | 88.7k | if (auto* map = FindKey(settings.ro_config, "")) { Branch (64:15): [True: 0, False: 88.7k]
| 65 | 0 | if (auto* values = FindKey(*map, name)) { Branch (65:19): [True: 0, False: 0]
| 66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | 67 | 0 | } | 68 | 0 | } | 69 | 88.7k | } |
|
70 | | } // namespace |
71 | | |
72 | | bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors) |
73 | 11.0k | { |
74 | 11.0k | values.clear(); |
75 | 11.0k | errors.clear(); |
76 | | |
77 | | // Ok for file to not exist |
78 | 11.0k | if (!fs::exists(path)) return true; Branch (78:9): [True: 11.0k, False: 0]
|
79 | | |
80 | 0 | std::ifstream file; |
81 | 0 | file.open(path); |
82 | 0 | if (!file.is_open()) { Branch (82:9): [True: 0, False: 0]
|
83 | 0 | errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path))); |
84 | 0 | return false; |
85 | 0 | } |
86 | | |
87 | 0 | SettingsValue in; |
88 | 0 | if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) { Branch (88:9): [True: 0, False: 0]
|
89 | 0 | errors.emplace_back(strprintf("Settings file %s does not contain valid JSON. This is probably caused by disk corruption or a crash, " |
90 | 0 | "and can be fixed by removing the file, which will reset settings to default values.", |
91 | 0 | fs::PathToString(path))); |
92 | 0 | return false; |
93 | 0 | } |
94 | | |
95 | 0 | if (file.fail()) { Branch (95:9): [True: 0, False: 0]
|
96 | 0 | errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path))); |
97 | 0 | return false; |
98 | 0 | } |
99 | 0 | file.close(); // Done with file descriptor. Release while copying data. |
100 | |
|
101 | 0 | if (!in.isObject()) { Branch (101:9): [True: 0, False: 0]
|
102 | 0 | errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path))); |
103 | 0 | return false; |
104 | 0 | } |
105 | | |
106 | 0 | const std::vector<std::string>& in_keys = in.getKeys(); |
107 | 0 | const std::vector<SettingsValue>& in_values = in.getValues(); |
108 | 0 | for (size_t i = 0; i < in_keys.size(); ++i) { Branch (108:24): [True: 0, False: 0]
|
109 | 0 | auto inserted = values.emplace(in_keys[i], in_values[i]); |
110 | 0 | if (!inserted.second) { Branch (110:13): [True: 0, False: 0]
|
111 | 0 | errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path))); |
112 | 0 | values.clear(); |
113 | 0 | break; |
114 | 0 | } |
115 | 0 | } |
116 | | |
117 | | // Remove auto-generated warning comment from the accessible settings. |
118 | 0 | values.erase(SETTINGS_WARN_MSG_KEY); |
119 | |
|
120 | 0 | return errors.empty(); |
121 | 0 | } |
122 | | |
123 | | bool WriteSettings(const fs::path& path, |
124 | | const std::map<std::string, SettingsValue>& values, |
125 | | std::vector<std::string>& errors) |
126 | 22.1k | { |
127 | 22.1k | SettingsValue out(SettingsValue::VOBJ); |
128 | | // Add auto-generated warning comment |
129 | 22.1k | out.pushKV(SETTINGS_WARN_MSG_KEY, strprintf("This file is automatically generated and updated by %s. Please do not edit this file while the node " |
130 | 22.1k | "is running, as any changes might be ignored or overwritten.", CLIENT_NAME)); |
131 | | // Push settings values |
132 | 22.1k | for (const auto& value : values) { Branch (132:28): [True: 11.0k, False: 22.1k]
|
133 | 11.0k | out.pushKVEnd(value.first, value.second); |
134 | 11.0k | } |
135 | 22.1k | std::ofstream file; |
136 | 22.1k | file.open(path); |
137 | 22.1k | if (file.fail()) { Branch (137:9): [True: 0, False: 22.1k]
|
138 | 0 | errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path))); |
139 | 0 | return false; |
140 | 0 | } |
141 | 22.1k | file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl; |
142 | 22.1k | file.close(); |
143 | 22.1k | return true; |
144 | 22.1k | } |
145 | | |
146 | | SettingsValue GetSetting(const Settings& settings, |
147 | | const std::string& section, |
148 | | const std::string& name, |
149 | | bool ignore_default_section_config, |
150 | | bool ignore_nonpersistent, |
151 | | bool get_chain_type) |
152 | 18.0M | { |
153 | 18.0M | SettingsValue result; |
154 | 18.0M | bool done = false; // Done merging any more settings sources. |
155 | 18.0M | MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { |
156 | | // Weird behavior preserved for backwards compatibility: Apply negated |
157 | | // setting even if non-negated setting would be ignored. A negated |
158 | | // value in the default section is applied to network specific options, |
159 | | // even though normal non-negated values there would be ignored. |
160 | 540k | const bool never_ignore_negated_setting = span.last_negated(); |
161 | | |
162 | | // Weird behavior preserved for backwards compatibility: Take first |
163 | | // assigned value instead of last. In general, later settings take |
164 | | // precedence over early settings, but for backwards compatibility in |
165 | | // the config file the precedence is reversed for all settings except |
166 | | // chain type settings. |
167 | 540k | const bool reverse_precedence = |
168 | 540k | (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && Branch (168:14): [True: 0, False: 540k]
Branch (168:63): [True: 0, False: 540k]
|
169 | 540k | !get_chain_type; Branch (169:13): [True: 0, False: 0]
|
170 | | |
171 | | // Weird behavior preserved for backwards compatibility: Negated |
172 | | // -regtest and -testnet arguments which you would expect to override |
173 | | // values set in the configuration file are currently accepted but |
174 | | // silently ignored. It would be better to apply these just like other |
175 | | // negated values, or at least warn they are ignored. |
176 | 540k | const bool skip_negated_command_line = get_chain_type; |
177 | | |
178 | 540k | if (done) return; Branch (178:13): [True: 0, False: 540k]
|
179 | | |
180 | | // Ignore settings in default config section if requested. |
181 | 540k | if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION && Branch (181:13): [True: 163k, False: 377k]
Branch (181:46): [True: 0, False: 163k]
|
182 | 540k | !never_ignore_negated_setting) { Branch (182:13): [True: 0, False: 0]
|
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | | // Ignore nonpersistent settings if requested. |
187 | 540k | if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return; Branch (187:13): [True: 0, False: 540k]
Branch (187:38): [True: 0, False: 0]
Branch (187:72): [True: 0, False: 0]
|
188 | | |
189 | | // Skip negated command line settings. |
190 | 540k | if (skip_negated_command_line && span.last_negated()) return; Branch (190:13): [True: 22.1k, False: 518k]
Branch (190:42): [True: 0, False: 22.1k]
|
191 | | |
192 | 540k | if (!span.empty()) { Branch (192:13): [True: 540k, False: 0]
|
193 | 540k | result = reverse_precedence ? span.begin()[0] : span.end()[-1]; Branch (193:22): [True: 0, False: 540k]
|
194 | 540k | done = true; |
195 | 540k | } else if (span.last_negated()) { Branch (195:20): [True: 0, False: 0]
|
196 | 0 | result = false; |
197 | 0 | done = true; |
198 | 0 | } |
199 | 540k | }); |
200 | 18.0M | return result; |
201 | 18.0M | } |
202 | | |
203 | | std::vector<SettingsValue> GetSettingsList(const Settings& settings, |
204 | | const std::string& section, |
205 | | const std::string& name, |
206 | | bool ignore_default_section_config) |
207 | 904k | { |
208 | 904k | std::vector<SettingsValue> result; |
209 | 904k | bool done = false; // Done merging any more settings sources. |
210 | 904k | bool prev_negated_empty = false; |
211 | 904k | MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { |
212 | | // Weird behavior preserved for backwards compatibility: Apply config |
213 | | // file settings even if negated on command line. Negating a setting on |
214 | | // command line will ignore earlier settings on the command line and |
215 | | // ignore settings in the config file, unless the negated command line |
216 | | // value is followed by non-negated value, in which case config file |
217 | | // settings will be brought back from the dead (but earlier command |
218 | | // line settings will still be ignored). |
219 | 110k | const bool add_zombie_config_values = |
220 | 110k | (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && Branch (220:14): [True: 0, False: 110k]
Branch (220:63): [True: 0, False: 110k]
|
221 | 110k | !prev_negated_empty; Branch (221:13): [True: 0, False: 0]
|
222 | | |
223 | | // Ignore settings in default config section if requested. |
224 | 110k | if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return; Branch (224:13): [True: 0, False: 110k]
Branch (224:46): [True: 0, False: 0]
|
225 | | |
226 | | // Add new settings to the result if isn't already complete, or if the |
227 | | // values are zombies. |
228 | 110k | if (!done || add_zombie_config_values) { Branch (228:13): [True: 110k, False: 0]
Branch (228:22): [True: 0, False: 0]
|
229 | 122k | for (const auto& value : span) { Branch (229:36): [True: 122k, False: 110k]
|
230 | 122k | if (value.isArray()) { Branch (230:21): [True: 0, False: 122k]
|
231 | 0 | result.insert(result.end(), value.getValues().begin(), value.getValues().end()); |
232 | 122k | } else { |
233 | 122k | result.push_back(value); |
234 | 122k | } |
235 | 122k | } |
236 | 110k | } |
237 | | |
238 | | // If a setting was negated, or if a setting was forced, set |
239 | | // done to true to ignore any later lower priority settings. |
240 | 110k | done |= span.negated() > 0 || source == Source::FORCED; Branch (240:17): [True: 0, False: 110k]
Branch (240:39): [True: 0, False: 110k]
|
241 | | |
242 | | // Update the negated and empty state used for the zombie values check. |
243 | 110k | prev_negated_empty |= span.last_negated() && result.empty(); Branch (243:31): [True: 0, False: 110k]
Branch (243:54): [True: 0, False: 0]
|
244 | 110k | }); |
245 | 904k | return result; |
246 | 904k | } |
247 | | |
248 | | bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name) |
249 | 88.7k | { |
250 | 88.7k | bool has_default_section_setting = false; |
251 | 88.7k | bool has_other_setting = false; |
252 | 88.7k | MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { |
253 | 22.1k | if (span.empty()) return; Branch (253:13): [True: 0, False: 22.1k]
|
254 | 22.1k | else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true; Branch (254:18): [True: 0, False: 22.1k]
|
255 | 22.1k | else has_other_setting = true; |
256 | 22.1k | }); |
257 | | // If a value is set in the default section and not explicitly overwritten by the |
258 | | // user on the command line or in a different section, then we want to enable |
259 | | // warnings about the value being ignored. |
260 | 88.7k | return has_default_section_setting && !has_other_setting; Branch (260:12): [True: 0, False: 88.7k]
Branch (260:43): [True: 0, False: 0]
|
261 | 88.7k | } |
262 | | |
263 | 662k | SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {} |
264 | 110k | const SettingsValue* SettingsSpan::begin() const { return data + negated(); } |
265 | 651k | const SettingsValue* SettingsSpan::end() const { return data + size; } |
266 | 563k | bool SettingsSpan::empty() const { return size == 0 || last_negated(); } Branch (266:43): [True: 0, False: 563k]
Branch (266:56): [True: 0, False: 563k]
|
267 | 1.23M | bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); } Branch (267:50): [True: 1.23M, False: 0]
Branch (267:62): [True: 0, False: 1.23M]
|
268 | | size_t SettingsSpan::negated() const |
269 | 221k | { |
270 | 465k | for (size_t i = size; i > 0; --i) { Branch (270:27): [True: 244k, False: 221k]
|
271 | 244k | if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value) Branch (271:13): [True: 0, False: 244k]
|
272 | 244k | } |
273 | 221k | return 0; |
274 | 221k | } |
275 | | |
276 | | } // namespace common |