diff options
| author | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2006-04-06 12:52:16 +0000 |
|---|---|---|
| committer | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2006-04-06 12:52:16 +0000 |
| commit | 9a52be6a29369cb7ac97c20c11218b16c2a648dd (patch) | |
| tree | 8f6abdedda606fb320d8874f383430677d1a2d18 /src | |
| parent | 7228402dadd541e3bea0b939d17c36202180012a (diff) | |
2006-04-06 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
To print ETA:
* src/TorrentDownloadEngine.cc (afterEachIteration): Added
download
completion handling when dealing with
TorrentMan::isPartialDownloadingMode() == true.
* src/TorrentDownloadEngine.h (onPartialDownloadingCompletes):
New function.
* src/TorrentConsoleDownloadEngine.h (startup): New variable.
(sessionDownloadLength): New variable.
(avgSpeed): New variable.
(eta): New variable.
* src/TorrentConsoleDownloadEngine.cc (initStatistics):
Initialized
new variables: eta, avgSpeed, startup.
(calculateSpeed): Calculate average speed and ETA.
(printStatistics): Added ETA.
* src/Util.h (secfmt): New function.
* src/Util.cc (secfmt): New function.
2006-04-05 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
To detect "keep alive" flooding:
* src/PeerInteractionCommand.h (keepAliveCount): New variable
* src/PeerInteractionCommand.cc (Constructor): Initialized new
variable: keepAliveCount.
(detectMessageFlooding): Added "keep alive" flooding detection.
(receiveMessage): Increase keepAliveCount when "keep alive"
message
received.
To add the ability to download only specified files in
multi-file
torrent:
* src/BitfieldMan.h (filterBitfield): New variable.
(filterEnabled): New variable.
(setFilterBit): New function.
(enableFilter): New function.
(disableFilter): New function.
(isFilterEnabled): New function.
(getFilteredTotalLength): New function.
(getCompletedLength): New function.
* src/BitfieldMan.cc (Constructor): Initialized new variable:
filterBitfield, filterEnabled.
(CopyConstructor): Added filterBitfield and filterEnabled.
(operator==): Added filterBitfield and filterEnabled.
(Destructor): Added filterBitfield.
(getMissingIndex): Use filterBitfield.
(getMissingUnusedIndex): Use filterBitfield.
(getFirstMissingUnusedIndex): Use filterBitfield.
(getFirstMissingUnusedIndex): Use filterBitfield.
(getAllMissingIndexes): Use filterBitfield.
(countMissingBlock): Use filterBitfield.
(countBlock): Use filterBitfield.
(setBitInternal): Added new argument on.
(setUseBit): Use setBitInternal.
(unsetUseBit): Use setBitInternal.
(setBit): Use setBitInternal.
(unsetBit): Use setBitInternal.
(isAllBitSet): Use filterBitfield.
(setFilterBit): New function.
(addFilter): New function.
(enableFilter): New function.
(disableFilter): New function.
(clearFilter): New function.
(isFilterEnabled): New function.
(getFilteredTotalLength): New function.
(getCompletedLength): New function.
* src/TorrentMan.h [FileEntry](Constructor): Updated signature.
Initalized newly added variables.
[FileEntry](offset): New variable.
[FileEntry](extracted): New variable.
[FileEntry](requested): New variable.
(readFileEntry): New function.
(option): New variable.
(splitMultiFile): Removed const qualifier.
(fixFilename): Removed const qualifier.
(readFileEntryFromMetaInfoFile): New function.
(finishPartialDownloadingMode): New function.
(isPartialDownloadingMode): New function.
(setFileEntriesToDownload): New function.
(getCompletedLength): New function.
(getPartialTotalLength): New function.
* src/TorrentMan.cc (readFileEntry): New function.
(setup): Use readFileEntry. If no-preallocation option is
specified,
use DefaultDiskWriter.
(readFileEntryFromMetaInfoFile): New function.
(fixFilename): Removed const qualifier.
(splitMultiFile): Removed const qualifier.
(setFileEntriesToDownload): New function.
(isPartialDownloadingMode): New function.
(finishPartialDownloadingMode): New function.
(getCompletedLength): New function.
(getPartialTotalLength): New function.
* src/TorrentConsoleDownloadEngine.h
(partialDownloadLengthDiff):
New variable.
(partialTotalLength): New variable.
(downloadLength): New variable.
(totalLength): New variable.
* src/TorrentConsoleDownloadEngine.cc
(onPartialDownloadingCompletes):
Added log.
(initStatistics): Initialized new variables:
partialDownloadLengthDiff,
partialTotalLength, downloadLength, totalLength.
(calculate): Calculate downloadLength and totalLength.
* src/prefs.h :New definition PREF_NO_PREALLOCATION
* src/main.cc (addCommand): Changed argument signature.
(main): Added new variable: args. Added new option
"torrent-show-files"
"no-preallocation". Usage is not updated yet.
2006-04-02 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
* src/PeerMessage.cc (setBitfield): Fixed invalid memory
de-allocation.
Diffstat (limited to 'src')
| -rw-r--r-- | src/BitfieldMan.cc | 185 | ||||
| -rw-r--r-- | src/BitfieldMan.h | 40 | ||||
| -rw-r--r-- | src/PeerInteractionCommand.cc | 6 | ||||
| -rw-r--r-- | src/PeerInteractionCommand.h | 1 | ||||
| -rw-r--r-- | src/PeerMessage.cc | 4 | ||||
| -rw-r--r-- | src/TorrentConsoleDownloadEngine.cc | 47 | ||||
| -rw-r--r-- | src/TorrentConsoleDownloadEngine.h | 10 | ||||
| -rw-r--r-- | src/TorrentDownloadEngine.cc | 10 | ||||
| -rw-r--r-- | src/TorrentDownloadEngine.h | 1 | ||||
| -rw-r--r-- | src/TorrentMan.cc | 130 | ||||
| -rw-r--r-- | src/TorrentMan.h | 26 | ||||
| -rw-r--r-- | src/Util.cc | 21 | ||||
| -rw-r--r-- | src/Util.h | 2 | ||||
| -rw-r--r-- | src/main.cc | 44 | ||||
| -rw-r--r-- | src/prefs.h | 3 |
15 files changed, 454 insertions, 76 deletions
diff --git a/src/BitfieldMan.cc b/src/BitfieldMan.cc index 7eda3fe2..b7e9aed6 100644 --- a/src/BitfieldMan.cc +++ b/src/BitfieldMan.cc @@ -23,7 +23,8 @@ #include <string.h> BitfieldMan::BitfieldMan(int blockLength, long long int totalLength) - :blockLength(blockLength), totalLength(totalLength) { + :blockLength(blockLength), totalLength(totalLength), filterBitfield(0), + filterEnabled(false) { if(blockLength > 0 && totalLength > 0) { blocks = totalLength/blockLength+(totalLength%blockLength ? 1 : 0); bitfieldLength = blocks/8+(blocks%8 ? 1 : 0); @@ -43,11 +44,21 @@ BitfieldMan::BitfieldMan(const BitfieldMan& bitfieldMan) { useBitfield = new unsigned char[bitfieldLength]; memcpy(bitfield, bitfieldMan.bitfield, bitfieldLength); memcpy(useBitfield, bitfieldMan.useBitfield, bitfieldLength); + filterEnabled = bitfieldMan.filterEnabled; + if(bitfieldMan.filterBitfield) { + filterBitfield = new unsigned char[bitfieldLength]; + memcpy(filterBitfield, bitfieldMan.filterBitfield, bitfieldLength); + } else { + filterBitfield = 0; + } } BitfieldMan::~BitfieldMan() { delete [] bitfield; delete [] useBitfield; + if(filterBitfield) { + delete [] filterBitfield; + } } BitfieldMan& BitfieldMan::operator=(const BitfieldMan& bitfieldMan) { @@ -64,6 +75,17 @@ BitfieldMan& BitfieldMan::operator=(const BitfieldMan& bitfieldMan) { bitfieldLength = bitfieldMan.bitfieldLength; memcpy(bitfield, bitfieldMan.bitfield, bitfieldLength); memcpy(useBitfield, bitfieldMan.useBitfield, bitfieldLength); + filterEnabled = bitfieldMan.filterEnabled; + if(bitfieldLength != bitfieldMan.bitfieldLength) { + delete [] filterBitfield; + filterBitfield = 0; + } + if(bitfieldMan.filterBitfield) { + if(!filterBitfield) { + filterBitfield = new unsigned char[bitfieldLength]; + } + memcpy(filterBitfield, bitfieldMan.filterBitfield, bitfieldLength); + } } return *this; } @@ -108,6 +130,9 @@ int BitfieldMan::getMissingIndex(const unsigned char* peerBitfield, int length) unsigned char* tempBitfield = new unsigned char[bitfieldLength]; for(int i = 0; i < bitfieldLength; i++) { tempBitfield[i] = peerBitfield[i] & ~bitfield[i]; + if(filterEnabled) { + tempBitfield[i] &= filterBitfield[i]; + } } int max = countSetBit(tempBitfield, bitfieldLength); int index = getMissingIndexRandomly(tempBitfield, bitfieldLength, max); @@ -122,6 +147,9 @@ int BitfieldMan::getMissingUnusedIndex(const unsigned char* peerBitfield, int le unsigned char* tempBitfield = new unsigned char[bitfieldLength]; for(int i = 0; i < bitfieldLength; i++) { tempBitfield[i] = peerBitfield[i] & ~bitfield[i] & ~useBitfield[i]; + if(filterEnabled) { + tempBitfield[i] &= filterBitfield[i]; + } } int max = countSetBit(tempBitfield, bitfieldLength); int index = getMissingIndexRandomly(tempBitfield, bitfieldLength, max); @@ -135,6 +163,9 @@ int BitfieldMan::getFirstMissingUnusedIndex(const unsigned char* peerBitfield, i } for(int i = 0; i < bitfieldLength; i++) { unsigned char bit = peerBitfield[i] & ~bitfield[i] & ~useBitfield[i]; + if(filterEnabled) { + bit &= filterBitfield[i]; + } for(int bs = 7; bs >= 0 && i*8+7-bs < blocks; bs--) { unsigned char mask = 1 << bs; if(bit & mask) { @@ -148,6 +179,9 @@ int BitfieldMan::getFirstMissingUnusedIndex(const unsigned char* peerBitfield, i int BitfieldMan::getFirstMissingUnusedIndex() const { for(int i = 0; i < bitfieldLength; i++) { unsigned char bit = ~bitfield[i] & ~useBitfield[i]; + if(filterEnabled) { + bit &= filterBitfield[i]; + } for(int bs = 7; bs >= 0 && i*8+7-bs < blocks; bs--) { unsigned char mask = 1 << bs; if(bit & mask) { @@ -162,6 +196,9 @@ BlockIndexes BitfieldMan::getAllMissingIndexes() const { BlockIndexes missingIndexes; for(int i = 0; i < bitfieldLength; i++) { unsigned char bit = ~bitfield[i]; + if(filterEnabled) { + bit &= filterBitfield[i]; + } for(int bs = 7; bs >= 0 && i*8+7-bs < blocks; bs--) { unsigned char mask = 1 << bs; if(bit & mask) { @@ -173,47 +210,75 @@ BlockIndexes BitfieldMan::getAllMissingIndexes() const { } int BitfieldMan::countMissingBlock() const { - return blocks-countSetBit(bitfield, bitfieldLength); + if(filterEnabled) { + unsigned char* temp = new unsigned char[bitfieldLength]; + for(int i = 0; i < bitfieldLength; i++) { + temp[i] = bitfield[i]&filterBitfield[i]; + } + int count = countSetBit(filterBitfield, bitfieldLength)- + countSetBit(temp, bitfieldLength); + delete [] temp; + return count; + } else { + return blocks-countSetBit(bitfield, bitfieldLength); + } } -bool BitfieldMan::setUseBit(int index) { +int BitfieldMan::countBlock() const { + if(filterEnabled) { + return countSetBit(filterBitfield, bitfieldLength); + } else { + return blocks; + } +} + +bool BitfieldMan::setBitInternal(unsigned char* bitfield, int index, bool on) { if(blocks <= index) { return false; } unsigned char mask = 128 >> index%8; - useBitfield[index/8] |= mask; + if(on) { + bitfield[index/8] |= mask; + } else { + bitfield[index/8] &= ~mask; + } return true; } +bool BitfieldMan::setUseBit(int index) { + return setBitInternal(useBitfield, index, true); +} + bool BitfieldMan::unsetUseBit(int index) { - if(blocks <= index) { return false; } - unsigned char mask = 128 >> index%8; - useBitfield[index/8] &= ~mask; - return true; + return setBitInternal(useBitfield, index, false); } bool BitfieldMan::setBit(int index) { - if(blocks <= index) { return false; } - unsigned char mask = 128 >> index%8; - bitfield[index/8] |= mask; - return true; + return setBitInternal(bitfield, index, true); } + bool BitfieldMan::unsetBit(int index) { - if(blocks <= index) { return false; } - unsigned char mask = 128 >> index%8; - bitfield[index/8] &= ~mask; - return true; + return setBitInternal(bitfield, index, false); } bool BitfieldMan::isAllBitSet() const { - for(int i = 0; i < bitfieldLength-1; i++) { - if(bitfield[i] != 0xff) { + if(filterEnabled) { + for(int i = 0; i < bitfieldLength-1; i++) { + if((bitfield[i]&filterBitfield[i]) != filterBitfield[i]) { + return false; + } + } + return true; + } else { + for(int i = 0; i < bitfieldLength-1; i++) { + if(bitfield[i] != 0xff) { + return false; + } + } + unsigned char b = ~((128 >> (blocks-1)%8)-1); + if(bitfield[bitfieldLength-1] != b) { return false; } + return true; } - unsigned char b = ~((128 >> (blocks-1)%8)-1); - if(bitfield[bitfieldLength-1] != b) { - return false; - } - return true; } bool BitfieldMan::isBitSetInternal(const unsigned char* bitfield, int index) const { @@ -248,3 +313,77 @@ void BitfieldMan::setAllBit() { setBit(i); } } + +bool BitfieldMan::setFilterBit(int index) { + return setBitInternal(filterBitfield, index, true); +} + +void BitfieldMan::addFilter(long long int offset, long long int length) { + if(!filterBitfield) { + filterBitfield = new unsigned char[bitfieldLength]; + memset(filterBitfield, 0, bitfieldLength); + } + int startBlock = offset/blockLength; + int endBlock = (offset+length-1)/blockLength; + for(int i = startBlock; i <= endBlock && i < blocks; i++) { + setFilterBit(i); + } +} + +void BitfieldMan::enableFilter() { + filterEnabled = true; +} + +void BitfieldMan::disableFilter() { + filterEnabled = false; +} + +void BitfieldMan::clearFilter() { + if(filterBitfield) { + delete [] filterBitfield; + filterBitfield = 0; + } + filterEnabled = false; +} + +bool BitfieldMan::isFilterEnabled() const { + return filterEnabled; +} + +long long int BitfieldMan::getFilteredTotalLength() const { + if(!filterBitfield) { + return 0; + } + int filteredBlocks = countSetBit(filterBitfield, bitfieldLength); + if(filteredBlocks == 0) { + return 0; + } + if(isBitSetInternal(filterBitfield, blocks-1)) { + return ((long long int)filteredBlocks-1)*blockLength+getLastBlockLength(); + } else { + return ((long long int)filteredBlocks)*blockLength; + } +} + +long long int BitfieldMan::getCompletedLength() const { + unsigned char* temp = new unsigned char[bitfieldLength]; + for(int i = 0; i < bitfieldLength; i++) { + temp[i] = bitfield[i]; + if(filterEnabled) { + temp[i] &= filterBitfield[i]; + } + } + int completedBlocks = countSetBit(temp, bitfieldLength); + long long int completedLength = 0; + if(completedBlocks == 0) { + completedLength = 0; + } else { + if(isBitSetInternal(temp, blocks-1)) { + completedLength = ((long long int)completedBlocks-1)*blockLength+getLastBlockLength(); + } else { + completedLength = ((long long int)completedBlocks)*blockLength; + } + } + delete [] temp; + return completedLength; +} diff --git a/src/BitfieldMan.h b/src/BitfieldMan.h index 3f8165ae..f329a52f 100644 --- a/src/BitfieldMan.h +++ b/src/BitfieldMan.h @@ -33,12 +33,15 @@ private: long long int totalLength; unsigned char* bitfield; unsigned char* useBitfield; + unsigned char* filterBitfield; int bitfieldLength; int blocks; - + bool filterEnabled; int countSetBit(const unsigned char* bitfield, int len) const; int getMissingIndexRandomly(const unsigned char* bitfield, int len, int randMax) const; bool isBitSetInternal(const unsigned char* bitfield, int index) const; + bool setBitInternal(unsigned char* bitfield, int index, bool on); + bool setFilterBit(int index); public: BitfieldMan(int blockLength, long long int totalLength); BitfieldMan(const BitfieldMan& bitfieldMan); @@ -61,11 +64,29 @@ public: } long long int getTotalLength() const { return totalLength; } + /** + * affected by filter + */ int getMissingIndex(const unsigned char* bitfield, int len) const; + /** + * affected by filter + */ int getFirstMissingUnusedIndex(const unsigned char* bitfield, int len) const; + /** + * affected by filter + */ int getFirstMissingUnusedIndex() const; + /** + * affected by filter + */ int getMissingUnusedIndex(const unsigned char* bitfield, int len) const; + /** + * affected by filter + */ BlockIndexes getAllMissingIndexes() const; + /** + * affected by filter + */ int countMissingBlock() const; bool setUseBit(int index); bool unsetUseBit(int index); @@ -76,12 +97,18 @@ public: bool isBitSet(int index) const; bool isUseBitSet(int index) const; + /** + * affected by filter + */ bool isAllBitSet() const; const unsigned char* getBitfield() const { return bitfield; } int getBitfieldLength() const { return bitfieldLength; } - int countBlock() const { return blocks; } + /** + * affected by filter + */ + int countBlock() const; void setBitfield(const unsigned char* bitfield, int bitfieldLength); @@ -90,6 +117,15 @@ public: void addFilter(long long int offset, long long int length); void clearFilter(); + + void enableFilter(); + void disableFilter(); + bool isFilterEnabled() const; + long long int getFilteredTotalLength() const; + /** + * affected by filter + */ + long long int getCompletedLength() const; }; #endif // _D_BITFIELD_MAN_H_ diff --git a/src/PeerInteractionCommand.cc b/src/PeerInteractionCommand.cc index 80dac752..691f9043 100644 --- a/src/PeerInteractionCommand.cc +++ b/src/PeerInteractionCommand.cc @@ -50,6 +50,7 @@ PeerInteractionCommand::PeerInteractionCommand(int cuid, Peer* peer, freqCheckPoint.tv_usec = 0; chokeUnchokeCount = 0; haveCount = 0; + keepAliveCount = 0; } PeerInteractionCommand::~PeerInteractionCommand() { @@ -147,11 +148,13 @@ void PeerInteractionCommand::detectMessageFlooding() { } else { if(Util::difftv(now, freqCheckPoint) >= 5*1000000) { if(chokeUnchokeCount*1.0/(Util::difftv(now, freqCheckPoint)/1000000) >= 0.4 - || haveCount*1.0/(Util::difftv(now, freqCheckPoint)/1000000) >= 20.0) { + || haveCount*1.0/(Util::difftv(now, freqCheckPoint)/1000000) >= 20.0 + || keepAliveCount*1.0/(Util::difftv(now, freqCheckPoint)/1000000) >= 1.0) { throw new DlAbortEx("flooding detected."); } else { chokeUnchokeCount = 0; haveCount = 0; + keepAliveCount = 0; freqCheckPoint = now; } } @@ -220,6 +223,7 @@ void PeerInteractionCommand::receiveMessage() { try { switch(message->getId()) { case PeerMessage::KEEP_ALIVE: + keepAliveCount++; break; case PeerMessage::CHOKE: if(!peer->peerChoking) { diff --git a/src/PeerInteractionCommand.h b/src/PeerInteractionCommand.h index 24e7057e..c3c0123b 100644 --- a/src/PeerInteractionCommand.h +++ b/src/PeerInteractionCommand.h @@ -41,6 +41,7 @@ private: struct timeval freqCheckPoint; int chokeUnchokeCount; int haveCount; + int keepAliveCount; void receiveMessage(); void detectMessageFlooding(); void checkLongTimePeerChoking(); diff --git a/src/PeerMessage.cc b/src/PeerMessage.cc index ddaa3066..65690ced 100644 --- a/src/PeerMessage.cc +++ b/src/PeerMessage.cc @@ -24,7 +24,7 @@ void PeerMessage::setBitfield(const unsigned char* bitfield, int bitfieldLength) { if(this->bitfield != NULL) { - delete [] bitfield; + delete [] this->bitfield; } this->bitfieldLength = bitfieldLength; this->bitfield = new unsigned char[this->bitfieldLength]; @@ -33,7 +33,7 @@ void PeerMessage::setBitfield(const unsigned char* bitfield, int bitfieldLength) void PeerMessage::setBlock(const char* block, int blockLength) { if(this->block != NULL) { - delete [] block; + delete [] this->block; } this->blockLength = blockLength; this->block = new char[this->blockLength]; diff --git a/src/TorrentConsoleDownloadEngine.cc b/src/TorrentConsoleDownloadEngine.cc index 9bf6adb8..8ae62910 100644 --- a/src/TorrentConsoleDownloadEngine.cc +++ b/src/TorrentConsoleDownloadEngine.cc @@ -26,21 +26,26 @@ TorrentConsoleDownloadEngine::TorrentConsoleDownloadEngine() {} TorrentConsoleDownloadEngine::~TorrentConsoleDownloadEngine() {} +void TorrentConsoleDownloadEngine::onPartialDownloadingCompletes() { + printf("Download of specified files has completed. Continue normal download operation.\n"); +} + void TorrentConsoleDownloadEngine::printStatistics() { printf("\r "); printf("\r"); if(torrentMan->downloadComplete()) { printf("Download Completed "); } else { - printf("%s/%sB %d%% DW:%.2f", - Util::llitos(torrentMan->getDownloadLength(), true).c_str(), - Util::llitos(torrentMan->getTotalLength(), true).c_str(), - (torrentMan->getTotalLength() == 0 ? - 0 : (int)((torrentMan->getDownloadLength()*100)/torrentMan->getTotalLength())), - downloadSpeed/1000.0); + printf("%s/%sB %d%% %s D:%.2f", + Util::llitos(downloadLength, true).c_str(), + Util::llitos(totalLength, true).c_str(), + (totalLength == 0 ? + 0 : (int)((downloadLength*100)/totalLength)), + avgSpeed == 0 ? "-" : Util::secfmt(eta).c_str(), + downloadSpeed/1024.0); } - printf(" UP:%.2f(%s) %dpeers", - uploadSpeed/1000.0, + printf(" U:%.2f(%s) %dpeers", + uploadSpeed/1024.0, Util::llitos(torrentMan->getUploadLength(), true).c_str(), torrentMan->connections); fflush(stdout); @@ -52,11 +57,21 @@ void TorrentConsoleDownloadEngine::initStatistics() { lastElapsed = 0; gettimeofday(&cp[0], NULL); gettimeofday(&cp[1], NULL); + gettimeofday(&startup, NULL); sessionDownloadLengthArray[0] = 0; sessionDownloadLengthArray[1] = 0; sessionUploadLengthArray[0] = 0; sessionUploadLengthArray[1] = 0; currentCp = 0; + eta = 0; + avgSpeed = 0; + sessionDownloadLength = 0; + downloadLength = 0; + totalLength = 0; + if(torrentMan->isPartialDownloadingMode()) { + partialDownloadLengthDiff = torrentMan->getDownloadLength()-torrentMan->getCompletedLength(); + partialTotalLength = torrentMan->getPartialTotalLength(); + } } int TorrentConsoleDownloadEngine::calculateSpeed(long long int sessionLength, long long int elapsed) { @@ -74,12 +89,28 @@ void TorrentConsoleDownloadEngine::calculateStatistics() { sessionDownloadLengthArray[1] += torrentMan->getDeltaDownloadLength(); sessionUploadLengthArray[1] += torrentMan->getDeltaUploadLength(); + sessionDownloadLength += torrentMan->getDeltaDownloadLength(); + downloadSpeed = calculateSpeed(sessionDownloadLengthArray[currentCp], elapsed); uploadSpeed = calculateSpeed(sessionUploadLengthArray[currentCp], elapsed); torrentMan->resetDeltaDownloadLength(); torrentMan->resetDeltaUploadLength(); + if(torrentMan->isPartialDownloadingMode()) { + downloadLength = torrentMan->getDownloadLength()-partialDownloadLengthDiff; + totalLength = partialTotalLength; + } else { + downloadLength = torrentMan->getDownloadLength(); + totalLength = torrentMan->getTotalLength(); + } + + avgSpeed = calculateSpeed(sessionDownloadLength, + Util::difftv(now, startup)); + if(avgSpeed != 0) { + eta = (totalLength-downloadLength)/avgSpeed; + } + if(elapsed-lastElapsed >= 1000000) { printStatistics(); lastElapsed = elapsed; diff --git a/src/TorrentConsoleDownloadEngine.h b/src/TorrentConsoleDownloadEngine.h index eb34dff4..0d177e14 100644 --- a/src/TorrentConsoleDownloadEngine.h +++ b/src/TorrentConsoleDownloadEngine.h @@ -39,11 +39,21 @@ private: int downloadSpeed; int uploadSpeed; long long int lastElapsed; + long long int partialDownloadLengthDiff; + long long int partialTotalLength; + struct timeval startup; + long long int sessionDownloadLength; + int avgSpeed; + int eta; + long long int downloadLength; + long long int totalLength; + void printStatistics(); int calculateSpeed(long long int sessionLength, long long int elapsed); protected: void initStatistics(); void calculateStatistics(); + void onPartialDownloadingCompletes(); public: TorrentConsoleDownloadEngine(); ~TorrentConsoleDownloadEngine(); diff --git a/src/TorrentDownloadEngine.cc b/src/TorrentDownloadEngine.cc index 9ab1520a..c2c8c254 100644 --- a/src/TorrentDownloadEngine.cc +++ b/src/TorrentDownloadEngine.cc @@ -35,7 +35,15 @@ void TorrentDownloadEngine::afterEachIteration() { torrentMan->diskWriter->closeFile(); torrentMan->save(); torrentMan->fixFilename(); - filenameFixed = true; + if(torrentMan->isPartialDownloadingMode()) { + torrentMan->finishPartialDownloadingMode(); + onPartialDownloadingCompletes(); + if(torrentMan->downloadComplete()) { + filenameFixed = true; + } + } else { + filenameFixed = true; + } torrentMan->diskWriter->openExistingFile(torrentMan->getTempFilePath()); } } diff --git a/src/TorrentDownloadEngine.h b/src/TorrentDownloadEngine.h index 8bbd7606..b8a8d47b 100644 --- a/src/TorrentDownloadEngine.h +++ b/src/TorrentDownloadEngine.h @@ -31,6 +31,7 @@ private: protected: void onEndOfRun(); void afterEachIteration(); + virtual void onPartialDownloadingCompletes() = 0; public: TorrentDownloadEngine():filenameFixed(false) {} virtual ~TorrentDownloadEngine() {} diff --git a/src/TorrentMan.cc b/src/TorrentMan.cc index 275547aa..04e6cd11 100644 --- a/src/TorrentMan.cc +++ b/src/TorrentMan.cc @@ -29,6 +29,8 @@ #include "File.h" #include "message.h" #include "PreAllocationDiskWriter.h" +#include "DefaultDiskWriter.h" +#include "prefs.h" #include <errno.h> #include <libgen.h> #include <string.h> @@ -295,28 +297,12 @@ bool TorrentMan::downloadComplete() const { return bitfield->isAllBitSet(); } -void TorrentMan::setup(string metaInfoFile) { - peerId = "-A2****-"; - for(int i = 0; i < 12; i++) { - peerId += Util::itos((int)(((double)10)*random()/(RAND_MAX+1.0))); - } - - uploadLength = 0; - downloadLength = 0; - Dictionary* topDic = (Dictionary*)MetaFileUtil::parseMetaFile(metaInfoFile); - const Dictionary* infoDic = (const Dictionary*)topDic->get("info"); - ShaVisitor v; - infoDic->accept(&v); - unsigned char md[20]; - int len; - v.getHash(md, len); - setInfoHash(md); - +void TorrentMan::readFileEntry(const Dictionary* infoDic, const string& defaultName) { Data* topName = (Data*)infoDic->get("name"); if(topName != NULL) { name = topName->toString(); } else { - char* basec = strdup(metaInfoFile.c_str()); + char* basec = strdup(defaultName.c_str()); name = string(basename(basec))+".file"; free(basec); } @@ -328,6 +314,7 @@ void TorrentMan::setup(string metaInfoFile) { totalLength = length->toLLInt(); } else { long long int length = 0; + long long int offset = 0; // multi-file mode setFileMode(MULTI); multiFileTopDir = new Directory(name); @@ -350,11 +337,33 @@ void TorrentMan::setup(string metaInfoFile) { } Data* lastpath = (Data*)paths.back(); filePath.append("/").append(lastpath->toString()); - FileEntry fileEntry(filePath, lengthData->toLLInt()); + FileEntry fileEntry(filePath, lengthData->toLLInt(), offset); multiFileEntries.push_back(fileEntry); + offset += fileEntry.length; } totalLength = length; } +} + +void TorrentMan::setup(string metaInfoFile) { + peerId = "-A2****-"; + for(int i = 0; i < 12; i++) { + peerId += Util::itos((int)(((double)10)*random()/(RAND_MAX+1.0))); + } + + uploadLength = 0; + downloadLength = 0; + Dictionary* topDic = (Dictionary*)MetaFileUtil::parseMetaFile(metaInfoFile); + const Dictionary* infoDic = (const Dictionary*)topDic->get("info"); + ShaVisitor v; + infoDic->accept(&v); + unsigned char md[20]; + int len; + v.getHash(md, len); + setInfoHash(md); + + readFileEntry(infoDic, metaInfoFile); + announce = ((Data*)topDic->get("announce"))->toString(); pieceLength = ((Data*)infoDic->get("piece length"))->toInt(); pieces = totalLength/pieceLength+(totalLength%pieceLength ? 1 : 0); @@ -371,7 +380,11 @@ void TorrentMan::setup(string metaInfoFile) { initBitfield(); delete topDic; - diskWriter = new PreAllocationDiskWriter(totalLength); + if(option->get(PREF_NO_PREALLOCATION) == V_TRUE) { + diskWriter = new DefaultDiskWriter(); + } else { + diskWriter = new PreAllocationDiskWriter(totalLength); + } if(segmentFileExists()) { load(); diskWriter->openExistingFile(getTempFilePath()); @@ -385,6 +398,12 @@ const MultiFileEntries& TorrentMan::getMultiFileEntries() const { return multiFileEntries; } +void TorrentMan::readFileEntryFromMetaInfoFile(const string& metaInfoFile) { + Dictionary* topDic = (Dictionary*)MetaFileUtil::parseMetaFile(metaInfoFile); + const Dictionary* infoDic = (const Dictionary*)topDic->get("info"); + readFileEntry(infoDic, metaInfoFile); +} + string TorrentMan::getName() const { return name; } @@ -498,7 +517,7 @@ void TorrentMan::remove() const { } } -void TorrentMan::fixFilename() const { +void TorrentMan::fixFilename() { if(fileMode == SINGLE) { copySingleFile(); } else { @@ -511,15 +530,18 @@ void TorrentMan::copySingleFile() const { Util::fileCopy(getFilePath(), getTempFilePath()); } -void TorrentMan::splitMultiFile() const { +void TorrentMan::splitMultiFile() { logger->info("creating directories"); multiFileTopDir->createDir(storeDir, true); long long int offset = 0; - for(MultiFileEntries::const_iterator itr = multiFileEntries.begin(); + for(MultiFileEntries::iterator itr = multiFileEntries.begin(); itr != multiFileEntries.end(); itr++) { - string dest = storeDir+"/"+itr->path; - logger->info("writing file %s", dest.c_str()); - Util::rangedFileCopy(dest, getTempFilePath(), offset, itr->length); + if(!itr->extracted && itr->requested) { + string dest = storeDir+"/"+itr->path; + logger->info("writing file %s", dest.c_str()); + Util::rangedFileCopy(dest, getTempFilePath(), offset, itr->length); + itr->extracted = true; + } offset += itr->length; } } @@ -527,3 +549,59 @@ void TorrentMan::splitMultiFile() const { void TorrentMan::deleteTempFile() const { unlink(getTempFilePath().c_str()); } + +// bool TorrentMan::unextractedFileEntryExists() const { +// if(fileMode == SINGLE) { +// return false; +// } +// for(MultiFileEntries::const_iterator itr = multiFileEntries.begin(); +// itr != multiFileEntries.end(); itr++) { +// if(!itr->extracted) { +// return true; +// } +// } +// } + +void TorrentMan::setFileEntriesToDownload(const Strings& filePaths) { + if(fileMode != MULTI) { + throw new DlAbortEx("only multi-mode supports partial downloading mode."); + } + // clear all requested flags in multiFileEntries. + for(MultiFileEntries::iterator itr = multiFileEntries.begin(); + itr != multiFileEntries.end(); itr++) { + itr->requested = false; + } + for(Strings::const_iterator pitr = filePaths.begin(); + pitr != filePaths.end(); pitr++) { + bool found = false; + for(MultiFileEntries::iterator itr = multiFileEntries.begin(); + itr != multiFileEntries.end(); itr++) { + if(*pitr == itr->path) { + itr->requested = true; + found = true; + bitfield->addFilter(itr->offset, itr->length); + break; + } + } + if(!found) { + throw new DlAbortEx("no such file entry <%s>", (*pitr).c_str()); + } + } + bitfield->enableFilter(); +} + +bool TorrentMan::isPartialDownloadingMode() const { + return bitfield->isFilterEnabled(); +} + +void TorrentMan::finishPartialDownloadingMode() { + bitfield->clearFilter(); +} + +long long int TorrentMan::getCompletedLength() const { + return bitfield->getCompletedLength(); +} + +long long int TorrentMan::getPartialTotalLength() const { + return bitfield->getFilteredTotalLength(); +} diff --git a/src/TorrentMan.h b/src/TorrentMan.h index 25b24a86..42affb9b 100644 --- a/src/TorrentMan.h +++ b/src/TorrentMan.h @@ -29,6 +29,8 @@ #include "DiskWriter.h" #include "Piece.h" #include "Directory.h" +#include "Dictionary.h" +#include "Option.h" #include <deque> #include <map> #include <string> @@ -48,7 +50,12 @@ class FileEntry { public: string path; long long int length; - FileEntry(string path, long long int length):path(path), length(length) {} + long long int offset; + bool extracted; + bool requested; + FileEntry(string path, long long int length, long long int offset): + path(path), length(length), offset(offset), + extracted(false), requested(true) {} ~FileEntry() {} }; @@ -90,6 +97,7 @@ private: void deleteUsedPiece(const Piece& piece); int deleteUsedPiecesByFillRate(int fillRate, int toDelete); void reduceUsedPieces(int max); + void readFileEntry(const Dictionary* infoDic, const string& defaultName); public: int pieceLength; int pieces; @@ -108,6 +116,7 @@ public: const Logger* logger; DiskWriter* diskWriter; + const Option* option; int getNewCuid() { return ++cuidCounter; } @@ -213,8 +222,8 @@ public: void remove() const; void copySingleFile() const; - void splitMultiFile() const; - void fixFilename() const; + void splitMultiFile(); + void fixFilename(); void deleteTempFile() const; void setPort(int port) { this->port = port; } @@ -223,9 +232,20 @@ public: int countUsedPiece() const { return usedPieces.size(); } int countAdvertisedPiece() const { return haves.size(); } + void readFileEntryFromMetaInfoFile(const string& metaInfoFile); const MultiFileEntries& getMultiFileEntries() const; string getName() const; + //bool unextractedFileEntryExists() const; + + void finishPartialDownloadingMode(); + bool isPartialDownloadingMode() const; + + void setFileEntriesToDownload(const Strings& filePaths); + + long long int getCompletedLength() const; + long long int getPartialTotalLength() const; + enum FILE_MODE { SINGLE, MULTI diff --git a/src/Util.cc b/src/Util.cc index ee7f5693..d6328add 100644 --- a/src/Util.cc +++ b/src/Util.cc @@ -258,3 +258,24 @@ bool Util::isPowerOf(int num, int base) { } return false; } + +string Util::secfmt(int sec) { + string str; + if(sec >= 3600) { + str = itos(sec/3600)+"h"; + sec %= 3600; + } + if(sec >= 60) { + int min = sec/60; + if(min < 10) { + str += "0"; + } + str += itos(min)+"m"; + sec %= 60; + } + if(sec < 10) { + str += "0"; + } + str += itos(sec)+"s"; + return str; +} @@ -68,6 +68,8 @@ public: static void rangedFileCopy(const string& destFile, const string& src, long long int srcOffset, long long int length); static bool isPowerOf(int num, int base); + + static string secfmt(int sec); }; #endif // _D_UTIL_H_ diff --git a/src/main.cc b/src/main.cc index ca730444..af49abfb 100644 --- a/src/main.cc +++ b/src/main.cc @@ -99,7 +99,7 @@ void torrentHandler(int signal) { exit(0); } -void addCommand(int cuid, const char* url, string referer, Requests& requests) { +void addCommand(int cuid, const string& url, string referer, Requests& requests) { Request* req = new Request(); req->setReferer(referer); if(req->setUrl(url)) { @@ -232,6 +232,7 @@ int main(int argc, char* argv[]) { bool daemonMode = false; string referer; string torrentFile; + Strings args; #ifdef ENABLE_BITTORRENT bool followTorrent = true; #else @@ -281,6 +282,8 @@ int main(int argc, char* argv[]) { #ifdef ENABLE_BITTORRENT { "torrent-file", required_argument, &lopt, 15 }, { "follow-torrent", required_argument, &lopt, 16 }, + { "torrent-show-files", no_argument, &lopt, 17 }, + { "no-preallocation", no_argument, &lopt, 18 }, #endif // ENABLE_BITTORRENT { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -407,6 +410,13 @@ int main(int argc, char* argv[]) { showUsage(); exit(1); } + break; + case 17: + op->put(PREF_TORRENT_SHOW_FILES, V_TRUE); + break; + case 18: + op->put(PREF_NO_PREALLOCATION, V_TRUE); + break; } break; } @@ -482,6 +492,11 @@ int main(int argc, char* argv[]) { exit(1); } } + + for(int i = 1; optind+i-1 < argc; i++) { + args.push_back(argv[optind+i-1]); + } + #ifdef HAVE_LIBSSL // for SSL initialization SSL_load_error_strings(); @@ -530,11 +545,14 @@ int main(int argc, char* argv[]) { e->segmentMan->splitter = splitter; Requests requests; - for(int i = 1; optind+i-1 < argc; i++) { + int cuidCounter = 1; + for(Strings::const_iterator itr = args.begin(); itr != args.end(); itr++) { for(int s = 1; s <= split; s++) { - addCommand(split*(i-1)+s, argv[optind+i-1], referer, requests); + addCommand(cuidCounter, *itr, referer, requests); + cuidCounter++; } } + e->run(); if(e->segmentMan->finished()) { @@ -575,26 +593,34 @@ int main(int argc, char* argv[]) { te->torrentMan = new TorrentMan(); te->torrentMan->setStoreDir(dir); te->torrentMan->logger = logger; - te->torrentMan->setup(torrentFile.empty() ? - downloadedTorrentFile : torrentFile); + te->torrentMan->option = op; + string targetTorrentFile = torrentFile.empty() ? + downloadedTorrentFile : torrentFile; if(op->get(PREF_TORRENT_SHOW_FILES) == V_TRUE) { - cout << "File listing:" << endl; + te->torrentMan->readFileEntryFromMetaInfoFile(targetTorrentFile); + cout << "Files:" << endl; switch(te->torrentMan->getFileMode()) { case TorrentMan::SINGLE: - printf("%s %s\nBytes", te->torrentMan->getName().c_str(), + printf("%s %s Bytes\n", te->torrentMan->getName().c_str(), Util::llitos(te->torrentMan->getTotalLength(), true).c_str()); break; case TorrentMan::MULTI: { const MultiFileEntries& entries = te->torrentMan->getMultiFileEntries(); for(MultiFileEntries::const_iterator itr = entries.begin(); itr != entries.end(); itr++) { - printf("%s %s\nBytes", itr->path.c_str(), + printf("%s %s Bytes\n", itr->path.c_str(), Util::llitos(itr->length, true).c_str()); - break; } + break; } } exit(0); + } else { + te->torrentMan->setup(targetTorrentFile); + if(!torrentFile.empty() && !args.empty() && + te->torrentMan->getFileMode() == TorrentMan::MULTI) { + te->torrentMan->setFileEntriesToDownload(args); + } } PeerListenCommand* listenCommand = new PeerListenCommand(te->torrentMan->getNewCuid(), te); diff --git a/src/prefs.h b/src/prefs.h index ae8c57af..dc17ea4f 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -90,5 +90,6 @@ #define PREF_PEER_CONNECTION_TIMEOUT "peer_connection_timeout" // values: true | false #define PREF_TORRENT_SHOW_FILES "torrent_show_files" - +// values: true | false +#define PREF_NO_PREALLOCATION "no_preallocation" #endif // _D_PREFS_H_ |
