Exemplo n.º 1
0
void DownloadManager::checkDownloads(UserConnection* aConn) {
    dcassert(aConn->getDownload() == NULL);

    QueueItem::Priority prio = QueueManager::getInstance()->hasDownload(aConn->getUser());
    if(!startDownload(prio)) {
        removeConnection(aConn);
        return;
    }

    Download* d = QueueManager::getInstance()->getDownload(*aConn, aConn->isSet(UserConnection::FLAG_SUPPORTS_TTHL));

    if(!d) {
        Lock l(cs);
        aConn->setState(UserConnection::STATE_IDLE);
        idlers.push_back(aConn);
        return;
    }

    aConn->setState(UserConnection::STATE_SND);

    if(aConn->isSet(UserConnection::FLAG_SUPPORTS_XML_BZLIST) && d->getType() == Transfer::TYPE_FULL_LIST) {
        d->setFlag(Download::FLAG_XML_BZ_LIST);
    }

    {
        Lock l(cs);
        downloads.push_back(d);
    }
    fire(DownloadManagerListener::Requesting(), d);

    dcdebug("Requesting " I64_FMT "/" I64_FMT "\n", static_cast<long long int>(d->getStartPos()), static_cast<long long int>(d->getSize()));

    aConn->send(d->getCommand(aConn->isSet(UserConnection::FLAG_SUPPORTS_ZLIB_GET)));
}
Exemplo n.º 2
0
Segment QueueItem::getNextSegment(int64_t  blockSize, int64_t wantedSize, int64_t lastSpeed, const PartialSource::Ptr partialSource) const {
	if(getSize() == -1 || blockSize == 0) {
		return Segment(0, -1);
	}
	
	if(!BOOLSETTING(MULTI_CHUNK)) {
		if(!downloads.empty()) {
			return Segment(-1, 0);
		}

		int64_t start = 0;
		int64_t end = getSize();

		if(!done.empty()) {
			const Segment& first = *done.begin();

			if(first.getStart() > 0) {
				end = Util::roundUp(first.getStart(), blockSize);
			} else {
				start = Util::roundDown(first.getEnd(), blockSize);

				if(done.size() > 1) {
					const Segment& second = *(++done.begin());
					end = Util::roundUp(second.getStart(), blockSize);
				}
			}
		}

		return Segment(start, std::min(getSize(), end) - start);
	}
	
	if(downloads.size() >= maxSegments ||
		(BOOLSETTING(DONT_BEGIN_SEGMENT) && (size_t)(SETTING(DONT_BEGIN_SEGMENT_SPEED) * 1024) < getAverageSpeed()))
	{
		// no other segments if we have reached the speed or segment limit
		return Segment(-1, 0);
	}

	/* added for PFS */
	vector<int64_t> posArray;
	vector<Segment> neededParts;

	if(partialSource) {
		posArray.reserve(partialSource->getPartialInfo().size());

		// Convert block index to file position
		for(PartsInfo::const_iterator i = partialSource->getPartialInfo().begin(); i != partialSource->getPartialInfo().end(); i++)
			posArray.push_back(min(getSize(), (int64_t)(*i) * blockSize));
	}

	/***************************/

	double donePart = static_cast<double>(getDownloadedBytes()) / getSize();
		
	// We want smaller blocks at the end of the transfer, squaring gives a nice curve...
	int64_t targetSize = static_cast<int64_t>(static_cast<double>(wantedSize) * std::max(0.25, (1. - (donePart * donePart))));
		
	if(targetSize > blockSize) {
		// Round off to nearest block size
		targetSize = Util::roundDown(targetSize, blockSize);
	} else {
		targetSize = blockSize;
	}		

	int64_t start = 0;
	int64_t curSize = targetSize;

	while(start < getSize()) {
		int64_t end = std::min(getSize(), start + curSize);
		Segment block(start, end - start);
		bool overlaps = false;
		for(SegmentConstIter i = done.begin(); !overlaps && i != done.end(); ++i) {
			if(curSize <= blockSize) {
				int64_t dstart = i->getStart();
				int64_t dend = i->getEnd();
				// We accept partial overlaps, only consider the block done if it is fully consumed by the done block
				if(dstart <= start && dend >= end) {
					overlaps = true;
				}
			} else {
				overlaps = block.overlaps(*i);
			}
		}
		
		for(auto i = downloads.begin(); !overlaps && i != downloads.end(); ++i) {
			overlaps = block.overlaps((*i)->getSegment());
		}
		
		if(!overlaps) {
			if(partialSource) {
				// store all chunks we could need
				for(vector<int64_t>::const_iterator j = posArray.begin(); j < posArray.end(); j += 2){
					if( (*j <= start && start < *(j+1)) || (start <= *j && *j < end) ) {
						int64_t b = max(start, *j);
						int64_t e = min(end, *(j+1));

						// segment must be blockSize aligned
						dcassert(b % blockSize == 0);
						dcassert(e % blockSize == 0 || e == getSize());

						neededParts.push_back(Segment(b, e - b));
					}
				}
			} else {
				return block;
			}
		}
		
		if(overlaps && (curSize > blockSize)) {
			curSize -= blockSize;
		} else {
			start = end;
			curSize = targetSize;
		}
	}

	if(!neededParts.empty()) {
		// select random chunk for download
		dcdebug("Found chunks: %d\n", neededParts.size());
		
		Segment& selected = neededParts[Util::rand(0, neededParts.size())];
		selected.setSize(std::min(selected.getSize(), targetSize));	// request only wanted size
		
		return selected;
	}
	
	if(partialSource == NULL && BOOLSETTING(OVERLAP_CHUNKS) && lastSpeed > 0) {
		// overlap slow running chunk

		for(auto i = downloads.begin(); i != downloads.end(); ++i) {
			Download* d = *i;
			
			// current chunk mustn't be already overlapped
			if(d->getOverlapped())
				continue;

			// current chunk must be running at least for 4 seconds
			if(d->getStart() == 0 || GET_TICK() - d->getStart() < 4000) 
				continue;

			// current chunk mustn't be finished in next 20 seconds
			if(d->getSecondsLeft() < 20)
				continue;

			// overlap current chunk at last block boundary
			int64_t pos = d->getPos() - (d->getPos() % blockSize);
			int64_t size = d->getSize() - pos;

			// new user should finish this chunk more than 2x faster
			int64_t newChunkLeft = size / lastSpeed;
			if(2 * newChunkLeft < d->getSecondsLeft()) {
				dcdebug("Overlapping... old user: %I64d s, new user: %I64d s\n", d->getSecondsLeft(), newChunkLeft);
				return Segment(d->getStartPos() + pos, size, true);
			}
		}
	}

	return Segment(0, 0);
}
Exemplo n.º 3
0
void DownloadManager::startData(UserConnection* aSource, int64_t start, int64_t bytes, bool z) {
    Download* d = aSource->getDownload();
    dcassert(d != NULL);

    dcdebug("Preparing " I64_FMT ":" I64_FMT ", " I64_FMT ":" I64_FMT"\n",
            static_cast<long long int>(d->getStartPos()), static_cast<long long int>(start),
            static_cast<long long int>(d->getSize()), static_cast<long long int>(bytes));
    if(d->getSize() == -1) {
        if(bytes >= 0) {
            d->setSize(bytes);
        } else {
            failDownload(aSource, _("Invalid size"));
            return;
        }
    } else if(d->getSize() != bytes || d->getStartPos() != start) {
        // This is not what we requested...
        failDownload(aSource, _("Response does not match request"));
        return;
    }

    try {
        QueueManager::getInstance()->setFile(d);
    } catch(const FileException& e) {
        failDownload(aSource, str(F_("Could not open target file: %1%") % e.getError()));
        return;
    } catch(const Exception& e) {
        failDownload(aSource, e.getError());
        return;
    }

    if((d->getType() == Transfer::TYPE_FILE || d->getType() == Transfer::TYPE_FULL_LIST) && SETTING(BUFFER_SIZE) > 0 ) {
        d->setFile(new BufferedOutputStream<true>(d->getFile()));
    }

    if(d->getType() == Transfer::TYPE_FILE) {
        typedef MerkleCheckOutputStream<TigerTree, true> MerkleStream;

        d->setFile(new MerkleStream(d->getTigerTree(), d->getFile(), d->getStartPos()));
        d->setFlag(Download::FLAG_TTH_CHECK);
    }

    // Check that we don't get too many bytes
    d->setFile(new LimitedOutputStream<true>(d->getFile(), bytes));

    if(z) {
        d->setFlag(Download::FLAG_ZDOWNLOAD);
        d->setFile(new FilteredOutputStream<UnZFilter, true>(d->getFile()));
    }

    d->setStart(GET_TICK());
    d->tick();
    aSource->setState(UserConnection::STATE_RUNNING);

    fire(DownloadManagerListener::Starting(), d);

    if(d->getPos() == d->getSize()) {
        try {
            // Already finished? A zero-byte file list could cause this...
            endData(aSource);
        } catch(const Exception& e) {
            failDownload(aSource, e.getError());
        }
    } else {
        aSource->setDataMode();
    }
}