forked from premiere/premiere-libtorrent
added an asynchronous save_resume_data to make it easier to synchronize with the disk IO thread
This commit is contained in:
parent
6639f72804
commit
3fea2080fd
348
docs/manual.html
348
docs/manual.html
|
@ -24,146 +24,147 @@
|
|||
<div class="contents topic" id="table-of-contents">
|
||||
<p class="topic-title first">Table of contents</p>
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#overview" id="id18">overview</a></li>
|
||||
<li><a class="reference internal" href="#network-primitives" id="id19">network primitives</a></li>
|
||||
<li><a class="reference internal" href="#session" id="id20">session</a><ul>
|
||||
<li><a class="reference internal" href="#id1" id="id21">session()</a></li>
|
||||
<li><a class="reference internal" href="#id2" id="id22">~session()</a></li>
|
||||
<li><a class="reference internal" href="#abort" id="id23">abort()</a></li>
|
||||
<li><a class="reference internal" href="#add-torrent" id="id24">add_torrent()</a></li>
|
||||
<li><a class="reference internal" href="#remove-torrent" id="id25">remove_torrent()</a></li>
|
||||
<li><a class="reference internal" href="#find-torrent-get-torrents" id="id26">find_torrent() get_torrents()</a></li>
|
||||
<li><a class="reference internal" href="#set-upload-rate-limit-set-download-rate-limit-upload-rate-limit-download-rate-limit" id="id27">set_upload_rate_limit() set_download_rate_limit() upload_rate_limit() download_rate_limit()</a></li>
|
||||
<li><a class="reference internal" href="#set-max-uploads-set-max-connections" id="id28">set_max_uploads() set_max_connections()</a></li>
|
||||
<li><a class="reference internal" href="#num-uploads-num-connections" id="id29">num_uploads() num_connections()</a></li>
|
||||
<li><a class="reference internal" href="#set-max-half-open-connections-max-half-open-connections" id="id30">set_max_half_open_connections() max_half_open_connections()</a></li>
|
||||
<li><a class="reference internal" href="#load-asnum-db-load-country-db" id="id31">load_asnum_db() load_country_db()</a></li>
|
||||
<li><a class="reference internal" href="#load-state-state" id="id32">load_state() state()</a></li>
|
||||
<li><a class="reference internal" href="#set-ip-filter" id="id33">set_ip_filter()</a></li>
|
||||
<li><a class="reference internal" href="#status" id="id34">status()</a></li>
|
||||
<li><a class="reference internal" href="#get-cache-status" id="id35">get_cache_status()</a></li>
|
||||
<li><a class="reference internal" href="#get-cache-info" id="id36">get_cache_info()</a></li>
|
||||
<li><a class="reference internal" href="#is-listening-listen-port-listen-on" id="id37">is_listening() listen_port() listen_on()</a></li>
|
||||
<li><a class="reference internal" href="#pop-alert-set-severity-level" id="id38">pop_alert() set_severity_level()</a></li>
|
||||
<li><a class="reference internal" href="#add-extension" id="id39">add_extension()</a></li>
|
||||
<li><a class="reference internal" href="#set-settings-set-pe-settings" id="id40">set_settings() set_pe_settings()</a></li>
|
||||
<li><a class="reference internal" href="#set-peer-proxy-set-web-seed-proxy-set-tracker-proxy-set-dht-proxy" id="id41">set_peer_proxy() set_web_seed_proxy() set_tracker_proxy() set_dht_proxy()</a></li>
|
||||
<li><a class="reference internal" href="#peer-proxy-web-seed-proxy-tracker-proxy-dht-proxy" id="id42">peer_proxy() web_seed_proxy() tracker_proxy() dht_proxy()</a></li>
|
||||
<li><a class="reference internal" href="#start-dht-stop-dht-set-dht-settings-dht-state" id="id43">start_dht() stop_dht() set_dht_settings() dht_state()</a></li>
|
||||
<li><a class="reference internal" href="#add-dht-node-add-dht-router" id="id44">add_dht_node() add_dht_router()</a></li>
|
||||
<li><a class="reference internal" href="#start-lsd-stop-lsd" id="id45">start_lsd() stop_lsd()</a></li>
|
||||
<li><a class="reference internal" href="#start-upnp-stop-upnp" id="id46">start_upnp() stop_upnp()</a></li>
|
||||
<li><a class="reference internal" href="#start-natpmp-stop-natpmp" id="id47">start_natpmp() stop_natpmp()</a></li>
|
||||
<li><a class="reference internal" href="#overview" id="id17">overview</a></li>
|
||||
<li><a class="reference internal" href="#network-primitives" id="id18">network primitives</a></li>
|
||||
<li><a class="reference internal" href="#session" id="id19">session</a><ul>
|
||||
<li><a class="reference internal" href="#id1" id="id20">session()</a></li>
|
||||
<li><a class="reference internal" href="#id2" id="id21">~session()</a></li>
|
||||
<li><a class="reference internal" href="#abort" id="id22">abort()</a></li>
|
||||
<li><a class="reference internal" href="#add-torrent" id="id23">add_torrent()</a></li>
|
||||
<li><a class="reference internal" href="#remove-torrent" id="id24">remove_torrent()</a></li>
|
||||
<li><a class="reference internal" href="#find-torrent-get-torrents" id="id25">find_torrent() get_torrents()</a></li>
|
||||
<li><a class="reference internal" href="#set-upload-rate-limit-set-download-rate-limit-upload-rate-limit-download-rate-limit" id="id26">set_upload_rate_limit() set_download_rate_limit() upload_rate_limit() download_rate_limit()</a></li>
|
||||
<li><a class="reference internal" href="#set-max-uploads-set-max-connections" id="id27">set_max_uploads() set_max_connections()</a></li>
|
||||
<li><a class="reference internal" href="#num-uploads-num-connections" id="id28">num_uploads() num_connections()</a></li>
|
||||
<li><a class="reference internal" href="#set-max-half-open-connections-max-half-open-connections" id="id29">set_max_half_open_connections() max_half_open_connections()</a></li>
|
||||
<li><a class="reference internal" href="#load-asnum-db-load-country-db" id="id30">load_asnum_db() load_country_db()</a></li>
|
||||
<li><a class="reference internal" href="#load-state-state" id="id31">load_state() state()</a></li>
|
||||
<li><a class="reference internal" href="#set-ip-filter" id="id32">set_ip_filter()</a></li>
|
||||
<li><a class="reference internal" href="#status" id="id33">status()</a></li>
|
||||
<li><a class="reference internal" href="#get-cache-status" id="id34">get_cache_status()</a></li>
|
||||
<li><a class="reference internal" href="#get-cache-info" id="id35">get_cache_info()</a></li>
|
||||
<li><a class="reference internal" href="#is-listening-listen-port-listen-on" id="id36">is_listening() listen_port() listen_on()</a></li>
|
||||
<li><a class="reference internal" href="#pop-alert-set-severity-level" id="id37">pop_alert() set_severity_level()</a></li>
|
||||
<li><a class="reference internal" href="#add-extension" id="id38">add_extension()</a></li>
|
||||
<li><a class="reference internal" href="#set-settings-set-pe-settings" id="id39">set_settings() set_pe_settings()</a></li>
|
||||
<li><a class="reference internal" href="#set-peer-proxy-set-web-seed-proxy-set-tracker-proxy-set-dht-proxy" id="id40">set_peer_proxy() set_web_seed_proxy() set_tracker_proxy() set_dht_proxy()</a></li>
|
||||
<li><a class="reference internal" href="#peer-proxy-web-seed-proxy-tracker-proxy-dht-proxy" id="id41">peer_proxy() web_seed_proxy() tracker_proxy() dht_proxy()</a></li>
|
||||
<li><a class="reference internal" href="#start-dht-stop-dht-set-dht-settings-dht-state" id="id42">start_dht() stop_dht() set_dht_settings() dht_state()</a></li>
|
||||
<li><a class="reference internal" href="#add-dht-node-add-dht-router" id="id43">add_dht_node() add_dht_router()</a></li>
|
||||
<li><a class="reference internal" href="#start-lsd-stop-lsd" id="id44">start_lsd() stop_lsd()</a></li>
|
||||
<li><a class="reference internal" href="#start-upnp-stop-upnp" id="id45">start_upnp() stop_upnp()</a></li>
|
||||
<li><a class="reference internal" href="#start-natpmp-stop-natpmp" id="id46">start_natpmp() stop_natpmp()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#entry" id="id48">entry</a><ul>
|
||||
<li><a class="reference internal" href="#integer-string-list-dict-type" id="id49">integer() string() list() dict() type()</a></li>
|
||||
<li><a class="reference internal" href="#operator" id="id50">operator[]</a></li>
|
||||
<li><a class="reference internal" href="#find-key" id="id51">find_key()</a></li>
|
||||
<li><a class="reference internal" href="#entry" id="id47">entry</a><ul>
|
||||
<li><a class="reference internal" href="#integer-string-list-dict-type" id="id48">integer() string() list() dict() type()</a></li>
|
||||
<li><a class="reference internal" href="#operator" id="id49">operator[]</a></li>
|
||||
<li><a class="reference internal" href="#find-key" id="id50">find_key()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#torrent-info" id="id52">torrent_info</a><ul>
|
||||
<li><a class="reference internal" href="#id3" id="id53">torrent_info()</a></li>
|
||||
<li><a class="reference internal" href="#set-comment-set-piece-size-set-creator-set-hash-add-tracker-add-file" id="id54">set_comment() set_piece_size() set_creator() set_hash() add_tracker() add_file()</a></li>
|
||||
<li><a class="reference internal" href="#create-torrent" id="id55">create_torrent()</a></li>
|
||||
<li><a class="reference internal" href="#remap-files" id="id56">remap_files()</a></li>
|
||||
<li><a class="reference internal" href="#begin-files-end-files-rbegin-files-rend-files" id="id57">begin_files() end_files() rbegin_files() rend_files()</a></li>
|
||||
<li><a class="reference internal" href="#num-files-file-at" id="id58">num_files() file_at()</a></li>
|
||||
<li><a class="reference internal" href="#map-block" id="id59">map_block()</a></li>
|
||||
<li><a class="reference internal" href="#map-file" id="id60">map_file()</a></li>
|
||||
<li><a class="reference internal" href="#url-seeds-add-url-seed" id="id61">url_seeds() add_url_seed()</a></li>
|
||||
<li><a class="reference internal" href="#print" id="id62">print()</a></li>
|
||||
<li><a class="reference internal" href="#trackers" id="id63">trackers()</a></li>
|
||||
<li><a class="reference internal" href="#total-size-piece-length-piece-size-num-pieces" id="id64">total_size() piece_length() piece_size() num_pieces()</a></li>
|
||||
<li><a class="reference internal" href="#hash-for-piece-info-hash" id="id65">hash_for_piece() info_hash()</a></li>
|
||||
<li><a class="reference internal" href="#name-comment-creation-date-creator" id="id66">name() comment() creation_date() creator()</a></li>
|
||||
<li><a class="reference internal" href="#priv-set-priv" id="id67">priv() set_priv()</a></li>
|
||||
<li><a class="reference internal" href="#nodes" id="id68">nodes()</a></li>
|
||||
<li><a class="reference internal" href="#add-node" id="id69">add_node()</a></li>
|
||||
<li><a class="reference internal" href="#torrent-info" id="id51">torrent_info</a><ul>
|
||||
<li><a class="reference internal" href="#id3" id="id52">torrent_info()</a></li>
|
||||
<li><a class="reference internal" href="#set-comment-set-piece-size-set-creator-set-hash-add-tracker-add-file" id="id53">set_comment() set_piece_size() set_creator() set_hash() add_tracker() add_file()</a></li>
|
||||
<li><a class="reference internal" href="#create-torrent" id="id54">create_torrent()</a></li>
|
||||
<li><a class="reference internal" href="#remap-files" id="id55">remap_files()</a></li>
|
||||
<li><a class="reference internal" href="#begin-files-end-files-rbegin-files-rend-files" id="id56">begin_files() end_files() rbegin_files() rend_files()</a></li>
|
||||
<li><a class="reference internal" href="#num-files-file-at" id="id57">num_files() file_at()</a></li>
|
||||
<li><a class="reference internal" href="#map-block" id="id58">map_block()</a></li>
|
||||
<li><a class="reference internal" href="#map-file" id="id59">map_file()</a></li>
|
||||
<li><a class="reference internal" href="#url-seeds-add-url-seed" id="id60">url_seeds() add_url_seed()</a></li>
|
||||
<li><a class="reference internal" href="#print" id="id61">print()</a></li>
|
||||
<li><a class="reference internal" href="#trackers" id="id62">trackers()</a></li>
|
||||
<li><a class="reference internal" href="#total-size-piece-length-piece-size-num-pieces" id="id63">total_size() piece_length() piece_size() num_pieces()</a></li>
|
||||
<li><a class="reference internal" href="#hash-for-piece-info-hash" id="id64">hash_for_piece() info_hash()</a></li>
|
||||
<li><a class="reference internal" href="#name-comment-creation-date-creator" id="id65">name() comment() creation_date() creator()</a></li>
|
||||
<li><a class="reference internal" href="#priv-set-priv" id="id66">priv() set_priv()</a></li>
|
||||
<li><a class="reference internal" href="#nodes" id="id67">nodes()</a></li>
|
||||
<li><a class="reference internal" href="#add-node" id="id68">add_node()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#torrent-handle" id="id70">torrent_handle</a><ul>
|
||||
<li><a class="reference internal" href="#piece-priority-prioritize-pieces-piece-priorities-prioritize-files" id="id71">piece_priority() prioritize_pieces() piece_priorities() prioritize_files()</a></li>
|
||||
<li><a class="reference internal" href="#file-progress" id="id72">file_progress()</a></li>
|
||||
<li><a class="reference internal" href="#save-path" id="id73">save_path()</a></li>
|
||||
<li><a class="reference internal" href="#move-storage" id="id74">move_storage()</a></li>
|
||||
<li><a class="reference internal" href="#force-reannounce" id="id75">force_reannounce()</a></li>
|
||||
<li><a class="reference internal" href="#scrape-tracker" id="id76">scrape_tracker()</a></li>
|
||||
<li><a class="reference internal" href="#connect-peer" id="id77">connect_peer()</a></li>
|
||||
<li><a class="reference internal" href="#name" id="id78">name()</a></li>
|
||||
<li><a class="reference internal" href="#set-ratio" id="id79">set_ratio()</a></li>
|
||||
<li><a class="reference internal" href="#set-upload-limit-set-download-limit-upload-limit-download-limit" id="id80">set_upload_limit() set_download_limit() upload_limit() download_limit()</a></li>
|
||||
<li><a class="reference internal" href="#set-sequential-download" id="id81">set_sequential_download()</a></li>
|
||||
<li><a class="reference internal" href="#set-peer-upload-limit-set-peer-download-limit" id="id82">set_peer_upload_limit() set_peer_download_limit()</a></li>
|
||||
<li><a class="reference internal" href="#pause-resume-is-paused" id="id83">pause() resume() is_paused()</a></li>
|
||||
<li><a class="reference internal" href="#resolve-countries" id="id84">resolve_countries()</a></li>
|
||||
<li><a class="reference internal" href="#is-seed" id="id85">is_seed()</a></li>
|
||||
<li><a class="reference internal" href="#has-metadata" id="id86">has_metadata()</a></li>
|
||||
<li><a class="reference internal" href="#set-tracker-login" id="id87">set_tracker_login()</a></li>
|
||||
<li><a class="reference internal" href="#trackers-replace-trackers" id="id88">trackers() replace_trackers()</a></li>
|
||||
<li><a class="reference internal" href="#add-url-seed-remove-url-seed-url-seeds" id="id89">add_url_seed() remove_url_seed() url_seeds()</a></li>
|
||||
<li><a class="reference internal" href="#use-interface" id="id90">use_interface()</a></li>
|
||||
<li><a class="reference internal" href="#info-hash" id="id91">info_hash()</a></li>
|
||||
<li><a class="reference internal" href="#id5" id="id92">set_max_uploads() set_max_connections()</a></li>
|
||||
<li><a class="reference internal" href="#write-resume-data" id="id93">write_resume_data()</a></li>
|
||||
<li><a class="reference internal" href="#id6" id="id94">status()</a></li>
|
||||
<li><a class="reference internal" href="#get-download-queue" id="id95">get_download_queue()</a></li>
|
||||
<li><a class="reference internal" href="#get-peer-info" id="id96">get_peer_info()</a></li>
|
||||
<li><a class="reference internal" href="#get-torrent-info" id="id97">get_torrent_info()</a></li>
|
||||
<li><a class="reference internal" href="#is-valid" id="id98">is_valid()</a></li>
|
||||
<li><a class="reference internal" href="#torrent-handle" id="id69">torrent_handle</a><ul>
|
||||
<li><a class="reference internal" href="#piece-priority-prioritize-pieces-piece-priorities-prioritize-files" id="id70">piece_priority() prioritize_pieces() piece_priorities() prioritize_files()</a></li>
|
||||
<li><a class="reference internal" href="#file-progress" id="id71">file_progress()</a></li>
|
||||
<li><a class="reference internal" href="#save-path" id="id72">save_path()</a></li>
|
||||
<li><a class="reference internal" href="#move-storage" id="id73">move_storage()</a></li>
|
||||
<li><a class="reference internal" href="#force-reannounce" id="id74">force_reannounce()</a></li>
|
||||
<li><a class="reference internal" href="#scrape-tracker" id="id75">scrape_tracker()</a></li>
|
||||
<li><a class="reference internal" href="#connect-peer" id="id76">connect_peer()</a></li>
|
||||
<li><a class="reference internal" href="#name" id="id77">name()</a></li>
|
||||
<li><a class="reference internal" href="#set-ratio" id="id78">set_ratio()</a></li>
|
||||
<li><a class="reference internal" href="#set-upload-limit-set-download-limit-upload-limit-download-limit" id="id79">set_upload_limit() set_download_limit() upload_limit() download_limit()</a></li>
|
||||
<li><a class="reference internal" href="#set-sequential-download" id="id80">set_sequential_download()</a></li>
|
||||
<li><a class="reference internal" href="#set-peer-upload-limit-set-peer-download-limit" id="id81">set_peer_upload_limit() set_peer_download_limit()</a></li>
|
||||
<li><a class="reference internal" href="#pause-resume-is-paused" id="id82">pause() resume() is_paused()</a></li>
|
||||
<li><a class="reference internal" href="#resolve-countries" id="id83">resolve_countries()</a></li>
|
||||
<li><a class="reference internal" href="#is-seed" id="id84">is_seed()</a></li>
|
||||
<li><a class="reference internal" href="#has-metadata" id="id85">has_metadata()</a></li>
|
||||
<li><a class="reference internal" href="#set-tracker-login" id="id86">set_tracker_login()</a></li>
|
||||
<li><a class="reference internal" href="#trackers-replace-trackers" id="id87">trackers() replace_trackers()</a></li>
|
||||
<li><a class="reference internal" href="#add-url-seed-remove-url-seed-url-seeds" id="id88">add_url_seed() remove_url_seed() url_seeds()</a></li>
|
||||
<li><a class="reference internal" href="#use-interface" id="id89">use_interface()</a></li>
|
||||
<li><a class="reference internal" href="#info-hash" id="id90">info_hash()</a></li>
|
||||
<li><a class="reference internal" href="#id5" id="id91">set_max_uploads() set_max_connections()</a></li>
|
||||
<li><a class="reference internal" href="#save-resume-data" id="id92">save_resume_data()</a></li>
|
||||
<li><a class="reference internal" href="#id6" id="id93">status()</a></li>
|
||||
<li><a class="reference internal" href="#get-download-queue" id="id94">get_download_queue()</a></li>
|
||||
<li><a class="reference internal" href="#get-peer-info" id="id95">get_peer_info()</a></li>
|
||||
<li><a class="reference internal" href="#get-torrent-info" id="id96">get_torrent_info()</a></li>
|
||||
<li><a class="reference internal" href="#is-valid" id="id97">is_valid()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#torrent-status" id="id99">torrent_status</a></li>
|
||||
<li><a class="reference internal" href="#peer-info" id="id100">peer_info</a></li>
|
||||
<li><a class="reference internal" href="#session-settings" id="id101">session_settings</a></li>
|
||||
<li><a class="reference internal" href="#pe-settings" id="id102">pe_settings</a></li>
|
||||
<li><a class="reference internal" href="#proxy-settings" id="id103">proxy_settings</a></li>
|
||||
<li><a class="reference internal" href="#ip-filter" id="id104">ip_filter</a><ul>
|
||||
<li><a class="reference internal" href="#id9" id="id105">ip_filter()</a></li>
|
||||
<li><a class="reference internal" href="#add-rule" id="id106">add_rule()</a></li>
|
||||
<li><a class="reference internal" href="#access" id="id107">access()</a></li>
|
||||
<li><a class="reference internal" href="#export-filter" id="id108">export_filter()</a></li>
|
||||
<li><a class="reference internal" href="#torrent-status" id="id98">torrent_status</a></li>
|
||||
<li><a class="reference internal" href="#peer-info" id="id99">peer_info</a></li>
|
||||
<li><a class="reference internal" href="#session-settings" id="id100">session_settings</a></li>
|
||||
<li><a class="reference internal" href="#pe-settings" id="id101">pe_settings</a></li>
|
||||
<li><a class="reference internal" href="#proxy-settings" id="id102">proxy_settings</a></li>
|
||||
<li><a class="reference internal" href="#ip-filter" id="id103">ip_filter</a><ul>
|
||||
<li><a class="reference internal" href="#id9" id="id104">ip_filter()</a></li>
|
||||
<li><a class="reference internal" href="#add-rule" id="id105">add_rule()</a></li>
|
||||
<li><a class="reference internal" href="#access" id="id106">access()</a></li>
|
||||
<li><a class="reference internal" href="#export-filter" id="id107">export_filter()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#big-number" id="id109">big_number</a></li>
|
||||
<li><a class="reference internal" href="#hasher" id="id110">hasher</a></li>
|
||||
<li><a class="reference internal" href="#fingerprint" id="id111">fingerprint</a></li>
|
||||
<li><a class="reference internal" href="#upnp-and-nat-pmp" id="id112">UPnP and NAT-PMP</a><ul>
|
||||
<li><a class="reference internal" href="#add-mapping" id="id113">add_mapping</a></li>
|
||||
<li><a class="reference internal" href="#delete-mapping" id="id114">delete_mapping</a></li>
|
||||
<li><a class="reference internal" href="#router-model" id="id115">router_model()</a></li>
|
||||
<li><a class="reference internal" href="#big-number" id="id108">big_number</a></li>
|
||||
<li><a class="reference internal" href="#hasher" id="id109">hasher</a></li>
|
||||
<li><a class="reference internal" href="#fingerprint" id="id110">fingerprint</a></li>
|
||||
<li><a class="reference internal" href="#upnp-and-nat-pmp" id="id111">UPnP and NAT-PMP</a><ul>
|
||||
<li><a class="reference internal" href="#add-mapping" id="id112">add_mapping</a></li>
|
||||
<li><a class="reference internal" href="#delete-mapping" id="id113">delete_mapping</a></li>
|
||||
<li><a class="reference internal" href="#router-model" id="id114">router_model()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#free-functions" id="id116">free functions</a><ul>
|
||||
<li><a class="reference internal" href="#identify-client" id="id117">identify_client()</a></li>
|
||||
<li><a class="reference internal" href="#client-fingerprint" id="id118">client_fingerprint()</a></li>
|
||||
<li><a class="reference internal" href="#bdecode-bencode" id="id119">bdecode() bencode()</a></li>
|
||||
<li><a class="reference internal" href="#free-functions" id="id115">free functions</a><ul>
|
||||
<li><a class="reference internal" href="#identify-client" id="id116">identify_client()</a></li>
|
||||
<li><a class="reference internal" href="#client-fingerprint" id="id117">client_fingerprint()</a></li>
|
||||
<li><a class="reference internal" href="#bdecode-bencode" id="id118">bdecode() bencode()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#alerts" id="id120">alerts</a><ul>
|
||||
<li><a class="reference internal" href="#external-ip-alert" id="id121">external_ip_alert</a></li>
|
||||
<li><a class="reference internal" href="#listen-failed-alert" id="id122">listen_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#portmap-error-alert" id="id123">portmap_error_alert</a></li>
|
||||
<li><a class="reference internal" href="#portmap-alert" id="id124">portmap_alert</a></li>
|
||||
<li><a class="reference internal" href="#file-error-alert" id="id125">file_error_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-announce-alert" id="id126">tracker_announce_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-alert" id="id127">tracker_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-reply-alert" id="id128">tracker_reply_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-warning-alert" id="id129">tracker_warning_alert</a></li>
|
||||
<li><a class="reference internal" href="#scrape-reply-alert" id="id130">scrape_reply_alert</a></li>
|
||||
<li><a class="reference internal" href="#scrape-failed-alert" id="id131">scrape_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#url-seed-alert" id="id132">url_seed_alert</a></li>
|
||||
<li><a class="reference internal" href="#hash-failed-alert" id="id133">hash_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#peer-ban-alert" id="id134">peer_ban_alert</a></li>
|
||||
<li><a class="reference internal" href="#peer-error-alert" id="id135">peer_error_alert</a></li>
|
||||
<li><a class="reference internal" href="#invalid-request-alert" id="id136">invalid_request_alert</a></li>
|
||||
<li><a class="reference internal" href="#torrent-finished-alert" id="id137">torrent_finished_alert</a></li>
|
||||
<li><a class="reference internal" href="#metadata-failed-alert" id="id138">metadata_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#metadata-received-alert" id="id139">metadata_received_alert</a></li>
|
||||
<li><a class="reference internal" href="#fastresume-rejected-alert" id="id140">fastresume_rejected_alert</a></li>
|
||||
<li><a class="reference internal" href="#peer-blocked-alert" id="id141">peer_blocked_alert</a></li>
|
||||
<li><a class="reference internal" href="#storage-moved-alert" id="id142">storage_moved_alert</a></li>
|
||||
<li><a class="reference internal" href="#torrent-paused-alert" id="id143">torrent_paused_alert</a></li>
|
||||
<li><a class="reference internal" href="#alerts" id="id119">alerts</a><ul>
|
||||
<li><a class="reference internal" href="#external-ip-alert" id="id120">external_ip_alert</a></li>
|
||||
<li><a class="reference internal" href="#listen-failed-alert" id="id121">listen_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#portmap-error-alert" id="id122">portmap_error_alert</a></li>
|
||||
<li><a class="reference internal" href="#portmap-alert" id="id123">portmap_alert</a></li>
|
||||
<li><a class="reference internal" href="#file-error-alert" id="id124">file_error_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-announce-alert" id="id125">tracker_announce_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-alert" id="id126">tracker_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-reply-alert" id="id127">tracker_reply_alert</a></li>
|
||||
<li><a class="reference internal" href="#tracker-warning-alert" id="id128">tracker_warning_alert</a></li>
|
||||
<li><a class="reference internal" href="#scrape-reply-alert" id="id129">scrape_reply_alert</a></li>
|
||||
<li><a class="reference internal" href="#scrape-failed-alert" id="id130">scrape_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#url-seed-alert" id="id131">url_seed_alert</a></li>
|
||||
<li><a class="reference internal" href="#hash-failed-alert" id="id132">hash_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#peer-ban-alert" id="id133">peer_ban_alert</a></li>
|
||||
<li><a class="reference internal" href="#peer-error-alert" id="id134">peer_error_alert</a></li>
|
||||
<li><a class="reference internal" href="#invalid-request-alert" id="id135">invalid_request_alert</a></li>
|
||||
<li><a class="reference internal" href="#torrent-finished-alert" id="id136">torrent_finished_alert</a></li>
|
||||
<li><a class="reference internal" href="#metadata-failed-alert" id="id137">metadata_failed_alert</a></li>
|
||||
<li><a class="reference internal" href="#metadata-received-alert" id="id138">metadata_received_alert</a></li>
|
||||
<li><a class="reference internal" href="#fastresume-rejected-alert" id="id139">fastresume_rejected_alert</a></li>
|
||||
<li><a class="reference internal" href="#peer-blocked-alert" id="id140">peer_blocked_alert</a></li>
|
||||
<li><a class="reference internal" href="#storage-moved-alert" id="id141">storage_moved_alert</a></li>
|
||||
<li><a class="reference internal" href="#torrent-paused-alert" id="id142">torrent_paused_alert</a></li>
|
||||
<li><a class="reference internal" href="#save-resume-data-alert" id="id143">save_resume_data_alert</a></li>
|
||||
<li><a class="reference internal" href="#dispatcher" id="id144">dispatcher</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -181,7 +182,7 @@
|
|||
<li><a class="reference internal" href="#write" id="id154">write()</a></li>
|
||||
<li><a class="reference internal" href="#id11" id="id155">move_storage()</a></li>
|
||||
<li><a class="reference internal" href="#verify-resume-data" id="id156">verify_resume_data()</a></li>
|
||||
<li><a class="reference internal" href="#id12" id="id157">write_resume_data( )</a></li>
|
||||
<li><a class="reference internal" href="#write-resume-data" id="id157">write_resume_data()</a></li>
|
||||
<li><a class="reference internal" href="#move-slot" id="id158">move_slot()</a></li>
|
||||
<li><a class="reference internal" href="#swap-slots" id="id159">swap_slots()</a></li>
|
||||
<li><a class="reference internal" href="#swap-slots3" id="id160">swap_slots3()</a></li>
|
||||
|
@ -233,7 +234,7 @@ the <tt class="docutils literal"><span class="pre">session</span></tt>, it conta
|
|||
</blockquote>
|
||||
</li>
|
||||
<li><p class="first">save resume data for all torrent_handles (optional, see
|
||||
<a class="reference internal" href="#write-resume-data">write_resume_data()</a>)</p>
|
||||
<a class="reference internal" href="#save-resume-data">save_resume_data()</a>)</p>
|
||||
</li>
|
||||
<li><p class="first">destruct session object</p>
|
||||
</li>
|
||||
|
@ -466,7 +467,7 @@ for checking, being checked or downloading) <tt class="docutils literal"><span c
|
|||
<a class="reference internal" href="#duplicate-torrent">duplicate_torrent</a> which derives from <tt class="docutils literal"><span class="pre">std::exception</span></tt>.</p>
|
||||
<p>The optional parameter, <tt class="docutils literal"><span class="pre">resume_data</span></tt> can be given if up to date fast-resume data
|
||||
is available. The fast-resume data can be acquired from a running torrent by calling
|
||||
<tt class="docutils literal"><span class="pre">torrent_handle::write_resume_data()</span></tt>. See <a class="reference internal" href="#fast-resume">fast resume</a>.</p>
|
||||
<a class="reference internal" href="#save-resume-data">save_resume_data()</a> on <a class="reference internal" href="#torrent-handle">torrent_handle</a>. See <a class="reference internal" href="#fast-resume">fast resume</a>.</p>
|
||||
<p>The <tt class="docutils literal"><span class="pre">storage_mode</span></tt> parameter refers to the layout of the storage for this torrent.
|
||||
There are 3 different modes:</p>
|
||||
<dl class="docutils">
|
||||
|
@ -1627,7 +1628,7 @@ struct torrent_handle
|
|||
|
||||
std::string name() const;
|
||||
|
||||
entry write_resume_data() const;
|
||||
void save_resume_data() const;
|
||||
void force_reannounce() const;
|
||||
void force_reannounce(boost::posix_time::time_duration) const;
|
||||
void scrape_tracker() const;
|
||||
|
@ -2024,16 +2025,18 @@ connections are used up, incoming connections may be refused or poor connections
|
|||
This must be at least 2. The default is unlimited number of connections. If -1 is given to the
|
||||
function, it means unlimited.</p>
|
||||
</div>
|
||||
<div class="section" id="write-resume-data">
|
||||
<h2>write_resume_data()</h2>
|
||||
<div class="section" id="save-resume-data">
|
||||
<h2>save_resume_data()</h2>
|
||||
<blockquote>
|
||||
<pre class="literal-block">
|
||||
entry write_resume_data() const;
|
||||
void save_resume_data() const;
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p><tt class="docutils literal"><span class="pre">write_resume_data()</span></tt> generates fast-resume data and returns it as an <a class="reference internal" href="#entry">entry</a>. This <a class="reference internal" href="#entry">entry</a>
|
||||
<p><tt class="docutils literal"><span class="pre">save_resume_data()</span></tt> generates fast-resume data and returns it as an <a class="reference internal" href="#entry">entry</a>. This <a class="reference internal" href="#entry">entry</a>
|
||||
is suitable for being bencoded. For more information about how fast-resume works, see <a class="reference internal" href="#fast-resume">fast resume</a>.</p>
|
||||
<p>There are three cases where this function will just return an empty <tt class="docutils literal"><span class="pre">entry</span></tt>:</p>
|
||||
<p>This operation is asynchronous, <tt class="docutils literal"><span class="pre">save_resume_data</span></tt> will return immediately. The resume data
|
||||
is delivered when it's done through an <a class="reference internal" href="#save-resume-data-alert">save_resume_data_alert</a>.</p>
|
||||
<p>The fast resume data will be empty in the following cases:</p>
|
||||
<blockquote>
|
||||
<ol class="arabic simple">
|
||||
<li>The torrent handle is invalid.</li>
|
||||
|
@ -2043,20 +2046,59 @@ not be ready to write resume data.</li>
|
|||
(see libtorrent's <a class="reference internal" href="#metadata-from-peers">metadata from peers</a> extension)</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<p>Note that by the time this function returns, the resume data may already be invalid if the torrent
|
||||
<p>Note that by the time you receive the fast resume data, it may already be invalid if the torrent
|
||||
is still downloading! The recommended practice is to first pause the torrent, then generate the
|
||||
fast resume data, and then close it down. Since the disk IO is done in a separate thread, in order
|
||||
to synchronize, you shoule to wait for the <tt class="docutils literal"><span class="pre">torrent_paused_alert</span></tt> before you write the resume
|
||||
data.</p>
|
||||
fast resume data, and then close it down. Make sure to not <a class="reference internal" href="#remove-torrent">remove_torrent()</a> before you receive
|
||||
the <a class="reference internal" href="#save-resume-data-alert">save_resume_data_alert</a> though. Only pause the torrent before you save the resume data
|
||||
if you will remove the torrent afterwards. There's no need to pause when saving intermittent
|
||||
resume data.</p>
|
||||
<p>In full allocation mode the reume data is never invalidated by subsequent
|
||||
writes to the files, since pieces won't move around. This means that you don't need to
|
||||
pause before writing resume data in full or sparse mode. If you don't, however, any data written to
|
||||
disk after you saved resume data and before the session closed is lost.</p>
|
||||
disk after you saved resume data and before the <a class="reference internal" href="#session">session</a> closed is lost.</p>
|
||||
<p>It also means that if the resume data is out dated, libtorrent will not re-check the files, but assume
|
||||
that it is fairly recent. The assumption is that it's better to loose a little bit than to re-check
|
||||
the entire file.</p>
|
||||
<p>It is still a good idea to save resume data periodically during download as well as when
|
||||
closing down.</p>
|
||||
<p>Example code to pause and save resume data for all torrents and wait for the alerts:</p>
|
||||
<pre class="literal-block">
|
||||
int num_resume_data = 0;
|
||||
std::vector<torrent_handle> handles = ses.get_torrents();
|
||||
for (std::vector<torrent_handle>::iterator i = handles.begin();
|
||||
i != handles.end(); ++i)
|
||||
{
|
||||
torrent_handle& h = *i;
|
||||
if (!h.has_metadata()) continue;
|
||||
|
||||
h.pause();
|
||||
h.save_resume_data();
|
||||
++num_resume_data;
|
||||
}
|
||||
|
||||
while (num_resume_data > 0)
|
||||
{
|
||||
alert const* a = ses.wait_for_alert(seconds(10));
|
||||
|
||||
// if we don't get an alert within 10 seconds, abort
|
||||
if (a == 0) break;
|
||||
|
||||
std::auto_ptr<alert> holder = ses.pop_alert();
|
||||
save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
|
||||
if (rd == 0)
|
||||
{
|
||||
process_alert(a);
|
||||
continue;
|
||||
}
|
||||
|
||||
torrent_handle h = rd->handle;
|
||||
boost::filesystem::ofstream out(h.save_path()
|
||||
/ (h.get_torrent_info().name() + ".fastresume"), std::ios_base::binary);
|
||||
out.unsetf(std::ios_base::skipws);
|
||||
bencode(std::ostream_iterator<char>(out), *rd->resume_data);
|
||||
--num_resume_data;
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="id6">
|
||||
<h2>status()</h2>
|
||||
|
@ -3866,6 +3908,22 @@ struct torrent_paused_alert: torrent_alert
|
|||
};
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="save-resume-data-alert">
|
||||
<h2>save_resume_data_alert</h2>
|
||||
<p>This alert is generated as a response to a <tt class="docutils literal"><span class="pre">torrent_handle::save_resume_data</span></tt> request.
|
||||
It is generated once the disk IO thread is done writing the state for this torrent.
|
||||
The <tt class="docutils literal"><span class="pre">resume_data</span></tt> member points to the resume data or is 0 on errors.</p>
|
||||
<pre class="literal-block">
|
||||
struct save_resume_data_alert: torrent_alert
|
||||
{
|
||||
save_resume_alert(torrent_handle const& h, std::string const& msg);
|
||||
|
||||
boost::shared_ptr<entry> resume_data;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const;
|
||||
};
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="dispatcher">
|
||||
<h2>dispatcher</h2>
|
||||
<p>The <tt class="docutils literal"><span class="pre">handle_alert</span></tt> class is defined in <tt class="docutils literal"><span class="pre"><libtorrent/alert.hpp></span></tt>.</p>
|
||||
|
@ -4051,8 +4109,8 @@ on disk. If the resume data seems to be up-to-date, return true. If
|
|||
not, set <tt class="docutils literal"><span class="pre">error</span></tt> to a description of what mismatched and return false.</p>
|
||||
<p>The default storage may compare file sizes and time stamps of the files.</p>
|
||||
</div>
|
||||
<div class="section" id="id12">
|
||||
<h2>write_resume_data( )</h2>
|
||||
<div class="section" id="write-resume-data">
|
||||
<h2>write_resume_data()</h2>
|
||||
<blockquote>
|
||||
<pre class="literal-block">
|
||||
void write_resume_data(entry& rd) const = 0;
|
||||
|
@ -4145,7 +4203,7 @@ void delete_files() = 0;
|
|||
<h1>fast resume</h1>
|
||||
<p>The fast resume mechanism is a way to remember which pieces are downloaded
|
||||
and where they are put between sessions. You can generate fast resume data by
|
||||
calling <tt class="docutils literal"><span class="pre">torrent_handle::write_resume_data()</span></tt> on <a class="reference internal" href="#torrent-handle">torrent_handle</a>. You can
|
||||
calling <a class="reference internal" href="#save-resume-data">save_resume_data()</a> on <a class="reference internal" href="#torrent-handle">torrent_handle</a>. You can
|
||||
then save this data to disk and use it when resuming the torrent. libtorrent
|
||||
will not check the piece hashes then, and rely on the information given in the
|
||||
fast-resume data. The fast-resume data also contains information about which
|
||||
|
@ -4180,6 +4238,10 @@ is the number of blocks per (normal sized) piece. Usually
|
|||
each block is 16 * 1024 bytes in size. But if piece size is
|
||||
greater than 4 megabytes, the block size will increase.</td>
|
||||
</tr>
|
||||
<tr><td><tt class="docutils literal"><span class="pre">pieces</span></tt></td>
|
||||
<td>A string with piece flags, one character per piece.
|
||||
Bit 1 means we have that piece.</td>
|
||||
</tr>
|
||||
<tr><td><tt class="docutils literal"><span class="pre">slots</span></tt></td>
|
||||
<td><p class="first">list of integers. The list maps slots to piece indices. It
|
||||
tells which piece is on which slot. If piece index is -2 it
|
||||
|
|
|
@ -28,7 +28,7 @@ The basic usage is as follows:
|
|||
* add and remove torrents from the session at run-time
|
||||
|
||||
* save resume data for all torrent_handles (optional, see
|
||||
`write_resume_data()`_)
|
||||
`save_resume_data()`_)
|
||||
* destruct session object
|
||||
|
||||
Each class and function is described in this manual.
|
||||
|
@ -272,7 +272,7 @@ duplicate_torrent_ which derives from ``std::exception``.
|
|||
|
||||
The optional parameter, ``resume_data`` can be given if up to date fast-resume data
|
||||
is available. The fast-resume data can be acquired from a running torrent by calling
|
||||
``torrent_handle::write_resume_data()``. See `fast resume`_.
|
||||
`save_resume_data()`_ on `torrent_handle`_. See `fast resume`_.
|
||||
|
||||
The ``storage_mode`` parameter refers to the layout of the storage for this torrent.
|
||||
There are 3 different modes:
|
||||
|
@ -1573,7 +1573,7 @@ Its declaration looks like this::
|
|||
|
||||
std::string name() const;
|
||||
|
||||
entry write_resume_data() const;
|
||||
void save_resume_data() const;
|
||||
void force_reannounce() const;
|
||||
void force_reannounce(boost::posix_time::time_duration) const;
|
||||
void scrape_tracker() const;
|
||||
|
@ -1992,17 +1992,20 @@ This must be at least 2. The default is unlimited number of connections. If -1 i
|
|||
function, it means unlimited.
|
||||
|
||||
|
||||
write_resume_data()
|
||||
-------------------
|
||||
save_resume_data()
|
||||
------------------
|
||||
|
||||
::
|
||||
|
||||
entry write_resume_data() const;
|
||||
void save_resume_data() const;
|
||||
|
||||
``write_resume_data()`` generates fast-resume data and returns it as an entry_. This entry_
|
||||
``save_resume_data()`` generates fast-resume data and returns it as an entry_. This entry_
|
||||
is suitable for being bencoded. For more information about how fast-resume works, see `fast resume`_.
|
||||
|
||||
There are three cases where this function will just return an empty ``entry``:
|
||||
This operation is asynchronous, ``save_resume_data`` will return immediately. The resume data
|
||||
is delivered when it's done through an `save_resume_data_alert`_.
|
||||
|
||||
The fast resume data will be empty in the following cases:
|
||||
|
||||
1. The torrent handle is invalid.
|
||||
2. The torrent is checking (or is queued for checking) its storage, it will obviously
|
||||
|
@ -2010,16 +2013,17 @@ There are three cases where this function will just return an empty ``entry``:
|
|||
3. The torrent hasn't received valid metadata and was started without metadata
|
||||
(see libtorrent's `metadata from peers`_ extension)
|
||||
|
||||
Note that by the time this function returns, the resume data may already be invalid if the torrent
|
||||
Note that by the time you receive the fast resume data, it may already be invalid if the torrent
|
||||
is still downloading! The recommended practice is to first pause the torrent, then generate the
|
||||
fast resume data, and then close it down. Since the disk IO is done in a separate thread, in order
|
||||
to synchronize, you shoule to wait for the ``torrent_paused_alert`` before you write the resume
|
||||
data.
|
||||
fast resume data, and then close it down. Make sure to not `remove_torrent()`_ before you receive
|
||||
the `save_resume_data_alert`_ though. Only pause the torrent before you save the resume data
|
||||
if you will remove the torrent afterwards. There's no need to pause when saving intermittent
|
||||
resume data.
|
||||
|
||||
In full allocation mode the reume data is never invalidated by subsequent
|
||||
writes to the files, since pieces won't move around. This means that you don't need to
|
||||
pause before writing resume data in full or sparse mode. If you don't, however, any data written to
|
||||
disk after you saved resume data and before the session closed is lost.
|
||||
disk after you saved resume data and before the session_ closed is lost.
|
||||
|
||||
It also means that if the resume data is out dated, libtorrent will not re-check the files, but assume
|
||||
that it is fairly recent. The assumption is that it's better to loose a little bit than to re-check
|
||||
|
@ -2028,6 +2032,45 @@ the entire file.
|
|||
It is still a good idea to save resume data periodically during download as well as when
|
||||
closing down.
|
||||
|
||||
Example code to pause and save resume data for all torrents and wait for the alerts::
|
||||
|
||||
int num_resume_data = 0;
|
||||
std::vector<torrent_handle> handles = ses.get_torrents();
|
||||
for (std::vector<torrent_handle>::iterator i = handles.begin();
|
||||
i != handles.end(); ++i)
|
||||
{
|
||||
torrent_handle& h = *i;
|
||||
if (!h.has_metadata()) continue;
|
||||
|
||||
h.pause();
|
||||
h.save_resume_data();
|
||||
++num_resume_data;
|
||||
}
|
||||
|
||||
while (num_resume_data > 0)
|
||||
{
|
||||
alert const* a = ses.wait_for_alert(seconds(10));
|
||||
|
||||
// if we don't get an alert within 10 seconds, abort
|
||||
if (a == 0) break;
|
||||
|
||||
std::auto_ptr<alert> holder = ses.pop_alert();
|
||||
save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
|
||||
if (rd == 0)
|
||||
{
|
||||
process_alert(a);
|
||||
continue;
|
||||
}
|
||||
|
||||
torrent_handle h = rd->handle;
|
||||
boost::filesystem::ofstream out(h.save_path()
|
||||
/ (h.get_torrent_info().name() + ".fastresume"), std::ios_base::binary);
|
||||
out.unsetf(std::ios_base::skipws);
|
||||
bencode(std::ostream_iterator<char>(out), *rd->resume_data);
|
||||
--num_resume_data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
status()
|
||||
--------
|
||||
|
@ -4019,6 +4062,24 @@ This is useful for synchronizing with the disk.
|
|||
virtual std::auto_ptr<alert> clone() const;
|
||||
};
|
||||
|
||||
save_resume_data_alert
|
||||
----------------------
|
||||
|
||||
This alert is generated as a response to a ``torrent_handle::save_resume_data`` request.
|
||||
It is generated once the disk IO thread is done writing the state for this torrent.
|
||||
The ``resume_data`` member points to the resume data or is 0 on errors.
|
||||
|
||||
::
|
||||
|
||||
struct save_resume_data_alert: torrent_alert
|
||||
{
|
||||
save_resume_alert(torrent_handle const& h, std::string const& msg);
|
||||
|
||||
boost::shared_ptr<entry> resume_data;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const;
|
||||
};
|
||||
|
||||
|
||||
dispatcher
|
||||
----------
|
||||
|
@ -4235,8 +4296,8 @@ not, set ``error`` to a description of what mismatched and return false.
|
|||
The default storage may compare file sizes and time stamps of the files.
|
||||
|
||||
|
||||
write_resume_data( )
|
||||
--------------------
|
||||
write_resume_data()
|
||||
-------------------
|
||||
|
||||
::
|
||||
|
||||
|
@ -4340,7 +4401,7 @@ fast resume
|
|||
|
||||
The fast resume mechanism is a way to remember which pieces are downloaded
|
||||
and where they are put between sessions. You can generate fast resume data by
|
||||
calling ``torrent_handle::write_resume_data()`` on torrent_handle_. You can
|
||||
calling `save_resume_data()`_ on torrent_handle_. You can
|
||||
then save this data to disk and use it when resuming the torrent. libtorrent
|
||||
will not check the piece hashes then, and rely on the information given in the
|
||||
fast-resume data. The fast-resume data also contains information about which
|
||||
|
@ -4374,6 +4435,9 @@ The file format is a bencoded dictionary containing the following fields:
|
|||
| | greater than 4 megabytes, the block size will increase. |
|
||||
| | |
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
| ``pieces`` | A string with piece flags, one character per piece. |
|
||||
| | Bit 1 means we have that piece. |
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
| ``slots`` | list of integers. The list maps slots to piece indices. It |
|
||||
| | tells which piece is on which slot. If piece index is -2 it |
|
||||
| | means it is free, that there's no piece there. If it is -1, |
|
||||
|
|
|
@ -596,16 +596,10 @@ void scan_dir(path const& dir_path
|
|||
}
|
||||
|
||||
h.pause();
|
||||
if (h.has_metadata())
|
||||
{
|
||||
entry data = h.write_resume_data();
|
||||
std::stringstream s;
|
||||
s << h.get_torrent_info().name() << ".fastresume";
|
||||
boost::filesystem::ofstream out(h.save_path() / s.str(), std::ios_base::binary);
|
||||
out.unsetf(std::ios_base::skipws);
|
||||
bencode(std::ostream_iterator<char>(out), data);
|
||||
}
|
||||
ses.remove_torrent(h);
|
||||
// the alert handler for save_resume_data_alert
|
||||
// will save it to disk and remove the torrent
|
||||
h.save_resume_data();
|
||||
|
||||
handles.erase(i++);
|
||||
}
|
||||
}
|
||||
|
@ -988,21 +982,49 @@ int main(int ac, char* av[])
|
|||
{
|
||||
if (c == 'q')
|
||||
{
|
||||
// keep track of the number of resume data
|
||||
// alerts to wait for
|
||||
int num_resume_data = 0;
|
||||
for (handles_t::iterator i = handles.begin();
|
||||
i != handles.end(); ++i)
|
||||
{
|
||||
torrent_handle& h = i->second;
|
||||
if (!h.is_valid() || !h.has_metadata()) continue;
|
||||
|
||||
// pause
|
||||
std::cout << "pausing " << h.name() << std::endl;
|
||||
h.pause();
|
||||
// save_resume_data will generate an alert when it's done
|
||||
h.save_resume_data();
|
||||
++num_resume_data;
|
||||
}
|
||||
std::cout << "waiting for resume data" << std::endl;
|
||||
|
||||
entry data = h.write_resume_data();
|
||||
std::stringstream s;
|
||||
s << h.get_torrent_info().name() << ".fastresume";
|
||||
boost::filesystem::ofstream out(h.save_path() / s.str(), std::ios_base::binary);
|
||||
while (num_resume_data > 0)
|
||||
{
|
||||
alert const* a = ses.wait_for_alert(seconds(10));
|
||||
if (a == 0)
|
||||
{
|
||||
std::cout << " aborting with " << num_resume_data << " outstanding "
|
||||
"torrents to save resume data for" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
std::auto_ptr<alert> holder = ses.pop_alert();
|
||||
save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
|
||||
if (rd == 0)
|
||||
{
|
||||
std::cout << a->msg() << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
torrent_handle h = rd->handle;
|
||||
boost::filesystem::ofstream out(h.save_path()
|
||||
/ (h.get_torrent_info().name() + ".fastresume"), std::ios_base::binary);
|
||||
out.unsetf(std::ios_base::skipws);
|
||||
bencode(std::ostream_iterator<char>(out), data);
|
||||
ses.remove_torrent(h);
|
||||
bencode(std::ostream_iterator<char>(out), *rd->resume_data);
|
||||
std::cout << "fast resume data saved for " << h.name() << std::endl;
|
||||
--num_resume_data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1085,15 +1107,9 @@ int main(int ac, char* av[])
|
|||
|
||||
// write resume data for the finished torrent
|
||||
torrent_handle h = p->handle;
|
||||
entry data = h.write_resume_data();
|
||||
std::stringstream s;
|
||||
s << h.get_torrent_info().name() << ".fastresume";
|
||||
boost::filesystem::ofstream out(h.save_path() / s.str(), std::ios_base::binary);
|
||||
out.unsetf(std::ios_base::skipws);
|
||||
bencode(std::ostream_iterator<char>(out), data);
|
||||
h.save_resume_data();
|
||||
|
||||
event_string << p->handle.get_torrent_info().name() << ": "
|
||||
<< a->msg();
|
||||
event_string << h.name() << ": " << a->msg();
|
||||
}
|
||||
else if (peer_error_alert* p = dynamic_cast<peer_error_alert*>(a.get()))
|
||||
{
|
||||
|
@ -1119,6 +1135,18 @@ int main(int ac, char* av[])
|
|||
{
|
||||
event_string << "(" << p->ip << ") " << p->msg();
|
||||
}
|
||||
else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a.get()))
|
||||
{
|
||||
torrent_handle h = p->handle;
|
||||
if (p->resume_data)
|
||||
{
|
||||
boost::filesystem::ofstream out(h.save_path() / (h.name() + ".fastresume"), std::ios_base::binary);
|
||||
out.unsetf(std::ios_base::skipws);
|
||||
bencode(std::ostream_iterator<char>(out), *p->resume_data);
|
||||
if (h.is_paused()) ses.remove_torrent(h);
|
||||
}
|
||||
event_string << "(" << h.name() << ") " << p->msg();
|
||||
}
|
||||
else if (torrent_alert* p = dynamic_cast<torrent_alert*>(a.get()))
|
||||
{
|
||||
std::string name;
|
||||
|
@ -1356,6 +1384,7 @@ int main(int ac, char* av[])
|
|||
}
|
||||
}
|
||||
|
||||
std::cout << "saving session state" << std::endl;
|
||||
{
|
||||
entry session_state = ses.state();
|
||||
boost::filesystem::ofstream out(".ses_state"
|
||||
|
@ -1365,12 +1394,14 @@ int main(int ac, char* av[])
|
|||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
std::cout << "saving DHT state" << std::endl;
|
||||
dht_state = ses.dht_state();
|
||||
boost::filesystem::ofstream out(".dht_state"
|
||||
, std::ios_base::binary);
|
||||
out.unsetf(std::ios_base::skipws);
|
||||
bencode(std::ostream_iterator<char>(out), dht_state);
|
||||
#endif
|
||||
std::cout << "closing session" << std::endl;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
|
|
@ -291,6 +291,20 @@ namespace libtorrent
|
|||
{ return std::auto_ptr<alert>(new torrent_deleted_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT save_resume_data_alert: torrent_alert
|
||||
{
|
||||
save_resume_data_alert(boost::shared_ptr<entry> const& rd
|
||||
, torrent_handle const& h, std::string const& msg)
|
||||
: torrent_alert(h, alert::warning, msg)
|
||||
, resume_data(rd)
|
||||
{}
|
||||
|
||||
boost::shared_ptr<entry> resume_data;
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new save_resume_data_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT torrent_paused_alert: torrent_alert
|
||||
{
|
||||
torrent_paused_alert(torrent_handle const& h, std::string const& msg)
|
||||
|
|
|
@ -81,6 +81,7 @@ namespace libtorrent
|
|||
, delete_files
|
||||
, check_fastresume
|
||||
, check_files
|
||||
, save_resume_data
|
||||
};
|
||||
|
||||
action_t action;
|
||||
|
@ -104,6 +105,8 @@ namespace libtorrent
|
|||
// with lower priority
|
||||
int priority;
|
||||
|
||||
boost::shared_ptr<entry> resume_data;
|
||||
|
||||
// this is called when operation completes
|
||||
boost::function<void(int, disk_io_job const&)> callback;
|
||||
};
|
||||
|
|
|
@ -212,7 +212,7 @@ namespace libtorrent
|
|||
|
||||
torrent_info const* info() const { return m_info.get(); }
|
||||
|
||||
void write_resume_data(entry& rd, std::vector<bool> const& have) const;
|
||||
void write_resume_data(entry& rd) const;
|
||||
|
||||
void async_check_fastresume(entry const* resume_data
|
||||
, boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
@ -242,6 +242,9 @@ namespace libtorrent
|
|||
void async_move_storage(fs::path const& p
|
||||
, boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
void async_save_resume_data(
|
||||
boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
enum return_t
|
||||
{
|
||||
// return values from check_fastresume and check_files
|
||||
|
|
|
@ -178,6 +178,7 @@ namespace libtorrent
|
|||
void pause();
|
||||
void resume();
|
||||
bool is_paused() const { return m_paused; }
|
||||
void save_resume_data();
|
||||
|
||||
void delete_files();
|
||||
|
||||
|
@ -516,6 +517,8 @@ namespace libtorrent
|
|||
|
||||
torrent_handle get_handle();
|
||||
|
||||
void write_resume_data(entry& rd) const;
|
||||
|
||||
// LOGGING
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
virtual void debug_log(const std::string& line);
|
||||
|
@ -563,6 +566,7 @@ namespace libtorrent
|
|||
void on_files_released(int ret, disk_io_job const& j);
|
||||
void on_torrent_paused(int ret, disk_io_job const& j);
|
||||
void on_storage_moved(int ret, disk_io_job const& j);
|
||||
void on_save_resume_data(int ret, disk_io_job const& j);
|
||||
|
||||
void on_piece_verified(int ret, disk_io_job const& j
|
||||
, boost::function<void(int)> f);
|
||||
|
|
|
@ -315,6 +315,7 @@ namespace libtorrent
|
|||
bool is_paused() const;
|
||||
void pause() const;
|
||||
void resume() const;
|
||||
void save_resume_data() const;
|
||||
|
||||
#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
|
||||
void resolve_countries(bool r);
|
||||
|
@ -354,7 +355,9 @@ namespace libtorrent
|
|||
// to.
|
||||
void use_interface(const char* net_interface) const;
|
||||
|
||||
entry write_resume_data() const;
|
||||
// use save_resume_data() instead. It is async. and
|
||||
// will return the resume data in an alert
|
||||
entry write_resume_data() const TORRENT_DEPRECATED;
|
||||
|
||||
// forces this torrent to reannounce
|
||||
// (make a rerequest from the tracker)
|
||||
|
|
|
@ -773,13 +773,13 @@ namespace libtorrent
|
|||
std::string const& error_string = j.storage->error();
|
||||
if (!error_string.empty())
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::cout << "ERROR: '" << error_string << "' " << j.error_file << std::endl;
|
||||
#endif
|
||||
j.str = error_string;
|
||||
j.error_file = j.storage->error_file();
|
||||
j.storage->clear_error();
|
||||
ret = -1;
|
||||
#ifndef NDEBUG
|
||||
std::cout << "ERROR: " << error_string << " " << j.error_file << std::endl;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1042,6 +1042,16 @@ namespace libtorrent
|
|||
}
|
||||
break;
|
||||
}
|
||||
case disk_io_job::save_resume_data:
|
||||
{
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
m_log << log_time() << " save resume data" << std::endl;
|
||||
#endif
|
||||
j.resume_data.reset(new entry(entry::dictionary_t));
|
||||
j.storage->write_resume_data(*j.resume_data);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
|
|
222
src/storage.cpp
222
src/storage.cpp
|
@ -559,11 +559,8 @@ namespace libtorrent
|
|||
|
||||
bool storage::write_resume_data(entry& rd) const
|
||||
{
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
set_error("", "invalid fastresume file");
|
||||
return true;
|
||||
}
|
||||
TORRENT_ASSERT(rd.type() == entry::dictionary_t);
|
||||
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes
|
||||
= get_filesizes(*m_info, m_save_path);
|
||||
|
||||
|
@ -583,7 +580,7 @@ namespace libtorrent
|
|||
{
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "invalid fastresume file";
|
||||
error = "invalid fastresume file (not a dictionary)";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1146,6 +1143,15 @@ namespace libtorrent
|
|||
{
|
||||
}
|
||||
|
||||
void piece_manager::async_save_resume_data(
|
||||
boost::function<void(int, disk_io_job const&)> const& handler)
|
||||
{
|
||||
disk_io_job j;
|
||||
j.storage = this;
|
||||
j.action = disk_io_job::save_resume_data;
|
||||
m_io_thread.add_job(j, handler);
|
||||
}
|
||||
|
||||
void piece_manager::async_release_files(
|
||||
boost::function<void(int, disk_io_job const&)> const& handler)
|
||||
{
|
||||
|
@ -1280,8 +1286,7 @@ namespace libtorrent
|
|||
return false;
|
||||
}
|
||||
|
||||
void piece_manager::write_resume_data(entry& rd
|
||||
, std::vector<bool> const& have) const
|
||||
void piece_manager::write_resume_data(entry& rd) const
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(m_mutex);
|
||||
|
||||
|
@ -1289,9 +1294,9 @@ namespace libtorrent
|
|||
|
||||
m_storage->write_resume_data(rd);
|
||||
|
||||
entry::list_type& slots = rd["slots"].list();
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
{
|
||||
entry::list_type& slots = rd["slots"].list();
|
||||
slots.clear();
|
||||
std::vector<int>::const_reverse_iterator last;
|
||||
for (last = m_slot_to_piece.rbegin();
|
||||
|
@ -1307,13 +1312,6 @@ namespace libtorrent
|
|||
slots.push_back((*i >= 0) ? *i : unassigned);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < m_info->num_pieces(); ++i)
|
||||
{
|
||||
slots.push_back(have[i] ? i : unassigned);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void piece_manager::mark_failed(int piece_index)
|
||||
|
@ -1710,116 +1708,122 @@ namespace libtorrent
|
|||
&& allocation->string() != "compact")
|
||||
storage_mode = storage_mode_sparse;
|
||||
|
||||
// read piece map
|
||||
entry const* slots = rd.find_key("slots");
|
||||
if (slots == 0 || slots->type() != entry::list_t)
|
||||
{
|
||||
error = "missing slot list";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
if ((int)slots->list().size() > m_info->num_pieces())
|
||||
{
|
||||
error = "file has more slots than torrent (slots: "
|
||||
+ boost::lexical_cast<std::string>(slots->list().size()) + " size: "
|
||||
+ boost::lexical_cast<std::string>(m_info->num_pieces()) + " )";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
// assume no piece is out of place (i.e. in a slot
|
||||
// other than the one it should be in)
|
||||
bool out_of_place = false;
|
||||
|
||||
if (storage_mode == storage_mode_compact)
|
||||
|
||||
// if we don't have a piece map, we need the slots
|
||||
// if we're in compact mode, we also need the slots map
|
||||
if (storage_mode == storage_mode_compact || rd.find_key("pieces") == 0)
|
||||
{
|
||||
int num_pieces = int(m_info->num_pieces());
|
||||
m_slot_to_piece.resize(num_pieces, unallocated);
|
||||
m_piece_to_slot.resize(num_pieces, has_no_slot);
|
||||
int slot = 0;
|
||||
for (entry::list_type::const_iterator i = slots->list().begin();
|
||||
i != slots->list().end(); ++i, ++slot)
|
||||
// read slots map
|
||||
entry const* slots = rd.find_key("slots");
|
||||
if (slots == 0 || slots->type() != entry::list_t)
|
||||
{
|
||||
if (i->type() != entry::int_t)
|
||||
error = "missing slot list";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
if ((int)slots->list().size() > m_info->num_pieces())
|
||||
{
|
||||
error = "file has more slots than torrent (slots: "
|
||||
+ boost::lexical_cast<std::string>(slots->list().size()) + " size: "
|
||||
+ boost::lexical_cast<std::string>(m_info->num_pieces()) + " )";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
if (storage_mode == storage_mode_compact)
|
||||
{
|
||||
int num_pieces = int(m_info->num_pieces());
|
||||
m_slot_to_piece.resize(num_pieces, unallocated);
|
||||
m_piece_to_slot.resize(num_pieces, has_no_slot);
|
||||
int slot = 0;
|
||||
for (entry::list_type::const_iterator i = slots->list().begin();
|
||||
i != slots->list().end(); ++i, ++slot)
|
||||
{
|
||||
error = "invalid entry type in slot list";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
int index = int(i->integer());
|
||||
if (index >= num_pieces || index < -2)
|
||||
{
|
||||
error = "too high index number in slot map (index: "
|
||||
+ boost::lexical_cast<std::string>(index) + " size: "
|
||||
+ boost::lexical_cast<std::string>(num_pieces) + ")";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
if (index >= 0)
|
||||
{
|
||||
m_slot_to_piece[slot] = index;
|
||||
m_piece_to_slot[index] = slot;
|
||||
if (slot != index) out_of_place = true;
|
||||
}
|
||||
else if (index == unassigned)
|
||||
{
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
m_free_slots.push_back(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
TORRENT_ASSERT(index == unallocated);
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
m_unallocated_slots.push_back(slot);
|
||||
if (i->type() != entry::int_t)
|
||||
{
|
||||
error = "invalid entry type in slot list";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
int index = int(i->integer());
|
||||
if (index >= num_pieces || index < -2)
|
||||
{
|
||||
error = "too high index number in slot map (index: "
|
||||
+ boost::lexical_cast<std::string>(index) + " size: "
|
||||
+ boost::lexical_cast<std::string>(num_pieces) + ")";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
if (index >= 0)
|
||||
{
|
||||
m_slot_to_piece[slot] = index;
|
||||
m_piece_to_slot[index] = slot;
|
||||
if (slot != index) out_of_place = true;
|
||||
}
|
||||
else if (index == unassigned)
|
||||
{
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
m_free_slots.push_back(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
TORRENT_ASSERT(index == unallocated);
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
m_unallocated_slots.push_back(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int slot = 0;
|
||||
for (entry::list_type::const_iterator i = slots->list().begin();
|
||||
i != slots->list().end(); ++i, ++slot)
|
||||
else
|
||||
{
|
||||
if (i->type() != entry::int_t)
|
||||
int slot = 0;
|
||||
for (entry::list_type::const_iterator i = slots->list().begin();
|
||||
i != slots->list().end(); ++i, ++slot)
|
||||
{
|
||||
error = "invalid entry type in slot list";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
int index = int(i->integer());
|
||||
if (index != slot && index >= 0)
|
||||
{
|
||||
error = "invalid slot index";
|
||||
return check_no_fastresume(error);
|
||||
if (i->type() != entry::int_t)
|
||||
{
|
||||
error = "invalid entry type in slot list";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
|
||||
int index = int(i->integer());
|
||||
if (index != slot && index >= 0)
|
||||
{
|
||||
error = "invalid slot index";
|
||||
return check_no_fastresume(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_storage->verify_resume_data(rd, error))
|
||||
return check_no_fastresume(error);
|
||||
if (!m_storage->verify_resume_data(rd, error))
|
||||
return check_no_fastresume(error);
|
||||
|
||||
// This will corrupt the storage
|
||||
// use while debugging to find
|
||||
// states that cannot be scanned
|
||||
// by check_pieces.
|
||||
// m_storage->shuffle();
|
||||
// This will corrupt the storage
|
||||
// use while debugging to find
|
||||
// states that cannot be scanned
|
||||
// by check_pieces.
|
||||
// m_storage->shuffle();
|
||||
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
{
|
||||
if (m_unallocated_slots.empty()) switch_to_full_mode();
|
||||
}
|
||||
else
|
||||
{
|
||||
TORRENT_ASSERT(m_free_slots.empty());
|
||||
TORRENT_ASSERT(m_unallocated_slots.empty());
|
||||
|
||||
if (out_of_place)
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
{
|
||||
// in this case we're in full allocation mode, but
|
||||
// we're resuming a compact allocated storage
|
||||
m_state = state_expand_pieces;
|
||||
m_current_slot = 0;
|
||||
error = "pieces needs to be reordered";
|
||||
return need_full_check;
|
||||
if (m_unallocated_slots.empty()) switch_to_full_mode();
|
||||
}
|
||||
else
|
||||
{
|
||||
TORRENT_ASSERT(m_free_slots.empty());
|
||||
TORRENT_ASSERT(m_unallocated_slots.empty());
|
||||
|
||||
if (out_of_place)
|
||||
{
|
||||
// in this case we're in full allocation mode, but
|
||||
// we're resuming a compact allocated storage
|
||||
m_state = state_expand_pieces;
|
||||
m_current_slot = 0;
|
||||
error = "pieces needs to be reordered";
|
||||
return need_full_check;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return check_init_storage(error);
|
||||
|
|
171
src/torrent.cpp
171
src/torrent.cpp
|
@ -520,21 +520,18 @@ namespace libtorrent
|
|||
if (!fastresume_rejected)
|
||||
{
|
||||
TORRENT_ASSERT(m_resume_data.type() == entry::dictionary_t);
|
||||
// parse slots
|
||||
entry const* slots_ent = m_resume_data.find_key("slots");
|
||||
if (slots_ent != 0 && slots_ent->type() == entry::list_t)
|
||||
|
||||
// parse have bitmask
|
||||
entry const* pieces = m_resume_data.find_key("pieces");
|
||||
if (pieces && pieces->type() == entry::string_t
|
||||
&& pieces->string().length() == m_torrent_file->num_pieces())
|
||||
{
|
||||
entry::list_type const& slots = slots_ent->list();
|
||||
|
||||
for (entry::list_type::const_iterator i = slots.begin();
|
||||
i != slots.end(); ++i)
|
||||
std::string const& pieces_str = pieces->string();
|
||||
for (int i = 0, end(pieces_str.size()); i < end; ++i)
|
||||
{
|
||||
if (i->type() != entry::int_t) continue;
|
||||
int piece_index = int(i->integer());
|
||||
if (piece_index < 0 || piece_index >= torrent_file().num_pieces())
|
||||
continue;
|
||||
m_have_pieces[piece_index] = true;
|
||||
++m_num_pieces;
|
||||
bool have = pieces_str[i] & 1;
|
||||
m_have_pieces[i] = have;
|
||||
m_num_pieces += have;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -557,10 +554,11 @@ namespace libtorrent
|
|||
if (piece_index < 0 || piece_index >= torrent_file().num_pieces())
|
||||
continue;
|
||||
|
||||
// if this assert is hit, the resume data file was corrupt
|
||||
TORRENT_ASSERT(m_have_pieces[piece_index]);
|
||||
m_have_pieces[piece_index] = false;
|
||||
--m_num_pieces;
|
||||
if (m_have_pieces[piece_index])
|
||||
{
|
||||
m_have_pieces[piece_index] = false;
|
||||
--m_num_pieces;
|
||||
}
|
||||
|
||||
entry const* bitmask_ent = i->find_key("bitmask");
|
||||
if (bitmask_ent == 0 || bitmask_ent->type() != entry::string_t) break;
|
||||
|
@ -1387,6 +1385,18 @@ namespace libtorrent
|
|||
*/
|
||||
}
|
||||
|
||||
void torrent::on_save_resume_data(int ret, disk_io_job const& j)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
||||
if (alerts().should_post(alert::warning))
|
||||
{
|
||||
write_resume_data(*j.resume_data);
|
||||
alerts().post_alert(save_resume_data_alert(j.resume_data
|
||||
, get_handle(), "resume data generated"));
|
||||
}
|
||||
}
|
||||
|
||||
void torrent::on_torrent_paused(int ret, disk_io_job const& j)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
@ -2238,6 +2248,112 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
void torrent::write_resume_data(entry& ret) const
|
||||
{
|
||||
ret["file-format"] = "libtorrent resume file";
|
||||
ret["file-version"] = 1;
|
||||
|
||||
ret["allocation"] = m_storage_mode == storage_mode_sparse?"sparse"
|
||||
:m_storage_mode == storage_mode_allocate?"full":"compact";
|
||||
|
||||
const sha1_hash& info_hash = torrent_file().info_hash();
|
||||
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
|
||||
|
||||
// blocks per piece
|
||||
int num_blocks_per_piece =
|
||||
static_cast<int>(torrent_file().piece_length()) / block_size();
|
||||
ret["blocks per piece"] = num_blocks_per_piece;
|
||||
|
||||
// if this torrent is a seed, we won't have a piece picker
|
||||
// and there will be no half-finished pieces.
|
||||
if (!is_seed())
|
||||
{
|
||||
const std::vector<piece_picker::downloading_piece>& q
|
||||
= m_picker->get_download_queue();
|
||||
|
||||
// unfinished pieces
|
||||
ret["unfinished"] = entry::list_type();
|
||||
entry::list_type& up = ret["unfinished"].list();
|
||||
|
||||
// info for each unfinished piece
|
||||
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
||||
= q.begin(); i != q.end(); ++i)
|
||||
{
|
||||
if (i->finished == 0) continue;
|
||||
|
||||
entry piece_struct(entry::dictionary_t);
|
||||
|
||||
// the unfinished piece's index
|
||||
piece_struct["piece"] = i->index;
|
||||
|
||||
std::string bitmask;
|
||||
const int num_bitmask_bytes
|
||||
= (std::max)(num_blocks_per_piece / 8, 1);
|
||||
|
||||
for (int j = 0; j < num_bitmask_bytes; ++j)
|
||||
{
|
||||
unsigned char v = 0;
|
||||
int bits = (std::min)(num_blocks_per_piece - j*8, 8);
|
||||
for (int k = 0; k < bits; ++k)
|
||||
v |= (i->info[j*8+k].state == piece_picker::block_info::state_finished)
|
||||
? (1 << k) : 0;
|
||||
bitmask.insert(bitmask.end(), v);
|
||||
TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1);
|
||||
}
|
||||
piece_struct["bitmask"] = bitmask;
|
||||
// push the struct onto the unfinished-piece list
|
||||
up.push_back(piece_struct);
|
||||
}
|
||||
}
|
||||
|
||||
// write have bitmask
|
||||
entry::string_type& pieces = ret["pieces"].string();
|
||||
pieces.resize(m_torrent_file->num_pieces());
|
||||
for (int i = 0, end(pieces.size()); i < end; ++i)
|
||||
pieces[i] = m_have_pieces[i] ? 1 : 0;
|
||||
|
||||
// write local peers
|
||||
|
||||
entry::list_type& peer_list = ret["peers"].list();
|
||||
entry::list_type& banned_peer_list = ret["banned_peers"].list();
|
||||
|
||||
int max_failcount = m_ses.m_settings.max_failcount;
|
||||
|
||||
for (policy::const_iterator i = m_policy.begin_peer()
|
||||
, end(m_policy.end_peer()); i != end; ++i)
|
||||
{
|
||||
asio::error_code ec;
|
||||
if (i->second.banned)
|
||||
{
|
||||
tcp::endpoint ip = i->second.ip;
|
||||
entry peer(entry::dictionary_t);
|
||||
peer["ip"] = ip.address().to_string(ec);
|
||||
if (ec) continue;
|
||||
peer["port"] = ip.port();
|
||||
banned_peer_list.push_back(peer);
|
||||
continue;
|
||||
}
|
||||
// we cannot save remote connection
|
||||
// since we don't know their listen port
|
||||
// unless they gave us their listen port
|
||||
// through the extension handshake
|
||||
// so, if the peer is not connectable (i.e. we
|
||||
// don't know its listen port) or if it has
|
||||
// been banned, don't save it.
|
||||
if (i->second.type == policy::peer::not_connectable) continue;
|
||||
|
||||
// don't save peers that doesn't work
|
||||
if (i->second.failcount >= max_failcount) continue;
|
||||
|
||||
tcp::endpoint ip = i->second.ip;
|
||||
entry peer(entry::dictionary_t);
|
||||
peer["ip"] = ip.address().to_string(ec);
|
||||
if (ec) continue;
|
||||
peer["port"] = ip.port();
|
||||
peer_list.push_back(peer);
|
||||
}
|
||||
}
|
||||
|
||||
void torrent::get_full_peer_list(std::vector<peer_list_entry>& v) const
|
||||
{
|
||||
v.clear();
|
||||
|
@ -3103,6 +3219,27 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
// this is an async operation triggered by the client
|
||||
void torrent::save_resume_data()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_owning_storage.get())
|
||||
{
|
||||
TORRENT_ASSERT(m_storage);
|
||||
m_storage->async_save_resume_data(
|
||||
bind(&torrent::on_save_resume_data, shared_from_this(), _1, _2));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alerts().should_post(alert::warning))
|
||||
{
|
||||
alerts().post_alert(save_resume_data_alert(boost::shared_ptr<entry>()
|
||||
, get_handle(), "save resume data failed, torrent is being destructed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void torrent::pause()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
|
|
@ -253,6 +253,12 @@ namespace libtorrent
|
|||
TORRENT_FORWARD(pause());
|
||||
}
|
||||
|
||||
void torrent_handle::save_resume_data() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
TORRENT_FORWARD(save_resume_data());
|
||||
}
|
||||
|
||||
void torrent_handle::resume() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
@ -431,131 +437,9 @@ namespace libtorrent
|
|||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
if (!t)
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return entry();
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
session_impl::mutex_t::scoped_lock l(t->session().m_mutex);
|
||||
if (!t->valid_metadata())
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return entry();
|
||||
#else
|
||||
throw_invalid_handle();
|
||||
#endif
|
||||
|
||||
std::vector<bool> have_pieces = t->pieces();
|
||||
|
||||
entry ret(entry::dictionary_t);
|
||||
|
||||
ret["file-format"] = "libtorrent resume file";
|
||||
ret["file-version"] = 1;
|
||||
|
||||
storage_mode_t sm = t->storage_mode();
|
||||
ret["allocation"] = sm == storage_mode_sparse?"sparse"
|
||||
:sm == storage_mode_allocate?"full":"compact";
|
||||
|
||||
const sha1_hash& info_hash = t->torrent_file().info_hash();
|
||||
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
|
||||
|
||||
// blocks per piece
|
||||
int num_blocks_per_piece =
|
||||
static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
|
||||
ret["blocks per piece"] = num_blocks_per_piece;
|
||||
|
||||
// if this torrent is a seed, we won't have a piece picker
|
||||
// and there will be no half-finished pieces.
|
||||
if (!t->is_seed())
|
||||
{
|
||||
const piece_picker& p = t->picker();
|
||||
|
||||
const std::vector<piece_picker::downloading_piece>& q
|
||||
= p.get_download_queue();
|
||||
|
||||
// unfinished pieces
|
||||
ret["unfinished"] = entry::list_type();
|
||||
entry::list_type& up = ret["unfinished"].list();
|
||||
|
||||
// info for each unfinished piece
|
||||
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
||||
= q.begin(); i != q.end(); ++i)
|
||||
{
|
||||
if (i->finished == 0) continue;
|
||||
|
||||
entry piece_struct(entry::dictionary_t);
|
||||
|
||||
// the unfinished piece's index
|
||||
piece_struct["piece"] = i->index;
|
||||
|
||||
have_pieces[i->index] = true;
|
||||
|
||||
std::string bitmask;
|
||||
const int num_bitmask_bytes
|
||||
= (std::max)(num_blocks_per_piece / 8, 1);
|
||||
|
||||
for (int j = 0; j < num_bitmask_bytes; ++j)
|
||||
{
|
||||
unsigned char v = 0;
|
||||
int bits = (std::min)(num_blocks_per_piece - j*8, 8);
|
||||
for (int k = 0; k < bits; ++k)
|
||||
v |= (i->info[j*8+k].state == piece_picker::block_info::state_finished)
|
||||
? (1 << k) : 0;
|
||||
bitmask.insert(bitmask.end(), v);
|
||||
TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1);
|
||||
}
|
||||
piece_struct["bitmask"] = bitmask;
|
||||
// push the struct onto the unfinished-piece list
|
||||
up.push_back(piece_struct);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> piece_index;
|
||||
t->filesystem().write_resume_data(ret, have_pieces);
|
||||
|
||||
// write local peers
|
||||
|
||||
entry::list_type& peer_list = ret["peers"].list();
|
||||
entry::list_type& banned_peer_list = ret["banned_peers"].list();
|
||||
|
||||
policy& pol = t->get_policy();
|
||||
|
||||
int max_failcount = t->settings().max_failcount;
|
||||
|
||||
for (policy::iterator i = pol.begin_peer()
|
||||
, end(pol.end_peer()); i != end; ++i)
|
||||
{
|
||||
asio::error_code ec;
|
||||
if (i->second.banned)
|
||||
{
|
||||
tcp::endpoint ip = i->second.ip;
|
||||
entry peer(entry::dictionary_t);
|
||||
peer["ip"] = ip.address().to_string(ec);
|
||||
if (ec) continue;
|
||||
peer["port"] = ip.port();
|
||||
banned_peer_list.push_back(peer);
|
||||
continue;
|
||||
}
|
||||
// we cannot save remote connection
|
||||
// since we don't know their listen port
|
||||
// unless they gave us their listen port
|
||||
// through the extension handshake
|
||||
// so, if the peer is not connectable (i.e. we
|
||||
// don't know its listen port) or if it has
|
||||
// been banned, don't save it.
|
||||
if (i->second.type == policy::peer::not_connectable) continue;
|
||||
|
||||
// don't save peers that doesn't work
|
||||
if (i->second.failcount >= max_failcount) continue;
|
||||
|
||||
tcp::endpoint ip = i->second.ip;
|
||||
entry peer(entry::dictionary_t);
|
||||
peer["ip"] = ip.address().to_string(ec);
|
||||
if (ec) continue;
|
||||
peer["port"] = ip.port();
|
||||
peer_list.push_back(peer);
|
||||
}
|
||||
TORRENT_FORWARD(write_resume_data(ret));
|
||||
t->filesystem().write_resume_data(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue