Skip to content

Commit bb6f2b9

Browse files
committed
extend file_storage tests. Make pad files separate paths and file names properly
1 parent 721c4d0 commit bb6f2b9

File tree

3 files changed

+218
-29
lines changed

3 files changed

+218
-29
lines changed

include/libtorrent/file_storage.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,8 @@ namespace libtorrent {
535535

536536
private:
537537

538+
int get_or_add_path(string_view path);
539+
538540
void add_pad_file(int size
539541
, std::vector<internal_file_entry>::iterator& i
540542
, std::int64_t& offset

src/file_storage.cpp

+26-24
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@ POSSIBILITY OF SUCH DAMAGE.
4646

4747
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
4848
#define TORRENT_SEPARATOR '\\'
49-
#define TORRENT_SEPARATOR_STR "\\"
5049
#else
5150
#define TORRENT_SEPARATOR '/'
52-
#define TORRENT_SEPARATOR_STR "/"
5351
#endif
5452

5553
using namespace std::placeholders;
@@ -108,13 +106,14 @@ namespace libtorrent {
108106

109107
namespace {
110108

111-
bool compare_file_offset(internal_file_entry const& lhs
112-
, internal_file_entry const& rhs)
113-
{
114-
return lhs.offset < rhs.offset;
115-
}
109+
bool compare_file_offset(internal_file_entry const& lhs
110+
, internal_file_entry const& rhs)
111+
{
112+
return lhs.offset < rhs.offset;
116113
}
117114

115+
}
116+
118117
// path is not supposed to include the name of the torrent itself.
119118
void file_storage::update_path_index(internal_file_entry& e
120119
, std::string const& path, bool const set_name)
@@ -167,30 +166,30 @@ namespace {
167166
e.no_root_dir = true;
168167
}
169168

169+
e.path_index = get_or_add_path({branch_path, aux::numeric_cast<std::size_t>(branch_len)});
170+
if (set_name) e.set_name(leaf);
171+
}
172+
173+
int file_storage::get_or_add_path(string_view const path)
174+
{
170175
// do we already have this path in the path list?
171176
auto p = std::find_if(m_paths.rbegin(), m_paths.rend()
172177
, [&] (std::string const& str)
173-
{
174-
if (int(str.size()) != branch_len) return false;
175-
return std::memcmp(str.c_str(), branch_path, aux::numeric_cast<std::size_t>(branch_len)) == 0;
176-
});
178+
{ return str == path; });
177179

178180
if (p == m_paths.rend())
179181
{
180182
// no, we don't. add it
181-
e.path_index = int(m_paths.size());
182-
TORRENT_ASSERT(branch_len == 0 || branch_path[0] != '/');
183-
184-
// poor man's emplace back
185-
m_paths.resize(m_paths.size() + 1);
186-
m_paths.back().assign(branch_path, aux::numeric_cast<std::size_t>(branch_len));
183+
int const ret = int(m_paths.size());
184+
TORRENT_ASSERT(path.size() == 0 || path[0] != '/');
185+
m_paths.emplace_back(path.data(), path.size());
186+
return ret;
187187
}
188188
else
189189
{
190190
// yes we do. use it
191-
e.path_index = int(p.base() - m_paths.begin() - 1);
191+
return int(p.base() - m_paths.begin() - 1);
192192
}
193-
if (set_name) e.set_name(leaf);
194193
}
195194

196195
#ifndef TORRENT_NO_DEPRECATE
@@ -413,6 +412,8 @@ namespace {
413412

414413
file_index_t file_storage::file_index_at_offset(std::int64_t const offset) const
415414
{
415+
TORRENT_ASSERT_PRECOND(offset >= 0);
416+
TORRENT_ASSERT_PRECOND(offset < m_total_size);
416417
// find the file iterator and file offset
417418
internal_file_entry target;
418419
target.offset = aux::numeric_cast<std::uint64_t>(offset);
@@ -441,6 +442,8 @@ namespace {
441442
std::vector<file_slice> file_storage::map_block(piece_index_t const piece
442443
, std::int64_t const offset, int size) const
443444
{
445+
TORRENT_ASSERT_PRECOND(piece >= piece_index_t{0});
446+
TORRENT_ASSERT_PRECOND(piece < end_piece());
444447
TORRENT_ASSERT_PRECOND(num_files() > 0);
445448
std::vector<file_slice> ret;
446449

@@ -1049,11 +1052,10 @@ namespace {
10491052
i = m_files.begin() + cur_index;
10501053
e.size = aux::numeric_cast<std::uint64_t>(size);
10511054
e.offset = aux::numeric_cast<std::uint64_t>(offset);
1052-
char name[30];
1053-
std::snprintf(name, sizeof(name), ".pad" TORRENT_SEPARATOR_STR "%d"
1054-
, pad_file_counter);
1055-
std::string path = combine_path(m_name, name);
1056-
e.set_name(path.c_str());
1055+
e.path_index = get_or_add_path(".pad");
1056+
char name[15];
1057+
std::snprintf(name, sizeof(name), "%d", pad_file_counter);
1058+
e.set_name(name);
10571059
e.pad_file = true;
10581060
offset += size;
10591061
++pad_file_counter;

test/test_file_storage.cpp

+190-5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ POSSIBILITY OF SUCH DAMAGE.
3030
3131
*/
3232

33+
#include <iostream>
3334
#include "test.hpp"
3435
#include "setup_transfer.hpp"
3536

@@ -368,12 +369,196 @@ TORRENT_TEST(piece_range)
368369
TEST_CHECK(aux::file_piece_range_exclusive(fs, file_index_t(1)) == std::make_tuple(piece_index_t(3), piece_index_t(7)));
369370
}
370371

371-
// TODO: test file_storage::optimize
372-
// TODO: test map_block
373-
// TODO: test piece_size(int piece)
374-
// TODO: test file_index_at_offset
372+
void test_optimize(std::vector<int> file_sizes
373+
, int const alignment
374+
, int const pad_file_limit
375+
, bool const tail_padding
376+
, std::vector<int> const expected_order)
377+
{
378+
file_storage fs;
379+
int i = 0;
380+
for (int s : file_sizes)
381+
{
382+
fs.add_file(combine_path("test", std::to_string(i++)), s);
383+
}
384+
fs.optimize(pad_file_limit, alignment, tail_padding);
385+
386+
TEST_EQUAL(fs.num_files(), int(expected_order.size()));
387+
if (fs.num_files() != int(expected_order.size())) return;
388+
389+
file_index_t idx{0};
390+
int num_pad_files = 0;
391+
std::cout << "{ ";
392+
for (file_index_t i{0}; i != fs.end_file(); ++i)
393+
{
394+
if (fs.file_flags(i) & file_storage::flag_pad_file) std::cout << "*";
395+
std::cout << fs.file_size(i) << " ";
396+
}
397+
std::cout << "}\n";
398+
for (int expect : expected_order)
399+
{
400+
if (expect == -1)
401+
{
402+
TEST_CHECK(fs.file_flags(idx) & file_storage::flag_pad_file);
403+
TEST_EQUAL(fs.file_name(idx), std::to_string(num_pad_files++));
404+
}
405+
else
406+
{
407+
TEST_EQUAL(fs.file_name(idx), std::to_string(expect));
408+
TEST_EQUAL(fs.file_size(idx), file_sizes[expect]);
409+
}
410+
++idx;
411+
}
412+
}
413+
414+
TORRENT_TEST(optimize_order_large_first)
415+
{
416+
test_optimize({1000, 3000, 10000}, 1024, 1024, false, {2, -1, 1, 0});
417+
}
418+
419+
TORRENT_TEST(optimize_tail_padding2)
420+
{
421+
// when tail padding is enabled, a pad file is added at the end
422+
test_optimize({2000}, 1024, 1024, true, {0, -1});
423+
}
424+
425+
TORRENT_TEST(optimize_tail_padding3)
426+
{
427+
// when tail padding is enabled, a pad file is added at the end, even if the
428+
// file is smaller than the alignment, as long as pad_file_limit is 0 *(which
429+
// means files are aligned unconditionally)
430+
test_optimize({1000}, 1024, 0, true, {0, -1});
431+
}
432+
433+
TORRENT_TEST(optimize_tail_padding_small_files)
434+
{
435+
// files smaller than the pad file limit are not tail-padded
436+
test_optimize({1000, 1, 2}, 1024, 50, true, {0, -1, 2, 1});
437+
}
438+
439+
TORRENT_TEST(optimize_tail_padding_small_files2)
440+
{
441+
// files larger than the pad file limit are not tail-padded
442+
test_optimize({1000, 1, 2}, 1024, 0, true, {0, -1, 2, -1, 1, -1});
443+
}
444+
445+
TORRENT_TEST(optimize_prioritize_aligned_size)
446+
{
447+
// file 0 of size 1024 will be chosen over the larger file, since it won't
448+
// affect the alignment of the next file
449+
test_optimize({1024, 3000, 10}, 1024, 1024, false, {0, 1, 2});
450+
}
451+
452+
TORRENT_TEST(optimize_fill_with_small_files)
453+
{
454+
// fill in space that otherwise would just be a pad file with other small
455+
// files.
456+
test_optimize({2000, 5000, 48, 120}, 1024, 1024, false, {1, 3, 0, 2});
457+
}
458+
459+
TORRENT_TEST(optimize_pad_all)
460+
{
461+
// when pad_size_limit is 0, every file is padded to alignment, regardless of
462+
// how big it is
463+
// the empty file is first, since it doesn't affect alignment of the next
464+
// file
465+
test_optimize({48, 1, 0, 5000}, 1024, 0, false, {2, 3, -1, 0, -1, 1});
466+
}
467+
468+
TORRENT_TEST(optimize_pad_all_with_tail)
469+
{
470+
// when pad_size_limit is 0, every file is padded to alignment, regardless of
471+
// how big it is, also with tail-padding enabled
472+
test_optimize({48, 1, 0, 5000}, 1024, 0, true, {2, 3, -1, 0, -1, 1, -1});
473+
}
474+
475+
TORRENT_TEST(piece_size_last_piece)
476+
{
477+
file_storage fs;
478+
fs.set_piece_length(1024);
479+
fs.add_file("0", 100);
480+
fs.set_num_pieces(int((fs.total_size() + 1023) / 1024));
481+
TEST_EQUAL(fs.piece_size(piece_index_t{0}), 100);
482+
}
483+
484+
TORRENT_TEST(piece_size_middle_piece)
485+
{
486+
file_storage fs;
487+
fs.set_piece_length(1024);
488+
fs.add_file("0", 2000);
489+
fs.set_num_pieces(int((fs.total_size() + 1023) / 1024));
490+
TEST_EQUAL(fs.piece_size(piece_index_t{0}), 1024);
491+
TEST_EQUAL(fs.piece_size(piece_index_t{1}), 2000 - 1024);
492+
}
493+
494+
TORRENT_TEST(file_index_at_offset)
495+
{
496+
file_storage fs;
497+
fs.set_piece_length(1024);
498+
fs.add_file("test/0", 1);
499+
fs.add_file("test/1", 2);
500+
fs.add_file("test/2", 3);
501+
fs.add_file("test/3", 4);
502+
fs.add_file("test/4", 5);
503+
std::int64_t offset = 0;
504+
for (int f : {0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4})
505+
{
506+
TEST_EQUAL(fs.file_index_at_offset(offset++), file_index_t{f});
507+
}
508+
}
509+
510+
TORRENT_TEST(map_block_start)
511+
{
512+
file_storage fs;
513+
fs.set_piece_length(1024);
514+
fs.add_file("test/0", 1);
515+
fs.add_file("test/1", 2);
516+
fs.add_file("test/2", 3);
517+
fs.add_file("test/3", 4);
518+
fs.add_file("test/4", 5);
519+
fs.set_num_pieces(int((fs.total_size() + 1023) / 1024));
520+
int len = 0;
521+
for (int f : {0, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5})
522+
{
523+
std::vector<file_slice> const map = fs.map_block(piece_index_t{0}, 0, len);
524+
TEST_EQUAL(int(map.size()), f);
525+
file_index_t file_index{0};
526+
std::int64_t actual_len = 0;
527+
for (auto file : map)
528+
{
529+
TEST_EQUAL(file.file_index, file_index++);
530+
TEST_EQUAL(file.offset, 0);
531+
actual_len += file.size;
532+
}
533+
TEST_EQUAL(actual_len, len);
534+
++len;
535+
}
536+
}
537+
538+
TORRENT_TEST(map_block_mid)
539+
{
540+
file_storage fs;
541+
fs.set_piece_length(1024);
542+
fs.add_file("test/0", 1);
543+
fs.add_file("test/1", 2);
544+
fs.add_file("test/2", 3);
545+
fs.add_file("test/3", 4);
546+
fs.add_file("test/4", 5);
547+
fs.set_num_pieces(int((fs.total_size() + 1023) / 1024));
548+
int offset = 0;
549+
for (int f : {0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4})
550+
{
551+
std::vector<file_slice> const map = fs.map_block(piece_index_t{0}, offset, 1);
552+
TEST_EQUAL(int(map.size()), 1);
553+
auto const& file = map[0];
554+
TEST_EQUAL(file.file_index, file_index_t{f});
555+
TEST_CHECK(file.offset <= offset);
556+
TEST_EQUAL(file.size, 1);
557+
++offset;
558+
}
559+
}
560+
375561
// TODO: test file attributes
376562
// TODO: test symlinks
377-
// TODO: test pad_files
378563
// TODO: test reorder_file (make sure internal_file_entry::swap() is used)
379564

0 commit comments

Comments
 (0)