/* */ #include "SegmentMan.h" #include "Util.h" #include "message.h" #include "prefs.h" #include "PiecedSegment.h" #include "GrowSegment.h" #include "LogFactory.h" #include "Logger.h" #include "PieceStorage.h" #include "PeerStat.h" #include "Option.h" #include "DownloadContext.h" #include "Piece.h" #include #include namespace aria2 { SegmentEntry::SegmentEntry(int32_t cuid, const SegmentHandle& segment): cuid(cuid), segment(segment) {} SegmentEntry::~SegmentEntry() {} SegmentMan::SegmentMan(const Option* option, const DownloadContextHandle& downloadContext, const PieceStorageHandle& pieceStorage): _option(option), logger(LogFactory::getInstance()), _downloadContext(downloadContext), _pieceStorage(pieceStorage) {} SegmentMan::~SegmentMan() {} bool SegmentMan::downloadFinished() const { if(_pieceStorage.isNull()) { return false; } else { return _pieceStorage->downloadFinished(); } } void SegmentMan::init() { // TODO Do we have to do something about DownloadContext and PieceStorage here? } uint64_t SegmentMan::getTotalLength() const { if(_pieceStorage.isNull()) { return 0; } else { return _pieceStorage->getTotalLength(); } } void SegmentMan::setPieceStorage(const PieceStorageHandle& pieceStorage) { _pieceStorage = pieceStorage; } void SegmentMan::setDownloadContext(const DownloadContextHandle& downloadContext) { _downloadContext = downloadContext; } SegmentHandle SegmentMan::checkoutSegment(int32_t cuid, const PieceHandle& piece) { if(piece.isNull()) { return SharedHandle(); } logger->debug("Attach segment#%d to CUID#%d.", piece->getIndex(), cuid); SegmentHandle segment; if(piece->getLength() == 0) { segment.reset(new GrowSegment(piece)); } else { segment.reset(new PiecedSegment(_downloadContext->getPieceLength(), piece)); } SegmentEntryHandle entry(new SegmentEntry(cuid, segment)); usedSegmentEntries.push_back(entry); logger->debug("index=%d, length=%d, segmentLength=%d, writtenLength=%d", segment->getIndex(), segment->getLength(), segment->getSegmentLength(), segment->getWrittenLength()); return segment; } SegmentEntryHandle SegmentMan::findSlowerSegmentEntry (const PeerStatHandle& peerStat) { unsigned int speed = peerStat->getAvgDownloadSpeed()*0.8; SegmentEntryHandle slowSegmentEntry; int startupIdleTime = _option->getAsInt(PREF_STARTUP_IDLE_TIME); for(std::deque >::const_iterator itr = usedSegmentEntries.begin(); itr != usedSegmentEntries.end(); ++itr) { const SharedHandle& segmentEntry = *itr; if(segmentEntry->cuid == 0) { continue; } SharedHandle p = getPeerStat(segmentEntry->cuid); if(p.isNull()) { // "p is null" means that it hasn't used DownloadCommand yet, i.e. waiting // response from HTTP server after sending HTTP request. p.reset(new PeerStat(segmentEntry->cuid)); registerPeerStat(p); slowSegmentEntry = segmentEntry; } else { if(p->getCuid() == peerStat->getCuid() || (p->getStatus() == PeerStat::ACTIVE && !p->getDownloadStartTime().elapsed(startupIdleTime))) { continue; } unsigned int pSpeed = p->calculateDownloadSpeed(); if(pSpeed < speed) { speed = pSpeed; slowSegmentEntry = segmentEntry; } } } return slowSegmentEntry; } void SegmentMan::getInFlightSegment(std::deque >& segments, int32_t cuid) { for(SegmentEntries::iterator itr = usedSegmentEntries.begin(); itr != usedSegmentEntries.end(); ++itr) { const SegmentEntryHandle& segmentEntry = *itr; if(segmentEntry->cuid == cuid) { segments.push_back(segmentEntry->segment); } } } SegmentHandle SegmentMan::getSegment(int32_t cuid) { PieceHandle piece = _pieceStorage->getMissingPiece(); if(piece.isNull()) { PeerStatHandle myPeerStat = getPeerStat(cuid); if(myPeerStat.isNull()) { return SharedHandle(); } SegmentEntryHandle slowSegmentEntry = findSlowerSegmentEntry(myPeerStat); if(slowSegmentEntry.get()) { logger->info(MSG_SEGMENT_FORWARDING, slowSegmentEntry->cuid, slowSegmentEntry->segment->getIndex(), cuid); PeerStatHandle slowPeerStat = getPeerStat(slowSegmentEntry->cuid); slowPeerStat->requestIdle(); cancelSegment(slowSegmentEntry->cuid); SharedHandle piece = _pieceStorage->getMissingPiece(slowSegmentEntry->segment->getIndex()); assert(!piece.isNull()); return checkoutSegment(cuid, piece); } else { return SharedHandle(); } } else { return checkoutSegment(cuid, piece); } } SegmentHandle SegmentMan::getSegment(int32_t cuid, size_t index) { if(_downloadContext->getNumPieces() <= index) { return SharedHandle(); } return checkoutSegment(cuid, _pieceStorage->getMissingPiece(index)); } void SegmentMan::cancelSegment(int32_t cuid) { for(SegmentEntries::iterator itr = usedSegmentEntries.begin(); itr != usedSegmentEntries.end();) { if((*itr)->cuid == cuid) { _pieceStorage->cancelPiece((*itr)->segment->getPiece()); itr = usedSegmentEntries.erase(itr); } else { ++itr; } } } class FindSegmentEntry { private: SegmentHandle _segment; public: FindSegmentEntry(const SegmentHandle& segment):_segment(segment) {} bool operator()(const SegmentEntryHandle& segmentEntry) const { return segmentEntry->segment->getIndex() == _segment->getIndex(); } }; bool SegmentMan::completeSegment(int32_t cuid, const SegmentHandle& segment) { _pieceStorage->completePiece(segment->getPiece()); _pieceStorage->advertisePiece(cuid, segment->getPiece()->getIndex()); SegmentEntries::iterator itr = std::find_if(usedSegmentEntries.begin(), usedSegmentEntries.end(), FindSegmentEntry(segment)); if(itr == usedSegmentEntries.end()) { return false; } else { usedSegmentEntries.erase(itr); return true; } } bool SegmentMan::hasSegment(size_t index) const { return _pieceStorage->hasPiece(index); } uint64_t SegmentMan::getDownloadLength() const { if(_pieceStorage.isNull()) { return 0; } else { return _pieceStorage->getCompletedLength(); } } class FindPeerStat { public: bool operator()(const SharedHandle& peerStat, int32_t cuid) const { return peerStat->getCuid() < cuid; } }; bool SegmentMan::registerPeerStat(const SharedHandle& peerStat) { std::deque >::iterator i = std::lower_bound(peerStats.begin(), peerStats.end(),peerStat->getCuid(), FindPeerStat()); if(i == peerStats.end() || (*i)->getCuid() != peerStat->getCuid()) { peerStats.insert(i, peerStat); return true ; } else { return false; } } PeerStatHandle SegmentMan::getPeerStat(int32_t cuid) const { std::deque >::const_iterator i = std::lower_bound(peerStats.begin(), peerStats.end(), cuid, FindPeerStat()); if(i != peerStats.end() && (*i)->getCuid() == cuid) { return *i; } else { return SharedHandle(); } } const std::deque >& SegmentMan::getPeerStats() const { return peerStats; } unsigned int SegmentMan::calculateDownloadSpeed() const { unsigned int speed = 0; for(std::deque >::const_iterator itr = peerStats.begin(); itr != peerStats.end(); itr++) { const PeerStatHandle& peerStat = *itr; if(peerStat->getStatus() == PeerStat::ACTIVE) { speed += peerStat->calculateDownloadSpeed(); } } return speed; } size_t SegmentMan::countFreePieceFrom(size_t index) const { size_t numPieces = _downloadContext->getNumPieces(); for(size_t i = index; i < numPieces; ++i) { if(_pieceStorage->hasPiece(i) || _pieceStorage->isPieceUsed(i)) { return i-index; } } return _downloadContext->getNumPieces()-index; } } // namespace aria2