/* */ #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 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) const { unsigned int speed = peerStat->getAvgDownloadSpeed()*0.8; SegmentEntryHandle slowSegmentEntry; for(SegmentEntries::const_iterator itr = usedSegmentEntries.begin(); itr != usedSegmentEntries.end(); ++itr) { const SegmentEntryHandle& segmentEntry = *itr; if(segmentEntry->cuid == 0) { continue; } PeerStatHandle p = getPeerStat(segmentEntry->cuid); if(!p.get() || p->getCuid() == peerStat->getCuid() || p->getStatus() != PeerStat::ACTIVE || !p->getDownloadStartTime().elapsed(_option->getAsInt(PREF_STARTUP_IDLE_TIME))) { 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); return checkoutSegment(cuid, slowSegmentEntry->segment->getPiece()); } 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(); } } void SegmentMan::registerPeerStat(const PeerStatHandle& peerStat) { PeerStatHandle temp = getPeerStat(peerStat->getCuid()); if(!temp.get()) { peerStats.push_back(peerStat); } } PeerStatHandle SegmentMan::getPeerStat(int32_t cuid) const { for(std::deque >::const_iterator itr = peerStats.begin(); itr != peerStats.end(); ++itr) { const PeerStatHandle& peerStat = *itr; if(peerStat->getCuid() == cuid) { return peerStat; } } return SharedHandle(); } 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