mirror of
https://github.com/aria2/aria2.git
synced 2026-04-11 15:29:02 +00:00
This option excludes seed only downloads when counting concurrent active downloads (-j option). This means that if -j3 is given and this option is turned on and 3 downloads are active and one of those enters seed mode, then it is excluded from active download count (thus it becomes 2), and the next download waiting in queue gets started. But be aware that seeding item is still recognized as active download in RPC method.
871 lines
24 KiB
C++
871 lines
24 KiB
C++
/* <!-- copyright */
|
|
/*
|
|
* aria2 - The high speed download utility
|
|
*
|
|
* Copyright (C) 2006 Tatsuhiro Tsujikawa
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* In addition, as a special exception, the copyright holders give
|
|
* permission to link the code of portions of this program with the
|
|
* OpenSSL library under certain conditions as described in each
|
|
* individual source file, and distribute linked combinations
|
|
* including the two.
|
|
* You must obey the GNU General Public License in all respects
|
|
* for all of the code used other than OpenSSL. If you modify
|
|
* file(s) with this exception, you may extend this exception to your
|
|
* version of the file(s), but you are not obligated to do so. If you
|
|
* do not wish to do so, delete this exception statement from your
|
|
* version. If you delete this exception statement from all source
|
|
* files in the program, then also delete it here.
|
|
*/
|
|
/* copyright --> */
|
|
#include "DefaultPieceStorage.h"
|
|
|
|
#include <numeric>
|
|
#include <algorithm>
|
|
|
|
#include "DownloadContext.h"
|
|
#include "Piece.h"
|
|
#include "Peer.h"
|
|
#include "LogFactory.h"
|
|
#include "Logger.h"
|
|
#include "prefs.h"
|
|
#include "DirectDiskAdaptor.h"
|
|
#include "MultiDiskAdaptor.h"
|
|
#include "DiskWriter.h"
|
|
#include "BitfieldMan.h"
|
|
#include "message.h"
|
|
#include "DefaultDiskWriterFactory.h"
|
|
#include "FileEntry.h"
|
|
#include "DlAbortEx.h"
|
|
#include "util.h"
|
|
#include "a2functional.h"
|
|
#include "Option.h"
|
|
#include "fmt.h"
|
|
#include "RarestPieceSelector.h"
|
|
#include "DefaultStreamPieceSelector.h"
|
|
#include "InorderStreamPieceSelector.h"
|
|
#include "GeomStreamPieceSelector.h"
|
|
#include "array_fun.h"
|
|
#include "PieceStatMan.h"
|
|
#include "wallclock.h"
|
|
#include "bitfield.h"
|
|
#include "SingletonHolder.h"
|
|
#include "Notifier.h"
|
|
#include "WrDiskCache.h"
|
|
#include "RequestGroup.h"
|
|
#ifdef ENABLE_BITTORRENT
|
|
# include "bittorrent_helper.h"
|
|
#endif // ENABLE_BITTORRENT
|
|
|
|
namespace aria2 {
|
|
|
|
DefaultPieceStorage::DefaultPieceStorage
|
|
(const std::shared_ptr<DownloadContext>& downloadContext, const Option* option)
|
|
: downloadContext_(downloadContext),
|
|
bitfieldMan_(make_unique<BitfieldMan>
|
|
(downloadContext->getPieceLength(),
|
|
downloadContext->getTotalLength())),
|
|
diskWriterFactory_(std::make_shared<DefaultDiskWriterFactory>()),
|
|
endGame_(false),
|
|
endGamePieceNum_(END_GAME_PIECE_NUM),
|
|
option_(option),
|
|
pieceStatMan_(std::make_shared<PieceStatMan>
|
|
(downloadContext->getNumPieces(), true)),
|
|
pieceSelector_(make_unique<RarestPieceSelector>(pieceStatMan_)),
|
|
wrDiskCache_(nullptr)
|
|
{
|
|
const std::string& pieceSelectorOpt =
|
|
option_->get(PREF_STREAM_PIECE_SELECTOR);
|
|
if(pieceSelectorOpt.empty() || pieceSelectorOpt == A2_V_DEFAULT) {
|
|
streamPieceSelector_ = make_unique<DefaultStreamPieceSelector>
|
|
(bitfieldMan_.get());
|
|
} else if(pieceSelectorOpt == V_INORDER) {
|
|
streamPieceSelector_ = make_unique<InorderStreamPieceSelector>
|
|
(bitfieldMan_.get());
|
|
} else if(pieceSelectorOpt == A2_V_GEOM) {
|
|
streamPieceSelector_ = make_unique<GeomStreamPieceSelector>
|
|
(bitfieldMan_.get(), 1.5);
|
|
}
|
|
}
|
|
|
|
DefaultPieceStorage::~DefaultPieceStorage()
|
|
{}
|
|
|
|
std::shared_ptr<Piece> DefaultPieceStorage::checkOutPiece
|
|
(size_t index, cuid_t cuid)
|
|
{
|
|
bitfieldMan_->setUseBit(index);
|
|
|
|
std::shared_ptr<Piece> piece = findUsedPiece(index);
|
|
if(!piece) {
|
|
piece = std::make_shared<Piece>
|
|
(index, bitfieldMan_->getBlockLength(index));
|
|
piece->setHashType(downloadContext_->getPieceHashType());
|
|
|
|
addUsedPiece(piece);
|
|
}
|
|
piece->addUser(cuid);
|
|
RequestGroup* group = downloadContext_->getOwnerRequestGroup();
|
|
if((!group || !group->inMemoryDownload()) &&
|
|
wrDiskCache_ && !piece->getWrDiskCacheEntry()) {
|
|
// So, we rely on the fact that diskAdaptor_ is not reinitialized
|
|
// in the session.
|
|
piece->initWrCache(wrDiskCache_, diskAdaptor_);
|
|
}
|
|
return piece;
|
|
}
|
|
|
|
/**
|
|
* Newly instantiated piece is not added to usedPieces.
|
|
* Because it is waste of memory and there is no chance to use them later.
|
|
*/
|
|
std::shared_ptr<Piece> DefaultPieceStorage::getPiece(size_t index)
|
|
{
|
|
std::shared_ptr<Piece> piece;
|
|
if(index <= bitfieldMan_->getMaxIndex()) {
|
|
piece = findUsedPiece(index);
|
|
if(!piece) {
|
|
piece = std::make_shared<Piece>(index,
|
|
bitfieldMan_->getBlockLength(index));
|
|
if(hasPiece(index)) {
|
|
piece->setAllBlock();
|
|
}
|
|
}
|
|
}
|
|
return piece;
|
|
}
|
|
|
|
void DefaultPieceStorage::addUsedPiece(const std::shared_ptr<Piece>& piece)
|
|
{
|
|
usedPieces_.insert(piece);
|
|
A2_LOG_DEBUG(fmt("usedPieces_.size()=%lu",
|
|
static_cast<unsigned long>(usedPieces_.size())));
|
|
}
|
|
|
|
std::shared_ptr<Piece> DefaultPieceStorage::findUsedPiece(size_t index) const
|
|
{
|
|
auto p = std::make_shared<Piece>();
|
|
p->setIndex(index);
|
|
|
|
auto i = usedPieces_.find(p);
|
|
if(i == usedPieces_.end()) {
|
|
p.reset();
|
|
return p;
|
|
} else {
|
|
return *i;
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_BITTORRENT
|
|
|
|
bool DefaultPieceStorage::hasMissingPiece(const std::shared_ptr<Peer>& peer)
|
|
{
|
|
return bitfieldMan_->hasMissingPiece(peer->getBitfield(),
|
|
peer->getBitfieldLength());
|
|
}
|
|
|
|
void DefaultPieceStorage::getMissingPiece
|
|
(std::vector<std::shared_ptr<Piece> >& pieces,
|
|
size_t minMissingBlocks,
|
|
const unsigned char* bitfield,
|
|
size_t length,
|
|
cuid_t cuid)
|
|
{
|
|
const size_t mislen = bitfieldMan_->getBitfieldLength();
|
|
auto misbitfield = make_unique<unsigned char[]>(mislen);
|
|
size_t blocks = bitfieldMan_->countBlock();
|
|
size_t misBlock = 0;
|
|
if(isEndGame()) {
|
|
bool r = bitfieldMan_->getAllMissingIndexes
|
|
(misbitfield.get(), mislen, bitfield, length);
|
|
if(!r) {
|
|
return;
|
|
}
|
|
std::vector<size_t> indexes;
|
|
for(size_t i = 0; i < blocks; ++i) {
|
|
if(bitfield::test(misbitfield, blocks, i)) {
|
|
indexes.push_back(i);
|
|
}
|
|
}
|
|
std::random_shuffle(indexes.begin(), indexes.end());
|
|
for(std::vector<size_t>::const_iterator i = indexes.begin(),
|
|
eoi = indexes.end(); i != eoi && misBlock < minMissingBlocks; ++i) {
|
|
std::shared_ptr<Piece> piece = checkOutPiece(*i, cuid);
|
|
if(piece->getUsedBySegment()) {
|
|
// We don't share piece downloaded via HTTP/FTP
|
|
piece->removeUser(cuid);
|
|
} else {
|
|
pieces.push_back(piece);
|
|
misBlock += piece->countMissingBlock();
|
|
}
|
|
}
|
|
} else {
|
|
bool r = bitfieldMan_->getAllMissingUnusedIndexes
|
|
(misbitfield.get(), mislen, bitfield, length);
|
|
if(!r) {
|
|
return;
|
|
}
|
|
while(misBlock < minMissingBlocks) {
|
|
size_t index;
|
|
if(pieceSelector_->select(index, misbitfield.get(), blocks)) {
|
|
pieces.push_back(checkOutPiece(index, cuid));
|
|
bitfield::flipBit(misbitfield.get(), blocks, index);
|
|
misBlock += pieces.back()->countMissingBlock();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
void unsetExcludedIndexes(BitfieldMan& bitfield,
|
|
const std::vector<size_t>& excludedIndexes)
|
|
{
|
|
using namespace std::placeholders;
|
|
std::for_each(excludedIndexes.begin(), excludedIndexes.end(),
|
|
std::bind(&BitfieldMan::unsetBit, &bitfield, _1));
|
|
}
|
|
} // namespace
|
|
|
|
void DefaultPieceStorage::createFastIndexBitfield
|
|
(BitfieldMan& bitfield, const std::shared_ptr<Peer>& peer)
|
|
{
|
|
const auto& is = peer->getPeerAllowedIndexSet();
|
|
for(const auto& i: is) {
|
|
if(!bitfieldMan_->isBitSet(i) && peer->hasPiece(i)) {
|
|
bitfield.setBit(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DefaultPieceStorage::getMissingPiece
|
|
(std::vector<std::shared_ptr<Piece> >& pieces,
|
|
size_t minMissingBlocks,
|
|
const std::shared_ptr<Peer>& peer,
|
|
cuid_t cuid)
|
|
{
|
|
getMissingPiece(pieces, minMissingBlocks,
|
|
peer->getBitfield(), peer->getBitfieldLength(),
|
|
cuid);
|
|
}
|
|
|
|
|
|
void DefaultPieceStorage::getMissingPiece
|
|
(std::vector<std::shared_ptr<Piece> >& pieces,
|
|
size_t minMissingBlocks,
|
|
const std::shared_ptr<Peer>& peer,
|
|
const std::vector<size_t>& excludedIndexes,
|
|
cuid_t cuid)
|
|
{
|
|
BitfieldMan tempBitfield(bitfieldMan_->getBlockLength(),
|
|
bitfieldMan_->getTotalLength());
|
|
tempBitfield.setBitfield(peer->getBitfield(), peer->getBitfieldLength());
|
|
unsetExcludedIndexes(tempBitfield, excludedIndexes);
|
|
getMissingPiece(pieces, minMissingBlocks,
|
|
tempBitfield.getBitfield(), tempBitfield.getBitfieldLength(),
|
|
cuid);
|
|
}
|
|
|
|
void DefaultPieceStorage::getMissingFastPiece
|
|
(std::vector<std::shared_ptr<Piece> >& pieces,
|
|
size_t minMissingBlocks,
|
|
const std::shared_ptr<Peer>& peer,
|
|
cuid_t cuid)
|
|
{
|
|
if(peer->isFastExtensionEnabled() && peer->countPeerAllowedIndexSet() > 0) {
|
|
BitfieldMan tempBitfield(bitfieldMan_->getBlockLength(),
|
|
bitfieldMan_->getTotalLength());
|
|
createFastIndexBitfield(tempBitfield, peer);
|
|
getMissingPiece(pieces, minMissingBlocks,
|
|
tempBitfield.getBitfield(),
|
|
tempBitfield.getBitfieldLength(),
|
|
cuid);
|
|
}
|
|
}
|
|
|
|
void DefaultPieceStorage::getMissingFastPiece
|
|
(std::vector<std::shared_ptr<Piece> >& pieces,
|
|
size_t minMissingBlocks,
|
|
const std::shared_ptr<Peer>& peer,
|
|
const std::vector<size_t>& excludedIndexes,
|
|
cuid_t cuid)
|
|
{
|
|
if(peer->isFastExtensionEnabled() && peer->countPeerAllowedIndexSet() > 0) {
|
|
BitfieldMan tempBitfield(bitfieldMan_->getBlockLength(),
|
|
bitfieldMan_->getTotalLength());
|
|
createFastIndexBitfield(tempBitfield, peer);
|
|
unsetExcludedIndexes(tempBitfield, excludedIndexes);
|
|
getMissingPiece(pieces, minMissingBlocks,
|
|
tempBitfield.getBitfield(),
|
|
tempBitfield.getBitfieldLength(),
|
|
cuid);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Piece>
|
|
DefaultPieceStorage::getMissingPiece
|
|
(const std::shared_ptr<Peer>& peer,
|
|
cuid_t cuid)
|
|
{
|
|
std::vector<std::shared_ptr<Piece> > pieces;
|
|
getMissingPiece(pieces, 1, peer, cuid);
|
|
if(pieces.empty()) {
|
|
return nullptr;
|
|
} else {
|
|
return pieces.front();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Piece> DefaultPieceStorage::getMissingPiece
|
|
(const std::shared_ptr<Peer>& peer,
|
|
const std::vector<size_t>& excludedIndexes,
|
|
cuid_t cuid)
|
|
{
|
|
std::vector<std::shared_ptr<Piece> > pieces;
|
|
getMissingPiece(pieces, 1, peer, excludedIndexes, cuid);
|
|
if(pieces.empty()) {
|
|
return nullptr;
|
|
} else {
|
|
return pieces.front();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Piece> DefaultPieceStorage::getMissingFastPiece
|
|
(const std::shared_ptr<Peer>& peer,
|
|
cuid_t cuid)
|
|
{
|
|
std::vector<std::shared_ptr<Piece> > pieces;
|
|
getMissingFastPiece(pieces, 1, peer, cuid);
|
|
if(pieces.empty()) {
|
|
return nullptr;
|
|
} else {
|
|
return pieces.front();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Piece> DefaultPieceStorage::getMissingFastPiece
|
|
(const std::shared_ptr<Peer>& peer,
|
|
const std::vector<size_t>& excludedIndexes,
|
|
cuid_t cuid)
|
|
{
|
|
std::vector<std::shared_ptr<Piece> > pieces;
|
|
getMissingFastPiece(pieces, 1, peer, excludedIndexes, cuid);
|
|
if(pieces.empty()) {
|
|
return nullptr;
|
|
} else {
|
|
return pieces.front();
|
|
}
|
|
}
|
|
|
|
#endif // ENABLE_BITTORRENT
|
|
|
|
bool DefaultPieceStorage::hasMissingUnusedPiece()
|
|
{
|
|
size_t index;
|
|
return bitfieldMan_->getFirstMissingUnusedIndex(index);
|
|
}
|
|
|
|
std::shared_ptr<Piece> DefaultPieceStorage::getMissingPiece
|
|
(size_t minSplitSize,
|
|
const unsigned char* ignoreBitfield,
|
|
size_t length,
|
|
cuid_t cuid)
|
|
{
|
|
size_t index;
|
|
if(streamPieceSelector_->select
|
|
(index, minSplitSize, ignoreBitfield, length)) {
|
|
return checkOutPiece(index, cuid);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Piece> DefaultPieceStorage::getMissingPiece
|
|
(size_t index,
|
|
cuid_t cuid)
|
|
{
|
|
if(hasPiece(index) || isPieceUsed(index)) {
|
|
return nullptr;
|
|
} else {
|
|
return checkOutPiece(index, cuid);
|
|
}
|
|
}
|
|
|
|
void DefaultPieceStorage::deleteUsedPiece(const std::shared_ptr<Piece>& piece)
|
|
{
|
|
if(!piece) {
|
|
return;
|
|
}
|
|
usedPieces_.erase(piece);
|
|
piece->releaseWrCache(wrDiskCache_);
|
|
}
|
|
|
|
// void DefaultPieceStorage::reduceUsedPieces(size_t upperBound)
|
|
// {
|
|
// size_t usedPiecesSize = usedPieces.size();
|
|
// if(usedPiecesSize <= upperBound) {
|
|
// return;
|
|
// }
|
|
// size_t delNum = usedPiecesSize-upperBound;
|
|
// int fillRate = 10;
|
|
// while(delNum && fillRate <= 15) {
|
|
// delNum -= deleteUsedPiecesByFillRate(fillRate, delNum);
|
|
// fillRate += 5;
|
|
// }
|
|
// }
|
|
|
|
// size_t DefaultPieceStorage::deleteUsedPiecesByFillRate(int fillRate,
|
|
// size_t delNum)
|
|
// {
|
|
// size_t deleted = 0;
|
|
// for(Pieces::iterator itr = usedPieces.begin();
|
|
// itr != usedPieces.end() && deleted < delNum;) {
|
|
// std::shared_ptr<Piece>& piece = *itr;
|
|
// if(!bitfieldMan->isUseBitSet(piece->getIndex()) &&
|
|
// piece->countCompleteBlock() <= piece->countBlock()*(fillRate/100.0)) {
|
|
// logger->info(MSG_DELETING_USED_PIECE,
|
|
// piece->getIndex(),
|
|
// (piece->countCompleteBlock()*100)/piece->countBlock(),
|
|
// fillRate);
|
|
// itr = usedPieces.erase(itr);
|
|
// ++deleted;
|
|
// } else {
|
|
// ++itr;
|
|
// }
|
|
// }
|
|
// return deleted;
|
|
// }
|
|
|
|
void DefaultPieceStorage::completePiece(const std::shared_ptr<Piece>& piece)
|
|
{
|
|
if(!piece) {
|
|
return;
|
|
}
|
|
deleteUsedPiece(piece);
|
|
// if(!isEndGame()) {
|
|
// reduceUsedPieces(100);
|
|
// }
|
|
if(allDownloadFinished()) {
|
|
return;
|
|
}
|
|
bitfieldMan_->setBit(piece->getIndex());
|
|
bitfieldMan_->unsetUseBit(piece->getIndex());
|
|
addPieceStats(piece->getIndex());
|
|
if(downloadFinished()) {
|
|
downloadContext_->resetDownloadStopTime();
|
|
if(isSelectiveDownloadingMode()) {
|
|
A2_LOG_NOTICE(MSG_SELECTIVE_DOWNLOAD_COMPLETED);
|
|
// following line was commented out in order to stop sending request
|
|
// message after user-specified files were downloaded.
|
|
//finishSelectiveDownloadingMode();
|
|
} else {
|
|
A2_LOG_INFO(MSG_DOWNLOAD_COMPLETED);
|
|
}
|
|
#ifdef ENABLE_BITTORRENT
|
|
if(downloadContext_->hasAttribute(CTX_ATTR_BT)) {
|
|
if(!bittorrent::getTorrentAttrs(downloadContext_)->metadata.empty()) {
|
|
#ifdef __MINGW32__
|
|
// On Windows, if aria2 opens files with GENERIC_WRITE access
|
|
// right, some programs cannot open them aria2 is seeding. To
|
|
// avoid this situation, re-open the files with read-only
|
|
// enabled.
|
|
A2_LOG_INFO("Closing files and re-open them with read-only mode"
|
|
" enabled.");
|
|
diskAdaptor_->closeFile();
|
|
diskAdaptor_->enableReadOnly();
|
|
diskAdaptor_->openFile();
|
|
#endif // __MINGW32__
|
|
auto group = downloadContext_->getOwnerRequestGroup();
|
|
|
|
util::executeHookByOptName(group, option_,
|
|
PREF_ON_BT_DOWNLOAD_COMPLETE);
|
|
SingletonHolder<Notifier>::instance()->
|
|
notifyDownloadEvent(EVENT_ON_BT_DOWNLOAD_COMPLETE, group);
|
|
|
|
group->enableSeedOnly();
|
|
}
|
|
}
|
|
#endif // ENABLE_BITTORRENT
|
|
}
|
|
}
|
|
|
|
bool DefaultPieceStorage::isSelectiveDownloadingMode()
|
|
{
|
|
return bitfieldMan_->isFilterEnabled();
|
|
}
|
|
|
|
// not unittested
|
|
void DefaultPieceStorage::cancelPiece
|
|
(const std::shared_ptr<Piece>& piece, cuid_t cuid)
|
|
{
|
|
if(!piece) {
|
|
return;
|
|
}
|
|
piece->removeUser(cuid);
|
|
if(!piece->getUsed()) {
|
|
bitfieldMan_->unsetUseBit(piece->getIndex());
|
|
}
|
|
if(!isEndGame()) {
|
|
if(piece->getCompletedLength() == 0) {
|
|
deleteUsedPiece(piece);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DefaultPieceStorage::hasPiece(size_t index)
|
|
{
|
|
return bitfieldMan_->isBitSet(index);
|
|
}
|
|
|
|
bool DefaultPieceStorage::isPieceUsed(size_t index)
|
|
{
|
|
return bitfieldMan_->isUseBitSet(index);
|
|
}
|
|
|
|
int64_t DefaultPieceStorage::getTotalLength()
|
|
{
|
|
return bitfieldMan_->getTotalLength();
|
|
}
|
|
|
|
int64_t DefaultPieceStorage::getFilteredTotalLength()
|
|
{
|
|
return bitfieldMan_->getFilteredTotalLength();
|
|
}
|
|
|
|
int64_t DefaultPieceStorage::getCompletedLength()
|
|
{
|
|
int64_t completedLength =
|
|
bitfieldMan_->getCompletedLength()+getInFlightPieceCompletedLength();
|
|
int64_t totalLength = getTotalLength();
|
|
if(completedLength > totalLength) {
|
|
completedLength = totalLength;
|
|
}
|
|
return completedLength;
|
|
}
|
|
|
|
int64_t DefaultPieceStorage::getFilteredCompletedLength()
|
|
{
|
|
return bitfieldMan_->getFilteredCompletedLength()+
|
|
getInFlightPieceFilteredCompletedLength();
|
|
}
|
|
|
|
int64_t DefaultPieceStorage::getInFlightPieceCompletedLength() const
|
|
{
|
|
int64_t len = 0;
|
|
for(auto & elem : usedPieces_) {
|
|
len += elem->getCompletedLength();
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int64_t DefaultPieceStorage::getInFlightPieceFilteredCompletedLength() const
|
|
{
|
|
int64_t len = 0;
|
|
for(auto & elem : usedPieces_) {
|
|
if(bitfieldMan_->isFilterBitSet(elem->getIndex())) {
|
|
len += elem->getCompletedLength();
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
// not unittested
|
|
void DefaultPieceStorage::setupFileFilter()
|
|
{
|
|
const std::vector<std::shared_ptr<FileEntry> >& fileEntries =
|
|
downloadContext_->getFileEntries();
|
|
bool allSelected = true;
|
|
for(auto & e : fileEntries) {
|
|
if(!e->isRequested()) {
|
|
allSelected = false;
|
|
break;
|
|
}
|
|
}
|
|
if(allSelected) {
|
|
return;
|
|
}
|
|
for(auto & e: fileEntries) {
|
|
if(e->isRequested()) {
|
|
bitfieldMan_->addFilter(e->getOffset(), e->getLength());
|
|
}
|
|
}
|
|
bitfieldMan_->enableFilter();
|
|
}
|
|
|
|
// not unittested
|
|
void DefaultPieceStorage::clearFileFilter()
|
|
{
|
|
bitfieldMan_->clearFilter();
|
|
}
|
|
|
|
// not unittested
|
|
bool DefaultPieceStorage::downloadFinished()
|
|
{
|
|
// TODO iterate all requested FileEntry and Call
|
|
// bitfieldMan->isBitSetOffsetRange()
|
|
return bitfieldMan_->isFilteredAllBitSet();
|
|
}
|
|
|
|
// not unittested
|
|
bool DefaultPieceStorage::allDownloadFinished()
|
|
{
|
|
return bitfieldMan_->isAllBitSet();
|
|
}
|
|
|
|
// not unittested
|
|
void DefaultPieceStorage::initStorage()
|
|
{
|
|
if(downloadContext_->getFileEntries().size() == 1) {
|
|
A2_LOG_DEBUG("Instantiating DirectDiskAdaptor");
|
|
auto directDiskAdaptor = make_unique<DirectDiskAdaptor>();
|
|
directDiskAdaptor->setTotalLength(downloadContext_->getTotalLength());
|
|
directDiskAdaptor->setFileEntries
|
|
(downloadContext_->getFileEntries().begin(),
|
|
downloadContext_->getFileEntries().end());
|
|
|
|
directDiskAdaptor->setDiskWriter
|
|
(diskWriterFactory_->newDiskWriter(directDiskAdaptor->getFilePath()));
|
|
diskAdaptor_ = std::move(directDiskAdaptor);
|
|
} else {
|
|
A2_LOG_DEBUG("Instantiating MultiDiskAdaptor");
|
|
auto multiDiskAdaptor = make_unique<MultiDiskAdaptor>();
|
|
multiDiskAdaptor->setFileEntries(downloadContext_->getFileEntries().begin(),
|
|
downloadContext_->getFileEntries().end());
|
|
multiDiskAdaptor->setPieceLength(downloadContext_->getPieceLength());
|
|
diskAdaptor_ = std::move(multiDiskAdaptor);
|
|
}
|
|
if(option_->get(PREF_FILE_ALLOCATION) == V_FALLOC) {
|
|
diskAdaptor_->setFileAllocationMethod(DiskAdaptor::FILE_ALLOC_FALLOC);
|
|
} else if(option_->get(PREF_FILE_ALLOCATION) == V_TRUNC) {
|
|
diskAdaptor_->setFileAllocationMethod(DiskAdaptor::FILE_ALLOC_TRUNC);
|
|
}
|
|
}
|
|
|
|
void DefaultPieceStorage::setBitfield(const unsigned char* bitfield,
|
|
size_t bitfieldLength)
|
|
{
|
|
bitfieldMan_->setBitfield(bitfield, bitfieldLength);
|
|
addPieceStats(bitfield, bitfieldLength);
|
|
}
|
|
|
|
size_t DefaultPieceStorage::getBitfieldLength()
|
|
{
|
|
return bitfieldMan_->getBitfieldLength();
|
|
}
|
|
|
|
const unsigned char* DefaultPieceStorage::getBitfield()
|
|
{
|
|
return bitfieldMan_->getBitfield();
|
|
}
|
|
|
|
std::shared_ptr<DiskAdaptor> DefaultPieceStorage::getDiskAdaptor() {
|
|
return diskAdaptor_;
|
|
}
|
|
|
|
WrDiskCache* DefaultPieceStorage::getWrDiskCache()
|
|
{
|
|
return wrDiskCache_;
|
|
}
|
|
|
|
void DefaultPieceStorage::flushWrDiskCacheEntry()
|
|
{
|
|
if(!wrDiskCache_) {
|
|
return;
|
|
}
|
|
// UsedPieceSet is sorted by piece index. It means we can flush
|
|
// cache by non-decreasing offset, which is good to reduce disk seek
|
|
// unless the file is heavily fragmented.
|
|
for(auto & piece : usedPieces_) {
|
|
auto ce = piece->getWrDiskCacheEntry();
|
|
if(ce) {
|
|
piece->flushWrCache(wrDiskCache_);
|
|
piece->releaseWrCache(wrDiskCache_);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t DefaultPieceStorage::getPieceLength(size_t index)
|
|
{
|
|
return bitfieldMan_->getBlockLength(index);
|
|
}
|
|
|
|
void DefaultPieceStorage::advertisePiece(cuid_t cuid, size_t index)
|
|
{
|
|
HaveEntry entry(cuid, index, global::wallclock());
|
|
haves_.push_front(entry);
|
|
}
|
|
|
|
void
|
|
DefaultPieceStorage::getAdvertisedPieceIndexes(std::vector<size_t>& indexes,
|
|
cuid_t myCuid,
|
|
const Timer& lastCheckTime)
|
|
{
|
|
for(std::deque<HaveEntry>::const_iterator itr = haves_.begin(),
|
|
eoi = haves_.end(); itr != eoi; ++itr) {
|
|
const HaveEntry& have = *itr;
|
|
if(lastCheckTime > have.getRegisteredTime()) {
|
|
break;
|
|
}
|
|
indexes.push_back(have.getIndex());
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class FindElapsedHave
|
|
{
|
|
private:
|
|
time_t elapsed;
|
|
public:
|
|
FindElapsedHave(time_t elapsed):elapsed(elapsed) {}
|
|
|
|
bool operator()(const HaveEntry& have) {
|
|
if(have.getRegisteredTime().difference(global::wallclock()) >= elapsed) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void DefaultPieceStorage::removeAdvertisedPiece(time_t elapsed)
|
|
{
|
|
auto itr = std::find_if(haves_.begin(), haves_.end(),
|
|
FindElapsedHave(elapsed));
|
|
if(itr != haves_.end()) {
|
|
A2_LOG_DEBUG(fmt(MSG_REMOVED_HAVE_ENTRY,
|
|
static_cast<unsigned long>(haves_.end()-itr)));
|
|
haves_.erase(itr, haves_.end());
|
|
}
|
|
}
|
|
|
|
void DefaultPieceStorage::markAllPiecesDone()
|
|
{
|
|
bitfieldMan_->setAllBit();
|
|
}
|
|
|
|
void DefaultPieceStorage::markPiecesDone(int64_t length)
|
|
{
|
|
if(length == bitfieldMan_->getTotalLength()) {
|
|
bitfieldMan_->setAllBit();
|
|
} else if(length == 0) {
|
|
// TODO this would go to markAllPiecesUndone()
|
|
bitfieldMan_->clearAllBit();
|
|
usedPieces_.clear();
|
|
} else {
|
|
size_t numPiece = length/bitfieldMan_->getBlockLength();
|
|
if(numPiece > 0) {
|
|
bitfieldMan_->setBitRange(0, numPiece-1);
|
|
}
|
|
size_t r = (length%bitfieldMan_->getBlockLength())/Piece::BLOCK_LENGTH;
|
|
if(r > 0) {
|
|
auto p = std::make_shared<Piece>(numPiece,
|
|
bitfieldMan_->getBlockLength(numPiece));
|
|
|
|
for(size_t i = 0; i < r; ++i) {
|
|
p->completeBlock(i);
|
|
}
|
|
|
|
p->setHashType(downloadContext_->getPieceHashType());
|
|
|
|
addUsedPiece(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DefaultPieceStorage::markPieceMissing(size_t index)
|
|
{
|
|
bitfieldMan_->unsetBit(index);
|
|
}
|
|
|
|
void DefaultPieceStorage::addInFlightPiece
|
|
(const std::vector<std::shared_ptr<Piece> >& pieces)
|
|
{
|
|
usedPieces_.insert(pieces.begin(), pieces.end());
|
|
}
|
|
|
|
size_t DefaultPieceStorage::countInFlightPiece()
|
|
{
|
|
return usedPieces_.size();
|
|
}
|
|
|
|
void DefaultPieceStorage::getInFlightPieces
|
|
(std::vector<std::shared_ptr<Piece> >& pieces)
|
|
{
|
|
pieces.insert(pieces.end(), usedPieces_.begin(), usedPieces_.end());
|
|
}
|
|
|
|
void DefaultPieceStorage::setDiskWriterFactory
|
|
(const std::shared_ptr<DiskWriterFactory>& diskWriterFactory)
|
|
{
|
|
diskWriterFactory_ = diskWriterFactory;
|
|
}
|
|
|
|
void DefaultPieceStorage::addPieceStats(const unsigned char* bitfield,
|
|
size_t bitfieldLength)
|
|
{
|
|
pieceStatMan_->addPieceStats(bitfield, bitfieldLength);
|
|
}
|
|
|
|
void DefaultPieceStorage::subtractPieceStats(const unsigned char* bitfield,
|
|
size_t bitfieldLength)
|
|
{
|
|
pieceStatMan_->subtractPieceStats(bitfield, bitfieldLength);
|
|
}
|
|
|
|
void DefaultPieceStorage::updatePieceStats(const unsigned char* newBitfield,
|
|
size_t newBitfieldLength,
|
|
const unsigned char* oldBitfield)
|
|
{
|
|
pieceStatMan_->updatePieceStats(newBitfield, newBitfieldLength,
|
|
oldBitfield);
|
|
}
|
|
|
|
void DefaultPieceStorage::addPieceStats(size_t index)
|
|
{
|
|
pieceStatMan_->addPieceStats(index);
|
|
}
|
|
|
|
size_t DefaultPieceStorage::getNextUsedIndex(size_t index)
|
|
{
|
|
for(size_t i = index+1; i < bitfieldMan_->countBlock(); ++i) {
|
|
if(bitfieldMan_->isUseBitSet(i) || bitfieldMan_->isBitSet(i)) {
|
|
return i;
|
|
}
|
|
}
|
|
return bitfieldMan_->countBlock();
|
|
}
|
|
|
|
void DefaultPieceStorage::onDownloadIncomplete()
|
|
{
|
|
streamPieceSelector_->onBitfieldInit();
|
|
}
|
|
|
|
void DefaultPieceStorage::setPieceSelector
|
|
(std::unique_ptr<PieceSelector> pieceSelector)
|
|
{
|
|
pieceSelector_ = std::move(pieceSelector);
|
|
}
|
|
|
|
std::unique_ptr<PieceSelector> DefaultPieceStorage::popPieceSelector()
|
|
{
|
|
return std::move(pieceSelector_);
|
|
}
|
|
|
|
} // namespace aria2
|