00001
00002
00003
00004
00005
00006
00007
00008 #include <ctime>
00009 #include <iterator>
00010 #include <algorithm>
00011 #include <set>
00012 #include <functional>
00013
00014 #ifdef _MSC_VER
00015 #pragma warning(push, 1)
00016 #endif
00017
00018 #include <boost/lexical_cast.hpp>
00019 #include <boost/filesystem/convenience.hpp>
00020 #include <boost/filesystem/operations.hpp>
00021 #include <boost/filesystem/fstream.hpp>
00022 #include <boost/thread/mutex.hpp>
00023 #include <boost/ref.hpp>
00024 #include <boost/date_time/posix_time/posix_time_types.hpp>
00025 #include <boost/bind.hpp>
00026 #include <boost/version.hpp>
00027 #include <boost/multi_index_container.hpp>
00028 #include <boost/multi_index/member.hpp>
00029 #include <boost/multi_index/ordered_index.hpp>
00030
00031 #ifdef _MSC_VER
00032 #pragma warning(pop)
00033 #endif
00034
00035 #include "libtorrent/storage.hpp"
00036 #include "libtorrent/torrent.hpp"
00037 #include "libtorrent/hasher.hpp"
00038 #include "libtorrent/session.hpp"
00039 #include "libtorrent/peer_id.hpp"
00040 #include "libtorrent/file.hpp"
00041 #include "libtorrent/invariant_check.hpp"
00042 #include "libtorrent/file_pool.hpp"
00043 #include "libtorrent/aux_/session_impl.hpp"
00044
00045 #ifndef NDEBUG
00046 #include <ios>
00047 #include <iostream>
00048 #include <iomanip>
00049 #include <cstdio>
00050 #endif
00051
00052 #if defined(_WIN32) && defined(UNICODE)
00053
00054 #include <windows.h>
00055 #include <boost/filesystem/exception.hpp>
00056 #include "libtorrent/utf8.hpp"
00057
00058 namespace libtorrent
00059 {
00060 std::wstring safe_convert(std::string const& s)
00061 {
00062 try
00063 {
00064 return libtorrent::utf8_wchar(s);
00065 }
00066 catch (std::exception)
00067 {
00068 std::wstring ret;
00069 const char* end = &s[0] + s.size();
00070 for (const char* i = &s[0]; i < end;)
00071 {
00072 wchar_t c = '.';
00073 int result = std::mbtowc(&c, i, end - i);
00074 if (result > 0) i += result;
00075 else ++i;
00076 ret += c;
00077 }
00078 return ret;
00079 }
00080 }
00081 }
00082
00083 namespace
00084 {
00085 using libtorrent::safe_convert;
00086 using namespace boost::filesystem;
00087
00088
00089 bool create_directories_win(const path& ph)
00090 {
00091 if (ph.empty() || exists(ph))
00092 {
00093 if ( !ph.empty() && !is_directory(ph) )
00094 boost::throw_exception( filesystem_error(
00095 "boost::filesystem::create_directories",
00096 ph, "path exists and is not a directory",
00097 not_directory_error ) );
00098 return false;
00099 }
00100
00101
00102 create_directories_win(ph.branch_path());
00103
00104 std::wstring wph(safe_convert(ph.native_directory_string()));
00105 CreateDirectory(wph.c_str(), 0);
00106 return true;
00107 }
00108
00109 bool exists_win( const path & ph )
00110 {
00111 std::wstring wpath(safe_convert(ph.string()));
00112 if(::GetFileAttributes( wpath.c_str() ) == 0xFFFFFFFF)
00113 {
00114 UINT err = ::GetLastError();
00115 if((err == ERROR_FILE_NOT_FOUND)
00116 || (err == ERROR_INVALID_PARAMETER)
00117 || (err == ERROR_NOT_READY)
00118 || (err == ERROR_PATH_NOT_FOUND)
00119 || (err == ERROR_INVALID_NAME)
00120 || (err == ERROR_BAD_NETPATH ))
00121 return false;
00122
00123
00124 return true;
00125 }
00126 return true;
00127 }
00128
00129 boost::intmax_t file_size_win( const path & ph )
00130 {
00131 std::wstring wpath(safe_convert(ph.string()));
00132
00133 WIN32_FILE_ATTRIBUTE_DATA fad;
00134 if ( !::GetFileAttributesExW( wpath.c_str(),
00135 ::GetFileExInfoStandard, &fad ) )
00136 boost::throw_exception( filesystem_error(
00137 "boost::filesystem::file_size",
00138 ph, detail::system_error_code() ) );
00139 if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 )
00140 boost::throw_exception( filesystem_error(
00141 "boost::filesystem::file_size",
00142 ph, "invalid: is a directory",
00143 is_directory_error ) );
00144 return (static_cast<boost::intmax_t>(fad.nFileSizeHigh)
00145 << (sizeof(fad.nFileSizeLow)*8))
00146 + fad.nFileSizeLow;
00147 }
00148
00149 std::time_t last_write_time_win( const path & ph )
00150 {
00151 struct _stat path_stat;
00152 std::wstring wph(safe_convert(ph.native_file_string()));
00153 if ( ::_wstat( wph.c_str(), &path_stat ) != 0 )
00154 boost::throw_exception( filesystem_error(
00155 "boost::filesystem::last_write_time",
00156 ph, detail::system_error_code() ) );
00157 return path_stat.st_mtime;
00158 }
00159
00160 void rename_win( const path & old_path,
00161 const path & new_path )
00162 {
00163 std::wstring wold_path(safe_convert(old_path.string()));
00164 std::wstring wnew_path(safe_convert(new_path.string()));
00165 if ( !::MoveFile( wold_path.c_str(), wnew_path.c_str() ) )
00166 boost::throw_exception( filesystem_error(
00167 "boost::filesystem::rename",
00168 old_path, new_path, detail::system_error_code() ) );
00169 }
00170
00171 }
00172
00173 #endif
00174
00175 #if BOOST_VERSION < 103200
00176 bool operator<(boost::filesystem::path const& lhs
00177 , boost::filesystem::path const& rhs)
00178 {
00179 return lhs.string() < rhs.string();
00180 }
00181 #endif
00182
00183 using namespace boost::filesystem;
00184 namespace pt = boost::posix_time;
00185 using boost::bind;
00186 using namespace ::boost::multi_index;
00187 using boost::multi_index::multi_index_container;
00188
00189 namespace
00190 {
00191 using namespace libtorrent;
00192
00193 void print_to_log(const std::string& s)
00194 {
00195 static std::ofstream log("log.txt");
00196 log << s;
00197 log.flush();
00198 }
00199
00200 }
00201
00202 namespace libtorrent
00203 {
00204
00205 std::vector<std::pair<size_type, std::time_t> > get_filesizes(
00206 torrent_info const& t, path p)
00207 {
00208 p = complete(p);
00209 std::vector<std::pair<size_type, std::time_t> > sizes;
00210 for (torrent_info::file_iterator i = t.begin_files();
00211 i != t.end_files(); ++i)
00212 {
00213 size_type size = 0;
00214 std::time_t time = 0;
00215 try
00216 {
00217 path f = p / i->path;
00218 #if defined(_WIN32) && defined(UNICODE)
00219 size = file_size_win(f);
00220 time = last_write_time_win(f);
00221 #else
00222 size = file_size(f);
00223 time = last_write_time(f);
00224 #endif
00225 }
00226 catch (std::exception&) {}
00227 sizes.push_back(std::make_pair(size, time));
00228 }
00229 return sizes;
00230 }
00231
00232 bool match_filesizes(
00233 torrent_info const& t
00234 , path p
00235 , std::vector<std::pair<size_type, std::time_t> > const& sizes
00236 , std::string* error)
00237 {
00238 if ((int)sizes.size() != t.num_files())
00239 {
00240 if (error) *error = "mismatching number of files";
00241 return false;
00242 }
00243 p = complete(p);
00244
00245 std::vector<std::pair<size_type, std::time_t> >::const_iterator s
00246 = sizes.begin();
00247 for (torrent_info::file_iterator i = t.begin_files();
00248 i != t.end_files(); ++i, ++s)
00249 {
00250 size_type size = 0;
00251 std::time_t time = 0;
00252 try
00253 {
00254 path f = p / i->path;
00255 #if defined(_WIN32) && defined(UNICODE)
00256 size = file_size_win(f);
00257 time = last_write_time_win(f);
00258 #else
00259 size = file_size(f);
00260 time = last_write_time(f);
00261 #endif
00262 }
00263 catch (std::exception&) {}
00264 if (size != s->first)
00265 {
00266 if (error) *error = "filesize mismatch for file '"
00267 + i->path.native_file_string()
00268 + "', expected to be " + boost::lexical_cast<std::string>(s->first)
00269 + " bytes";
00270 return false;
00271 }
00272 if (time != s->second)
00273 {
00274 if (error) *error = "timestamp mismatch for file '"
00275 + i->path.native_file_string()
00276 + "', expected to have modification date "
00277 + boost::lexical_cast<std::string>(s->second);
00278 return false;
00279 }
00280 }
00281 return true;
00282 }
00283
00284 struct thread_safe_storage
00285 {
00286 thread_safe_storage(std::size_t n)
00287 : slots(n, false)
00288 {}
00289
00290 boost::mutex mutex;
00291 boost::condition condition;
00292 std::vector<bool> slots;
00293 };
00294
00295 struct slot_lock
00296 {
00297 slot_lock(thread_safe_storage& s, int slot_)
00298 : storage_(s)
00299 , slot(slot_)
00300 {
00301 assert(slot_>=0 && slot_ < (int)s.slots.size());
00302 boost::mutex::scoped_lock lock(storage_.mutex);
00303
00304 while (storage_.slots[slot])
00305 storage_.condition.wait(lock);
00306 storage_.slots[slot] = true;
00307 }
00308
00309 ~slot_lock()
00310 {
00311 storage_.slots[slot] = false;
00312 storage_.condition.notify_all();
00313 }
00314
00315 thread_safe_storage& storage_;
00316 int slot;
00317 };
00318
00319 class storage::impl : public thread_safe_storage, boost::noncopyable
00320 {
00321 public:
00322 impl(torrent_info const& info, path const& path, file_pool& fp)
00323 : thread_safe_storage(info.num_pieces())
00324 , info(info)
00325 , files(fp)
00326 {
00327 save_path = complete(path);
00328 assert(save_path.is_complete());
00329 }
00330
00331 impl(impl const& x)
00332 : thread_safe_storage(x.info.num_pieces())
00333 , info(x.info)
00334 , save_path(x.save_path)
00335 , files(x.files)
00336 {}
00337
00338 ~impl()
00339 {
00340 files.release(this);
00341 }
00342
00343 torrent_info const& info;
00344 path save_path;
00345
00346
00347
00348 file_pool& files;
00349 };
00350
00351 storage::storage(torrent_info const& info, path const& path
00352 , file_pool& fp)
00353 : m_pimpl(new impl(info, path, fp))
00354 {
00355 assert(info.begin_files() != info.end_files());
00356 }
00357
00358 void storage::release_files()
00359 {
00360 m_pimpl->files.release(m_pimpl.get());
00361 }
00362
00363 void storage::swap(storage& other)
00364 {
00365 m_pimpl.swap(other.m_pimpl);
00366 }
00367
00368
00369 bool storage::move_storage(path save_path)
00370 {
00371 path old_path;
00372 path new_path;
00373
00374 save_path = complete(save_path);
00375
00376 #if defined(_WIN32) && defined(UNICODE)
00377 std::wstring wsave_path(safe_convert(save_path.native_file_string()));
00378 if (!exists_win(save_path))
00379 {
00380 CreateDirectory(wsave_path.c_str(), 0);
00381 }
00382 else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0)
00383 {
00384 return false;
00385 }
00386 #else
00387 if(!exists(save_path))
00388 create_directory(save_path);
00389 else if(!is_directory(save_path))
00390 return false;
00391 #endif
00392
00393 m_pimpl->files.release(m_pimpl.get());
00394
00395 old_path = m_pimpl->save_path / m_pimpl->info.name();
00396 new_path = save_path / m_pimpl->info.name();
00397
00398 try
00399 {
00400 #if defined(_WIN32) && defined(UNICODE)
00401 rename_win(old_path, new_path);
00402 #else
00403 rename(old_path, new_path);
00404 #endif
00405 m_pimpl->save_path = save_path;
00406 return true;
00407 }
00408 catch (std::exception&) {}
00409 return false;
00410 }
00411
00412 #ifndef NDEBUG
00413
00414 void storage::shuffle()
00415 {
00416 int num_pieces = m_pimpl->info.num_pieces();
00417
00418 std::vector<int> pieces(num_pieces);
00419 for (std::vector<int>::iterator i = pieces.begin();
00420 i != pieces.end();
00421 ++i)
00422 {
00423 *i = static_cast<int>(i - pieces.begin());
00424 }
00425 std::srand((unsigned int)std::time(0));
00426 std::vector<int> targets(pieces);
00427 std::random_shuffle(pieces.begin(), pieces.end());
00428 std::random_shuffle(targets.begin(), targets.end());
00429
00430 for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
00431 {
00432 const int slot_index = targets[i];
00433 const int piece_index = pieces[i];
00434 const int slot_size =static_cast<int>(m_pimpl->info.piece_size(slot_index));
00435 std::vector<char> buf(slot_size);
00436 read(&buf[0], piece_index, 0, slot_size);
00437 write(&buf[0], slot_index, 0, slot_size);
00438 }
00439 }
00440
00441 #endif
00442
00443 size_type storage::read(
00444 char* buf
00445 , int slot
00446 , int offset
00447 , int size)
00448 {
00449 assert(buf != 0);
00450 assert(slot >= 0 && slot < m_pimpl->info.num_pieces());
00451 assert(offset >= 0);
00452 assert(offset < m_pimpl->info.piece_size(slot));
00453 assert(size > 0);
00454
00455 slot_lock lock(*m_pimpl, slot);
00456
00457 #ifndef NDEBUG
00458 std::vector<file_slice> slices
00459 = m_pimpl->info.map_block(slot, offset, size);
00460 assert(!slices.empty());
00461 #endif
00462
00463 size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset;
00464 assert(start + size <= m_pimpl->info.total_size());
00465
00466
00467 size_type file_offset = start;
00468 std::vector<file_entry>::const_iterator file_iter;
00469
00470 for (file_iter = m_pimpl->info.begin_files();;)
00471 {
00472 if (file_offset < file_iter->size)
00473 break;
00474
00475 file_offset -= file_iter->size;
00476 ++file_iter;
00477 }
00478
00479 boost::shared_ptr<file> in(m_pimpl->files.open_file(
00480 m_pimpl.get()
00481 , m_pimpl->save_path / file_iter->path
00482 , file::in));
00483
00484 assert(file_offset < file_iter->size);
00485
00486 assert(slices[0].offset == file_offset);
00487
00488 size_type new_pos = in->seek(file_offset);
00489 if (new_pos != file_offset)
00490 {
00491
00492 throw file_error("slot has no storage");
00493 }
00494
00495 #ifndef NDEBUG
00496 size_type in_tell = in->tell();
00497 assert(in_tell == file_offset);
00498 #endif
00499
00500 int left_to_read = size;
00501 int slot_size = static_cast<int>(m_pimpl->info.piece_size(slot));
00502
00503 if (offset + left_to_read > slot_size)
00504 left_to_read = slot_size - offset;
00505
00506 assert(left_to_read >= 0);
00507
00508 size_type result = left_to_read;
00509 int buf_pos = 0;
00510
00511 #ifndef NDEBUG
00512 int counter = 0;
00513 #endif
00514
00515 while (left_to_read > 0)
00516 {
00517 int read_bytes = left_to_read;
00518 if (file_offset + read_bytes > file_iter->size)
00519 read_bytes = static_cast<int>(file_iter->size - file_offset);
00520
00521 if (read_bytes > 0)
00522 {
00523 #ifndef NDEBUG
00524 assert(int(slices.size()) > counter);
00525 size_type slice_size = slices[counter].size;
00526 assert(slice_size == read_bytes);
00527 assert(m_pimpl->info.file_at(slices[counter].file_index).path
00528 == file_iter->path);
00529 #endif
00530
00531 size_type actual_read = in->read(buf + buf_pos, read_bytes);
00532
00533 if (read_bytes != actual_read)
00534 {
00535
00536 throw file_error("slot has no storage");
00537 }
00538
00539 left_to_read -= read_bytes;
00540 buf_pos += read_bytes;
00541 assert(buf_pos >= 0);
00542 file_offset += read_bytes;
00543 }
00544
00545 if (left_to_read > 0)
00546 {
00547 ++file_iter;
00548 #ifndef NDEBUG
00549
00550
00551 if (read_bytes > 0) ++counter;
00552 #endif
00553 path path = m_pimpl->save_path / file_iter->path;
00554
00555 file_offset = 0;
00556 in = m_pimpl->files.open_file(
00557 m_pimpl.get()
00558 , path, file::in);
00559 in->seek(0);
00560 }
00561 }
00562
00563 return result;
00564 }
00565
00566
00567 void storage::write(
00568 const char* buf
00569 , int slot
00570 , int offset
00571 , int size)
00572 {
00573 assert(buf != 0);
00574 assert(slot >= 0);
00575 assert(slot < m_pimpl->info.num_pieces());
00576 assert(offset >= 0);
00577 assert(size > 0);
00578
00579 slot_lock lock(*m_pimpl, slot);
00580
00581 #ifndef NDEBUG
00582 std::vector<file_slice> slices
00583 = m_pimpl->info.map_block(slot, offset, size);
00584 assert(!slices.empty());
00585 #endif
00586
00587 size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset;
00588
00589
00590 size_type file_offset = start;
00591 std::vector<file_entry>::const_iterator file_iter;
00592
00593 for (file_iter = m_pimpl->info.begin_files();;)
00594 {
00595 if (file_offset < file_iter->size)
00596 break;
00597
00598 file_offset -= file_iter->size;
00599 ++file_iter;
00600 assert(file_iter != m_pimpl->info.end_files());
00601 }
00602
00603 path p(m_pimpl->save_path / file_iter->path);
00604 boost::shared_ptr<file> out = m_pimpl->files.open_file(
00605 m_pimpl.get()
00606 , p, file::out | file::in);
00607
00608 assert(file_offset < file_iter->size);
00609 assert(slices[0].offset == file_offset);
00610
00611 size_type pos = out->seek(file_offset);
00612
00613 if (pos != file_offset)
00614 {
00615 std::stringstream s;
00616 s << "no storage for slot " << slot;
00617 throw file_error(s.str());
00618 }
00619
00620 int left_to_write = size;
00621 int slot_size = static_cast<int>(m_pimpl->info.piece_size(slot));
00622
00623 if (offset + left_to_write > slot_size)
00624 left_to_write = slot_size - offset;
00625
00626 assert(left_to_write >= 0);
00627
00628 int buf_pos = 0;
00629 #ifndef NDEBUG
00630 int counter = 0;
00631 #endif
00632 while (left_to_write > 0)
00633 {
00634 int write_bytes = left_to_write;
00635 if (file_offset + write_bytes > file_iter->size)
00636 {
00637 assert(file_iter->size >= file_offset);
00638 write_bytes = static_cast<int>(file_iter->size - file_offset);
00639 }
00640
00641 if (write_bytes > 0)
00642 {
00643 assert(int(slices.size()) > counter);
00644 assert(slices[counter].size == write_bytes);
00645 assert(m_pimpl->info.file_at(slices[counter].file_index).path
00646 == file_iter->path);
00647
00648 assert(buf_pos >= 0);
00649 assert(write_bytes >= 0);
00650 size_type written = out->write(buf + buf_pos, write_bytes);
00651
00652 if (written != write_bytes)
00653 {
00654 std::stringstream s;
00655 s << "no storage for slot " << slot;
00656 throw file_error(s.str());
00657 }
00658
00659 left_to_write -= write_bytes;
00660 buf_pos += write_bytes;
00661 assert(buf_pos >= 0);
00662 file_offset += write_bytes;
00663 assert(file_offset <= file_iter->size);
00664 }
00665
00666 if (left_to_write > 0)
00667 {
00668 #ifndef NDEBUG
00669 if (write_bytes > 0) ++counter;
00670 #endif
00671 ++file_iter;
00672
00673 assert(file_iter != m_pimpl->info.end_files());
00674 path p = m_pimpl->save_path / file_iter->path;
00675 file_offset = 0;
00676 out = m_pimpl->files.open_file(
00677 m_pimpl.get()
00678 , p, file::out | file::in);
00679
00680 out->seek(0);
00681 }
00682 }
00683 }
00684
00685
00686
00687 class piece_manager::impl
00688 {
00689 friend class invariant_access;
00690 public:
00691
00692 impl(
00693 torrent_info const& info
00694 , path const& path
00695 , file_pool& fp);
00696
00697 bool check_fastresume(
00698 aux::piece_checker_data& d
00699 , std::vector<bool>& pieces
00700 , int& num_pieces
00701 , bool compact_mode);
00702
00703 std::pair<bool, float> check_files(
00704 std::vector<bool>& pieces
00705 , int& num_pieces, boost::recursive_mutex& mutex);
00706
00707 void release_files();
00708
00709 void allocate_slots(int num_slots);
00710 void mark_failed(int index);
00711 unsigned long piece_crc(
00712 int slot_index
00713 , int block_size
00714 , const std::bitset<256>& bitmask);
00715
00716 int slot_for_piece(int piece_index) const;
00717
00718 size_type read(
00719 char* buf
00720 , int piece_index
00721 , int offset
00722 , int size);
00723
00724 void write(
00725 const char* buf
00726 , int piece_index
00727 , int offset
00728 , int size);
00729
00730 path const& save_path() const
00731 { return m_save_path; }
00732
00733 bool move_storage(path save_path)
00734 {
00735 if (m_storage.move_storage(save_path))
00736 {
00737 m_save_path = complete(save_path);
00738 return true;
00739 }
00740 return false;
00741 }
00742
00743 void export_piece_map(std::vector<int>& p) const;
00744
00745
00746
00747
00748 int identify_data(
00749 const std::vector<char>& piece_data
00750 , int current_slot
00751 , std::vector<bool>& have_pieces
00752 , int& num_pieces
00753 , const std::multimap<sha1_hash, int>& hash_to_piece
00754 , boost::recursive_mutex& mutex);
00755
00756 int allocate_slot_for_piece(int piece_index);
00757 #ifndef NDEBUG
00758 void check_invariant() const;
00759 #ifdef TORRENT_STORAGE_DEBUG
00760 void debug_log() const;
00761 #endif
00762 #endif
00763 storage m_storage;
00764
00765
00766
00767
00768 bool m_compact_mode;
00769
00770
00771
00772
00773
00774 bool m_fill_mode;
00775
00776
00777 std::vector<bool> m_have_piece;
00778
00779 torrent_info const& m_info;
00780
00781
00782 std::vector<int> m_unallocated_slots;
00783
00784 std::vector<int> m_free_slots;
00785
00786 enum
00787 {
00788 has_no_slot = -3
00789 };
00790
00791
00792
00793 std::vector<int> m_piece_to_slot;
00794
00795 enum
00796 {
00797 unallocated = -1,
00798 unassigned = -2
00799 };
00800
00801
00802
00803 std::vector<int> m_slot_to_piece;
00804
00805 path m_save_path;
00806
00807 mutable boost::recursive_mutex m_mutex;
00808
00809 bool m_allocating;
00810 boost::mutex m_allocating_monitor;
00811 boost::condition m_allocating_condition;
00812
00813
00814
00815 enum {
00816
00817 state_none,
00818
00819 state_finished,
00820
00821 state_create_files,
00822
00823 state_full_check,
00824
00825 state_allocating
00826 } m_state;
00827 int m_current_slot;
00828
00829 std::vector<char> m_piece_data;
00830
00831
00832
00833
00834 std::multimap<sha1_hash, int> m_hash_to_piece;
00835
00836
00837
00838
00839
00840 std::vector<char> m_scratch_buffer;
00841 };
00842
00843 piece_manager::impl::impl(
00844 torrent_info const& info
00845 , path const& save_path
00846 , file_pool& fp)
00847 : m_storage(info, save_path, fp)
00848 , m_compact_mode(false)
00849 , m_fill_mode(true)
00850 , m_info(info)
00851 , m_save_path(complete(save_path))
00852 , m_allocating(false)
00853 {
00854 assert(m_save_path.is_complete());
00855 }
00856
00857 piece_manager::piece_manager(
00858 torrent_info const& info
00859 , path const& save_path
00860 , file_pool& fp)
00861 : m_pimpl(new impl(info, save_path, fp))
00862 {
00863 }
00864
00865 piece_manager::~piece_manager()
00866 {
00867 }
00868
00869 void piece_manager::release_files()
00870 {
00871 m_pimpl->release_files();
00872 }
00873
00874 void piece_manager::impl::release_files()
00875 {
00876 m_storage.release_files();
00877 }
00878
00879 void piece_manager::impl::export_piece_map(
00880 std::vector<int>& p) const
00881 {
00882
00883 boost::recursive_mutex::scoped_lock lock(m_mutex);
00884
00885
00886 INVARIANT_CHECK;
00887
00888 p.clear();
00889 std::vector<int>::const_reverse_iterator last;
00890 for (last = m_slot_to_piece.rbegin();
00891 last != m_slot_to_piece.rend(); ++last)
00892 {
00893 if (*last != unallocated) break;
00894 }
00895
00896 for (std::vector<int>::const_iterator i =
00897 m_slot_to_piece.begin();
00898 i != last.base(); ++i)
00899 {
00900 p.push_back(*i);
00901 }
00902 }
00903
00904 void piece_manager::export_piece_map(
00905 std::vector<int>& p) const
00906 {
00907 m_pimpl->export_piece_map(p);
00908 }
00909
00910 void piece_manager::impl::mark_failed(int piece_index)
00911 {
00912
00913 boost::recursive_mutex::scoped_lock lock(m_mutex);
00914
00915
00916 INVARIANT_CHECK;
00917
00918 assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
00919 assert(m_piece_to_slot[piece_index] >= 0);
00920
00921 int slot_index = m_piece_to_slot[piece_index];
00922
00923 assert(slot_index >= 0);
00924
00925 m_slot_to_piece[slot_index] = unassigned;
00926 m_piece_to_slot[piece_index] = has_no_slot;
00927 m_free_slots.push_back(slot_index);
00928 }
00929
00930 void piece_manager::mark_failed(int index)
00931 {
00932 m_pimpl->mark_failed(index);
00933 }
00934
00935 bool piece_manager::is_allocating() const
00936 {
00937 return m_pimpl->m_state
00938 == impl::state_allocating;
00939 }
00940
00941 int piece_manager::slot_for_piece(int piece_index) const
00942 {
00943 return m_pimpl->slot_for_piece(piece_index);
00944 }
00945
00946 int piece_manager::impl::slot_for_piece(int piece_index) const
00947 {
00948 assert(piece_index >= 0 && piece_index < m_info.num_pieces());
00949 return m_piece_to_slot[piece_index];
00950 }
00951
00952 unsigned long piece_manager::piece_crc(
00953 int index
00954 , int block_size
00955 , const std::bitset<256>& bitmask)
00956 {
00957 return m_pimpl->piece_crc(index, block_size, bitmask);
00958 }
00959
00960 unsigned long piece_manager::impl::piece_crc(
00961 int slot_index
00962 , int block_size
00963 , const std::bitset<256>& bitmask)
00964 {
00965 assert(slot_index >= 0);
00966 assert(slot_index < m_info.num_pieces());
00967 assert(block_size > 0);
00968
00969 adler32_crc crc;
00970 std::vector<char> buf(block_size);
00971 int num_blocks = static_cast<int>(m_info.piece_size(slot_index)) / block_size;
00972 int last_block_size = static_cast<int>(m_info.piece_size(slot_index)) % block_size;
00973 if (last_block_size == 0) last_block_size = block_size;
00974
00975 for (int i = 0; i < num_blocks-1; ++i)
00976 {
00977 if (!bitmask[i]) continue;
00978 m_storage.read(
00979 &buf[0]
00980 , slot_index
00981 , i * block_size
00982 , block_size);
00983 crc.update(&buf[0], block_size);
00984 }
00985 if (bitmask[num_blocks - 1])
00986 {
00987 m_storage.read(
00988 &buf[0]
00989 , slot_index
00990 , block_size * (num_blocks - 1)
00991 , last_block_size);
00992 crc.update(&buf[0], last_block_size);
00993 }
00994 return crc.final();
00995 }
00996
00997 size_type piece_manager::impl::read(
00998 char* buf
00999 , int piece_index
01000 , int offset
01001 , int size)
01002 {
01003 assert(buf);
01004 assert(offset >= 0);
01005 assert(size > 0);
01006 assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
01007 assert(m_piece_to_slot[piece_index] >= 0 && m_piece_to_slot[piece_index] < (int)m_slot_to_piece.size());
01008 int slot = m_piece_to_slot[piece_index];
01009 assert(slot >= 0 && slot < (int)m_slot_to_piece.size());
01010 return m_storage.read(buf, slot, offset, size);
01011 }
01012
01013 size_type piece_manager::read(
01014 char* buf
01015 , int piece_index
01016 , int offset
01017 , int size)
01018 {
01019 return m_pimpl->read(buf, piece_index, offset, size);
01020 }
01021
01022 void piece_manager::impl::write(
01023 const char* buf
01024 , int piece_index
01025 , int offset
01026 , int size)
01027 {
01028 assert(buf);
01029 assert(offset >= 0);
01030 assert(size > 0);
01031 assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
01032 int slot = allocate_slot_for_piece(piece_index);
01033 assert(slot >= 0 && slot < (int)m_slot_to_piece.size());
01034 m_storage.write(buf, slot, offset, size);
01035 }
01036
01037 void piece_manager::write(
01038 const char* buf
01039 , int piece_index
01040 , int offset
01041 , int size)
01042 {
01043 m_pimpl->write(buf, piece_index, offset, size);
01044 }
01045
01046 int piece_manager::impl::identify_data(
01047 const std::vector<char>& piece_data
01048 , int current_slot
01049 , std::vector<bool>& have_pieces
01050 , int& num_pieces
01051 , const std::multimap<sha1_hash, int>& hash_to_piece
01052 , boost::recursive_mutex& mutex)
01053 {
01054
01055 assert((int)have_pieces.size() == m_info.num_pieces());
01056
01057 const int piece_size = static_cast<int>(m_info.piece_length());
01058 const int last_piece_size = static_cast<int>(m_info.piece_size(
01059 m_info.num_pieces() - 1));
01060
01061 assert((int)piece_data.size() >= last_piece_size);
01062
01063
01064
01065
01066 hasher small_digest;
01067 small_digest.update(&piece_data[0], last_piece_size);
01068 hasher large_digest(small_digest);
01069 assert(piece_size - last_piece_size >= 0);
01070 if (piece_size - last_piece_size > 0)
01071 {
01072 large_digest.update(
01073 &piece_data[last_piece_size]
01074 , piece_size - last_piece_size);
01075 }
01076 sha1_hash large_hash = large_digest.final();
01077 sha1_hash small_hash = small_digest.final();
01078
01079 typedef std::multimap<sha1_hash, int>::const_iterator map_iter;
01080 map_iter begin1;
01081 map_iter end1;
01082 map_iter begin2;
01083 map_iter end2;
01084
01085
01086 boost::tie(begin1, end1) = hash_to_piece.equal_range(small_hash);
01087 boost::tie(begin2, end2) = hash_to_piece.equal_range(large_hash);
01088
01089
01090 std::vector<int> matching_pieces;
01091 for (map_iter i = begin1; i != end1; ++i)
01092 matching_pieces.push_back(i->second);
01093 for (map_iter i = begin2; i != end2; ++i)
01094 matching_pieces.push_back(i->second);
01095
01096
01097 if (matching_pieces.empty())
01098 return unassigned;
01099
01100
01101
01102
01103
01104 if (std::find(
01105 matching_pieces.begin()
01106 , matching_pieces.end()
01107 , current_slot) != matching_pieces.end())
01108 {
01109 const int piece_index = current_slot;
01110
01111
01112 boost::recursive_mutex::scoped_lock l(mutex);
01113
01114 if (have_pieces[piece_index])
01115 {
01116
01117
01118 int other_slot = m_piece_to_slot[piece_index];
01119 assert(other_slot >= 0);
01120
01121
01122
01123 int other_piece = -1;
01124 for (std::vector<int>::iterator i = matching_pieces.begin();
01125 i != matching_pieces.end(); ++i)
01126 {
01127 if (have_pieces[*i] || *i == piece_index) continue;
01128 other_piece = *i;
01129 break;
01130 }
01131 if (other_piece >= 0)
01132 {
01133
01134 assert(have_pieces[other_piece] == false);
01135 have_pieces[other_piece] = true;
01136 m_slot_to_piece[other_slot] = other_piece;
01137 m_piece_to_slot[other_piece] = other_slot;
01138 ++num_pieces;
01139 }
01140 else
01141 {
01142
01143
01144
01145
01146
01147 m_slot_to_piece[other_slot] = unassigned;
01148 m_free_slots.push_back(other_slot);
01149 }
01150 assert(m_piece_to_slot[piece_index] != current_slot);
01151 assert(m_piece_to_slot[piece_index] >= 0);
01152 m_piece_to_slot[piece_index] = has_no_slot;
01153 #ifndef NDEBUG
01154
01155 have_pieces[piece_index] = false;
01156 #endif
01157 }
01158 else
01159 {
01160 ++num_pieces;
01161 }
01162
01163 assert(have_pieces[piece_index] == false);
01164 assert(m_piece_to_slot[piece_index] == has_no_slot);
01165 have_pieces[piece_index] = true;
01166
01167 return piece_index;
01168 }
01169
01170
01171
01172 int free_piece = unassigned;
01173 for (std::vector<int>::iterator i = matching_pieces.begin();
01174 i != matching_pieces.end(); ++i)
01175 {
01176 if (have_pieces[*i]) continue;
01177 free_piece = *i;
01178 break;
01179 }
01180
01181 if (free_piece >= 0)
01182 {
01183
01184 boost::recursive_mutex::scoped_lock l(mutex);
01185
01186 assert(have_pieces[free_piece] == false);
01187 assert(m_piece_to_slot[free_piece] == has_no_slot);
01188 have_pieces[free_piece] = true;
01189 ++num_pieces;
01190
01191 return free_piece;
01192 }
01193 else
01194 {
01195 assert(free_piece == unassigned);
01196 return unassigned;
01197 }
01198 }
01199
01200
01201
01202
01203
01204 bool piece_manager::impl::check_fastresume(
01205 aux::piece_checker_data& data
01206 , std::vector<bool>& pieces
01207 , int& num_pieces, bool compact_mode)
01208 {
01209 assert(m_info.piece_length() > 0);
01210
01211 boost::recursive_mutex::scoped_lock lock(m_mutex);
01212
01213
01214 INVARIANT_CHECK;
01215
01216 m_compact_mode = compact_mode;
01217
01218
01219
01220
01221
01222
01223 m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot);
01224 m_slot_to_piece.resize(m_info.num_pieces(), unallocated);
01225 m_free_slots.clear();
01226 m_unallocated_slots.clear();
01227
01228 pieces.clear();
01229 pieces.resize(m_info.num_pieces(), false);
01230 num_pieces = 0;
01231
01232
01233
01234 if (!data.piece_map.empty()
01235 && data.piece_map.size() <= m_slot_to_piece.size())
01236 {
01237 for (int i = 0; i < (int)data.piece_map.size(); ++i)
01238 {
01239 m_slot_to_piece[i] = data.piece_map[i];
01240 if (data.piece_map[i] >= 0)
01241 {
01242 m_piece_to_slot[data.piece_map[i]] = i;
01243 int found_piece = data.piece_map[i];
01244
01245
01246
01247 if (std::find_if(
01248 data.unfinished_pieces.begin()
01249 , data.unfinished_pieces.end()
01250 , piece_picker::has_index(found_piece))
01251 == data.unfinished_pieces.end())
01252 {
01253 ++num_pieces;
01254 pieces[found_piece] = true;
01255 }
01256 }
01257 else if (data.piece_map[i] == unassigned)
01258 {
01259 m_free_slots.push_back(i);
01260 }
01261 else
01262 {
01263 assert(data.piece_map[i] == unallocated);
01264 m_unallocated_slots.push_back(i);
01265 }
01266 }
01267
01268 m_unallocated_slots.reserve(int(pieces.size() - data.piece_map.size()));
01269 for (int i = (int)data.piece_map.size(); i < (int)pieces.size(); ++i)
01270 {
01271 m_unallocated_slots.push_back(i);
01272 }
01273
01274 if (!m_compact_mode && !m_unallocated_slots.empty())
01275 {
01276 m_state = state_allocating;
01277 return false;
01278 }
01279 else
01280 {
01281 m_state = state_finished;
01282 return true;
01283 }
01284 }
01285
01286 m_state = state_create_files;
01287 return false;
01288 }
01289
01290
01291
01292
01293
01294
01295
01296 std::pair<bool, float> piece_manager::impl::check_files(
01297 std::vector<bool>& pieces, int& num_pieces, boost::recursive_mutex& mutex)
01298 {
01299 assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
01300
01301 if (m_state == state_allocating)
01302 {
01303 if (m_compact_mode)
01304 {
01305 m_state = state_finished;
01306 return std::make_pair(true, 1.f);
01307 }
01308
01309 if (m_unallocated_slots.empty())
01310 {
01311 m_state = state_finished;
01312 return std::make_pair(true, 1.f);
01313 }
01314
01315
01316
01317
01318 assert(!m_unallocated_slots.empty());
01319 allocate_slots(1);
01320
01321 return std::make_pair(false, 1.f - (float)m_unallocated_slots.size()
01322 / (float)m_slot_to_piece.size());
01323 }
01324
01325 if (m_state == state_create_files)
01326 {
01327
01328 path last_path;
01329 for (torrent_info::file_iterator file_iter = m_info.begin_files(),
01330 end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter)
01331 {
01332 path dir = (m_save_path / file_iter->path).branch_path();
01333
01334
01335
01336 if (dir == last_path
01337 && file_iter->size == 0)
01338 file(m_save_path / file_iter->path, file::out);
01339
01340 if (dir == last_path) continue;
01341 last_path = dir;
01342
01343 #if defined(_WIN32) && defined(UNICODE)
01344 if (!exists_win(last_path))
01345 create_directories_win(last_path);
01346 #else
01347 if (!exists(last_path))
01348 create_directories(last_path);
01349 #endif
01350
01351 if (file_iter->size == 0)
01352 file(m_save_path / file_iter->path, file::out);
01353 }
01354 m_current_slot = 0;
01355 m_state = state_full_check;
01356 m_piece_data.resize(int(m_info.piece_length()));
01357 return std::make_pair(false, 0.f);
01358 }
01359
01360 assert(m_state == state_full_check);
01361
01362
01363
01364
01365
01366 try
01367 {
01368
01369 m_storage.read(
01370 &m_piece_data[0]
01371 , m_current_slot
01372 , 0
01373 , int(m_info.piece_size(m_current_slot)));
01374
01375 if (m_hash_to_piece.empty())
01376 {
01377 for (int i = 0; i < m_info.num_pieces(); ++i)
01378 {
01379 m_hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i));
01380 }
01381 }
01382
01383 int piece_index = identify_data(
01384 m_piece_data
01385 , m_current_slot
01386 , pieces
01387 , num_pieces
01388 , m_hash_to_piece
01389 , mutex);
01390
01391 assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
01392 assert(piece_index == unassigned || piece_index >= 0);
01393
01394 const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
01395 const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot;
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424 if (this_should_move && !other_should_move)
01425 {
01426 assert(piece_index != m_current_slot);
01427
01428 const int other_slot = piece_index;
01429 assert(other_slot >= 0);
01430 int other_piece = m_slot_to_piece[other_slot];
01431
01432 m_slot_to_piece[other_slot] = piece_index;
01433 m_slot_to_piece[m_current_slot] = other_piece;
01434 m_piece_to_slot[piece_index] = piece_index;
01435 if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot;
01436
01437 if (other_piece == unassigned)
01438 {
01439 std::vector<int>::iterator i =
01440 std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
01441 assert(i != m_free_slots.end());
01442 m_free_slots.erase(i);
01443 m_free_slots.push_back(m_current_slot);
01444 }
01445
01446 const int slot1_size = static_cast<int>(m_info.piece_size(piece_index));
01447 const int slot2_size = other_piece >= 0 ? static_cast<int>(m_info.piece_size(other_piece)) : 0;
01448 std::vector<char> buf1(slot1_size);
01449 m_storage.read(&buf1[0], m_current_slot, 0, slot1_size);
01450 if (slot2_size > 0)
01451 {
01452 std::vector<char> buf2(slot2_size);
01453 m_storage.read(&buf2[0], piece_index, 0, slot2_size);
01454 m_storage.write(&buf2[0], m_current_slot, 0, slot2_size);
01455 }
01456 m_storage.write(&buf1[0], piece_index, 0, slot1_size);
01457 assert(m_slot_to_piece[m_current_slot] == unassigned
01458 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
01459 }
01460
01461 else if (!this_should_move && other_should_move)
01462 {
01463 assert(piece_index != m_current_slot);
01464
01465 const int other_piece = m_current_slot;
01466 const int other_slot = m_piece_to_slot[other_piece];
01467 assert(other_slot >= 0);
01468
01469 m_slot_to_piece[m_current_slot] = other_piece;
01470 m_slot_to_piece[other_slot] = piece_index;
01471 m_piece_to_slot[other_piece] = m_current_slot;
01472 if (piece_index >= 0) m_piece_to_slot[piece_index] = other_slot;
01473
01474 if (piece_index == unassigned)
01475 {
01476 m_free_slots.push_back(other_slot);
01477 }
01478
01479 const int slot1_size = static_cast<int>(m_info.piece_size(other_piece));
01480 const int slot2_size = piece_index >= 0 ? static_cast<int>(m_info.piece_size(piece_index)) : 0;
01481 std::vector<char> buf1(slot1_size);
01482 m_storage.read(&buf1[0], other_slot, 0, slot1_size);
01483 if (slot2_size > 0)
01484 {
01485 std::vector<char> buf2(slot2_size);
01486 m_storage.read(&buf2[0], m_current_slot, 0, slot2_size);
01487 m_storage.write(&buf2[0], other_slot, 0, slot2_size);
01488 }
01489 m_storage.write(&buf1[0], m_current_slot, 0, slot1_size);
01490 assert(m_slot_to_piece[m_current_slot] == unassigned
01491 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
01492 }
01493 else if (this_should_move && other_should_move)
01494 {
01495 assert(piece_index != m_current_slot);
01496 assert(piece_index >= 0);
01497
01498 const int piece1 = m_slot_to_piece[piece_index];
01499 const int piece2 = m_current_slot;
01500 const int slot1 = piece_index;
01501 const int slot2 = m_piece_to_slot[piece2];
01502
01503 assert(slot1 >= 0);
01504 assert(slot2 >= 0);
01505 assert(piece2 >= 0);
01506
01507 if (slot1 == slot2)
01508 {
01509
01510 assert(piece1 >= 0);
01511
01512
01513
01514
01515
01516
01517 m_slot_to_piece[slot1] = piece_index;
01518 m_slot_to_piece[m_current_slot] = piece1;
01519
01520 m_piece_to_slot[piece_index] = slot1;
01521 m_piece_to_slot[piece1] = m_current_slot;
01522
01523 assert(piece1 == m_current_slot);
01524 assert(piece_index == slot1);
01525
01526 const int slot1_size = static_cast<int>(m_info.piece_size(piece1));
01527 const int slot3_size = static_cast<int>(m_info.piece_size(piece_index));
01528 std::vector<char> buf1(static_cast<int>(slot1_size));
01529 std::vector<char> buf2(static_cast<int>(slot3_size));
01530
01531 m_storage.read(&buf2[0], m_current_slot, 0, slot3_size);
01532 m_storage.read(&buf1[0], slot1, 0, slot1_size);
01533 m_storage.write(&buf1[0], m_current_slot, 0, slot1_size);
01534 m_storage.write(&buf2[0], slot1, 0, slot3_size);
01535
01536 assert(m_slot_to_piece[m_current_slot] == unassigned
01537 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
01538 }
01539 else
01540 {
01541 assert(slot1 != slot2);
01542 assert(piece1 != piece2);
01543
01544
01545
01546
01547
01548
01549 m_slot_to_piece[slot1] = piece_index;
01550 m_slot_to_piece[slot2] = piece1;
01551 m_slot_to_piece[m_current_slot] = piece2;
01552
01553 m_piece_to_slot[piece_index] = slot1;
01554 m_piece_to_slot[m_current_slot] = piece2;
01555 if (piece1 >= 0) m_piece_to_slot[piece1] = slot2;
01556
01557 if (piece1 == unassigned)
01558 {
01559 std::vector<int>::iterator i =
01560 std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
01561 assert(i != m_free_slots.end());
01562 m_free_slots.erase(i);
01563 m_free_slots.push_back(slot2);
01564 }
01565
01566 const int slot1_size = piece1 >= 0 ? static_cast<int>(m_info.piece_size(piece1)) : 0;
01567 const int slot2_size = static_cast<int>(m_info.piece_size(piece2));
01568 const int slot3_size = static_cast<int>(m_info.piece_size(piece_index));
01569
01570 std::vector<char> buf1(static_cast<int>(m_info.piece_length()));
01571 std::vector<char> buf2(static_cast<int>(m_info.piece_length()));
01572
01573 m_storage.read(&buf2[0], m_current_slot, 0, slot3_size);
01574 m_storage.read(&buf1[0], slot2, 0, slot2_size);
01575 m_storage.write(&buf1[0], m_current_slot, 0, slot2_size);
01576 if (slot1_size > 0)
01577 {
01578 m_storage.read(&buf1[0], slot1, 0, slot1_size);
01579 m_storage.write(&buf1[0], slot2, 0, slot1_size);
01580 }
01581 m_storage.write(&buf2[0], slot1, 0, slot3_size);
01582 assert(m_slot_to_piece[m_current_slot] == unassigned
01583 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
01584 }
01585 }
01586 else
01587 {
01588 assert(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot);
01589 assert(m_slot_to_piece[m_current_slot] == unallocated);
01590 assert(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot);
01591
01592
01593 if (piece_index != unassigned)
01594 m_piece_to_slot[piece_index] = m_current_slot;
01595 else
01596 m_free_slots.push_back(m_current_slot);
01597
01598 m_slot_to_piece[m_current_slot] = piece_index;
01599
01600 assert(m_slot_to_piece[m_current_slot] == unassigned
01601 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
01602 }
01603 }
01604 catch (file_error&)
01605 {
01606
01607 size_type file_offset = 0;
01608 size_type current_offset = m_current_slot * m_info.piece_length();
01609 for (torrent_info::file_iterator i = m_info.begin_files();
01610 i != m_info.end_files(); ++i)
01611 {
01612 file_offset += i->size;
01613 if (file_offset > current_offset) break;
01614 }
01615
01616 assert(file_offset > current_offset);
01617 int skip_blocks = static_cast<int>(
01618 (file_offset - current_offset + m_info.piece_length() - 1)
01619 / m_info.piece_length());
01620
01621 for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
01622 {
01623 assert(m_slot_to_piece[i] == unallocated);
01624 m_unallocated_slots.push_back(i);
01625 }
01626
01627
01628 m_current_slot += skip_blocks - 1;
01629 }
01630 ++m_current_slot;
01631
01632 if (m_current_slot >= m_info.num_pieces())
01633 {
01634 assert(m_current_slot == m_info.num_pieces());
01635
01636
01637 std::vector<char>().swap(m_piece_data);
01638 std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
01639 m_state = state_allocating;
01640 assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
01641 return std::make_pair(false, 1.f);
01642 }
01643
01644 assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
01645
01646 return std::make_pair(false, (float)m_current_slot / m_info.num_pieces());
01647 }
01648
01649 bool piece_manager::check_fastresume(
01650 aux::piece_checker_data& d, std::vector<bool>& pieces
01651 , int& num_pieces, bool compact_mode)
01652 {
01653 return m_pimpl->check_fastresume(d, pieces, num_pieces, compact_mode);
01654 }
01655
01656 std::pair<bool, float> piece_manager::check_files(
01657 std::vector<bool>& pieces
01658 , int& num_pieces
01659 , boost::recursive_mutex& mutex)
01660 {
01661 return m_pimpl->check_files(pieces, num_pieces, mutex);
01662 }
01663
01664 int piece_manager::impl::allocate_slot_for_piece(int piece_index)
01665 {
01666
01667 boost::recursive_mutex::scoped_lock lock(m_mutex);
01668
01669
01670 assert(piece_index >= 0);
01671 assert(piece_index < (int)m_piece_to_slot.size());
01672 assert(m_piece_to_slot.size() == m_slot_to_piece.size());
01673
01674 int slot_index = m_piece_to_slot[piece_index];
01675
01676 if (slot_index != has_no_slot)
01677 {
01678 assert(slot_index >= 0);
01679 assert(slot_index < (int)m_slot_to_piece.size());
01680 return slot_index;
01681 }
01682
01683 if (m_free_slots.empty())
01684 {
01685 allocate_slots(1);
01686 assert(!m_free_slots.empty());
01687 }
01688
01689 std::vector<int>::iterator iter(
01690 std::find(
01691 m_free_slots.begin()
01692 , m_free_slots.end()
01693 , piece_index));
01694
01695 if (iter == m_free_slots.end())
01696 {
01697 assert(m_slot_to_piece[piece_index] != unassigned);
01698 assert(!m_free_slots.empty());
01699 iter = m_free_slots.end() - 1;
01700
01701
01702
01703 if (*iter == m_info.num_pieces() - 1 && piece_index != *iter)
01704 {
01705 if (m_free_slots.size() == 1)
01706 allocate_slots(1);
01707 assert(m_free_slots.size() > 1);
01708
01709
01710 iter = m_free_slots.end() - 1;
01711 }
01712 }
01713
01714 slot_index = *iter;
01715 m_free_slots.erase(iter);
01716
01717 assert(m_slot_to_piece[slot_index] == unassigned);
01718
01719 m_slot_to_piece[slot_index] = piece_index;
01720 m_piece_to_slot[piece_index] = slot_index;
01721
01722
01723
01724 if (slot_index != piece_index
01725 && m_slot_to_piece[piece_index] >= 0)
01726 {
01727
01728 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
01729 std::stringstream s;
01730
01731 s << "there is another piece at our slot, swapping..";
01732
01733 s << "\n piece_index: ";
01734 s << piece_index;
01735 s << "\n slot_index: ";
01736 s << slot_index;
01737 s << "\n piece at our slot: ";
01738 s << m_slot_to_piece[piece_index];
01739 s << "\n";
01740
01741 print_to_log(s.str());
01742 debug_log();
01743 #endif
01744
01745 int piece_at_our_slot = m_slot_to_piece[piece_index];
01746 assert(m_piece_to_slot[piece_at_our_slot] == piece_index);
01747
01748 std::swap(
01749 m_slot_to_piece[piece_index]
01750 , m_slot_to_piece[slot_index]);
01751
01752 std::swap(
01753 m_piece_to_slot[piece_index]
01754 , m_piece_to_slot[piece_at_our_slot]);
01755
01756 const int slot_size = static_cast<int>(m_info.piece_size(slot_index));
01757 std::vector<char> buf(slot_size);
01758 m_storage.read(&buf[0], piece_index, 0, slot_size);
01759 m_storage.write(&buf[0], slot_index, 0, slot_size);
01760
01761 assert(m_slot_to_piece[piece_index] == piece_index);
01762 assert(m_piece_to_slot[piece_index] == piece_index);
01763
01764 slot_index = piece_index;
01765
01766 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
01767 debug_log();
01768 #endif
01769 }
01770
01771 assert(slot_index >= 0);
01772 assert(slot_index < (int)m_slot_to_piece.size());
01773 return slot_index;
01774 }
01775
01776 namespace
01777 {
01778
01779
01780 struct allocation_syncronization
01781 {
01782 allocation_syncronization(
01783 bool& flag
01784 , boost::condition& cond
01785 , boost::mutex& monitor)
01786 : m_flag(flag)
01787 , m_cond(cond)
01788 , m_monitor(monitor)
01789 {
01790 boost::mutex::scoped_lock lock(m_monitor);
01791
01792 while (m_flag)
01793 m_cond.wait(lock);
01794
01795 m_flag = true;
01796 }
01797
01798 ~allocation_syncronization()
01799 {
01800 boost::mutex::scoped_lock lock(m_monitor);
01801 m_flag = false;
01802 m_cond.notify_one();
01803 }
01804
01805 bool& m_flag;
01806 boost::condition& m_cond;
01807 boost::mutex& m_monitor;
01808 };
01809
01810 }
01811
01812 void piece_manager::impl::allocate_slots(int num_slots)
01813 {
01814 assert(num_slots > 0);
01815
01816
01817
01818 allocation_syncronization sync_obj(
01819 m_allocating
01820 , m_allocating_condition
01821 , m_allocating_monitor);
01822
01823
01824 boost::recursive_mutex::scoped_lock lock(m_mutex);
01825
01826
01827 assert(!m_unallocated_slots.empty());
01828
01829 const int piece_size = static_cast<int>(m_info.piece_length());
01830
01831 std::vector<char>& buffer = m_scratch_buffer;
01832 buffer.resize(piece_size);
01833
01834 for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i)
01835 {
01836 int pos = m_unallocated_slots.front();
01837
01838 bool write_back = false;
01839
01840 int new_free_slot = pos;
01841 if (m_piece_to_slot[pos] != has_no_slot)
01842 {
01843 assert(m_piece_to_slot[pos] >= 0);
01844 m_storage.read(&buffer[0], m_piece_to_slot[pos], 0, static_cast<int>(m_info.piece_size(pos)));
01845 new_free_slot = m_piece_to_slot[pos];
01846 m_slot_to_piece[pos] = pos;
01847 m_piece_to_slot[pos] = pos;
01848 write_back = true;
01849 }
01850 m_unallocated_slots.erase(m_unallocated_slots.begin());
01851 m_slot_to_piece[new_free_slot] = unassigned;
01852 m_free_slots.push_back(new_free_slot);
01853
01854 if (write_back || m_fill_mode)
01855 m_storage.write(&buffer[0], pos, 0, static_cast<int>(m_info.piece_size(pos)));
01856 }
01857
01858 assert(m_free_slots.size() > 0);
01859 }
01860
01861 void piece_manager::allocate_slots(int num_slots)
01862 {
01863 m_pimpl->allocate_slots(num_slots);
01864 }
01865
01866 path const& piece_manager::save_path() const
01867 {
01868 return m_pimpl->save_path();
01869 }
01870
01871 bool piece_manager::move_storage(path const& save_path)
01872 {
01873 return m_pimpl->move_storage(save_path);
01874 }
01875
01876 #ifndef NDEBUG
01877 void piece_manager::impl::check_invariant() const
01878 {
01879
01880 boost::recursive_mutex::scoped_lock lock(m_mutex);
01881
01882 if (m_piece_to_slot.empty()) return;
01883
01884 assert((int)m_piece_to_slot.size() == m_info.num_pieces());
01885 assert((int)m_slot_to_piece.size() == m_info.num_pieces());
01886
01887 for (std::vector<int>::const_iterator i = m_free_slots.begin();
01888 i != m_free_slots.end(); ++i)
01889 {
01890 assert(*i < (int)m_slot_to_piece.size());
01891 assert(*i >= 0);
01892 assert(m_slot_to_piece[*i] == unassigned);
01893 assert(std::find(i+1, m_free_slots.end(), *i)
01894 == m_free_slots.end());
01895 }
01896
01897 for (std::vector<int>::const_iterator i = m_unallocated_slots.begin();
01898 i != m_unallocated_slots.end(); ++i)
01899 {
01900 assert(*i < (int)m_slot_to_piece.size());
01901 assert(*i >= 0);
01902 assert(m_slot_to_piece[*i] == unallocated);
01903 assert(std::find(i+1, m_unallocated_slots.end(), *i)
01904 == m_unallocated_slots.end());
01905 }
01906
01907 for (int i = 0; i < m_info.num_pieces(); ++i)
01908 {
01909
01910 if (m_piece_to_slot[i] != has_no_slot)
01911 {
01912 assert(m_piece_to_slot[i] >= 0);
01913 assert(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
01914 }
01915
01916
01917 if (m_slot_to_piece[i] != unallocated
01918 && m_slot_to_piece[i] != unassigned)
01919 {
01920 assert(m_slot_to_piece[i] >= 0);
01921 assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
01922 }
01923
01924
01925 if (m_piece_to_slot[i] >= 0)
01926 {
01927 assert(m_slot_to_piece[m_piece_to_slot[i]] == i);
01928 if (m_piece_to_slot[i] != i)
01929 {
01930 assert(m_slot_to_piece[i] == unallocated);
01931 }
01932 }
01933 else
01934 {
01935 assert(m_piece_to_slot[i] == has_no_slot);
01936 }
01937
01938
01939
01940 if (m_slot_to_piece[i] >= 0)
01941 {
01942 assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
01943 assert(m_piece_to_slot[m_slot_to_piece[i]] == i);
01944 #ifdef TORRENT_STORAGE_DEBUG
01945 assert(
01946 std::find(
01947 m_unallocated_slots.begin()
01948 , m_unallocated_slots.end()
01949 , i) == m_unallocated_slots.end()
01950 );
01951 assert(
01952 std::find(
01953 m_free_slots.begin()
01954 , m_free_slots.end()
01955 , i) == m_free_slots.end()
01956 );
01957 #endif
01958 }
01959 else if (m_slot_to_piece[i] == unallocated)
01960 {
01961 #ifdef TORRENT_STORAGE_DEBUG
01962 assert(m_unallocated_slots.empty()
01963 || (std::find(
01964 m_unallocated_slots.begin()
01965 , m_unallocated_slots.end()
01966 , i) != m_unallocated_slots.end())
01967 );
01968 #endif
01969 }
01970 else if (m_slot_to_piece[i] == unassigned)
01971 {
01972 #ifdef TORRENT_STORAGE_DEBUG
01973 assert(
01974 std::find(
01975 m_free_slots.begin()
01976 , m_free_slots.end()
01977 , i) != m_free_slots.end()
01978 );
01979 #endif
01980 }
01981 else
01982 {
01983 assert(false && "m_slot_to_piece[i] is invalid");
01984 }
01985 }
01986 }
01987
01988 #ifdef TORRENT_STORAGE_DEBUG
01989 void piece_manager::impl::debug_log() const
01990 {
01991 std::stringstream s;
01992
01993 s << "index\tslot\tpiece\n";
01994
01995 for (int i = 0; i < m_info.num_pieces(); ++i)
01996 {
01997 s << i << "\t" << m_slot_to_piece[i] << "\t";
01998 s << m_piece_to_slot[i] << "\n";
01999 }
02000
02001 s << "---------------------------------\n";
02002
02003 print_to_log(s.str());
02004 }
02005 #endif
02006 #endif
02007 }
02008