1 /*
2 
3 Copyright (c) 2012-2018, Arvid Norberg
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 
10     * Redistributions of source code must retain the above copyright
11       notice, this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright
13       notice, this list of conditions and the following disclaimer in
14       the documentation and/or other materials provided with the distribution.
15     * Neither the name of the author nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18 
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30 
31 */
32 
33 #include "libtorrent/config.hpp"
34 #include "libtorrent/assert.hpp"
35 #include "libtorrent/settings_pack.hpp"
36 #include "libtorrent/aux_/session_impl.hpp"
37 #include "libtorrent/aux_/array.hpp"
38 #include "libtorrent/aux_/session_settings.hpp"
39 
40 #include <algorithm>
41 
42 namespace {
43 
44 	template <class T>
compare_first(std::pair<std::uint16_t,T> const & lhs,std::pair<std::uint16_t,T> const & rhs)45 	bool compare_first(std::pair<std::uint16_t, T> const& lhs
46 		, std::pair<std::uint16_t, T> const& rhs)
47 	{
48 		return lhs.first < rhs.first;
49 	}
50 
51 	template <class T>
insort_replace(std::vector<std::pair<std::uint16_t,T>> & c,std::pair<std::uint16_t,T> v)52 	void insort_replace(std::vector<std::pair<std::uint16_t, T>>& c, std::pair<std::uint16_t, T> v)
53 	{
54 		auto i = std::lower_bound(c.begin(), c.end(), v, &compare_first<T>);
55 		if (i != c.end() && i->first == v.first) i->second = std::move(v.second);
56 		else c.emplace(i, std::move(v));
57 	}
58 
59 	// return the string, unless it's null, in which case the empty string is
60 	// returned
ensure_string(char const * str)61 	char const* ensure_string(char const* str)
62 	{ return str == nullptr ? "" : str; }
63 }
64 
65 namespace libtorrent {
66 
67 	struct str_setting_entry_t
68 	{
69 		// the name of this setting. used for serialization and deserialization
70 		char const* name;
71 		// if present, this function is called when the setting is changed
72 		void (aux::session_impl::*fun)();
73 		char const *default_value;
74 	};
75 
76 	struct int_setting_entry_t
77 	{
78 		// the name of this setting. used for serialization and deserialization
79 		char const* name;
80 		// if present, this function is called when the setting is changed
81 		void (aux::session_impl::*fun)();
82 		int default_value;
83 	};
84 
85 	struct bool_setting_entry_t
86 	{
87 		// the name of this setting. used for serialization and deserialization
88 		char const* name;
89 		// if present, this function is called when the setting is changed
90 		void (aux::session_impl::*fun)();
91 		bool default_value;
92 	};
93 
94 
95 #define SET(name, default_value, fun) { #name, fun, default_value }
96 
97 #if TORRENT_ABI_VERSION == 1
98 #define DEPRECATED_SET(name, default_value, fun) { #name, fun, default_value }
99 #define DEPRECATED_SET_STR(name, default_value, fun) { #name, fun, default_value }
100 #else
101 #define DEPRECATED_SET(name, default_value, fun) { "", nullptr, 0 }
102 #define DEPRECATED_SET_STR(name, default_value, fun) { "", nullptr, nullptr }
103 #endif
104 
105 #ifdef TORRENT_WINDOWS
106 constexpr int CLOSE_FILE_INTERVAL = 120;
107 #else
108 constexpr int CLOSE_FILE_INTERVAL = 0;
109 #endif
110 
111 	namespace {
112 
113 	using aux::session_impl;
114 
115 	aux::array<str_setting_entry_t, settings_pack::num_string_settings> const str_settings
116 	({{
117 		SET(user_agent, "libtorrent/" LIBTORRENT_VERSION, &session_impl::update_user_agent),
118 		SET(announce_ip, nullptr, nullptr),
119 		DEPRECATED_SET_STR(mmap_cache, nullptr, nullptr),
120 		SET(handshake_client_version, nullptr, nullptr),
121 		SET(outgoing_interfaces, "", &session_impl::update_outgoing_interfaces),
122 		SET(listen_interfaces, "0.0.0.0:6881,[::]:6881", &session_impl::update_listen_interfaces),
123 		SET(proxy_hostname, "", &session_impl::update_proxy),
124 		SET(proxy_username, "", &session_impl::update_proxy),
125 		SET(proxy_password, "", &session_impl::update_proxy),
126 		SET(i2p_hostname, "", &session_impl::update_i2p_bridge),
127 		SET(peer_fingerprint, "-LT12E0-", nullptr),
128 		SET(dht_bootstrap_nodes, "dht.libtorrent.org:25401", &session_impl::update_dht_bootstrap_nodes)
129 	}});
130 
131 	aux::array<bool_setting_entry_t, settings_pack::num_bool_settings> const bool_settings
132 	({{
133 		SET(allow_multiple_connections_per_ip, false, nullptr),
134 		DEPRECATED_SET(ignore_limits_on_local_network, true, &session_impl::update_ignore_rate_limits_on_local_network),
135 		SET(send_redundant_have, true, nullptr),
136 		DEPRECATED_SET(lazy_bitfields, false, nullptr),
137 		SET(use_dht_as_fallback, false, nullptr),
138 		SET(upnp_ignore_nonrouters, false, nullptr),
139 		SET(use_parole_mode, true, nullptr),
140 		SET(use_read_cache, true, nullptr),
141 		DEPRECATED_SET(use_write_cache, true, nullptr),
142 		DEPRECATED_SET(dont_flush_write_cache, false, nullptr),
143 #ifdef TORRENT_WINDOWS
144 		// the emulation of preadv/pwritev uses overlapped reads/writes to be able
145 		// to issue them all back to back. However, it appears windows fail to
146 		// merge them. At least for people reporting performance issues in
147 		// qBittorrent
148 		SET(coalesce_reads, true, nullptr),
149 		SET(coalesce_writes, true, nullptr),
150 #else
151 		SET(coalesce_reads, false, nullptr),
152 		SET(coalesce_writes, false, nullptr),
153 #endif
154 		SET(auto_manage_prefer_seeds, false, nullptr),
155 		SET(dont_count_slow_torrents, true, &session_impl::update_count_slow),
156 		SET(close_redundant_connections, true, nullptr),
157 		SET(prioritize_partial_pieces, false, nullptr),
158 		SET(rate_limit_ip_overhead, true, nullptr),
159 		SET(announce_to_all_trackers, false, nullptr),
160 		SET(announce_to_all_tiers, false, nullptr),
161 		SET(prefer_udp_trackers, true, nullptr),
162 		DEPRECATED_SET(strict_super_seeding, false, nullptr),
163 		DEPRECATED_SET(lock_disk_cache, false, nullptr),
164 		SET(disable_hash_checks, false, nullptr),
165 		SET(allow_i2p_mixed, false, nullptr),
166 		DEPRECATED_SET(low_prio_disk, true, nullptr),
167 		SET(volatile_read_cache, false, nullptr),
168 		DEPRECATED_SET(guided_read_cache, false, nullptr),
169 		SET(no_atime_storage, true, nullptr),
170 		SET(incoming_starts_queued_torrents, false, nullptr),
171 		SET(report_true_downloaded, false, nullptr),
172 		SET(strict_end_game_mode, true, nullptr),
173 		DEPRECATED_SET(broadcast_lsd, true, nullptr),
174 		SET(enable_outgoing_utp, true, nullptr),
175 		SET(enable_incoming_utp, true, nullptr),
176 		SET(enable_outgoing_tcp, true, nullptr),
177 		SET(enable_incoming_tcp, true, nullptr),
178 		SET(ignore_resume_timestamps, false, nullptr),
179 		SET(no_recheck_incomplete_resume, false, nullptr),
180 		SET(anonymous_mode, false, nullptr),
181 		SET(report_web_seed_downloads, true, &session_impl::update_report_web_seed_downloads),
182 		DEPRECATED_SET(rate_limit_utp, true, &session_impl::update_rate_limit_utp),
183 		DEPRECATED_SET(announce_double_nat, false, nullptr),
184 		SET(seeding_outgoing_connections, true, nullptr),
185 		SET(no_connect_privileged_ports, false, &session_impl::update_privileged_ports),
186 		SET(smooth_connects, true, nullptr),
187 		SET(always_send_user_agent, false, nullptr),
188 		SET(apply_ip_filter_to_trackers, true, nullptr),
189 		DEPRECATED_SET(use_disk_read_ahead, true, nullptr),
190 		DEPRECATED_SET(lock_files, false, nullptr),
191 		DEPRECATED_SET(contiguous_recv_buffer, true, nullptr),
192 		SET(ban_web_seeds, true, nullptr),
193 		SET(allow_partial_disk_writes, true, nullptr),
194 		DEPRECATED_SET(force_proxy, false, nullptr),
195 		SET(support_share_mode, true, nullptr),
196 		SET(support_merkle_torrents, true, nullptr),
197 		SET(report_redundant_bytes, true, nullptr),
198 		SET(listen_system_port_fallback, true, nullptr),
199 		DEPRECATED_SET(use_disk_cache_pool, false, nullptr),
200 		SET(announce_crypto_support, true, nullptr),
201 		SET(enable_upnp, true, &session_impl::update_upnp),
202 		SET(enable_natpmp, true, &session_impl::update_natpmp),
203 		SET(enable_lsd, true, &session_impl::update_lsd),
204 		SET(enable_dht, true, &session_impl::update_dht),
205 		SET(prefer_rc4, false, nullptr),
206 		SET(proxy_hostnames, true, nullptr),
207 		SET(proxy_peer_connections, true, nullptr),
208 		SET(auto_sequential, true, &session_impl::update_auto_sequential),
209 		SET(proxy_tracker_connections, true, nullptr),
210 		SET(enable_ip_notifier, true, &session_impl::update_ip_notifier),
211 		SET(dht_prefer_verified_node_ids, true, &session_impl::update_dht_settings),
212 		SET(piece_extent_affinity, false, nullptr),
213 		SET(validate_https_trackers, true, &session_impl::update_validate_https),
214 		SET(ssrf_mitigation, true, nullptr),
215 		SET(allow_idna, false, nullptr),
216 	}});
217 
218 	aux::array<int_setting_entry_t, settings_pack::num_int_settings> const int_settings
219 	({{
220 		SET(tracker_completion_timeout, 30, nullptr),
221 		SET(tracker_receive_timeout, 10, nullptr),
222 		SET(stop_tracker_timeout, 5, nullptr),
223 		SET(tracker_maximum_response_length, 1024*1024, nullptr),
224 		SET(piece_timeout, 20, nullptr),
225 		SET(request_timeout, 60, nullptr),
226 		SET(request_queue_time, 3, nullptr),
227 		SET(max_allowed_in_request_queue, 500, nullptr),
228 		SET(max_out_request_queue, 500, nullptr),
229 		SET(whole_pieces_threshold, 20, nullptr),
230 		SET(peer_timeout, 120, nullptr),
231 		SET(urlseed_timeout, 20, nullptr),
232 		SET(urlseed_pipeline_size, 5, nullptr),
233 		SET(urlseed_wait_retry, 30, nullptr),
234 		SET(file_pool_size, 40, nullptr),
235 		SET(max_failcount, 3, &session_impl::update_max_failcount),
236 		SET(min_reconnect_time, 60, nullptr),
237 		SET(peer_connect_timeout, 15, nullptr),
238 		SET(connection_speed, 30, &session_impl::update_connection_speed),
239 		SET(inactivity_timeout, 600, nullptr),
240 		SET(unchoke_interval, 15, nullptr),
241 		SET(optimistic_unchoke_interval, 30, nullptr),
242 		SET(num_want, 200, nullptr),
243 		SET(initial_picker_threshold, 4, nullptr),
244 		SET(allowed_fast_set_size, 5, nullptr),
245 		SET(suggest_mode, settings_pack::no_piece_suggestions, nullptr),
246 		SET(max_queued_disk_bytes, 1024 * 1024, &session_impl::update_queued_disk_bytes),
247 		SET(handshake_timeout, 10, nullptr),
248 		SET(send_buffer_low_watermark, 10 * 1024, nullptr),
249 		SET(send_buffer_watermark, 500 * 1024, nullptr),
250 		SET(send_buffer_watermark_factor, 50, nullptr),
251 		SET(choking_algorithm, settings_pack::fixed_slots_choker, nullptr),
252 		SET(seed_choking_algorithm, settings_pack::round_robin, nullptr),
253 		SET(cache_size, 2048, nullptr),
254 		DEPRECATED_SET(cache_buffer_chunk_size, 0, nullptr),
255 		SET(cache_expiry, 300, nullptr),
256 		SET(disk_io_write_mode, settings_pack::enable_os_cache, nullptr),
257 		SET(disk_io_read_mode, settings_pack::enable_os_cache, nullptr),
258 		SET(outgoing_port, 0, nullptr),
259 		SET(num_outgoing_ports, 0, nullptr),
260 		SET(peer_tos, 0x20, &session_impl::update_peer_tos),
261 		SET(active_downloads, 3, &session_impl::trigger_auto_manage),
262 		SET(active_seeds, 5, &session_impl::trigger_auto_manage),
263 		SET(active_checking, 1, &session_impl::trigger_auto_manage),
264 		SET(active_dht_limit, 88, nullptr),
265 		SET(active_tracker_limit, 1600, nullptr),
266 		SET(active_lsd_limit, 60, nullptr),
267 		SET(active_limit, 500, &session_impl::trigger_auto_manage),
268 		DEPRECATED_SET(active_loaded_limit, 0, &session_impl::trigger_auto_manage),
269 		SET(auto_manage_interval, 30, nullptr),
270 		SET(seed_time_limit, 24 * 60 * 60, nullptr),
271 		SET(auto_scrape_interval, 1800, nullptr),
272 		SET(auto_scrape_min_interval, 300, nullptr),
273 		SET(max_peerlist_size, 3000, nullptr),
274 		SET(max_paused_peerlist_size, 1000, nullptr),
275 		SET(min_announce_interval, 5 * 60, nullptr),
276 		SET(auto_manage_startup, 60, nullptr),
277 		SET(seeding_piece_quota, 20, nullptr),
278 		// TODO: deprecate this
279 		SET(max_rejects, 50, nullptr),
280 		SET(recv_socket_buffer_size, 0, &session_impl::update_socket_buffer_size),
281 		SET(send_socket_buffer_size, 0, &session_impl::update_socket_buffer_size),
282 		SET(max_peer_recv_buffer_size, 2 * 1024 * 1024, nullptr),
283 		DEPRECATED_SET(file_checks_delay_per_block, 0, nullptr),
284 		SET(read_cache_line_size, 32, nullptr),
285 		SET(write_cache_line_size, 16, nullptr),
286 		SET(optimistic_disk_retry, 10 * 60, nullptr),
287 		SET(max_suggest_pieces, 16, nullptr),
288 		SET(local_service_announce_interval, 5 * 60, nullptr),
289 		SET(dht_announce_interval, 15 * 60, &session_impl::update_dht_announce_interval),
290 		SET(udp_tracker_token_expiry, 60, nullptr),
291 		DEPRECATED_SET(default_cache_min_age, 1, nullptr),
292 		SET(num_optimistic_unchoke_slots, 0, nullptr),
293 		SET(default_est_reciprocation_rate, 16000, nullptr),
294 		SET(increase_est_reciprocation_rate, 20, nullptr),
295 		SET(decrease_est_reciprocation_rate, 3, nullptr),
296 		SET(max_pex_peers, 50, nullptr),
297 		SET(tick_interval, 500, nullptr),
298 		SET(share_mode_target, 3, nullptr),
299 		SET(upload_rate_limit, 0, &session_impl::update_upload_rate),
300 		SET(download_rate_limit, 0, &session_impl::update_download_rate),
301 		DEPRECATED_SET(local_upload_rate_limit, 0, &session_impl::update_local_upload_rate),
302 		DEPRECATED_SET(local_download_rate_limit, 0, &session_impl::update_local_download_rate),
303 		SET(dht_upload_rate_limit, 8000, &session_impl::update_dht_upload_rate_limit),
304 		SET(unchoke_slots_limit, 8, &session_impl::update_unchoke_limit),
305 		DEPRECATED_SET(half_open_limit, 0, nullptr),
306 		SET(connections_limit, 200, &session_impl::update_connections_limit),
307 		SET(connections_slack, 10, nullptr),
308 		SET(utp_target_delay, 100, nullptr),
309 		SET(utp_gain_factor, 3000, nullptr),
310 		SET(utp_min_timeout, 500, nullptr),
311 		SET(utp_syn_resends, 2, nullptr),
312 		SET(utp_fin_resends, 2, nullptr),
313 		SET(utp_num_resends, 3, nullptr),
314 		SET(utp_connect_timeout, 3000, nullptr),
315 		SET(utp_delayed_ack, 0, nullptr),
316 		SET(utp_loss_multiplier, 50, nullptr),
317 		SET(mixed_mode_algorithm, settings_pack::peer_proportional, nullptr),
318 		SET(listen_queue_size, 5, nullptr),
319 		SET(torrent_connect_boost, 30, nullptr),
320 		SET(alert_queue_size, 1000, &session_impl::update_alert_queue_size),
321 		SET(max_metadata_size, 3 * 1024 * 10240, nullptr),
322 		DEPRECATED_SET(hashing_threads, 1, nullptr),
323 		SET(checking_mem_usage, 1024, nullptr),
324 		SET(predictive_piece_announce, 0, nullptr),
325 		SET(aio_threads, 4, &session_impl::update_disk_threads),
326 		DEPRECATED_SET(aio_max, 300, nullptr),
327 		DEPRECATED_SET(network_threads, 0, nullptr),
328 		DEPRECATED_SET(ssl_listen, 0, &session_impl::update_ssl_listen),
329 		SET(tracker_backoff, 250, nullptr),
330 		SET(share_ratio_limit, 200, nullptr),
331 		SET(seed_time_ratio_limit, 700, nullptr),
332 		SET(peer_turnover, 4, nullptr),
333 		SET(peer_turnover_cutoff, 90, nullptr),
334 		SET(peer_turnover_interval, 300, nullptr),
335 		SET(connect_seed_every_n_download, 10, nullptr),
336 		SET(max_http_recv_buffer_size, 4*1024*204, nullptr),
337 		SET(max_retry_port_bind, 10, nullptr),
338 		SET(alert_mask, int(static_cast<std::uint32_t>(alert_category::error)), &session_impl::update_alert_mask),
339 		SET(out_enc_policy, settings_pack::pe_enabled, nullptr),
340 		SET(in_enc_policy, settings_pack::pe_enabled, nullptr),
341 		SET(allowed_enc_level, settings_pack::pe_both, nullptr),
342 		SET(inactive_down_rate, 2048, nullptr),
343 		SET(inactive_up_rate, 2048, nullptr),
344 		SET(proxy_type, settings_pack::none, &session_impl::update_proxy),
345 		SET(proxy_port, 0, &session_impl::update_proxy),
346 		SET(i2p_port, 0, &session_impl::update_i2p_bridge),
347 		SET(cache_size_volatile, 256, nullptr),
348 		SET(urlseed_max_request_bytes, 16 * 1024 * 1024, nullptr),
349 		SET(web_seed_name_lookup_retry, 1800, nullptr),
350 		SET(close_file_interval, CLOSE_FILE_INTERVAL, nullptr),
351 		SET(utp_cwnd_reduce_timer, 100, nullptr),
352 		SET(max_web_seed_connections, 3, nullptr),
353 		SET(resolver_cache_timeout, 1200, &session_impl::update_resolver_cache_timeout),
354 		SET(send_not_sent_low_watermark, 16384, nullptr),
355 		SET(rate_choker_initial_threshold, 1024, nullptr),
356 		SET(upnp_lease_duration, 3600, nullptr),
357 		SET(max_concurrent_http_announces, 50, nullptr),
358 	}});
359 
360 #undef SET
361 #undef DEPRECATED_SET
362 
363 	} // anonymous namespace
364 
setting_by_name(string_view const key)365 	int setting_by_name(string_view const key)
366 	{
367 		for (int k = 0; k < str_settings.end_index(); ++k)
368 		{
369 			if (key != str_settings[k].name) continue;
370 			return settings_pack::string_type_base + k;
371 		}
372 		for (int k = 0; k < int_settings.end_index(); ++k)
373 		{
374 			if (key != int_settings[k].name) continue;
375 			return settings_pack::int_type_base + k;
376 		}
377 		for (int k = 0; k < bool_settings.end_index(); ++k)
378 		{
379 			if (key != bool_settings[k].name) continue;
380 			return settings_pack::bool_type_base + k;
381 		}
382 		return -1;
383 	}
384 
name_for_setting(int s)385 	char const* name_for_setting(int s)
386 	{
387 		switch (s & settings_pack::type_mask)
388 		{
389 			case settings_pack::string_type_base:
390 				return str_settings[s - settings_pack::string_type_base].name;
391 			case settings_pack::int_type_base:
392 				return int_settings[s - settings_pack::int_type_base].name;
393 			case settings_pack::bool_type_base:
394 				return bool_settings[s - settings_pack::bool_type_base].name;
395 		}
396 		return "";
397 	}
398 
load_pack_from_dict(bdecode_node const & settings)399 	settings_pack load_pack_from_dict(bdecode_node const& settings)
400 	{
401 		settings_pack pack;
402 
403 		for (int i = 0; i < settings.dict_size(); ++i)
404 		{
405 			string_view key;
406 			bdecode_node val;
407 			std::tie(key, val) = settings.dict_at(i);
408 			switch (val.type())
409 			{
410 				case bdecode_node::dict_t:
411 				case bdecode_node::list_t:
412 					continue;
413 				case bdecode_node::int_t:
414 				{
415 					bool found = false;
416 					for (int k = 0; k < int_settings.end_index(); ++k)
417 					{
418 						if (key != int_settings[k].name) continue;
419 						pack.set_int(settings_pack::int_type_base | k, int(val.int_value()));
420 						found = true;
421 						break;
422 					}
423 					if (found) continue;
424 					for (int k = 0; k < bool_settings.end_index(); ++k)
425 					{
426 						if (key != bool_settings[k].name) continue;
427 						pack.set_bool(settings_pack::bool_type_base | k, val.int_value() != 0);
428 						break;
429 					}
430 				}
431 				break;
432 			case bdecode_node::string_t:
433 				for (int k = 0; k < str_settings.end_index(); ++k)
434 				{
435 					if (key != str_settings[k].name) continue;
436 					pack.set_str(settings_pack::string_type_base + k, val.string_value().to_string());
437 					break;
438 				}
439 				break;
440 			case bdecode_node::none_t:
441 				break;
442 			}
443 		}
444 		return pack;
445 	}
446 
save_settings_to_dict(aux::session_settings const & sett,entry::dictionary_type & out)447 	void save_settings_to_dict(aux::session_settings const& sett, entry::dictionary_type& out)
448 	{
449 		sett.bulk_get([&out](aux::session_settings_single_thread const& s)
450 		{
451 		// loop over all settings that differ from default
452 			for (int i = 0; i < settings_pack::num_string_settings; ++i)
453 			{
454 				if (ensure_string(str_settings[i].default_value) == s.get_str(i | settings_pack::string_type_base)) continue;
455 				out[str_settings[i].name] = s.get_str(i | settings_pack::string_type_base);
456 			}
457 
458 			for (int i = 0; i < settings_pack::num_int_settings; ++i)
459 			{
460 				if (int_settings[i].default_value == s.get_int(i | settings_pack::int_type_base)) continue;
461 				out[int_settings[i].name] = s.get_int(i | settings_pack::int_type_base);
462 			}
463 
464 			for (int i = 0; i < settings_pack::num_bool_settings; ++i)
465 			{
466 				if (bool_settings[i].default_value == s.get_bool(i | settings_pack::bool_type_base)) continue;
467 				out[bool_settings[i].name] = s.get_bool(i | settings_pack::bool_type_base);
468 			}
469 		});
470 	}
471 
run_all_updates(aux::session_impl & ses)472 	void run_all_updates(aux::session_impl& ses)
473 	{
474 		using fun_t = void (aux::session_impl::*)();
475 		for (int i = 0; i < settings_pack::num_string_settings; ++i)
476 		{
477 			fun_t const& f = str_settings[i].fun;
478 			if (f) (ses.*f)();
479 		}
480 
481 		for (int i = 0; i < settings_pack::num_int_settings; ++i)
482 		{
483 			fun_t const& f = int_settings[i].fun;
484 			if (f) (ses.*f)();
485 		}
486 
487 		for (int i = 0; i < settings_pack::num_bool_settings; ++i)
488 		{
489 			fun_t const& f = bool_settings[i].fun;
490 			if (f) (ses.*f)();
491 		}
492 	}
493 
initialize_default_settings(aux::session_settings_single_thread & s)494 	void initialize_default_settings(aux::session_settings_single_thread& s)
495 	{
496 		for (int i = 0; i < settings_pack::num_string_settings; ++i)
497 		{
498 			if (str_settings[i].default_value == nullptr) continue;
499 			s.set_str(settings_pack::string_type_base | i, str_settings[i].default_value);
500 			TORRENT_ASSERT(s.get_str(settings_pack::string_type_base + i) == str_settings[i].default_value);
501 		}
502 
503 		for (int i = 0; i < settings_pack::num_int_settings; ++i)
504 		{
505 			s.set_int(settings_pack::int_type_base | i, int_settings[i].default_value);
506 			TORRENT_ASSERT(s.get_int(settings_pack::int_type_base + i) == int_settings[i].default_value);
507 		}
508 
509 		for (int i = 0; i < settings_pack::num_bool_settings; ++i)
510 		{
511 			s.set_bool(settings_pack::bool_type_base | i, bool_settings[i].default_value);
512 			TORRENT_ASSERT(s.get_bool(settings_pack::bool_type_base + i) == bool_settings[i].default_value);
513 		}
514 	}
515 
default_settings()516 	settings_pack default_settings()
517 	{
518 		settings_pack ret;
519 		// TODO: it would be nice to reserve() these vectors up front
520 		for (int i = 0; i < settings_pack::num_string_settings; ++i)
521 		{
522 			if (str_settings[i].default_value == nullptr) continue;
523 			ret.set_str(settings_pack::string_type_base + i, str_settings[i].default_value);
524 		}
525 
526 		for (int i = 0; i < settings_pack::num_int_settings; ++i)
527 		{
528 			ret.set_int(settings_pack::int_type_base + i, int_settings[i].default_value);
529 		}
530 
531 		for (int i = 0; i < settings_pack::num_bool_settings; ++i)
532 		{
533 			ret.set_bool(settings_pack::bool_type_base + i, bool_settings[i].default_value);
534 		}
535 		return ret;
536 	}
537 
default_int_value(int const name)538 	int default_int_value(int const name)
539 	{
540 		TORRENT_ASSERT((name & settings_pack::type_mask) == settings_pack::int_type_base);
541 		return int_settings[name - settings_pack::int_type_base].default_value;
542 	}
543 
apply_pack(settings_pack const * pack,aux::session_settings & sett,aux::session_impl * ses)544 	void apply_pack(settings_pack const* pack, aux::session_settings& sett
545 		, aux::session_impl* ses)
546 	{
547 		using fun_t = void (aux::session_impl::*)();
548 		std::vector<fun_t> callbacks;
549 
550 		sett.bulk_set([&](aux::session_settings_single_thread& s)
551 		{
552 			apply_pack_impl(pack, s, ses ? &callbacks : nullptr);
553 		});
554 
555 		// call the callbacks once all the settings have been applied, and
556 		// only once per callback
557 		for (auto const& f : callbacks)
558 		{
559 			(ses->*f)();
560 		}
561 	}
562 
apply_pack_impl(settings_pack const * pack,aux::session_settings_single_thread & sett,std::vector<void (aux::session_impl::*)()> * callbacks)563 	void apply_pack_impl(settings_pack const* pack, aux::session_settings_single_thread& sett
564 		, std::vector<void(aux::session_impl::*)()>* callbacks)
565 	{
566 		for (auto const& p : pack->m_strings)
567 		{
568 			// disregard setting indices that are not string types
569 			if ((p.first & settings_pack::type_mask) != settings_pack::string_type_base)
570 				continue;
571 
572 			// ignore settings that are out of bounds
573 			int const index = p.first & settings_pack::index_mask;
574 			TORRENT_ASSERT_PRECOND(index >= 0 && index < settings_pack::num_string_settings);
575 			if (index < 0 || index >= settings_pack::num_string_settings)
576 				continue;
577 
578 			// if the value did not change, don't call the update callback
579 			if (sett.get_str(p.first) == p.second) continue;
580 
581 			sett.set_str(p.first, p.second);
582 			str_setting_entry_t const& sa = str_settings[index];
583 
584 			if (sa.fun && callbacks
585 				&& std::find(callbacks->begin(), callbacks->end(), sa.fun) == callbacks->end())
586 				callbacks->push_back(sa.fun);
587 		}
588 
589 		for (auto const& p : pack->m_ints)
590 		{
591 			// disregard setting indices that are not int types
592 			if ((p.first & settings_pack::type_mask) != settings_pack::int_type_base)
593 				continue;
594 
595 			// ignore settings that are out of bounds
596 			int const index = p.first & settings_pack::index_mask;
597 			TORRENT_ASSERT_PRECOND(index >= 0 && index < settings_pack::num_int_settings);
598 			if (index < 0 || index >= settings_pack::num_int_settings)
599 				continue;
600 
601 			// if the value did not change, don't call the update callback
602 			if (sett.get_int(p.first) == p.second) continue;
603 
604 			sett.set_int(p.first, p.second);
605 			int_setting_entry_t const& sa = int_settings[index];
606 			if (sa.fun && callbacks
607 				&& std::find(callbacks->begin(), callbacks->end(), sa.fun) == callbacks->end())
608 				callbacks->push_back(sa.fun);
609 		}
610 
611 		for (auto const& p : pack->m_bools)
612 		{
613 			// disregard setting indices that are not bool types
614 			if ((p.first & settings_pack::type_mask) != settings_pack::bool_type_base)
615 				continue;
616 
617 			// ignore settings that are out of bounds
618 			int const index = p.first & settings_pack::index_mask;
619 			TORRENT_ASSERT_PRECOND(index >= 0 && index < settings_pack::num_bool_settings);
620 			if (index < 0 || index >= settings_pack::num_bool_settings)
621 				continue;
622 
623 			// if the value did not change, don't call the update callback
624 			if (sett.get_bool(p.first) == p.second) continue;
625 
626 			sett.set_bool(p.first, p.second);
627 			bool_setting_entry_t const& sa = bool_settings[index];
628 			if (sa.fun && callbacks
629 				&& std::find(callbacks->begin(), callbacks->end(), sa.fun) == callbacks->end())
630 				callbacks->push_back(sa.fun);
631 		}
632 	}
633 
set_str(int const name,std::string val)634 	void settings_pack::set_str(int const name, std::string val)
635 	{
636 		TORRENT_ASSERT((name & type_mask) == string_type_base);
637 		if ((name & type_mask) != string_type_base) return;
638 		std::pair<std::uint16_t, std::string> v(aux::numeric_cast<std::uint16_t>(name), std::move(val));
639 		insort_replace(m_strings, std::move(v));
640 	}
641 
set_int(int const name,int const val)642 	void settings_pack::set_int(int const name, int const val)
643 	{
644 		TORRENT_ASSERT((name & type_mask) == int_type_base);
645 		if ((name & type_mask) != int_type_base) return;
646 		std::pair<std::uint16_t, int> v(aux::numeric_cast<std::uint16_t>(name), val);
647 		insort_replace(m_ints, v);
648 	}
649 
set_bool(int const name,bool const val)650 	void settings_pack::set_bool(int const name, bool const val)
651 	{
652 		TORRENT_ASSERT((name & type_mask) == bool_type_base);
653 		if ((name & type_mask) != bool_type_base) return;
654 		std::pair<std::uint16_t, bool> v(aux::numeric_cast<std::uint16_t>(name), val);
655 		insort_replace(m_bools, v);
656 	}
657 
has_val(int const name) const658 	bool settings_pack::has_val(int const name) const
659 	{
660 		switch (name & type_mask)
661 		{
662 			case string_type_base:
663 			{
664 				// this is an optimization. If the settings pack is complete,
665 				// i.e. has every key, we don't need to search, it's just a lookup
666 				if (m_strings.size() == settings_pack::num_string_settings)
667 					return true;
668 				std::pair<std::uint16_t, std::string> v(aux::numeric_cast<std::uint16_t>(name), std::string());
669 				auto i = std::lower_bound(m_strings.begin(), m_strings.end(), v
670 						, &compare_first<std::string>);
671 				return i != m_strings.end() && i->first == name;
672 			}
673 			case int_type_base:
674 			{
675 				// this is an optimization. If the settings pack is complete,
676 				// i.e. has every key, we don't need to search, it's just a lookup
677 				if (m_ints.size() == settings_pack::num_int_settings)
678 					return true;
679 				std::pair<std::uint16_t, int> v(aux::numeric_cast<std::uint16_t>(name), 0);
680 				auto i = std::lower_bound(m_ints.begin(), m_ints.end(), v
681 						, &compare_first<int>);
682 				return i != m_ints.end() && i->first == name;
683 			}
684 			case bool_type_base:
685 			{
686 				// this is an optimization. If the settings pack is complete,
687 				// i.e. has every key, we don't need to search, it's just a lookup
688 				if (m_bools.size() == settings_pack::num_bool_settings)
689 					return true;
690 				std::pair<std::uint16_t, bool> v(aux::numeric_cast<std::uint16_t>(name), false);
691 				auto i = std::lower_bound(m_bools.begin(), m_bools.end(), v
692 						, &compare_first<bool>);
693 				return i != m_bools.end() && i->first == name;
694 			}
695 		}
696 		TORRENT_ASSERT_FAIL();
697 		return false;
698 	}
699 
get_str(int name) const700 	std::string const& settings_pack::get_str(int name) const
701 	{
702 		static std::string const empty;
703 		TORRENT_ASSERT((name & type_mask) == string_type_base);
704 		if ((name & type_mask) != string_type_base) return empty;
705 
706 		// this is an optimization. If the settings pack is complete,
707 		// i.e. has every key, we don't need to search, it's just a lookup
708 		if (m_strings.size() == settings_pack::num_string_settings)
709 		{
710 			TORRENT_ASSERT(m_strings[name & index_mask].first == name);
711 			return m_strings[name & index_mask].second;
712 		}
713 		std::pair<std::uint16_t, std::string> v(aux::numeric_cast<std::uint16_t>(name), std::string());
714 		auto i = std::lower_bound(m_strings.begin(), m_strings.end(), v
715 				, &compare_first<std::string>);
716 		if (i != m_strings.end() && i->first == name) return i->second;
717 		return empty;
718 	}
719 
get_int(int name) const720 	int settings_pack::get_int(int name) const
721 	{
722 		TORRENT_ASSERT((name & type_mask) == int_type_base);
723 		if ((name & type_mask) != int_type_base) return 0;
724 
725 		// this is an optimization. If the settings pack is complete,
726 		// i.e. has every key, we don't need to search, it's just a lookup
727 		if (m_ints.size() == settings_pack::num_int_settings)
728 		{
729 			TORRENT_ASSERT(m_ints[name & index_mask].first == name);
730 			return m_ints[name & index_mask].second;
731 		}
732 		std::pair<std::uint16_t, int> v(aux::numeric_cast<std::uint16_t>(name), 0);
733 		auto i = std::lower_bound(m_ints.begin(), m_ints.end(), v
734 				, &compare_first<int>);
735 		if (i != m_ints.end() && i->first == name) return i->second;
736 		return 0;
737 	}
738 
get_bool(int name) const739 	bool settings_pack::get_bool(int name) const
740 	{
741 		TORRENT_ASSERT((name & type_mask) == bool_type_base);
742 		if ((name & type_mask) != bool_type_base) return false;
743 
744 		// this is an optimization. If the settings pack is complete,
745 		// i.e. has every key, we don't need to search, it's just a lookup
746 		if (m_bools.size() == settings_pack::num_bool_settings)
747 		{
748 			TORRENT_ASSERT(m_bools[name & index_mask].first == name);
749 			return m_bools[name & index_mask].second;
750 		}
751 		std::pair<std::uint16_t, bool> v(aux::numeric_cast<std::uint16_t>(name), false);
752 		auto i = std::lower_bound(m_bools.begin(), m_bools.end(), v
753 					, &compare_first<bool>);
754 		if (i != m_bools.end() && i->first == name) return i->second;
755 		return false;
756 	}
757 
clear()758 	void settings_pack::clear()
759 	{
760 		m_strings.clear();
761 		m_ints.clear();
762 		m_bools.clear();
763 	}
764 
clear(int const name)765 	void settings_pack::clear(int const name)
766 	{
767 		switch (name & type_mask)
768 		{
769 			case string_type_base:
770 			{
771 				std::pair<std::uint16_t, std::string> v(aux::numeric_cast<std::uint16_t>(name), std::string());
772 				auto const i = std::lower_bound(m_strings.begin(), m_strings.end()
773 					, v, &compare_first<std::string>);
774 				if (i != m_strings.end() && i->first == name) m_strings.erase(i);
775 				break;
776 			}
777 			case int_type_base:
778 			{
779 				std::pair<std::uint16_t, int> v(aux::numeric_cast<std::uint16_t>(name), 0);
780 				auto const i = std::lower_bound(m_ints.begin(), m_ints.end()
781 					, v, &compare_first<int>);
782 				if (i != m_ints.end() && i->first == name) m_ints.erase(i);
783 				break;
784 			}
785 			case bool_type_base:
786 			{
787 				std::pair<std::uint16_t, bool> v(aux::numeric_cast<std::uint16_t>(name), false);
788 				auto const i = std::lower_bound(m_bools.begin(), m_bools.end()
789 					, v, &compare_first<bool>);
790 				if (i != m_bools.end() && i->first == name) m_bools.erase(i);
791 				break;
792 			}
793 		}
794 	}
795 }
796