optimized memory usage of torrent_info and file_storage

This commit is contained in:
Arvid Norberg 2010-11-24 23:49:22 +00:00
parent 7cd628e78d
commit 894db973e8
22 changed files with 501 additions and 204 deletions

View File

@ -1,3 +1,5 @@
* optimized memory usage of torrent_info and file_storage, forcing some API changes
around file_storage and file_entry
* support trackerid tracker extension * support trackerid tracker extension
* graceful peer disconnect mode which finishes transactions before disconnecting peers * graceful peer disconnect mode which finishes transactions before disconnecting peers
* support chunked encoding for web seeds * support chunked encoding for web seeds

View File

@ -48,12 +48,12 @@ namespace
ct.add_node(std::make_pair(addr, port)); ct.add_node(std::make_pair(addr, port));
} }
void add_file(file_storage& ct, file_entry const& fe void add_file(file_storage& ct, file_entry const& fe
, std::string const& hash, std::string const& linkpath) , std::string const& hash, std::string const& linkpath)
{ {
ct.add_file(fe, hash.empty() ? 0 : &sha1_hash(hash) ct.add_file(fe, hash.empty() ? 0 : hash.c_str()
, linkpath.empty() ? 0 : &linkpath); , linkpath.empty() ? 0 : &linkpath);
} }
} }
void bind_create_torrent() void bind_create_torrent()
@ -82,6 +82,12 @@ void bind_create_torrent()
#endif #endif
.def("num_files", &file_storage::num_files) .def("num_files", &file_storage::num_files)
.def("at", &file_storage::at, return_internal_reference<>()) .def("at", &file_storage::at, return_internal_reference<>())
.def("hash", &file_storage::hash)
.def("symlink", &file_storage::symlink, return_internal_reference<>())
.def("file_index", &file_storage::file_index)
.def("file_base", &file_storage::file_base)
.def("set_file_base", &file_storage::set_file_base)
.def("file_path", &file_storage::file_path)
.def("total_size", &file_storage::total_size) .def("total_size", &file_storage::total_size)
.def("set_num_pieces", &file_storage::set_num_pieces) .def("set_num_pieces", &file_storage::set_num_pieces)
.def("num_pieces", &file_storage::num_pieces) .def("num_pieces", &file_storage::num_pieces)

View File

@ -102,6 +102,20 @@ namespace
bool get_send_stats(announce_entry const& ae) bool get_send_stats(announce_entry const& ae)
{ return ae.send_stats; } { return ae.send_stats; }
bool get_size(file_entry const& fe)
{ return fe.size; }
bool get_offset(file_entry const& fe)
{ return fe.offset; }
bool get_pad_file(file_entry const& fe)
{ return fe.pad_file; }
bool get_executable_attribute(file_entry const& fe)
{ return fe.executable_attribute; }
bool get_hidden_attribute(file_entry const& fe)
{ return fe.hidden_attribute; }
bool get_symlink_attribute(file_entry const& fe)
{ return fe.symlink_attribute; }
} // namespace unnamed } // namespace unnamed
void bind_torrent_info() void bind_torrent_info()
@ -168,14 +182,14 @@ void bind_torrent_info()
; ;
class_<file_entry>("file_entry") class_<file_entry>("file_entry")
.add_property("path" .def("filename", &file_entry::filename)
, make_getter( .def("set_name", &file_entry::set_name)
&file_entry::path, return_value_policy<copy_non_const_reference>() .add_property("pad_file", &get_pad_file)
) .add_property("executable_attribute", &get_executable_attribute)
) .add_property("hidden_attribute", &get_hidden_attribute)
.def_readonly("offset", &file_entry::offset) .add_property("symlink_attribute", &get_symlink_attribute)
.def_readonly("size", &file_entry::size) .add_property("offset", &get_offset)
.def_readonly("file_base", &file_entry::file_base) .add_property("size", &get_size)
; ;
class_<announce_entry>("announce_entry", init<std::string const&>()) class_<announce_entry>("announce_entry", init<std::string const&>())

View File

@ -172,8 +172,12 @@ file structure. Its synopsis::
int piece_length() const; int piece_length() const;
int piece_size(int index) const; int piece_size(int index) const;
sha1_hash const& hash(int index) const; sha1_hash const& hash(file_entry const& fe) const;
std::string const& symlink(int index) const; std::string const& symlink(file_entry const& fe) const;
time_t mtime(file_entry const& fe) const;
int file_index(file_entry const& fe) const;
size_type file_base(file_entry const& fe) const;
void set_file_base(file_entry const& fe, size_type off);
void set_name(std::string const& n); void set_name(std::string const& n);
void set_name(std::wstring const& n); void set_name(std::wstring const& n);
@ -218,17 +222,47 @@ can be changed by calling ``set_name``.
The built in functions to traverse a directory to add files will The built in functions to traverse a directory to add files will
make sure this requirement is fulfilled. make sure this requirement is fulfilled.
hash() symlink() hash() symlink() mtime() file_index()
---------------- -------------------------------------
:: ::
sha1_hash const& hash(int index) const; sha1_hash hash(file_entry const& fe) const;
std::string const& symlink(int index) const; std::string const& symlink(file_entry const& fe) const;
time_t mtime(file_entry const& fe) const;
int file_index(file_entry const& fe) const;
These functions are used to resolve the symlink index and file hash These functions are used to query the symlink, file hash,
index in ``file_entry``. modification time and the file-index from a ``file_entry``.
For these functions to function, the file entry must be an
actual object from this same ``file_storage`` object. It may
not be a copy.
The file hash is a sha-1 hash of the file, or 0 if none was
provided in the torrent file. This can potentially be used to
join a bittorrent network with other file sharing networks.
The modification time is the posix time when a file was last
modified when the torrent was created, or 0 if it was not provided.
The file index of a file is simply a 0 based index of the
file as they are ordered in the torrent file.
file_base() set_file_base()
---------------------------
::
size_type file_base(file_entry const& fe) const;
void set_file_base(file_entry const& fe, size_type off);
The file base of a file is the offset within the file on the filsystem
where it starts to write. For the most part, this is always 0. It's
possible to map several files (in the torrent) into a single file on
the filesystem by making them all point to the same filename, but with
different file bases, so that they don't overlap.
``torrent_info::remap_files`` can be used to use a new file layout.
create_torrent create_torrent
============== ==============

View File

@ -1350,9 +1350,6 @@ The ``torrent_info`` has the following synopsis::
{ {
public: public:
// flags for torrent_info constructor
enum flags_t { omit_filehashes = 1 };
// these constructors throws exceptions on error // these constructors throws exceptions on error
torrent_info(sha1_hash const& info_hash, int flags = 0); torrent_info(sha1_hash const& info_hash, int flags = 0);
torrent_info(lazy_entry const& torrent_file, int flags = 0); torrent_info(lazy_entry const& torrent_file, int flags = 0);
@ -1461,10 +1458,7 @@ torrent_info object. The overloads that do not take the extra error_code_ parame
always throw if an error occurs. These overloads are not available when building without always throw if an error occurs. These overloads are not available when building without
exception support. exception support.
The ``flags`` argument can be used to disable loading of potentially unnecessary hashes The ``flags`` argument is currently unused.
for individual files (if included in the torrent file). This is especially useful if
you're loading torrents with thousands of files on a memory constrained system. If so,
pass in ``torrent_info::omit_filehashes`` as the flags argument.
add_tracker() add_tracker()
@ -1547,7 +1541,7 @@ iterators with the type ``file_entry``.
struct file_entry struct file_entry
{ {
std::string path; std::string filename();
size_type offset; size_type offset;
size_type size; size_type size;
size_type file_base; size_type file_base;
@ -1560,9 +1554,9 @@ iterators with the type ``file_entry``.
bool symlink_attribute:1; bool symlink_attribute:1;
}; };
The ``path`` is the full (relative) path of each file. i.e. if it is a multi-file The ``filename`` function returns the filename of this file. It does not include the
torrent, all the files starts with a directory with the same name as ``torrent_info::name()``. path, just the leaf name. To get the full path name, use ``file_storage::file_path()``.
The filenames are encoded with UTF-8. The filenames are unicode strings encoded in UTF-8.
``size`` is the size of the file (in bytes) and ``offset`` is the byte offset ``size`` is the size of the file (in bytes) and ``offset`` is the byte offset
of the file within the torrent. i.e. the sum of all the sizes of the files of the file within the torrent. i.e. the sum of all the sizes of the files
@ -1584,7 +1578,7 @@ is set. To resolve the symlink, call ``file_storage::symlink(e.symlink_index)``.
``filehash_index`` is an index into an array of sha-1 hashes in ``file_storage``, or ``filehash_index`` is an index into an array of sha-1 hashes in ``file_storage``, or
-1 if this file doesn't have a hash specified. The hash is the hash of the actual -1 if this file doesn't have a hash specified. The hash is the hash of the actual
content of the file, and can be used to potentially find alternative sources for it. content of the file, and can be used to potentially find alternative sources for it.
To resolve the hash, use ``file_storage::hash(e.filehash_index)``. To resolve the hash, use ``file_storage::hash(e)``.
``pad_file`` is set to true for files that are not part of the data of the torrent. ``pad_file`` is set to true for files that are not part of the data of the torrent.
They are just there to make sure the next file is aligned to a particular byte offset They are just there to make sure the next file is aligned to a particular byte offset

View File

@ -1715,7 +1715,7 @@ int main(int argc, char* argv[])
, pad_file?esc("34"):"" , pad_file?esc("34"):""
, progress / 10.f , progress / 10.f
, add_suffix(file_progress[i]).c_str() , add_suffix(file_progress[i]).c_str()
, filename(info.file_at(i).path).c_str() , filename(info.files().file_path(info.file_at(i))).c_str()
, pad_file?esc("0"):""); , pad_file?esc("0"):"");
out += str; out += str;
} }

View File

@ -40,6 +40,8 @@ int main(int argc, char* argv[])
{ {
using namespace libtorrent; using namespace libtorrent;
printf("sizeof(file_entry): %d\n", int(sizeof(file_entry)));
if (argc != 2) if (argc != 2)
{ {
fputs("usage: dump_torrent torrent-file\n", stderr); fputs("usage: dump_torrent torrent-file\n", stderr);
@ -70,7 +72,7 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
printf("\n\n----- raw info -----\n\n%s\n", print_entry(e).c_str()); // printf("\n\n----- raw info -----\n\n%s\n", print_entry(e).c_str());
torrent_info t(e, ec); torrent_info t(e, ec);
if (ec) if (ec)
@ -78,6 +80,8 @@ int main(int argc, char* argv[])
fprintf(stderr, "%s\n", ec.message().c_str()); fprintf(stderr, "%s\n", ec.message().c_str());
return 1; return 1;
} }
e.clear();
std::vector<char>().swap(buf);
// print info about torrent // print info about torrent
printf("\n\n----- torrent file info -----\n\n" printf("\n\n----- torrent file info -----\n\n"
@ -106,6 +110,7 @@ int main(int argc, char* argv[])
"created by: %s\n" "created by: %s\n"
"magnet link: %s\n" "magnet link: %s\n"
"name: %s\n" "name: %s\n"
"number of files: %d\n"
"files:\n" "files:\n"
, t.num_pieces() , t.num_pieces()
, t.piece_length() , t.piece_length()
@ -113,24 +118,26 @@ int main(int argc, char* argv[])
, t.comment().c_str() , t.comment().c_str()
, t.creator().c_str() , t.creator().c_str()
, make_magnet_uri(t).c_str() , make_magnet_uri(t).c_str()
, t.name().c_str()); , t.name().c_str()
, t.num_files());
int index = 0; int index = 0;
for (torrent_info::file_iterator i = t.begin_files(); for (torrent_info::file_iterator i = t.begin_files();
i != t.end_files(); ++i, ++index) i != t.end_files(); ++i, ++index)
{ {
int first = t.map_file(index, 0, 0).piece; int first = t.map_file(index, 0, 0).piece;
int last = t.map_file(index, (std::max)(i->size-1, size_type(0)), 0).piece; int last = t.map_file(index, (std::max)(i->size-1, size_type(0)), 0).piece;
printf(" %11"PRId64" %c%c%c%c [ %4d, %4d ] %s %s %s%s\n" printf(" %11"PRId64" %c%c%c%c [ %4d, %4d ] %7u %s %s %s%s\n"
, i->size , i->size
, (i->pad_file?'p':'-') , (i->pad_file?'p':'-')
, (i->executable_attribute?'x':'-') , (i->executable_attribute?'x':'-')
, (i->hidden_attribute?'h':'-') , (i->hidden_attribute?'h':'-')
, (i->symlink_attribute?'l':'-') , (i->symlink_attribute?'l':'-')
, first, last , first, last
, i->filehash_index != -1 ? to_hex(t.files().hash(i->filehash_index).to_string()).c_str() : "" , boost::uint32_t(t.files().mtime(*i))
, i->path.c_str() , t.files().hash(*i) != sha1_hash(0) ? to_hex(t.files().hash(*i).to_string()).c_str() : ""
, t.files().file_path(*i).c_str()
, i->symlink_attribute ? "-> ": "" , i->symlink_attribute ? "-> ": ""
, i->symlink_attribute && i->symlink_index != -1 ? t.files().symlink(i->symlink_index).c_str() : ""); , i->symlink_attribute && i->symlink_index != -1 ? t.files().symlink(*i).c_str() : "");
} }
return 0; return 0;

View File

@ -84,7 +84,7 @@ int main(int argc, char const* argv[])
{ {
if (ti->file_at(i).size == files[i].first) continue; if (ti->file_at(i).size == files[i].first) continue;
fprintf(stderr, "Files for this torrent are missing or incomplete: %s was %"PRId64" bytes, expected %"PRId64" bytes\n" fprintf(stderr, "Files for this torrent are missing or incomplete: %s was %"PRId64" bytes, expected %"PRId64" bytes\n"
, ti->file_at(i).path.c_str(), files[i].first, ti->file_at(i).size); , ti->files().file_path(ti->file_at(i)).c_str(), files[i].first, ti->file_at(i).size);
return 1; return 1;
} }

View File

@ -51,15 +51,16 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
struct file_entry; struct file_entry;
struct file_storage;
struct TORRENT_EXPORT file_pool : boost::noncopyable struct TORRENT_EXPORT file_pool : boost::noncopyable
{ {
file_pool(int size = 40): m_size(size), m_low_prio_io(true) {} file_pool(int size = 40): m_size(size), m_low_prio_io(true) {}
boost::intrusive_ptr<file> open_file(void* st, std::string const& p boost::intrusive_ptr<file> open_file(void* st, std::string const& p
, file_entry const& fe, int m, error_code& ec); , file_entry const& fe, file_storage const& fs, int m, error_code& ec);
void release(void* st); void release(void* st);
void release(void* st, file_entry const& fe); void release(void* st, int file_index);
void resize(int size); void resize(int size);
int size_limit() const { return m_size; } int size_limit() const { return m_size; }
void set_low_prio_io(bool b) { m_low_prio_io = b; } void set_low_prio_io(bool b) { m_low_prio_io = b; }

View File

@ -41,7 +41,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/assert.hpp" #include "libtorrent/assert.hpp"
#include "libtorrent/peer_request.hpp" #include "libtorrent/peer_request.hpp"
#include "libtorrent/peer_id.hpp" #include "libtorrent/peer_id.hpp"
#include "libtorrent/copy_ptr.hpp"
namespace libtorrent namespace libtorrent
{ {
@ -49,43 +48,59 @@ namespace libtorrent
struct TORRENT_EXPORT file_entry struct TORRENT_EXPORT file_entry
{ {
friend class file_storage;
file_entry() file_entry()
: offset(0) : name(0)
, size(0) , offset(0)
, file_base(0)
, mtime(0)
, file_index(0)
, filehash_index(-1)
, symlink_index(-1) , symlink_index(-1)
, size(0)
, name_len(0)
, pad_file(false) , pad_file(false)
, hidden_attribute(false) , hidden_attribute(false)
, executable_attribute(false) , executable_attribute(false)
, symlink_attribute(false) , symlink_attribute(false)
, path_index(-1)
{} {}
std::string path; file_entry(file_entry const& fe);
file_entry& operator=(file_entry const& fe);
~file_entry();
void set_name(char const* n, int borrow_chars = 0);
std::string filename() const;
private:
// This string is not necessarily null terminated!
// that's why it's private, to keep people away from it
char const* name;
public:
// the offset of this file inside the torrent // the offset of this file inside the torrent
size_type offset; size_type offset:48;
// the size of this file
size_type size;
// the offset in the file where the storage starts.
// This is always 0 unless parts of the torrent is
// compressed into a single file, such as a so-called part file.
size_type file_base;
// modification time
time_t mtime;
// the index of this file, as ordered in the torrent
int file_index;
// index into file_storage::m_file_hashes or -1
// if this file does not have a hash
int filehash_index;
// index into file_storage::m_symlinks or -1 // index into file_storage::m_symlinks or -1
// if this is not a symlink // if this is not a symlink
int symlink_index; size_type symlink_index:16;
// the size of this file
size_type size:48;
// the number of characters in the name. If this is
// 0, name is null terminated and owned by this object
// (i.e. it should be freed in the destructor). If
// the len is > 0, the name pointer doesn not belong
// to this object, and it's not null terminated
size_type name_len:10;
bool pad_file:1; bool pad_file:1;
bool hidden_attribute:1; bool hidden_attribute:1;
bool executable_attribute:1; bool executable_attribute:1;
bool symlink_attribute:1; bool symlink_attribute:1;
// the index into file_storage::m_paths. To get
// the full path to this file, concatenate the path
// from that array with the 'name' field in
// this struct
int path_index;
}; };
struct TORRENT_EXPORT file_slice struct TORRENT_EXPORT file_slice
@ -114,11 +129,12 @@ namespace libtorrent
void reserve(int num_files); void reserve(int num_files);
void add_file(file_entry const& e, sha1_hash const* filehash = 0 void add_file(file_entry const& e, char const* filehash = 0
, std::string const* symlink = 0); , std::string const* symlink = 0, time_t mtime = 0);
void add_file(std::string const& p, size_type size, int flags = 0 void add_file(std::string const& p, size_type size, int flags = 0
, std::time_t mtime = 0, std::string const& s_p = ""); , std::time_t mtime = 0, std::string const& s_p = "");
void rename_file(int index, std::string const& new_filename); void rename_file(int index, std::string const& new_filename);
#if TORRENT_USE_WSTRING #if TORRENT_USE_WSTRING
@ -149,17 +165,14 @@ namespace libtorrent
return m_files[index]; return m_files[index];
} }
sha1_hash const& hash(int index) const sha1_hash hash(file_entry const& fe) const;
{ std::string const& symlink(file_entry const& fe) const;
TORRENT_ASSERT(index >= 0 && index < int(m_file_hashes.size())); time_t mtime(file_entry const& fe) const;
return m_file_hashes[index]; int file_index(file_entry const& fe) const;
} size_type file_base(file_entry const& fe) const;
void set_file_base(file_entry const& fe, size_type off);
std::string const& symlink(int index) const
{ std::string file_path(file_entry const& fe) const;
TORRENT_ASSERT(index >= 0 && index < int(m_symlinks.size()));
return m_symlinks[index];
}
size_type total_size() const { return m_total_size; } size_type total_size() const { return m_total_size; }
void set_num_pieces(int n) { m_num_pieces = n; } void set_num_pieces(int n) { m_num_pieces = n; }
@ -188,19 +201,40 @@ namespace libtorrent
private: private:
void update_path_index(file_entry& e);
void reorder_file(int index, int dst);
// the list of files that this torrent consists of // the list of files that this torrent consists of
std::vector<file_entry> m_files; std::vector<file_entry> m_files;
// if there are sha1 hashes for each individual file // if there are sha1 hashes for each individual file
// each file_entry has an index into this vector // there are as many entries in this array as the
// and the actual hashes are in here // m_files array. Each entry in m_files has a corresponding
std::vector<sha1_hash> m_file_hashes; // hash pointer in this array. The reason to split it up
// in separate arrays is to save memory in case the torrent
// doesn't have file hashes
std::vector<char const*> m_file_hashes;
// for files that are symlinks, the symlink // for files that are symlinks, the symlink
// path_index in the file_entry indexes // path_index in the file_entry indexes
// this vector of strings // this vector of strings
std::vector<std::string> m_symlinks; std::vector<std::string> m_symlinks;
// the modification times of each file. This vector
// is empty if no file have a modification time.
// each element corresponds to the file with the same
// index in m_files
std::vector<time_t> m_mtime;
// if any file has a non-zero file base (i.e. multiple
// files residing in the same physical file at different
// offsets)
std::vector<size_type> m_file_base;
// all unique paths files have. The file_entry::path_index
// points into this array
std::vector<std::string> m_paths;
// name of torrent. For multi-file torrents // name of torrent. For multi-file torrents
// this is always the root directory // this is always the root directory
std::string m_name; std::string m_name;

View File

@ -78,7 +78,7 @@ namespace libtorrent
none_t, dict_t, list_t, string_t, int_t none_t, dict_t, list_t, string_t, int_t
}; };
lazy_entry() : m_begin(0), m_end(0), m_type(none_t) lazy_entry() : m_begin(0), m_len(0), m_type(none_t)
{ m_data.start = 0; } { m_data.start = 0; }
entry_type_t type() const { return (entry_type_t)m_type; } entry_type_t type() const { return (entry_type_t)m_type; }
@ -92,7 +92,7 @@ namespace libtorrent
m_data.start = start; m_data.start = start;
m_size = length; m_size = length;
m_begin = start - 1; // include 'i' m_begin = start - 1; // include 'i'
m_end = start + length + 1; // include 'e' m_len = length + 2; // include 'e'
} }
size_type int_value() const; size_type int_value() const;
@ -202,7 +202,7 @@ namespace libtorrent
void set_end(char const* end) void set_end(char const* end)
{ {
TORRENT_ASSERT(end > m_begin); TORRENT_ASSERT(end > m_begin);
m_end = end; m_len = end - m_begin;
} }
void clear(); void clear();
@ -235,7 +235,7 @@ namespace libtorrent
swap(m_data.start, e.m_data.start); swap(m_data.start, e.m_data.start);
swap(m_size, e.m_size); swap(m_size, e.m_size);
swap(m_begin, e.m_begin); swap(m_begin, e.m_begin);
swap(m_end, e.m_end); swap(m_len, e.m_len);
} }
private: private:
@ -250,11 +250,16 @@ namespace libtorrent
// used for dictionaries and lists to record the range // used for dictionaries and lists to record the range
// in the original buffer they are based on // in the original buffer they are based on
char const* m_begin; char const* m_begin;
char const* m_end; // the number of bytes this entry extends in the
// bencoded byffer
boost::uint32_t m_len;
boost::uint32_t m_size; // if list or dictionary, the number of items // if list or dictionary, the number of items
boost::uint32_t m_capacity:29; // if list or dictionary, allocated number of items boost::uint32_t m_size;
unsigned int m_type:3; // if list or dictionary, allocated number of items
boost::uint32_t m_capacity:29;
// element type (dict, list, int, string)
boost::uint32_t m_type:3;
// non-copyable // non-copyable
lazy_entry(lazy_entry const&); lazy_entry(lazy_entry const&);

View File

@ -227,8 +227,6 @@ namespace libtorrent
{ {
public: public:
enum flags_t { omit_filehashes = 1 };
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
torrent_info(lazy_entry const& torrent_file, int flags = 0); torrent_info(lazy_entry const& torrent_file, int flags = 0);
torrent_info(char const* buffer, int size, int flags = 0); torrent_info(char const* buffer, int size, int flags = 0);

View File

@ -118,7 +118,7 @@ namespace libtorrent
// return instead of crash in release mode // return instead of crash in release mode
if (fs.num_files() == 0) return; if (fs.num_files() == 0) return;
if (!m_multifile && has_parent_path(m_files.at(0).path)) m_multifile = true; if (!m_multifile && has_parent_path(m_files.file_path(m_files.at(0)))) m_multifile = true;
// a piece_size of 0 means automatic // a piece_size of 0 means automatic
if (piece_size == 0 && !m_merkle_torrent) if (piece_size == 0 && !m_merkle_torrent)
@ -301,7 +301,7 @@ namespace libtorrent
if (!m_multifile) if (!m_multifile)
{ {
if (m_include_mtime) info["mtime"] = m_files.at(0).mtime; if (m_include_mtime) info["mtime"] = m_files.mtime(m_files.at(0));
info["length"] = m_files.at(0).size; info["length"] = m_files.at(0).size;
if (m_files.at(0).pad_file if (m_files.at(0).pad_file
|| m_files.at(0).hidden_attribute || m_files.at(0).hidden_attribute
@ -320,7 +320,7 @@ namespace libtorrent
{ {
entry& sympath_e = info["symlink path"]; entry& sympath_e = info["symlink path"];
std::string split = split_path(m_files.symlink(m_files.at(0).symlink_index)); std::string split = split_path(m_files.symlink(m_files.at(0)));
for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) for (char const* e = split.c_str(); e != 0; e = next_path_element(e))
sympath_e.list().push_back(entry(e)); sympath_e.list().push_back(entry(e));
} }
@ -340,13 +340,13 @@ namespace libtorrent
{ {
files.list().push_back(entry()); files.list().push_back(entry());
entry& file_e = files.list().back(); entry& file_e = files.list().back();
if (m_include_mtime) file_e["mtime"] = i->mtime; if (m_include_mtime && m_files.mtime(*i)) file_e["mtime"] = m_files.mtime(*i);
file_e["length"] = i->size; file_e["length"] = i->size;
entry& path_e = file_e["path"]; entry& path_e = file_e["path"];
TORRENT_ASSERT(has_parent_path(i->path)); TORRENT_ASSERT(has_parent_path(m_files.file_path(*i)));
std::string split = split_path(i->path); std::string split = split_path(m_files.file_path(*i));
TORRENT_ASSERT(split.c_str() == m_files.name()); TORRENT_ASSERT(split.c_str() == m_files.name());
for (char const* e = next_path_element(split.c_str()); for (char const* e = next_path_element(split.c_str());
@ -370,7 +370,7 @@ namespace libtorrent
{ {
entry& sympath_e = file_e["symlink path"]; entry& sympath_e = file_e["symlink path"];
std::string split = split_path(m_files.symlink(i->symlink_index)); std::string split = split_path(m_files.symlink(*i));
for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) for (char const* e = split.c_str(); e != 0; e = next_path_element(e))
sympath_e.list().push_back(entry(e)); sympath_e.list().push_back(entry(e));
} }

View File

@ -41,14 +41,14 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
boost::intrusive_ptr<file> file_pool::open_file(void* st, std::string const& p boost::intrusive_ptr<file> file_pool::open_file(void* st, std::string const& p
, file_entry const& fe, int m, error_code& ec) , file_entry const& fe, file_storage const& fs, int m, error_code& ec)
{ {
TORRENT_ASSERT(st != 0); TORRENT_ASSERT(st != 0);
TORRENT_ASSERT(is_complete(p)); TORRENT_ASSERT(is_complete(p));
TORRENT_ASSERT((m & file::rw_mask) == file::read_only TORRENT_ASSERT((m & file::rw_mask) == file::read_only
|| (m & file::rw_mask) == file::read_write); || (m & file::rw_mask) == file::read_write);
mutex::scoped_lock l(m_mutex); mutex::scoped_lock l(m_mutex);
file_set::iterator i = m_files.find(std::make_pair(st, fe.file_index)); file_set::iterator i = m_files.find(std::make_pair(st, fs.file_index(fe)));
if (i != m_files.end()) if (i != m_files.end())
{ {
lru_file_entry& e = i->second; lru_file_entry& e = i->second;
@ -77,7 +77,7 @@ namespace libtorrent
// the new read/write privilages // the new read/write privilages
TORRENT_ASSERT(e.file_ptr->refcount() == 1); TORRENT_ASSERT(e.file_ptr->refcount() == 1);
e.file_ptr->close(); e.file_ptr->close();
std::string full_path = combine_path(p, fe.path); std::string full_path = combine_path(p, fs.file_path(fe));
if (!e.file_ptr->open(full_path, m, ec)) if (!e.file_ptr->open(full_path, m, ec))
{ {
m_files.erase(i); m_files.erase(i);
@ -115,12 +115,12 @@ namespace libtorrent
ec = error_code(ENOMEM, get_posix_category()); ec = error_code(ENOMEM, get_posix_category());
return e.file_ptr; return e.file_ptr;
} }
std::string full_path = combine_path(p, fe.path); std::string full_path = combine_path(p, fs.file_path(fe));
if (!e.file_ptr->open(full_path, m, ec)) if (!e.file_ptr->open(full_path, m, ec))
return boost::intrusive_ptr<file>(); return boost::intrusive_ptr<file>();
e.mode = m; e.mode = m;
e.key = st; e.key = st;
m_files.insert(std::make_pair(std::make_pair(st, fe.file_index), e)); m_files.insert(std::make_pair(std::make_pair(st, fs.file_index(fe)), e));
TORRENT_ASSERT(e.file_ptr->is_open()); TORRENT_ASSERT(e.file_ptr->is_open());
return e.file_ptr; return e.file_ptr;
} }
@ -134,10 +134,10 @@ namespace libtorrent
m_files.erase(i); m_files.erase(i);
} }
void file_pool::release(void* st, file_entry const& fe) void file_pool::release(void* st, int file_index)
{ {
mutex::scoped_lock l(m_mutex); mutex::scoped_lock l(m_mutex);
file_set::iterator i = m_files.find(std::make_pair(st, fe.file_index)); file_set::iterator i = m_files.find(std::make_pair(st, file_index));
if (i != m_files.end()) m_files.erase(i); if (i != m_files.end()) m_files.erase(i);
} }

View File

@ -67,6 +67,88 @@ namespace libtorrent
return piece_length(); return piece_length();
} }
void file_storage::update_path_index(file_entry& e)
{
std::string parent = parent_path(e.filename());
if (parent.empty())
{
e.path_index = -1;
}
else
{
// do we already have this path in the path list?
std::vector<std::string>::reverse_iterator p
= std::find(m_paths.rbegin(), m_paths.rend(), parent);
if (p == m_paths.rend())
{
// no, we don't. add it
e.path_index = m_paths.size();
m_paths.push_back(parent);
}
else
{
// yes we do. use it
e.path_index = p.base() - m_paths.begin() - 1;
}
e.set_name(filename(e.filename()).c_str());
}
}
file_entry::~file_entry() { if (name_len == 0) free((void*)name); }
file_entry::file_entry(file_entry const& fe)
: name(0)
, offset(fe.offset)
, symlink_index(fe.symlink_index)
, size(fe.size)
, name_len(fe.name_len)
, pad_file(fe.pad_file)
, hidden_attribute(fe.hidden_attribute)
, executable_attribute(fe.executable_attribute)
, symlink_attribute(fe.symlink_attribute)
, path_index(fe.path_index)
{
set_name(fe.name, fe.name_len);
}
file_entry& file_entry::operator=(file_entry const& fe)
{
offset = fe.offset;
size = fe.size;
path_index = fe.path_index;
symlink_index = fe.symlink_index;
pad_file = fe.pad_file;
hidden_attribute = fe.hidden_attribute;
executable_attribute = fe.executable_attribute;
symlink_attribute = fe.symlink_attribute;
set_name(fe.name, fe.name_len);
return *this;
}
void file_entry::set_name(char const* n, int borrow_chars)
{
TORRENT_ASSERT(borrow_chars >= 0);
if (borrow_chars > 1023) borrow_chars = 1023;
if (name_len == 0) free((void*)name);
if (n == 0)
{
TORRENT_ASSERT(borrow_chars == 0);
name = 0;
}
else
{
name = borrow_chars ? n : strdup(n);
}
name_len = borrow_chars;
}
std::string file_entry::filename() const
{
if (name_len) return std::string(name, name_len);
return name ? name : "";
}
#if TORRENT_USE_WSTRING #if TORRENT_USE_WSTRING
void file_storage::set_name(std::wstring const& n) void file_storage::set_name(std::wstring const& n)
{ {
@ -80,7 +162,8 @@ namespace libtorrent
TORRENT_ASSERT(index >= 0 && index < int(m_files.size())); TORRENT_ASSERT(index >= 0 && index < int(m_files.size()));
std::string utf8; std::string utf8;
wchar_utf8(new_filename, utf8); wchar_utf8(new_filename, utf8);
m_files[index].path = utf8; m_files[index].set_name(utf8.c_str());
update_path_index(m_files[index]);
} }
void file_storage::add_file(std::wstring const& file, size_type size, int flags void file_storage::add_file(std::wstring const& file, size_type size, int flags
@ -95,7 +178,8 @@ namespace libtorrent
void file_storage::rename_file(int index, std::string const& new_filename) void file_storage::rename_file(int index, std::string const& new_filename)
{ {
TORRENT_ASSERT(index >= 0 && index < int(m_files.size())); TORRENT_ASSERT(index >= 0 && index < int(m_files.size()));
m_files[index].path = new_filename; m_files[index].set_name(new_filename.c_str());
update_path_index(m_files[index]);
} }
namespace namespace
@ -149,7 +233,7 @@ namespace libtorrent
{ {
file_slice f; file_slice f;
f.file_index = file_iter - begin(); f.file_index = file_iter - begin();
f.offset = file_offset + file_iter->file_base; f.offset = file_offset + file_base(*file_iter);
f.size = (std::min)(file_iter->size - file_offset, (size_type)size); f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
size -= f.size; size -= f.size;
file_offset += f.size; file_offset += f.size;
@ -161,6 +245,13 @@ namespace libtorrent
return ret; return ret;
} }
std::string file_storage::file_path(file_entry const& fe) const
{
TORRENT_ASSERT(fe.path_index >= -1 && fe.path_index < int(m_paths.size()));
if (fe.path_index == -1) return fe.filename();
return combine_path(m_paths[fe.path_index], fe.filename());
}
peer_request file_storage::map_file(int file_index, size_type file_offset peer_request file_storage::map_file(int file_index, size_type file_offset
, int size) const , int size) const
{ {
@ -196,9 +287,8 @@ namespace libtorrent
TORRENT_ASSERT(m_name == split_path(file).c_str()); TORRENT_ASSERT(m_name == split_path(file).c_str());
m_files.push_back(file_entry()); m_files.push_back(file_entry());
file_entry& e = m_files.back(); file_entry& e = m_files.back();
e.file_index = m_files.size() - 1; e.set_name(file.c_str());
e.size = size; e.size = size;
e.path = file;
e.offset = m_total_size; e.offset = m_total_size;
e.pad_file = (flags & pad_file) != 0; e.pad_file = (flags & pad_file) != 0;
e.hidden_attribute = (flags & attribute_hidden) != 0; e.hidden_attribute = (flags & attribute_hidden) != 0;
@ -208,45 +298,137 @@ namespace libtorrent
{ {
e.symlink_index = m_symlinks.size(); e.symlink_index = m_symlinks.size();
m_symlinks.push_back(symlink_path); m_symlinks.push_back(symlink_path);
} }
e.mtime = mtime; if (mtime)
{
if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size());
m_mtime[m_files.size() - 1] = mtime;
}
update_path_index(e);
m_total_size += size; m_total_size += size;
} }
void file_storage::add_file(file_entry const& ent, sha1_hash const* filehash void file_storage::add_file(file_entry const& ent, char const* filehash
, std::string const* symlink) , std::string const* symlink, time_t mtime)
{ {
if (!has_parent_path(ent.path)) if (!has_parent_path(ent.filename()))
{ {
// you have already added at least one file with a // you have already added at least one file with a
// path to the file (branch_path), which means that // path to the file (branch_path), which means that
// all the other files need to be in the same top // all the other files need to be in the same top
// directory as the first file. // directory as the first file.
TORRENT_ASSERT(m_files.empty()); TORRENT_ASSERT(m_files.empty());
m_name = ent.path; m_name = ent.filename();
} }
else else
{ {
if (m_files.empty()) if (m_files.empty())
m_name = *ent.path.begin(); m_name = split_path(ent.filename()).c_str();
} }
m_files.push_back(ent); m_files.push_back(ent);
file_entry& e = m_files.back(); file_entry& e = m_files.back();
e.offset = m_total_size; e.offset = m_total_size;
e.file_index = m_files.size() - 1;
m_total_size += ent.size; m_total_size += ent.size;
if (filehash) if (filehash)
{ {
e.filehash_index = m_file_hashes.size(); if (m_file_hashes.size() < m_files.size()) m_file_hashes.resize(m_files.size());
m_file_hashes.push_back(*filehash); m_file_hashes[m_files.size() - 1] = filehash;
} }
if (symlink) if (symlink)
{ {
e.symlink_index = m_symlinks.size(); e.symlink_index = m_symlinks.size();
m_symlinks.push_back(*symlink); m_symlinks.push_back(*symlink);
} }
if (mtime)
{
if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size());
m_mtime[m_files.size() - 1] = mtime;
}
update_path_index(e);
}
sha1_hash file_storage::hash(file_entry const& fe) const
{
int index = &fe - &m_files[0];
if (index >= m_file_hashes.size()) return sha1_hash(0);
return sha1_hash(m_file_hashes[index]);
}
std::string const& file_storage::symlink(file_entry const& fe) const
{
TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size()));
return m_symlinks[fe.symlink_index];
}
time_t file_storage::mtime(file_entry const& fe) const
{
int index = &fe - &m_files[0];
if (index >= m_mtime.size()) return 0;
return m_mtime[index];
}
int file_storage::file_index(file_entry const& fe) const
{
int index = &fe - &m_files[0];
TORRENT_ASSERT(index >= 0 && index < m_files.size());
return index;
}
void file_storage::set_file_base(file_entry const& fe, size_type off)
{
int index = &fe - &m_files[0];
TORRENT_ASSERT(index >= 0 && index < m_files.size());
if (m_file_base.size() <= index) m_file_base.resize(index);
m_file_base[index] = off;
}
size_type file_storage::file_base(file_entry const& fe) const
{
int index = &fe - &m_files[0];
if (index >= m_file_base.size()) return 0;
return m_file_base[index];
}
bool compare_file_entry_size(file_entry const& fe1, file_entry const& fe2)
{ return fe1.size < fe2.size; }
void file_storage::reorder_file(int index, int dst)
{
file_entry e = m_files[index];
m_files.erase(m_files.begin() + index);
m_files.insert(m_files.begin() + dst, e);
if (!m_mtime.empty())
{
time_t mtime = 0;
if (m_mtime.size() > index)
{
mtime = m_mtime[index];
m_mtime.erase(m_mtime.begin() + index);
}
m_mtime.insert(m_mtime.begin() + dst, mtime);
}
if (!m_file_hashes.empty())
{
char const* fh = 0;
if (m_file_hashes.size() > index)
{
fh = m_file_hashes[index];
m_file_hashes.erase(m_file_hashes.begin() + index);
}
m_file_hashes.insert(m_file_hashes.begin() + dst, fh);
}
if (!m_file_base.empty())
{
size_type base = 0;
if (m_file_base.size() > index)
{
base = m_file_base[index];
m_file_base.erase(m_file_base.begin() + index);
}
m_file_base.insert(m_file_base.begin() + dst, base);
}
} }
void file_storage::optimize(int pad_file_limit) void file_storage::optimize(int pad_file_limit)
@ -263,10 +445,10 @@ namespace libtorrent
// put the largest file at the front, to make sure // put the largest file at the front, to make sure
// it's aligned // it's aligned
std::vector<file_entry>::iterator i = std::max_element(m_files.begin(), m_files.end() std::vector<file_entry>::iterator i = std::max_element(m_files.begin(), m_files.end()
, boost::bind(&file_entry::size, _1) < boost::bind(&file_entry::size, _2)); , &compare_file_entry_size);
using std::iter_swap; int index = file_index(*i);
iter_swap(i, m_files.begin()); reorder_file(index, 0);
size_type off = 0; size_type off = 0;
int padding_file = 0; int padding_file = 0;
@ -296,9 +478,9 @@ namespace libtorrent
if (best_match != m_files.end()) if (best_match != m_files.end())
{ {
// we found one // we found one
file_entry e = *best_match; int index = file_index(*best_match);
m_files.erase(best_match); reorder_file(index, file_index(*i));
i = m_files.insert(i, e);
i->offset = off; i->offset = off;
off += i->size; off += i->size;
continue; continue;
@ -311,10 +493,9 @@ namespace libtorrent
i = m_files.insert(i, e); i = m_files.insert(i, e);
i->size = pad_size; i->size = pad_size;
i->offset = off; i->offset = off;
i->file_base = 0; char name[30];
char name[10]; std::sprintf(name, ".____padding_file/%d", padding_file);
std::sprintf(name, "%d", padding_file); i->set_name(name);
i->path = combine_path("_____padding_file", name);
i->pad_file = true; i->pad_file = true;
off += pad_size; off += pad_size;
++padding_file; ++padding_file;

View File

@ -245,7 +245,7 @@ namespace libtorrent
m_data.start = start; m_data.start = start;
m_size = length; m_size = length;
m_begin = start - 1 - num_digits(length); m_begin = start - 1 - num_digits(length);
m_end = start + length; m_len = start - m_begin + length;
} }
namespace namespace
@ -400,7 +400,7 @@ namespace libtorrent
std::pair<char const*, int> lazy_entry::data_section() const std::pair<char const*, int> lazy_entry::data_section() const
{ {
typedef std::pair<char const*, int> return_t; typedef std::pair<char const*, int> return_t;
return return_t(m_begin, m_end - m_begin); return return_t(m_begin, m_len);
} }
#if TORRENT_USE_IOSTREAM #if TORRENT_USE_IOSTREAM

View File

@ -166,7 +166,7 @@ namespace libtorrent
{ {
file_status s; file_status s;
error_code ec; error_code ec;
stat_file(combine_path(save_path, i->path), &s, ec); stat_file(combine_path(save_path, storage.file_path(*i)), &s, ec);
if (!ec) if (!ec)
{ {
@ -216,7 +216,7 @@ namespace libtorrent
file_status s; file_status s;
error_code ec; error_code ec;
stat_file(combine_path(p, i->path), &s, ec); stat_file(combine_path(p, fs.file_path(*i)), &s, ec);
if (!ec) if (!ec)
{ {
@ -516,7 +516,7 @@ namespace libtorrent
for (file_storage::iterator file_iter = files().begin(), for (file_storage::iterator file_iter = files().begin(),
end_iter = files().end(); file_iter != end_iter; ++file_iter) end_iter = files().end(); file_iter != end_iter; ++file_iter)
{ {
std::string file_path = combine_path(m_save_path, file_iter->path); std::string file_path = combine_path(m_save_path, files().file_path(*file_iter));
std::string dir = parent_path(file_path); std::string dir = parent_path(file_path);
if (dir != last_path) if (dir != last_path)
@ -527,7 +527,7 @@ namespace libtorrent
create_directories(last_path, ec); create_directories(last_path, ec);
} }
int file_index = file_iter - files().begin(); int file_index = files().file_index(*file_iter);
// ignore files that have priority 0 // ignore files that have priority 0
if (int(m_file_priority.size()) > file_index if (int(m_file_priority.size()) > file_index
@ -592,7 +592,7 @@ namespace libtorrent
{ {
error_code ec; error_code ec;
file_status s; file_status s;
stat_file(combine_path(m_save_path, i->path), &s, ec); stat_file(combine_path(m_save_path, files().file_path(*i)), &s, ec);
if (ec) continue; if (ec) continue;
if (s.mode & file_status::regular_file && i->size > 0) if (s.mode & file_status::regular_file && i->size > 0)
return true; return true;
@ -603,8 +603,8 @@ namespace libtorrent
bool storage::rename_file(int index, std::string const& new_filename) bool storage::rename_file(int index, std::string const& new_filename)
{ {
if (index < 0 || index >= m_files.num_files()) return true; if (index < 0 || index >= m_files.num_files()) return true;
std::string old_name = combine_path(m_save_path, files().at(index).path); std::string old_name = combine_path(m_save_path, files().file_path(files().at(index)));
m_pool.release(this, files().at(index)); m_pool.release(this, index);
error_code ec; error_code ec;
rename(old_name, combine_path(m_save_path, new_filename), ec); rename(old_name, combine_path(m_save_path, new_filename), ec);
@ -650,8 +650,9 @@ namespace libtorrent
for (file_storage::iterator i = files().begin() for (file_storage::iterator i = files().begin()
, end(files().end()); i != end; ++i) , end(files().end()); i != end; ++i)
{ {
std::string p = combine_path(m_save_path, i->path); std::string fp = files().file_path(*i);
std::string bp = parent_path(i->path); std::string p = combine_path(m_save_path, fp);
std::string bp = parent_path(fp);
std::pair<iter_t, bool> ret; std::pair<iter_t, bool> ret;
ret.second = true; ret.second = true;
while (ret.second && !bp.empty()) while (ret.second && !bp.empty())
@ -861,7 +862,7 @@ namespace libtorrent
for (file_storage::iterator i = f.begin() for (file_storage::iterator i = f.begin()
, end(f.end()); i != end; ++i) , end(f.end()); i != end; ++i)
{ {
std::string split = split_path(i->path); std::string split = split_path(f.file_path(*i));
to_move.insert(to_move.begin(), split); to_move.insert(to_move.begin(), split);
} }
@ -1178,8 +1179,8 @@ ret:
TORRENT_ASSERT(int(slices.size()) > counter); TORRENT_ASSERT(int(slices.size()) > counter);
size_type slice_size = slices[counter].size; size_type slice_size = slices[counter].size;
TORRENT_ASSERT(slice_size == file_bytes_left); TORRENT_ASSERT(slice_size == file_bytes_left);
TORRENT_ASSERT(files().at(slices[counter].file_index).path TORRENT_ASSERT(&files().at(slices[counter].file_index)
== file_iter->path); == &*file_iter);
++counter; ++counter;
#endif #endif
@ -1201,7 +1202,7 @@ ret:
file_handle = open_file(*file_iter, op.mode, ec); file_handle = open_file(*file_iter, op.mode, ec);
if (!file_handle || ec) if (!file_handle || ec)
{ {
std::string path = combine_path(m_save_path, file_iter->path); std::string path = combine_path(m_save_path, files().file_path(*file_iter));
TORRENT_ASSERT(ec); TORRENT_ASSERT(ec);
set_error(path, ec); set_error(path, ec);
return -1; return -1;
@ -1215,23 +1216,24 @@ ret:
// read is unaligned, we need to fall back on a slow // read is unaligned, we need to fall back on a slow
// special read that reads aligned buffers and copies // special read that reads aligned buffers and copies
// it into the one supplied // it into the one supplied
size_type adjusted_offset = files().file_base(*file_iter) + file_offset;
if ((file_handle->open_mode() & file::no_buffer) if ((file_handle->open_mode() & file::no_buffer)
&& (((file_iter->file_base + file_offset) & (file_handle->pos_alignment()-1)) != 0 && ((adjusted_offset & (file_handle->pos_alignment()-1)) != 0
|| (uintptr_t(tmp_bufs->iov_base) & (file_handle->buf_alignment()-1)) != 0)) || (uintptr_t(tmp_bufs->iov_base) & (file_handle->buf_alignment()-1)) != 0))
{ {
bytes_transferred = (this->*op.unaligned_op)(file_handle, file_iter->file_base bytes_transferred = (this->*op.unaligned_op)(file_handle, adjusted_offset
+ file_offset, tmp_bufs, num_tmp_bufs, ec); , tmp_bufs, num_tmp_bufs, ec);
} }
else else
{ {
bytes_transferred = (int)((*file_handle).*op.regular_op)(file_iter->file_base bytes_transferred = (int)((*file_handle).*op.regular_op)(adjusted_offset
+ file_offset, tmp_bufs, num_tmp_bufs, ec); , tmp_bufs, num_tmp_bufs, ec);
} }
file_offset = 0; file_offset = 0;
if (ec) if (ec)
{ {
set_error(combine_path(m_save_path, file_iter->path), ec); set_error(combine_path(m_save_path, files().file_path(*file_iter)), ec);
return -1; return -1;
} }
@ -1312,12 +1314,12 @@ ret:
int cache_setting = m_settings ? settings().disk_io_write_mode : 0; int cache_setting = m_settings ? settings().disk_io_write_mode : 0;
if (cache_setting == session_settings::disable_os_cache if (cache_setting == session_settings::disable_os_cache
|| (cache_setting == session_settings::disable_os_cache_for_aligned_files || (cache_setting == session_settings::disable_os_cache_for_aligned_files
&& ((fe.offset + fe.file_base) & (m_page_size-1)) == 0)) && ((fe.offset + files().file_base(fe)) & (m_page_size-1)) == 0))
mode |= file::no_buffer; mode |= file::no_buffer;
if (!m_allocate_files) mode |= file::sparse; if (!m_allocate_files) mode |= file::sparse;
if (m_settings && settings().no_atime_storage) mode |= file::no_atime; if (m_settings && settings().no_atime_storage) mode |= file::no_atime;
return m_pool.open_file(const_cast<storage*>(this), m_save_path, fe, mode, ec); return m_pool.open_file(const_cast<storage*>(this), m_save_path, fe, files(), mode, ec);
} }
storage_interface* default_storage_constructor(file_storage const& fs storage_interface* default_storage_constructor(file_storage const& fs

View File

@ -4142,7 +4142,7 @@ namespace libtorrent
for (torrent_info::file_iterator i = m_torrent_file->begin_files() for (torrent_info::file_iterator i = m_torrent_file->begin_files()
, end(m_torrent_file->end_files()); i != end; ++i) , end(m_torrent_file->end_files()); i != end; ++i)
{ {
fl.push_back(i->path); fl.push_back(m_torrent_file->files().file_path(*i));
} }
} }

View File

@ -184,8 +184,8 @@ namespace libtorrent
void verify_encoding(file_entry& target) void verify_encoding(file_entry& target)
{ {
std::string p = target.path; std::string p = target.filename();
if (!verify_encoding(p, true)) target.path = p; if (!verify_encoding(p, true)) target.set_name(p.c_str());
} }
// TODO: should this take a char const*? // TODO: should this take a char const*?
@ -233,18 +233,17 @@ namespace libtorrent
} }
bool extract_single_file(lazy_entry const& dict, file_entry& target bool extract_single_file(lazy_entry const& dict, file_entry& target
, std::string const& root_dir, sha1_hash* filehash, std::string* symlink) , std::string const& root_dir, lazy_entry const** filehash, std::string* symlink
, lazy_entry const** filename, time_t* mtime)
{ {
if (dict.type() != lazy_entry::dict_t) return false; if (dict.type() != lazy_entry::dict_t) return false;
lazy_entry const* length = dict.dict_find("length"); lazy_entry const* length = dict.dict_find("length");
if (length == 0 || length->type() != lazy_entry::int_t) if (length == 0 || length->type() != lazy_entry::int_t)
return false; return false;
target.size = length->int_value(); target.size = length->int_value();
target.path = root_dir;
target.file_base = 0;
size_type ts = dict.dict_find_int_value("mtime", -1); size_type ts = dict.dict_find_int_value("mtime", -1);
if (ts >= 0) target.mtime = std::time_t(ts); if (ts > 0) *mtime = std::time_t(ts);
// prefer the name.utf-8 // prefer the name.utf-8
// because if it exists, it is more // because if it exists, it is more
@ -256,21 +255,25 @@ namespace libtorrent
if (p == 0 || p->type() != lazy_entry::list_t) if (p == 0 || p->type() != lazy_entry::list_t)
return false; return false;
std::string path = root_dir;
for (int i = 0, end(p->list_size()); i < end; ++i) for (int i = 0, end(p->list_size()); i < end; ++i)
{ {
if (p->list_at(i)->type() != lazy_entry::string_t) if (p->list_at(i)->type() != lazy_entry::string_t)
return false; return false;
std::string path_element = p->list_at(i)->string_value(); std::string path_element = p->list_at(i)->string_value();
if (i == end - 1) *filename = p->list_at(i);
trim_path_element(path_element); trim_path_element(path_element);
target.path = combine_path(target.path, path_element); path = combine_path(path, path_element);
} }
target.path = sanitize_path(target.path); path = sanitize_path(path);
verify_encoding(target); verify_encoding(target);
// bitcomet pad file // bitcomet pad file
if (target.path.find("_____padding_file_") != std::string::npos) if (path.find("_____padding_file_") != std::string::npos)
target.pad_file = true; target.pad_file = true;
target.set_name(path.c_str());
lazy_entry const* attr = dict.dict_find_string("attr"); lazy_entry const* attr = dict.dict_find_string("attr");
if (attr) if (attr)
{ {
@ -288,11 +291,7 @@ namespace libtorrent
lazy_entry const* fh = dict.dict_find_string("sha1"); lazy_entry const* fh = dict.dict_find_string("sha1");
if (fh && fh->string_length() == 20 && filehash) if (fh && fh->string_length() == 20 && filehash)
{ *filehash = fh;
std::memcpy(&(*filehash)[0], fh->string_ptr(), 20);
// indicate that the file has a filehash
target.filehash_index = 0;
}
lazy_entry const* s_p = dict.dict_find("symlink path"); lazy_entry const* s_p = dict.dict_find("symlink path");
if (s_p != 0 && s_p->type() == lazy_entry::list_t && symlink) if (s_p != 0 && s_p->type() == lazy_entry::list_t && symlink)
@ -332,16 +331,19 @@ namespace libtorrent
}; };
bool extract_files(lazy_entry const& list, file_storage& target bool extract_files(lazy_entry const& list, file_storage& target
, std::string const& root_dir) , std::string const& root_dir, ptrdiff_t info_ptr_diff)
{ {
if (list.type() != lazy_entry::list_t) return false; if (list.type() != lazy_entry::list_t) return false;
target.reserve(list.list_size()); target.reserve(list.list_size());
for (int i = 0, end(list.list_size()); i < end; ++i) for (int i = 0, end(list.list_size()); i < end; ++i)
{ {
sha1_hash file_hash; lazy_entry const* file_hash = 0;
time_t mtime = 0;
std::string symlink; std::string symlink;
file_entry e; file_entry e;
if (!extract_single_file(*list.list_at(i), e, root_dir, &file_hash, &symlink)) lazy_entry const* fee = 0;
if (!extract_single_file(*list.list_at(i), e, root_dir
, &file_hash, &symlink, &fee, &mtime))
return false; return false;
// TODO: this logic should be a separate step // TODO: this logic should be a separate step
@ -352,15 +354,31 @@ namespace libtorrent
// as long as this file already exists // as long as this file already exists
// increase the counter // increase the counter
while (!files.insert(e.path).second) std::string path = e.filename();
while (!files.insert(path).second)
{ {
++cnt; ++cnt;
char suffix[50]; char suffix[50];
snprintf(suffix, sizeof(suffix), ".%d%s", cnt, extension(e.path).c_str()); snprintf(suffix, sizeof(suffix), ".%d%s", cnt, extension(path).c_str());
replace_extension(e.path, suffix); replace_extension(path, suffix);
}
e.set_name(path.c_str());
target.add_file(e, file_hash ? file_hash->string_ptr() + info_ptr_diff : 0
, e.symlink_index != -1 ? &symlink : 0, mtime);
// This is a memory optimization! Instead of having
// each entry keep a string for its filename, make it
// simply point into the info-section buffer
file_entry const& fe = target.at(target.num_files() - 1);
// TODO: once the filename renaming is removed from here
// this check can be removed as well
if (fee && fe.filename() == fee->string_value())
{
// this string pointer does not necessarily point into
// the m_info_section buffer.
char const* str_ptr = fee->string_ptr() + info_ptr_diff;
const_cast<file_entry&>(fe).set_name(str_ptr, fee->string_length());
} }
target.add_file(e, e.filehash_index != -1 ? &file_hash : 0
, e.symlink_index != -1 ? &symlink : 0);
} }
return true; return true;
} }
@ -748,6 +766,11 @@ namespace libtorrent
TORRENT_ASSERT(section.first[0] == 'd'); TORRENT_ASSERT(section.first[0] == 'd');
TORRENT_ASSERT(section.first[m_info_section_size-1] == 'e'); TORRENT_ASSERT(section.first[m_info_section_size-1] == 'e');
// when translating a pointer that points into the 'info' tree's
// backing buffer, into a pointer to our copy of the info section,
// this is the pointer offset to use.
ptrdiff_t info_ptr_diff = m_info_section.get() - section.first;
// extract piece length // extract piece length
int piece_length = info.dict_find_int_value("piece length", -1); int piece_length = info.dict_find_int_value("piece length", -1);
if (piece_length <= 0) if (piece_length <= 0)
@ -784,12 +807,12 @@ namespace libtorrent
// if there's no list of files, there has to be a length // if there's no list of files, there has to be a length
// field. // field.
file_entry e; file_entry e;
e.path = name; e.set_name(name.c_str());
e.offset = 0; e.offset = 0;
e.size = info.dict_find_int_value("length", -1); e.size = info.dict_find_int_value("length", -1);
size_type ts = info.dict_find_int_value("mtime", -1); size_type ts = info.dict_find_int_value("mtime", -1);
if (ts >= 0) time_t mtime = 0;
e.mtime = std::time_t(ts); if (ts > 0) mtime = std::time_t(ts);
lazy_entry const* attr = info.dict_find_string("attr"); lazy_entry const* attr = info.dict_find_string("attr");
if (attr) if (attr)
{ {
@ -818,29 +841,23 @@ namespace libtorrent
e.symlink_index = 0; e.symlink_index = 0;
} }
lazy_entry const* fh = info.dict_find_string("sha1"); lazy_entry const* fh = info.dict_find_string("sha1");
sha1_hash filehash; if (fh && fh->string_length() != 20) fh = 0;
if (fh && fh->string_length() == 20)
{
std::memcpy(&filehash[0], fh->string_ptr(), 20);
e.filehash_index = 0;
}
// bitcomet pad file // bitcomet pad file
if (e.path.find("_____padding_file_") != std::string::npos) if (e.filename().find("_____padding_file_") != std::string::npos)
e.pad_file = true; e.pad_file = true;
if (e.size < 0) if (e.size < 0)
{ {
ec = errors::torrent_invalid_length; ec = errors::torrent_invalid_length;
return false; return false;
} }
bool omit_hash = (flags & torrent_info::omit_filehashes) || e.filehash_index == -1; m_files.add_file(e, fh ? fh->string_ptr() + info_ptr_diff : 0
m_files.add_file(e, omit_hash ? 0 : &filehash , e.symlink_index != -1 ? &symlink : 0, mtime);
, e.symlink_index != -1 ? &symlink : 0);
m_multifile = false; m_multifile = false;
} }
else else
{ {
if (!extract_files(*i, m_files, name)) if (!extract_files(*i, m_files, name, info_ptr_diff))
{ {
ec = errors::torrent_file_parse_failed; ec = errors::torrent_file_parse_failed;
return false; return false;
@ -873,7 +890,7 @@ namespace libtorrent
return false; return false;
} }
m_piece_hashes = m_info_section.get() + (pieces->string_ptr() - section.first); m_piece_hashes = pieces->string_ptr() + info_ptr_diff;
TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); TORRENT_ASSERT(m_piece_hashes >= m_info_section.get());
TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size);
} }
@ -1204,7 +1221,7 @@ namespace libtorrent
os << "piece length: " << piece_length() << "\n"; os << "piece length: " << piece_length() << "\n";
os << "files:\n"; os << "files:\n";
for (file_storage::iterator i = m_files.begin(); i != m_files.end(); ++i) for (file_storage::iterator i = m_files.begin(); i != m_files.end(); ++i)
os << " " << std::setw(11) << i->size << " " << i->path << "\n"; os << " " << std::setw(11) << i->size << " " << m_files.file_path(*i) << "\n";
} }
// ------- end deprecation ------- // ------- end deprecation -------

View File

@ -210,7 +210,7 @@ namespace libtorrent
if (using_proxy) if (using_proxy)
{ {
request += m_url; request += m_url;
std::string path = info.orig_files().at(f.file_index).path; std::string path = info.orig_files().file_path(info.orig_files().at(f.file_index));
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
convert_path_to_posix(path); convert_path_to_posix(path);
#endif #endif
@ -219,7 +219,7 @@ namespace libtorrent
else else
{ {
std::string path = m_path; std::string path = m_path;
path += info.orig_files().at(f.file_index).path; path += info.orig_files().file_path(info.orig_files().at(f.file_index));
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
convert_path_to_posix(path); convert_path_to_posix(path);
#endif #endif
@ -421,7 +421,7 @@ namespace libtorrent
int file_index = m_file_requests.front(); int file_index = m_file_requests.front();
torrent_info const& info = t->torrent_file(); torrent_info const& info = t->torrent_file();
std::string path = info.orig_files().at(file_index).path; std::string path = info.orig_files().file_path(info.orig_files().at(file_index));
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
convert_path_to_posix(path); convert_path_to_posix(path);
#endif #endif

View File

@ -804,7 +804,7 @@ void run_test(std::string const& test_path, bool unbuffered)
file_storage fs; file_storage fs;
fs.add_file("temp_storage/test1.tmp", 3 * piece_size); fs.add_file("temp_storage/test1.tmp", 3 * piece_size);
libtorrent::create_torrent t(fs, piece_size, -1, 0); libtorrent::create_torrent t(fs, piece_size, -1, 0);
TEST_CHECK(fs.begin()->path == "temp_storage/test1.tmp"); TEST_CHECK(fs.file_path(*fs.begin()) == "temp_storage/test1.tmp");
t.set_hash(0, hasher(piece0, piece_size).final()); t.set_hash(0, hasher(piece0, piece_size).final());
t.set_hash(1, hasher(piece1, piece_size).final()); t.set_hash(1, hasher(piece1, piece_size).final());
t.set_hash(2, hasher(piece2, piece_size).final()); t.set_hash(2, hasher(piece2, piece_size).final());

View File

@ -151,7 +151,8 @@ void test_transfer(boost::intrusive_ptr<torrent_info> torrent_file
if (proxy) stop_proxy(8002); if (proxy) stop_proxy(8002);
TEST_CHECK(exists(combine_path("./tmp2_web_seed", torrent_file->file_at(0).path))); TEST_CHECK(exists(combine_path("./tmp2_web_seed", torrent_file->files().file_path(
torrent_file->file_at(0)))));
remove_all("./tmp2_web_seed", ec); remove_all("./tmp2_web_seed", ec);
} }
@ -256,10 +257,11 @@ int run_suite(char const* protocol, bool test_url_seed, bool chunked_encoding)
// verify that the file hashes are correct // verify that the file hashes are correct
for (int i = 0; i < torrent_file->num_files(); ++i) for (int i = 0; i < torrent_file->num_files(); ++i)
{ {
TEST_CHECK(torrent_file->file_at(i).filehash_index >= 0); sha1_hash h1 = torrent_file->files().hash(torrent_file->file_at(i));
sha1_hash h1 = torrent_file->files().hash(torrent_file->file_at(i).filehash_index); sha1_hash h2 = file_hash(combine_path("./tmp1_web_seed"
sha1_hash h2 = file_hash(combine_path("./tmp1_web_seed", torrent_file->file_at(i).path)); , torrent_file->files().file_path(torrent_file->file_at(i))));
fprintf(stderr, "%s: %s == %s\n", torrent_file->file_at(i).path.c_str() fprintf(stderr, "%s: %s == %s\n"
, torrent_file->files().file_path(torrent_file->file_at(i)).c_str()
, to_hex(h1.to_string()).c_str(), to_hex(h2.to_string()).c_str()); , to_hex(h1.to_string()).c_str(), to_hex(h2.to_string()).c_str());
TEST_EQUAL(h1, h2); TEST_EQUAL(h1, h2);
} }