first attempt at switching over to new storage allocation

This commit is contained in:
Marcos Pinto 2007-10-09 21:22:42 +00:00
parent 4ff8458b3f
commit bd02f837bb
16 changed files with 1198 additions and 387 deletions

View File

@ -0,0 +1,78 @@
/*
Copyright (c) 2003, Magnus Jonsson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
#include <map>
#include <utility>
#include <boost/shared_ptr.hpp>
#include "libtorrent/resource_request.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/session.hpp"
namespace libtorrent
{
class peer_connection;
class torrent;
int saturated_add(int a, int b);
// Function to allocate a limited resource fairly among many consumers.
// It takes into account the current use, and the consumer's desired use.
// Should be invoked periodically to allow it adjust to the situation (make
// sure "used" is updated between calls!).
// If resources = std::numeric_limits<int>::max() it means there is an infinite
// supply of resources (so everyone can get what they want).
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& torrents
, resource_request torrent::* res);
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& connections
, resource_request peer_connection::* res);
// Used for global limits.
void allocate_resources(
int resources
, std::vector<session*>& _sessions
, resource_request session::* res);
}
#endif

View File

@ -0,0 +1,328 @@
/*
Copyright (c) 2003, Magnus Jonsson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED
#define TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED
#include <map>
#include <utility>
#include <boost/shared_ptr.hpp>
#include "libtorrent/resource_request.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/size_type.hpp"
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
namespace libtorrent
{
int saturated_add(int a, int b);
namespace aux
{
// give num_resources to r,
// return how how many were actually accepted.
inline int give(resource_request& r, int num_resources)
{
assert(num_resources >= 0);
assert(r.given <= r.max);
int accepted = (std::min)(num_resources, r.max - r.given);
assert(accepted >= 0);
r.given += accepted;
assert(r.given <= r.max);
return accepted;
}
inline int div_round_up(int numerator, int denominator)
{
return (numerator + denominator - 1) / denominator;
}
#ifndef NDEBUG
template<class It, class T>
class allocate_resources_contract_check
{
int m_resources;
It m_start;
It m_end;
resource_request T::* m_res;
public:
allocate_resources_contract_check(
int resources
, It start
, It end
, resource_request T::* res)
: m_resources(resources)
, m_start(start)
, m_end(end)
, m_res(res)
{
assert(m_resources >= 0);
for (It i = m_start, end(m_end); i != end; ++i)
{
assert(((*i).*m_res).max >= 0);
assert(((*i).*m_res).given >= 0);
}
}
~allocate_resources_contract_check()
{
int sum_given = 0;
int sum_max = 0;
int sum_min = 0;
for (It i = m_start, end(m_end); i != end; ++i)
{
assert(((*i).*m_res).max >= 0);
assert(((*i).*m_res).min >= 0);
assert(((*i).*m_res).max >= ((*i).*m_res).min);
assert(((*i).*m_res).given >= 0);
assert(((*i).*m_res).given <= ((*i).*m_res).max);
sum_given = saturated_add(sum_given, ((*i).*m_res).given);
sum_max = saturated_add(sum_max, ((*i).*m_res).max);
sum_min = saturated_add(sum_min, ((*i).*m_res).min);
}
if (sum_given != (std::min)(std::max(m_resources, sum_min), sum_max))
{
std::cerr << sum_given << " " << m_resources << " " << sum_min << " " << sum_max << std::endl;
assert(false);
}
}
};
#endif
template<class It, class T>
void allocate_resources_impl(
int resources
, It start
, It end
, resource_request T::* res)
{
assert(resources >= 0);
#ifndef NDEBUG
allocate_resources_contract_check<It, T> contract_check(
resources
, start
, end
, res);
#endif
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
r.leftovers = (std::max)(r.used - r.given, 0);
}
if (resources == resource_request::inf)
{
// No competition for resources.
// Just give everyone what they want.
for (It i = start; i != end; ++i)
{
((*i).*res).given = ((*i).*res).max;
}
return;
}
// Resources are scarce
int sum_max = 0;
int sum_min = 0;
// the number of consumer that saturated their
// quota last time slice
int num_saturated = 0;
// the total resources that those saturated their
// quota used. This is used to calculate the mean
// of the saturating consumers, in order to
// balance their quotas for the next time slice.
size_type saturated_sum = 0;
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
sum_max = saturated_add(sum_max, r.max);
assert(r.min < resource_request::inf);
assert(r.min >= 0);
assert(r.min <= r.max);
sum_min += r.min;
// a consumer that uses 95% or more of its assigned
// quota is considered saturating
size_type used = r.used;
if (r.given == 0) continue;
if (used * 20 / r.given >= 19)
{
++num_saturated;
saturated_sum += r.given;
}
}
if (sum_max <= resources)
{
// it turns out that there's no competition for resources
// after all.
for (It i = start; i != end; ++i)
{
((*i).*res).given = ((*i).*res).max;
}
return;
}
if (sum_min >= resources)
{
// the amount of resources is smaller than
// the minimum resources to distribute, so
// give everyone the minimum
for (It i = start; i != end; ++i)
{
((*i).*res).given = ((*i).*res).min;
}
return;
}
// now, the "used" field will be used as a target value.
// the algorithm following this loop will then scale the
// used values to fit the available resources and store
// the scaled values as given. So, the ratios of the
// used values will be maintained.
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
int target;
size_type used = r.used;
if (r.given > 0 && used * 20 / r.given >= 19)
{
assert(num_saturated > 0);
target = div_round_up(saturated_sum, num_saturated);
target += div_round_up(target, 10);
}
else
{
target = r.used;
}
if (target > r.max) target = r.max;
else if (target < r.min) target = r.min;
// move 12.5% towards the the target value
r.used = r.given + div_round_up(target - r.given, 8);
r.given = r.min;
}
resources = (std::max)(resources, sum_min);
int resources_to_distribute = (std::min)(resources, sum_max) - sum_min;
assert(resources_to_distribute >= 0);
#ifndef NDEBUG
int prev_resources_to_distribute = resources_to_distribute;
#endif
while (resources_to_distribute > 0)
{
// in order to scale, we need to calculate the sum of
// all the used values.
size_type total_used = 0;
size_type max_used = 0;
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
if (r.given == r.max) continue;
assert(r.given < r.max);
max_used = (std::max)(max_used, (size_type)r.used + 1);
total_used += (size_type)r.used + 1;
}
size_type kNumer = resources_to_distribute;
size_type kDenom = total_used;
assert(kNumer >= 0);
assert(kDenom >= 0);
assert(kNumer <= (std::numeric_limits<int>::max)());
if (kNumer * max_used <= kDenom)
{
kNumer = 1;
kDenom = max_used;
assert(kDenom >= 0);
}
for (It i = start; i != end && resources_to_distribute > 0; ++i)
{
resource_request& r = (*i).*res;
if (r.given == r.max) continue;
assert(r.given < r.max);
size_type used = (size_type)r.used + 1;
if (used < 1) used = 1;
size_type to_give = used * kNumer / kDenom;
if (to_give > resources_to_distribute)
to_give = resources_to_distribute;
assert(to_give >= 0);
assert(to_give <= resources_to_distribute);
#ifndef NDEBUG
int tmp = resources_to_distribute;
#endif
resources_to_distribute -= give(r, (int)to_give);
assert(resources_to_distribute <= tmp);
assert(resources_to_distribute >= 0);
}
assert(resources_to_distribute >= 0);
assert(resources_to_distribute < prev_resources_to_distribute);
#ifndef NDEBUG
prev_resources_to_distribute = resources_to_distribute;
#endif
}
assert(resources_to_distribute == 0);
}
} // namespace libtorrent::aux
}
#endif

View File

@ -254,7 +254,7 @@ namespace libtorrent
boost::intrusive_ptr<torrent_info> ti boost::intrusive_ptr<torrent_info> ti
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data , entry const& resume_data
, bool compact_mode , storage_mode_t storage_mode
, storage_constructor_type sc , storage_constructor_type sc
, bool paused , bool paused
, void* userdata); , void* userdata);
@ -265,7 +265,7 @@ namespace libtorrent
, char const* name , char const* name
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data , entry const& resume_data
, bool compact_mode , storage_mode_t storage_mode
, storage_constructor_type sc , storage_constructor_type sc
, bool paused , bool paused
, void* userdata); , void* userdata);

View File

@ -0,0 +1,99 @@
/*
Copyright (c) 2003, Magnus Jonsson, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_RESOURCE_REQUEST_HPP_INCLUDED
#define TORRENT_RESOURCE_REQUEST_HPP_INCLUDED
#include <boost/integer_traits.hpp>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "libtorrent/config.hpp"
namespace libtorrent
{
struct TORRENT_EXPORT resource_request
{
resource_request()
: used(0)
, min(0)
, max(0)
, given(0)
, leftovers(0)
{}
resource_request(int used_, int min_, int max_, int given_)
: used(used_)
, min(min_)
, max(max_)
, given(given_)
, leftovers(0)
{}
int left() const
{
assert(given <= max);
assert(given >= min);
assert(used >= 0);
return (std::max)(given - used, 0);
}
void reset() { used = leftovers; leftovers = 0; }
static const int inf = boost::integer_traits<int>::const_max;
// right now I'm actively using this amount
int used;
// given cannot be smaller than min
// and not greater than max.
int min;
int max;
// Reply: Okay, you're allowed to use this amount (a compromise):
int given;
// this is the amount of resources that exceeded the
// given limit. When the used field is reset (after resources
// have been distributed), it is reset to this number.
int leftovers;
};
}
#endif

View File

@ -140,7 +140,7 @@ namespace libtorrent
torrent_info const& ti torrent_info const& ti
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data = entry() , entry const& resume_data = entry()
, bool compact_mode = true , storage_mode_t storage_mode = storage_mode_sparse
, bool paused = false , bool paused = false
, storage_constructor_type sc = default_storage_constructor) TORRENT_DEPRECATED; , storage_constructor_type sc = default_storage_constructor) TORRENT_DEPRECATED;
@ -148,7 +148,7 @@ namespace libtorrent
boost::intrusive_ptr<torrent_info> ti boost::intrusive_ptr<torrent_info> ti
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data = entry() , entry const& resume_data = entry()
, bool compact_mode = true , storage_mode_t storage_mode = storage_mode_sparse
, bool paused = false , bool paused = false
, storage_constructor_type sc = default_storage_constructor , storage_constructor_type sc = default_storage_constructor
, void* userdata = 0); , void* userdata = 0);
@ -159,7 +159,7 @@ namespace libtorrent
, char const* name , char const* name
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data = entry() , entry const& resume_data = entry()
, bool compact_mode = true , storage_mode_t storage_mode = storage_mode_sparse
, bool paused = false , bool paused = false
, storage_constructor_type sc = default_storage_constructor , storage_constructor_type sc = default_storage_constructor
, void* userdata = 0); , void* userdata = 0);

View File

@ -71,6 +71,13 @@ namespace libtorrent
struct file_pool; struct file_pool;
struct disk_io_job; struct disk_io_job;
enum storage_mode_t
{
storage_mode_allocate = 0,
storage_mode_sparse,
storage_mode_compact
};
#if defined(_WIN32) && defined(UNICODE) #if defined(_WIN32) && defined(UNICODE)
TORRENT_EXPORT std::wstring safe_convert(std::string const& s); TORRENT_EXPORT std::wstring safe_convert(std::string const& s);
@ -180,7 +187,8 @@ namespace libtorrent
~piece_manager(); ~piece_manager();
bool check_fastresume(aux::piece_checker_data& d bool check_fastresume(aux::piece_checker_data& d
, std::vector<bool>& pieces, int& num_pieces, bool compact_mode); , std::vector<bool>& pieces, int& num_pieces, storage_mode_t storage_mode
, std::string& error_msg);
std::pair<bool, float> check_files(std::vector<bool>& pieces std::pair<bool, float> check_files(std::vector<bool>& pieces
, int& num_pieces, boost::recursive_mutex& mutex); , int& num_pieces, boost::recursive_mutex& mutex);
@ -191,7 +199,7 @@ namespace libtorrent
bool verify_resume_data(entry& rd, std::string& error); bool verify_resume_data(entry& rd, std::string& error);
bool is_allocating() const bool is_allocating() const
{ return m_state == state_allocating; } { return m_state == state_expand_pieces; }
void mark_failed(int index); void mark_failed(int index);
@ -200,7 +208,8 @@ namespace libtorrent
, int block_size , int block_size
, piece_picker::block_info const* bi); , piece_picker::block_info const* bi);
int slot_for_piece(int piece_index) const; int slot_for(int piece) const;
int piece_for(int slot) const;
void async_read( void async_read(
peer_request const& r peer_request const& r
@ -228,10 +237,11 @@ namespace libtorrent
// slots to the piece that is stored (or // slots to the piece that is stored (or
// partially stored) there. -2 is the index // partially stored) there. -2 is the index
// of unassigned pieces and -1 is unallocated // of unassigned pieces and -1 is unallocated
void export_piece_map(std::vector<int>& pieces) const; void export_piece_map(std::vector<int>& pieces
, std::vector<bool> const& have) const;
bool compact_allocation() const bool compact_allocation() const
{ return m_compact_mode; } { return m_storage_mode == storage_mode_compact; }
#ifndef NDEBUG #ifndef NDEBUG
std::string name() const { return m_info->name(); } std::string name() const { return m_info->name(); }
@ -261,6 +271,7 @@ namespace libtorrent
, int offset , int offset
, int size); , int size);
void switch_to_full_mode();
sha1_hash hash_for_piece_impl(int piece); sha1_hash hash_for_piece_impl(int piece);
void release_files_impl(); void release_files_impl();
@ -276,16 +287,7 @@ namespace libtorrent
#endif #endif
boost::scoped_ptr<storage_interface> m_storage; boost::scoped_ptr<storage_interface> m_storage;
// if this is true, pieces are always allocated at the storage_mode_t m_storage_mode;
// lowest possible slot index. If it is false, pieces
// are always written to their final place immediately
bool m_compact_mode;
// if this is true, pieces that haven't been downloaded
// will be filled with zeroes. Not filling with zeroes
// will not work in some cases (where a seek cannot pass
// the end of the file).
bool m_fill_mode;
// a bitmask representing the pieces we have // a bitmask representing the pieces we have
std::vector<bool> m_have_piece; std::vector<bool> m_have_piece;
@ -329,10 +331,21 @@ namespace libtorrent
state_create_files, state_create_files,
// checking the files // checking the files
state_full_check, state_full_check,
// allocating files (in non-compact mode) // move pieces to their final position
state_allocating state_expand_pieces
} m_state; } m_state;
int m_current_slot; int m_current_slot;
// used during check. If any piece is found
// that is not in its final position, this
// is set to true
bool m_out_of_place;
// used to move pieces while expanding
// the storage from compact allocation
// to full allocation
std::vector<char> m_scratch_buffer;
std::vector<char> m_scratch_buffer2;
// the piece that is in the scratch buffer
int m_scratch_piece;
// this is saved in case we need to instantiate a new // this is saved in case we need to instantiate a new
// storage (osed when remapping files) // storage (osed when remapping files)

View File

@ -101,7 +101,7 @@ namespace libtorrent
, boost::intrusive_ptr<torrent_info> tf , boost::intrusive_ptr<torrent_info> tf
, fs::path const& save_path , fs::path const& save_path
, tcp::endpoint const& net_interface , tcp::endpoint const& net_interface
, bool compact_mode , storage_mode_t m_storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused); , bool paused);
@ -116,7 +116,7 @@ namespace libtorrent
, char const* name , char const* name
, fs::path const& save_path , fs::path const& save_path
, tcp::endpoint const& net_interface , tcp::endpoint const& net_interface
, bool compact_mode , storage_mode_t m_storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused); , bool paused);
@ -751,7 +751,7 @@ namespace libtorrent
fs::path m_save_path; fs::path m_save_path;
// determines the storage state for this torrent. // determines the storage state for this torrent.
const bool m_compact_mode; storage_mode_t m_storage_mode;
// defaults to 16 kiB, but can be set by the user // defaults to 16 kiB, but can be set by the user
// when creating the torrent // when creating the torrent

View File

@ -52,6 +52,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/torrent_info.hpp" #include "libtorrent/torrent_info.hpp"
#include "libtorrent/time.hpp" #include "libtorrent/time.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
#include "libtorrent/storage.hpp"
namespace libtorrent namespace libtorrent
{ {
@ -106,7 +107,7 @@ namespace libtorrent
, num_connections(0) , num_connections(0)
, uploads_limit(0) , uploads_limit(0)
, connections_limit(0) , connections_limit(0)
, compact_mode(false) , storage_mode(storage_mode_sparse)
{} {}
enum state_t enum state_t
@ -216,7 +217,7 @@ namespace libtorrent
// true if the torrent is saved in compact mode // true if the torrent is saved in compact mode
// false if it is saved in full allocation mode // false if it is saved in full allocation mode
bool compact_mode; storage_mode_t storage_mode;
}; };
struct TORRENT_EXPORT block_info struct TORRENT_EXPORT block_info

View File

@ -0,0 +1,225 @@
/*
Copyright (c) 2006, Magnus Jonsson, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
//The Standard Library defines the two template functions std::min()
//and std::max() in the <algorithm> header. In general, you should
//use these template functions for calculating the min and max values
//of a pair. Unfortunately, Visual C++ does not define these function
// templates. This is because the names min and max clash with
//the traditional min and max macros defined in <windows.h>.
//As a workaround, Visual C++ defines two alternative templates with
//identical functionality called _cpp_min() and _cpp_max(). You can
//use them instead of std::min() and std::max().To disable the
//generation of the min and max macros in Visual C++, #define
//NOMINMAX before #including <windows.h>.
#include "libtorrent/pch.hpp"
#ifdef _WIN32
//support boost1.32.0(2004-11-19 18:47)
//now all libs can be compiled and linked with static module
#define NOMINMAX
#endif
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/peer_connection.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/aux_/allocate_resources_impl.hpp"
#include <cassert>
#include <algorithm>
#include <boost/limits.hpp>
#if defined(_MSC_VER) && _MSC_VER < 1310
#define for if (false) {} else for
#else
#include <boost/iterator/transform_iterator.hpp>
#endif
namespace libtorrent
{
int saturated_add(int a, int b)
{
assert(a >= 0);
assert(b >= 0);
assert(a <= resource_request::inf);
assert(b <= resource_request::inf);
assert(resource_request::inf + resource_request::inf < 0);
unsigned int sum = unsigned(a) + unsigned(b);
if (sum > unsigned(resource_request::inf))
sum = resource_request::inf;
assert(sum >= unsigned(a) && sum >= unsigned(b));
return int(sum);
}
#if defined(_MSC_VER) && _MSC_VER < 1310
namespace detail
{
struct iterator_wrapper
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
orig_iter iter;
iterator_wrapper(orig_iter i): iter(i) {}
void operator++() { ++iter; }
torrent& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper& i) const
{ return iter != i.iter; }
};
struct iterator_wrapper2
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
orig_iter iter;
iterator_wrapper2(orig_iter i): iter(i) {}
void operator++() { ++iter; }
peer_connection& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper2& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper2& i) const
{ return iter != i.iter; }
};
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper(c.begin())
, detail::iterator_wrapper(c.end())
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper2(c.begin())
, detail::iterator_wrapper2(c.end())
, res);
}
#else
namespace aux
{
peer_connection& pick_peer(
std::pair<boost::shared_ptr<stream_socket>
, boost::intrusive_ptr<peer_connection> > const& p)
{
return *p.second;
}
peer_connection& pick_peer2(
std::pair<tcp::endpoint, peer_connection*> const& p)
{
return *p.second;
}
torrent& deref(std::pair<sha1_hash, boost::shared_ptr<torrent> > const& p)
{
return *p.second;
}
session& deref(session* p)
{
return *p;
}
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
typedef std::pair<sha1_hash, boost::shared_ptr<torrent> > in_param;
typedef boost::transform_iterator<torrent& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::deref)
, new_iter(c.end(), &aux::deref)
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
typedef std::pair<tcp::endpoint, peer_connection*> in_param;
typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::pick_peer2)
, new_iter(c.end(), &aux::pick_peer2)
, res);
}
void allocate_resources(
int resources
, std::vector<session*>& _sessions
, resource_request session::* res)
{
typedef std::vector<session*>::iterator orig_iter;
typedef session* in_param;
typedef boost::transform_iterator<session& (*)(in_param), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(_sessions.begin(), &aux::deref)
, new_iter(_sessions.end(), &aux::deref)
, res);
}
#endif
} // namespace libtorrent

View File

@ -1732,7 +1732,6 @@ namespace libtorrent
++i->writing; ++i->writing;
info.state = block_info::state_writing; info.state = block_info::state_writing;
if (info.num_peers > 0) --info.num_peers; if (info.num_peers > 0) --info.num_peers;
TORRENT_ASSERT(info.num_peers >= 0);
if (i->requested == 0) if (i->requested == 0)
{ {
@ -1855,7 +1854,6 @@ namespace libtorrent
block_info& info = i->info[block.block_index]; block_info& info = i->info[block.block_index];
--info.num_peers; --info.num_peers;
TORRENT_ASSERT(info.num_peers >= 0);
if (info.num_peers > 0) return; if (info.num_peers > 0) return;
if (i->info[block.block_index].state == block_info::state_finished if (i->info[block.block_index].state == block_info::state_finished

View File

@ -186,28 +186,28 @@ namespace libtorrent
torrent_info const& ti torrent_info const& ti
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data , entry const& resume_data
, bool compact_mode , storage_mode_t storage_mode
, bool paused , bool paused
, storage_constructor_type sc) , storage_constructor_type sc)
{ {
TORRENT_ASSERT(!ti.m_half_metadata); TORRENT_ASSERT(!ti.m_half_metadata);
boost::intrusive_ptr<torrent_info> tip(new torrent_info(ti)); boost::intrusive_ptr<torrent_info> tip(new torrent_info(ti));
return m_impl->add_torrent(tip, save_path, resume_data return m_impl->add_torrent(tip, save_path, resume_data
, compact_mode, sc, paused, 0); , storage_mode, sc, paused, 0);
} }
torrent_handle session::add_torrent( torrent_handle session::add_torrent(
boost::intrusive_ptr<torrent_info> ti boost::intrusive_ptr<torrent_info> ti
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data , entry const& resume_data
, bool compact_mode , storage_mode_t storage_mode
, bool paused , bool paused
, storage_constructor_type sc , storage_constructor_type sc
, void* userdata) , void* userdata)
{ {
TORRENT_ASSERT(!ti->m_half_metadata); TORRENT_ASSERT(!ti->m_half_metadata);
return m_impl->add_torrent(ti, save_path, resume_data return m_impl->add_torrent(ti, save_path, resume_data
, compact_mode, sc, paused, userdata); , storage_mode, sc, paused, userdata);
} }
torrent_handle session::add_torrent( torrent_handle session::add_torrent(
@ -216,13 +216,13 @@ namespace libtorrent
, char const* name , char const* name
, fs::path const& save_path , fs::path const& save_path
, entry const& e , entry const& e
, bool compact_mode , storage_mode_t storage_mode
, bool paused , bool paused
, storage_constructor_type sc , storage_constructor_type sc
, void* userdata) , void* userdata)
{ {
return m_impl->add_torrent(tracker_url, info_hash, name, save_path, e return m_impl->add_torrent(tracker_url, info_hash, name, save_path, e
, compact_mode, sc, paused, userdata); , storage_mode, sc, paused, userdata);
} }
void session::remove_torrent(const torrent_handle& h) void session::remove_torrent(const torrent_handle& h)

View File

@ -189,9 +189,11 @@ namespace detail
t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file() t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file()
, error_msg); , error_msg);
// lock the session to add the new torrent
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning)) if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning))
{ {
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
m_ses.m_alerts.post_alert(fastresume_rejected_alert( m_ses.m_alerts.post_alert(fastresume_rejected_alert(
t->torrent_ptr->get_handle() t->torrent_ptr->get_handle()
, error_msg)); , error_msg));
@ -202,8 +204,6 @@ namespace detail
#endif #endif
} }
// lock the session to add the new torrent
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
mutex::scoped_lock l2(m_mutex); mutex::scoped_lock l2(m_mutex);
if (m_torrents.empty() || m_torrents.front() != t) if (m_torrents.empty() || m_torrents.front() != t)
@ -1623,7 +1623,7 @@ namespace detail
boost::intrusive_ptr<torrent_info> ti boost::intrusive_ptr<torrent_info> ti
, fs::path const& save_path , fs::path const& save_path
, entry const& resume_data , entry const& resume_data
, bool compact_mode , storage_mode_t storage_mode
, storage_constructor_type sc , storage_constructor_type sc
, bool paused , bool paused
, void* userdata) , void* userdata)
@ -1655,7 +1655,7 @@ namespace detail
// the thread // the thread
boost::shared_ptr<torrent> torrent_ptr( boost::shared_ptr<torrent> torrent_ptr(
new torrent(*this, m_checker_impl, ti, save_path new torrent(*this, m_checker_impl, ti, save_path
, m_listen_interface, compact_mode, 16 * 1024 , m_listen_interface, storage_mode, 16 * 1024
, sc, paused)); , sc, paused));
torrent_ptr->start(); torrent_ptr->start();
@ -1701,7 +1701,7 @@ namespace detail
, char const* name , char const* name
, fs::path const& save_path , fs::path const& save_path
, entry const& , entry const&
, bool compact_mode , storage_mode_t storage_mode
, storage_constructor_type sc , storage_constructor_type sc
, bool paused , bool paused
, void* userdata) , void* userdata)
@ -1735,7 +1735,7 @@ namespace detail
// the thread // the thread
boost::shared_ptr<torrent> torrent_ptr( boost::shared_ptr<torrent> torrent_ptr(
new torrent(*this, m_checker_impl, tracker_url, info_hash, name new torrent(*this, m_checker_impl, tracker_url, info_hash, name
, save_path, m_listen_interface, compact_mode, 16 * 1024 , save_path, m_listen_interface, storage_mode, 16 * 1024
, sc, paused)); , sc, paused));
torrent_ptr->start(); torrent_ptr->start();

View File

@ -447,7 +447,7 @@ namespace libtorrent
} }
// if the file is empty, just create it. But also make sure // if the file is empty, just create it. But also make sure
// the directory exits. // the directory exists.
if (file_iter->size == 0) if (file_iter->size == 0)
{ {
file(m_save_path / file_iter->path, file::out); file(m_save_path / file_iter->path, file::out);
@ -931,107 +931,6 @@ namespace libtorrent
return new storage(ti, path, fp); return new storage(ti, path, fp);
} }
bool supports_sparse_files(fs::path const& p)
{
TORRENT_ASSERT(p.is_complete());
#if defined(_WIN32)
// assume windows API is available
DWORD max_component_len = 0;
DWORD volume_flags = 0;
std::string root_device = p.root_name() + "\\";
#if defined(UNICODE)
std::wstring wph(safe_convert(root_device));
bool ret = ::GetVolumeInformation(wph.c_str(), 0
, 0, 0, &max_component_len, &volume_flags, 0, 0);
#else
bool ret = ::GetVolumeInformation(root_device.c_str(), 0
, 0, 0, &max_component_len, &volume_flags, 0, 0);
#endif
if (!ret) return false;
if (volume_flags & FILE_SUPPORTS_SPARSE_FILES)
return true;
#endif
#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__)
// find the last existing directory of the save path
fs::path query_path = p;
while (!query_path.empty() && !exists(query_path))
query_path = query_path.branch_path();
#endif
#if defined(__APPLE__)
struct statfs fsinfo;
int ret = statfs(query_path.native_directory_string().c_str(), &fsinfo);
if (ret != 0) return false;
attrlist request;
request.bitmapcount = ATTR_BIT_MAP_COUNT;
request.reserved = 0;
request.commonattr = 0;
request.volattr = ATTR_VOL_CAPABILITIES;
request.dirattr = 0;
request.fileattr = 0;
request.forkattr = 0;
struct vol_capabilities_attr_buf
{
unsigned long length;
vol_capabilities_attr_t info;
} vol_cap;
ret = getattrlist(fsinfo.f_mntonname, &request, &vol_cap
, sizeof(vol_cap), 0);
if (ret != 0) return false;
if (vol_cap.info.capabilities[VOL_CAPABILITIES_FORMAT]
& (VOL_CAP_FMT_SPARSE_FILES | VOL_CAP_FMT_ZERO_RUNS))
{
return true;
}
// workaround for bugs in Mac OS X where zero run is not reported
if (!strcmp(fsinfo.f_fstypename, "hfs")
|| !strcmp(fsinfo.f_fstypename, "ufs"))
return true;
return false;
#endif
#if defined(__linux__) || defined(__FreeBSD__)
struct statfs buf;
int err = statfs(query_path.native_directory_string().c_str(), &buf);
if (err == 0)
{
switch (buf.f_type)
{
case 0x5346544e: // NTFS
case 0xEF51: // EXT2 OLD
case 0xEF53: // EXT2 and EXT3
case 0x00011954: // UFS
case 0x52654973: // ReiserFS
case 0x52345362: // Reiser4
case 0x58465342: // XFS
case 0x65735546: // NTFS-3G
case 0x19540119: // UFS2
return true;
}
}
#ifndef NDEBUG
else
{
std::cerr << "statfs returned " << err << std::endl;
std::cerr << "errno: " << errno << std::endl;
std::cerr << "path: " << query_path.native_directory_string() << std::endl;
}
#endif
#endif
// TODO: POSIX implementation
return false;
}
// -- piece_manager ----------------------------------------------------- // -- piece_manager -----------------------------------------------------
piece_manager::piece_manager( piece_manager::piece_manager(
@ -1042,15 +941,16 @@ namespace libtorrent
, disk_io_thread& io , disk_io_thread& io
, storage_constructor_type sc) , storage_constructor_type sc)
: m_storage(sc(ti, save_path, fp)) : m_storage(sc(ti, save_path, fp))
, m_compact_mode(false) , m_storage_mode(storage_mode_sparse)
, m_fill_mode(true)
, m_info(ti) , m_info(ti)
, m_save_path(complete(save_path)) , m_save_path(complete(save_path))
, m_current_slot(0)
, m_out_of_place(false)
, m_scratch_piece(-1)
, m_storage_constructor(sc) , m_storage_constructor(sc)
, m_io_thread(io) , m_io_thread(io)
, m_torrent(torrent) , m_torrent(torrent)
{ {
m_fill_mode = !supports_sparse_files(save_path);
} }
piece_manager::~piece_manager() piece_manager::~piece_manager()
@ -1158,7 +1058,7 @@ namespace libtorrent
m_piece_hasher.erase(i); m_piece_hasher.erase(i);
} }
int slot = m_piece_to_slot[piece]; int slot = slot_for(piece);
TORRENT_ASSERT(slot != has_no_slot); TORRENT_ASSERT(slot != has_no_slot);
return m_storage->hash_for_slot(slot, ph, m_info->piece_size(piece)); return m_storage->hash_for_slot(slot, ph, m_info->piece_size(piece));
} }
@ -1178,25 +1078,37 @@ namespace libtorrent
return false; return false;
} }
void piece_manager::export_piece_map( void piece_manager::export_piece_map(
std::vector<int>& p) const std::vector<int>& p, std::vector<bool> const& have) const
{ {
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
INVARIANT_CHECK; INVARIANT_CHECK;
if (m_storage_mode == storage_mode_compact)
{
p.clear(); p.clear();
p.reserve(m_info->num_pieces());
std::vector<int>::const_reverse_iterator last; std::vector<int>::const_reverse_iterator last;
for (last = m_slot_to_piece.rbegin(); for (last = m_slot_to_piece.rbegin();
last != m_slot_to_piece.rend(); ++last) last != m_slot_to_piece.rend(); ++last)
{ {
if (*last != unallocated) break; if (*last != unallocated && have[*last]) break;
} }
for (std::vector<int>::const_iterator i = for (std::vector<int>::const_iterator i =
m_slot_to_piece.begin(); m_slot_to_piece.begin();
i != last.base(); ++i) i != last.base(); ++i)
{ {
p.push_back(*i); p.push_back(have[*i] ? *i : unassigned);
}
}
else
{
p.reserve(m_info->num_pieces());
for (int i = 0; i < m_info->num_pieces(); ++i)
{
p.push_back(have[i] ? i : unassigned);
}
} }
} }
@ -1206,11 +1118,10 @@ namespace libtorrent
INVARIANT_CHECK; INVARIANT_CHECK;
if (m_storage_mode != storage_mode_compact) return;
TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0);
int slot_index = m_piece_to_slot[piece_index]; int slot_index = m_piece_to_slot[piece_index];
TORRENT_ASSERT(slot_index >= 0); TORRENT_ASSERT(slot_index >= 0);
m_slot_to_piece[slot_index] = unassigned; m_slot_to_piece[slot_index] = unassigned;
@ -1218,12 +1129,6 @@ namespace libtorrent
m_free_slots.push_back(slot_index); m_free_slots.push_back(slot_index);
} }
int piece_manager::slot_for_piece(int piece_index) const
{
TORRENT_ASSERT(piece_index >= 0 && piece_index < m_info->num_pieces());
return m_piece_to_slot[piece_index];
}
unsigned long piece_manager::piece_crc( unsigned long piece_manager::piece_crc(
int slot_index int slot_index
, int block_size , int block_size
@ -1275,11 +1180,7 @@ namespace libtorrent
TORRENT_ASSERT(buf); TORRENT_ASSERT(buf);
TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(size > 0); TORRENT_ASSERT(size > 0);
TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); int slot = slot_for(piece_index);
TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0
&& m_piece_to_slot[piece_index] < (int)m_slot_to_piece.size());
int slot = m_piece_to_slot[piece_index];
TORRENT_ASSERT(slot >= 0 && slot < (int)m_slot_to_piece.size());
return m_storage->read(buf, slot, offset, size); return m_storage->read(buf, slot, offset, size);
} }
@ -1292,7 +1193,7 @@ namespace libtorrent
TORRENT_ASSERT(buf); TORRENT_ASSERT(buf);
TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(size > 0); TORRENT_ASSERT(size > 0);
TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); TORRENT_ASSERT(piece_index >= 0 && piece_index < m_info->num_pieces());
if (offset == 0) if (offset == 0)
{ {
@ -1317,7 +1218,6 @@ namespace libtorrent
} }
int slot = allocate_slot_for_piece(piece_index); int slot = allocate_slot_for_piece(piece_index);
TORRENT_ASSERT(slot >= 0 && slot < (int)m_slot_to_piece.size());
m_storage->write(buf, slot, offset, size); m_storage->write(buf, slot, offset, size);
} }
@ -1426,6 +1326,7 @@ namespace libtorrent
// that piece as unassigned, since this slot // that piece as unassigned, since this slot
// is the correct place for the piece. // is the correct place for the piece.
m_slot_to_piece[other_slot] = unassigned; m_slot_to_piece[other_slot] = unassigned;
if (m_storage_mode == storage_mode_compact)
m_free_slots.push_back(other_slot); m_free_slots.push_back(other_slot);
} }
TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot); TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot);
@ -1485,7 +1386,8 @@ namespace libtorrent
bool piece_manager::check_fastresume( bool piece_manager::check_fastresume(
aux::piece_checker_data& data aux::piece_checker_data& data
, std::vector<bool>& pieces , std::vector<bool>& pieces
, int& num_pieces, bool compact_mode) , int& num_pieces, storage_mode_t storage_mode
, std::string& error_msg)
{ {
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
@ -1493,7 +1395,7 @@ namespace libtorrent
TORRENT_ASSERT(m_info->piece_length() > 0); TORRENT_ASSERT(m_info->piece_length() > 0);
m_compact_mode = compact_mode; m_storage_mode = storage_mode;
// This will corrupt the storage // This will corrupt the storage
// use while debugging to find // use while debugging to find
@ -1503,8 +1405,12 @@ namespace libtorrent
m_piece_to_slot.resize(m_info->num_pieces(), has_no_slot); m_piece_to_slot.resize(m_info->num_pieces(), has_no_slot);
m_slot_to_piece.resize(m_info->num_pieces(), unallocated); m_slot_to_piece.resize(m_info->num_pieces(), unallocated);
m_free_slots.clear(); TORRENT_ASSERT(m_free_slots.empty());
m_unallocated_slots.clear(); TORRENT_ASSERT(m_unallocated_slots.empty());
// 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;
pieces.clear(); pieces.clear();
pieces.resize(m_info->num_pieces(), false); pieces.resize(m_info->num_pieces(), false);
@ -1513,13 +1419,14 @@ namespace libtorrent
// if we have fast-resume info // if we have fast-resume info
// use it instead of doing the actual checking // use it instead of doing the actual checking
if (!data.piece_map.empty() if (!data.piece_map.empty()
&& data.piece_map.size() <= m_slot_to_piece.size()) && int(data.piece_map.size()) <= m_info->num_pieces())
{ {
for (int i = 0; i < (int)data.piece_map.size(); ++i) for (int i = 0; i < (int)data.piece_map.size(); ++i)
{ {
m_slot_to_piece[i] = data.piece_map[i]; m_slot_to_piece[i] = data.piece_map[i];
if (data.piece_map[i] >= 0) if (data.piece_map[i] >= 0)
{ {
if (data.piece_map[i] != i) out_of_place = true;
m_piece_to_slot[data.piece_map[i]] = i; m_piece_to_slot[data.piece_map[i]] = i;
int found_piece = data.piece_map[i]; int found_piece = data.piece_map[i];
@ -1537,30 +1444,58 @@ namespace libtorrent
} }
else if (data.piece_map[i] == unassigned) else if (data.piece_map[i] == unassigned)
{ {
if (m_storage_mode == storage_mode_compact)
m_free_slots.push_back(i); m_free_slots.push_back(i);
} }
else else
{ {
TORRENT_ASSERT(data.piece_map[i] == unallocated); TORRENT_ASSERT(data.piece_map[i] == unallocated);
if (m_storage_mode == storage_mode_compact)
m_unallocated_slots.push_back(i); m_unallocated_slots.push_back(i);
} }
} }
m_unallocated_slots.reserve(int(pieces.size() - data.piece_map.size())); if (m_storage_mode == storage_mode_compact)
for (int i = (int)data.piece_map.size(); i < (int)pieces.size(); ++i) {
m_unallocated_slots.reserve(int(m_info->num_pieces() - data.piece_map.size()));
for (int i = (int)data.piece_map.size(); i < (int)m_info->num_pieces(); ++i)
{ {
m_unallocated_slots.push_back(i); m_unallocated_slots.push_back(i);
} }
if (m_unallocated_slots.empty()) if (m_unallocated_slots.empty())
m_state = state_create_files; {
else if (m_compact_mode) switch_to_full_mode();
m_state = state_create_files; }
}
else else
m_state = state_allocating; {
if (!out_of_place)
{
// if no piece is out of place
// since we're in full allocation mode, we can
// forget the piece allocation tables
std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece);
m_state = state_create_files;
return false;
}
else
{
// 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_msg = "pieces needs to be reordered";
return false;
}
}
m_state = state_create_files;
return false; return false;
} }
error_msg = "empty piece map";
m_state = state_full_check; m_state = state_full_check;
return false; return false;
} }
@ -1572,18 +1507,13 @@ namespace libtorrent
| | | |
| v | v
| +------------+ | +------------+ +---------------+
| | full_check | | | full_check |-->| expand_pieses |
| +------------+ | +------------+ +---------------+
| | | | |
| v | v |
| +------------+ | +--------------+ |
|->| allocating | +->| create_files | <------+
| +------------+
| |
| v
| +--------------+
|->| create_files |
+--------------+ +--------------+
| |
v v
@ -1602,67 +1532,97 @@ namespace libtorrent
std::pair<bool, float> piece_manager::check_files( std::pair<bool, float> piece_manager::check_files(
std::vector<bool>& pieces, int& num_pieces, boost::recursive_mutex& mutex) std::vector<bool>& pieces, int& num_pieces, boost::recursive_mutex& mutex)
{ {
#ifndef NDEBUG
boost::recursive_mutex::scoped_lock l_(mutex);
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true)); TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
l_.unlock();
if (m_state == state_allocating) #endif
{
if (m_compact_mode || m_unallocated_slots.empty())
{
m_state = state_create_files;
return std::make_pair(false, 1.f);
}
if (int(m_unallocated_slots.size()) == m_info->num_pieces()
&& !m_fill_mode)
{
// if there is not a single file on disk, just
// create the files
m_state = state_create_files;
return std::make_pair(false, 1.f);
}
// if we're not in compact mode, make sure the
// pieces are spread out and placed at their
// final position.
TORRENT_ASSERT(!m_unallocated_slots.empty());
if (!m_fill_mode)
{
// if we're not filling the allocation
// just make sure we move the current pieces
// into place, and just skip all other
// allocation
// allocate_slots returns true if it had to
// move any data
allocate_slots(m_unallocated_slots.size(), true);
}
else
{
allocate_slots(1);
}
return std::make_pair(false, 1.f - (float)m_unallocated_slots.size()
/ (float)m_slot_to_piece.size());
}
if (m_state == state_create_files) if (m_state == state_create_files)
{ {
m_storage->initialize(!m_fill_mode && !m_compact_mode); m_storage->initialize(m_storage_mode == storage_mode_allocate);
if (!m_unallocated_slots.empty() && !m_compact_mode)
{
TORRENT_ASSERT(!m_fill_mode);
std::vector<int>().swap(m_unallocated_slots);
std::fill(m_slot_to_piece.begin(), m_slot_to_piece.end(), int(unassigned));
m_free_slots.resize(m_info->num_pieces());
for (int i = 0; i < m_info->num_pieces(); ++i)
m_free_slots[i] = i;
}
m_state = state_finished; m_state = state_finished;
return std::make_pair(true, 1.f); return std::make_pair(true, 1.f);
} }
if (m_state == state_expand_pieces)
{
INVARIANT_CHECK;
if (m_scratch_piece >= 0)
{
int piece = m_scratch_piece;
int other_piece = m_slot_to_piece[piece];
m_scratch_piece = -1;
if (other_piece >= 0)
{
if (m_scratch_buffer2.empty())
m_scratch_buffer2.resize(m_info->piece_length());
m_storage->read(&m_scratch_buffer2[0], piece, 0, m_info->piece_size(other_piece));
m_scratch_piece = other_piece;
m_piece_to_slot[other_piece] = unassigned;
}
// the slot where this piece belongs is
// free. Just move the piece there.
m_storage->write(&m_scratch_buffer[0], piece, 0, m_info->piece_size(piece));
m_piece_to_slot[piece] = piece;
m_slot_to_piece[piece] = piece;
if (other_piece >= 0)
m_scratch_buffer.swap(m_scratch_buffer2);
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces());
}
while (m_current_slot < m_info->num_pieces()
&& (m_slot_to_piece[m_current_slot] == m_current_slot
|| m_slot_to_piece[m_current_slot] < 0))
{
++m_current_slot;
}
if (m_current_slot == m_info->num_pieces())
{
m_state = state_create_files;
std::vector<char>().swap(m_scratch_buffer);
std::vector<char>().swap(m_scratch_buffer2);
if (m_storage_mode != storage_mode_compact)
{
std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece);
}
return std::make_pair(false, 1.f);
}
int piece = m_slot_to_piece[m_current_slot];
TORRENT_ASSERT(piece >= 0);
int other_piece = m_slot_to_piece[piece];
if (other_piece >= 0)
{
// there is another piece in the slot
// where this one goes. Store it in the scratch
// buffer until next iteration.
if (m_scratch_buffer.empty())
m_scratch_buffer.resize(m_info->piece_length());
m_storage->read(&m_scratch_buffer[0], piece, 0, m_info->piece_size(other_piece));
m_scratch_piece = other_piece;
m_piece_to_slot[other_piece] = unassigned;
}
// the slot where this piece belongs is
// free. Just move the piece there.
m_storage->move_slot(m_current_slot, piece);
m_piece_to_slot[piece] = piece;
m_slot_to_piece[m_current_slot] = unassigned;
m_slot_to_piece[piece] = piece;
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces());
}
TORRENT_ASSERT(m_state == state_full_check); TORRENT_ASSERT(m_state == state_full_check);
// ------------------------ // ------------------------
@ -1674,12 +1634,13 @@ namespace libtorrent
// initialization for the full check // initialization for the full check
if (m_hash_to_piece.empty()) if (m_hash_to_piece.empty())
{ {
m_current_slot = 0;
for (int i = 0; i < m_info->num_pieces(); ++i) for (int i = 0; i < m_info->num_pieces(); ++i)
{ {
m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i)); m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i));
} }
boost::recursive_mutex::scoped_lock l(mutex);
std::fill(pieces.begin(), pieces.end(), false); std::fill(pieces.begin(), pieces.end(), false);
num_pieces = 0;
} }
m_piece_data.resize(int(m_info->piece_length())); m_piece_data.resize(int(m_info->piece_length()));
@ -1694,6 +1655,10 @@ namespace libtorrent
int piece_index = identify_data(m_piece_data, m_current_slot int piece_index = identify_data(m_piece_data, m_current_slot
, pieces, num_pieces, m_hash_to_piece, mutex); , pieces, num_pieces, m_hash_to_piece, mutex);
if (piece_index != m_current_slot
&& piece_index >= 0)
m_out_of_place = true;
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true)); TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0); TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0);
@ -1745,9 +1710,12 @@ namespace libtorrent
std::vector<int>::iterator i = std::vector<int>::iterator i =
std::find(m_free_slots.begin(), m_free_slots.end(), other_slot); std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
TORRENT_ASSERT(i != m_free_slots.end()); TORRENT_ASSERT(i != m_free_slots.end());
if (m_storage_mode == storage_mode_compact)
{
m_free_slots.erase(i); m_free_slots.erase(i);
m_free_slots.push_back(m_current_slot); m_free_slots.push_back(m_current_slot);
} }
}
if (other_piece >= 0) if (other_piece >= 0)
m_storage->swap_slots(other_slot, m_current_slot); m_storage->swap_slots(other_slot, m_current_slot);
@ -1770,7 +1738,8 @@ namespace libtorrent
m_slot_to_piece[other_slot] = piece_index; m_slot_to_piece[other_slot] = piece_index;
m_piece_to_slot[other_piece] = m_current_slot; m_piece_to_slot[other_piece] = m_current_slot;
if (piece_index == unassigned) if (piece_index == unassigned
&& m_storage_mode == storage_mode_compact)
m_free_slots.push_back(other_slot); m_free_slots.push_back(other_slot);
if (piece_index >= 0) if (piece_index >= 0)
@ -1845,9 +1814,12 @@ namespace libtorrent
std::vector<int>::iterator i = std::vector<int>::iterator i =
std::find(m_free_slots.begin(), m_free_slots.end(), slot1); std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
TORRENT_ASSERT(i != m_free_slots.end()); TORRENT_ASSERT(i != m_free_slots.end());
if (m_storage_mode == storage_mode_compact)
{
m_free_slots.erase(i); m_free_slots.erase(i);
m_free_slots.push_back(slot2); m_free_slots.push_back(slot2);
} }
}
if (piece1 >= 0) if (piece1 >= 0)
{ {
@ -1873,7 +1845,7 @@ namespace libtorrent
// the slot was identified as piece 'piece_index' // the slot was identified as piece 'piece_index'
if (piece_index != unassigned) if (piece_index != unassigned)
m_piece_to_slot[piece_index] = m_current_slot; m_piece_to_slot[piece_index] = m_current_slot;
else else if (m_storage_mode == storage_mode_compact)
m_free_slots.push_back(m_current_slot); m_free_slots.push_back(m_current_slot);
m_slot_to_piece[m_current_slot] = piece_index; m_slot_to_piece[m_current_slot] = piece_index;
@ -1899,11 +1871,14 @@ namespace libtorrent
(file_offset - current_offset + m_info->piece_length() - 1) (file_offset - current_offset + m_info->piece_length() - 1)
/ m_info->piece_length()); / m_info->piece_length());
if (m_storage_mode == storage_mode_compact)
{
for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i) for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
{ {
TORRENT_ASSERT(m_slot_to_piece[i] == unallocated); TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
m_unallocated_slots.push_back(i); m_unallocated_slots.push_back(i);
} }
}
// current slot will increase by one at the end of the for-loop too // current slot will increase by one at the end of the for-loop too
m_current_slot += skip_blocks - 1; m_current_slot += skip_blocks - 1;
@ -1917,8 +1892,39 @@ namespace libtorrent
// clear the memory we've been using // clear the memory we've been using
std::vector<char>().swap(m_piece_data); std::vector<char>().swap(m_piece_data);
std::multimap<sha1_hash, int>().swap(m_hash_to_piece); std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
m_state = state_allocating;
if (m_storage_mode != storage_mode_compact)
{
if (!m_out_of_place)
{
// if no piece is out of place
// since we're in full allocation mode, we can
// forget the piece allocation tables
std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece);
m_state = state_create_files;
return std::make_pair(false, 1.f);
}
else
{
// 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;
return std::make_pair(false, 0.f);
}
}
else if (m_unallocated_slots.empty())
{
switch_to_full_mode();
}
m_state = state_create_files;
#ifndef NDEBUG
boost::recursive_mutex::scoped_lock l(mutex);
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true)); TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
#endif
return std::make_pair(false, 1.f); return std::make_pair(false, 1.f);
} }
@ -1927,10 +1933,26 @@ namespace libtorrent
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces()); return std::make_pair(false, (float)m_current_slot / m_info->num_pieces());
} }
void piece_manager::switch_to_full_mode()
{
TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
TORRENT_ASSERT(m_unallocated_slots.empty());
// we have allocated all slots, switch to
// full allocation mode in order to free
// some unnecessary memory.
m_storage_mode = storage_mode_sparse;
std::vector<int>().swap(m_unallocated_slots);
std::vector<int>().swap(m_free_slots);
std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece);
}
int piece_manager::allocate_slot_for_piece(int piece_index) int piece_manager::allocate_slot_for_piece(int piece_index)
{ {
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
if (m_storage_mode != storage_mode_compact) return piece_index;
// INVARIANT_CHECK; // INVARIANT_CHECK;
TORRENT_ASSERT(piece_index >= 0); TORRENT_ASSERT(piece_index >= 0);
@ -2030,25 +2052,26 @@ namespace libtorrent
debug_log(); debug_log();
#endif #endif
} }
TORRENT_ASSERT(slot_index >= 0); TORRENT_ASSERT(slot_index >= 0);
TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size()); TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
if (m_unallocated_slots.empty())
{
switch_to_full_mode();
}
return slot_index; return slot_index;
} }
bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk) bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk)
{ {
TORRENT_ASSERT(num_slots > 0);
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
TORRENT_ASSERT(num_slots > 0);
// INVARIANT_CHECK; // INVARIANT_CHECK;
TORRENT_ASSERT(!m_unallocated_slots.empty()); TORRENT_ASSERT(!m_unallocated_slots.empty());
TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
const int stack_buffer_size = 16*1024;
char zeroes[stack_buffer_size];
memset(zeroes, 0, stack_buffer_size);
bool written = false; bool written = false;
@ -2069,32 +2092,57 @@ namespace libtorrent
m_piece_to_slot[pos] = pos; m_piece_to_slot[pos] = pos;
written = true; written = true;
} }
else if (m_fill_mode)
{
int piece_size = int(m_info->piece_size(pos));
int offset = 0;
for (; piece_size > 0; piece_size -= stack_buffer_size
, offset += stack_buffer_size)
{
m_storage->write(zeroes, pos, offset
, (std::min)(piece_size, stack_buffer_size));
}
written = true;
}
m_unallocated_slots.erase(m_unallocated_slots.begin()); m_unallocated_slots.erase(m_unallocated_slots.begin());
m_slot_to_piece[new_free_slot] = unassigned; m_slot_to_piece[new_free_slot] = unassigned;
m_free_slots.push_back(new_free_slot); m_free_slots.push_back(new_free_slot);
if (abort_on_disk && written) return true; if (abort_on_disk && written) break;
} }
TORRENT_ASSERT(m_free_slots.size() > 0); TORRENT_ASSERT(m_free_slots.size() > 0);
return written; return written;
} }
int piece_manager::slot_for(int piece) const
{
if (m_storage_mode != storage_mode_compact) return piece;
TORRENT_ASSERT(piece < int(m_piece_to_slot.size()));
TORRENT_ASSERT(piece >= 0);
return m_piece_to_slot[piece];
}
int piece_manager::piece_for(int slot) const
{
if (m_storage_mode != storage_mode_compact) return slot;
TORRENT_ASSERT(slot < int(m_slot_to_piece.size()));
TORRENT_ASSERT(slot >= 0);
return m_slot_to_piece[slot];
}
#ifndef NDEBUG #ifndef NDEBUG
void piece_manager::check_invariant() const void piece_manager::check_invariant() const
{ {
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
if (m_unallocated_slots.empty() && m_state == state_finished)
{
TORRENT_ASSERT(m_storage_mode != storage_mode_compact);
}
if (m_storage_mode != storage_mode_compact)
{
TORRENT_ASSERT(m_unallocated_slots.empty());
TORRENT_ASSERT(m_free_slots.empty());
}
if (m_storage_mode != storage_mode_compact
&& m_state != state_expand_pieces
&& m_state != state_full_check)
{
TORRENT_ASSERT(m_piece_to_slot.empty());
TORRENT_ASSERT(m_slot_to_piece.empty());
}
else
{
if (m_piece_to_slot.empty()) return; if (m_piece_to_slot.empty()) return;
TORRENT_ASSERT((int)m_piece_to_slot.size() == m_info->num_pieces()); TORRENT_ASSERT((int)m_piece_to_slot.size() == m_info->num_pieces());
@ -2200,6 +2248,7 @@ namespace libtorrent
} }
} }
} }
}
#ifdef TORRENT_STORAGE_DEBUG #ifdef TORRENT_STORAGE_DEBUG
void piece_manager::debug_log() const void piece_manager::debug_log() const

View File

@ -154,7 +154,7 @@ namespace libtorrent
, boost::intrusive_ptr<torrent_info> tf , boost::intrusive_ptr<torrent_info> tf
, fs::path const& save_path , fs::path const& save_path
, tcp::endpoint const& net_interface , tcp::endpoint const& net_interface
, bool compact_mode , storage_mode_t storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused) , bool paused)
@ -195,7 +195,7 @@ namespace libtorrent
, m_total_redundant_bytes(0) , m_total_redundant_bytes(0)
, m_net_interface(net_interface.address(), 0) , m_net_interface(net_interface.address(), 0)
, m_save_path(complete(save_path)) , m_save_path(complete(save_path))
, m_compact_mode(compact_mode) , m_storage_mode(storage_mode)
, m_default_block_size(block_size) , m_default_block_size(block_size)
, m_connections_initialized(true) , m_connections_initialized(true)
, m_settings(ses.settings()) , m_settings(ses.settings())
@ -215,7 +215,7 @@ namespace libtorrent
, char const* name , char const* name
, fs::path const& save_path , fs::path const& save_path
, tcp::endpoint const& net_interface , tcp::endpoint const& net_interface
, bool compact_mode , storage_mode_t storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused) , bool paused)
@ -255,7 +255,7 @@ namespace libtorrent
, m_total_redundant_bytes(0) , m_total_redundant_bytes(0)
, m_net_interface(net_interface.address(), 0) , m_net_interface(net_interface.address(), 0)
, m_save_path(complete(save_path)) , m_save_path(complete(save_path))
, m_compact_mode(compact_mode) , m_storage_mode(storage_mode)
, m_default_block_size(block_size) , m_default_block_size(block_size)
, m_connections_initialized(false) , m_connections_initialized(false)
, m_settings(ses.settings()) , m_settings(ses.settings())
@ -2215,10 +2215,22 @@ namespace libtorrent
bool done = true; bool done = true;
try try
{ {
std::string error_msg;
TORRENT_ASSERT(m_storage); TORRENT_ASSERT(m_storage);
TORRENT_ASSERT(m_owning_storage.get()); TORRENT_ASSERT(m_owning_storage.get());
done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces
, m_compact_mode); , m_storage_mode, error_msg);
if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning))
{
m_ses.m_alerts.post_alert(fastresume_rejected_alert(
get_handle(), error_msg));
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_ses.m_logger) << "fastresume data for "
<< torrent_file().name() << " rejected: "
<< error_msg << "\n";
#endif
}
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -2768,7 +2780,7 @@ namespace libtorrent
!boost::bind(&peer_connection::is_connecting !boost::bind(&peer_connection::is_connecting
, boost::bind(&std::map<tcp::endpoint,peer_connection*>::value_type::second, _1))); , boost::bind(&std::map<tcp::endpoint,peer_connection*>::value_type::second, _1)));
st.compact_mode = m_compact_mode; st.storage_mode = m_storage_mode;
st.num_complete = m_complete; st.num_complete = m_complete;
st.num_incomplete = m_incomplete; st.num_incomplete = m_incomplete;

View File

@ -661,7 +661,7 @@ namespace libtorrent
if (!t->valid_metadata()) return entry(); if (!t->valid_metadata()) return entry();
t->filesystem().export_piece_map(piece_index); std::vector<bool> have_pieces = t->pieces();
entry ret(entry::dictionary_t); entry ret(entry::dictionary_t);
@ -673,10 +673,6 @@ namespace libtorrent
const sha1_hash& info_hash = t->torrent_file().info_hash(); const sha1_hash& info_hash = t->torrent_file().info_hash();
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
ret["slots"] = entry(entry::list_t);
entry::list_type& slots = ret["slots"].list();
std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
// blocks per piece // blocks per piece
int num_blocks_per_piece = int num_blocks_per_piece =
static_cast<int>(t->torrent_file().piece_length()) / t->block_size(); static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
@ -706,6 +702,8 @@ namespace libtorrent
// the unfinished piece's index // the unfinished piece's index
piece_struct["piece"] = i->index; piece_struct["piece"] = i->index;
have_pieces[i->index] = true;
std::string bitmask; std::string bitmask;
const int num_bitmask_bytes const int num_bitmask_bytes
= (std::max)(num_blocks_per_piece / 8, 1); = (std::max)(num_blocks_per_piece / 8, 1);
@ -722,10 +720,10 @@ namespace libtorrent
} }
piece_struct["bitmask"] = bitmask; piece_struct["bitmask"] = bitmask;
TORRENT_ASSERT(t->filesystem().slot_for_piece(i->index) >= 0); TORRENT_ASSERT(t->filesystem().slot_for(i->index) >= 0);
unsigned long adler unsigned long adler
= t->filesystem().piece_crc( = t->filesystem().piece_crc(
t->filesystem().slot_for_piece(i->index) t->filesystem().slot_for(i->index)
, t->block_size() , t->block_size()
, i->info); , i->info);
@ -735,6 +733,11 @@ namespace libtorrent
up.push_back(piece_struct); up.push_back(piece_struct);
} }
} }
t->filesystem().export_piece_map(piece_index, have_pieces);
entry::list_type& slots = ret["slots"].list();
std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
// write local peers // write local peers
entry::list_type& peer_list = ret["peers"].list(); entry::list_type& peer_list = ret["peers"].list();

View File

@ -242,8 +242,13 @@ boost::filesystem::path const& save_path)
// Create new torrent object // Create new torrent object
torrent_t new_torrent; torrent_t new_torrent;
libtorrent::storage_mode_t storage_mode;
torrent_handle h = M_ses->add_torrent(t, save_path, resume_data, compact_mode); if (compact_mode){
storage_mode = storage_mode_compact;
} else {
storage_mode = storage_mode_sparse;
}
torrent_handle h = M_ses->add_torrent(t, save_path, resume_data, storage_mode);
// h.set_max_connections(60); // at some point we should use this // h.set_max_connections(60); // at some point we should use this
h.set_max_uploads(-1); h.set_max_uploads(-1);
h.set_ratio(preferred_ratio); h.set_ratio(preferred_ratio);
@ -893,7 +898,7 @@ static PyObject *torrent_get_torrent_state(PyObject *self, PyObject *args)
"num_seeds", connected_seeds, "num_seeds", connected_seeds,
"distributed_copies", s.distributed_copies == -1.0 ? 0.0 : s.distributed_copies, "distributed_copies", s.distributed_copies == -1.0 ? 0.0 : s.distributed_copies,
"download_rate", s.download_payload_rate, "download_rate", s.download_payload_rate,
"compact_mode", s.compact_mode, "storage_mode", s.storage_mode,
"upload_rate", s.upload_payload_rate, "upload_rate", s.upload_payload_rate,
"total_download", s.total_download, "total_download", s.total_download,
"total_upload", s.total_upload, "total_upload", s.total_upload,