summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2006-04-06 12:52:16 +0000
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2006-04-06 12:52:16 +0000
commit9a52be6a29369cb7ac97c20c11218b16c2a648dd (patch)
tree8f6abdedda606fb320d8874f383430677d1a2d18 /src
parent7228402dadd541e3bea0b939d17c36202180012a (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.cc185
-rw-r--r--src/BitfieldMan.h40
-rw-r--r--src/PeerInteractionCommand.cc6
-rw-r--r--src/PeerInteractionCommand.h1
-rw-r--r--src/PeerMessage.cc4
-rw-r--r--src/TorrentConsoleDownloadEngine.cc47
-rw-r--r--src/TorrentConsoleDownloadEngine.h10
-rw-r--r--src/TorrentDownloadEngine.cc10
-rw-r--r--src/TorrentDownloadEngine.h1
-rw-r--r--src/TorrentMan.cc130
-rw-r--r--src/TorrentMan.h26
-rw-r--r--src/Util.cc21
-rw-r--r--src/Util.h2
-rw-r--r--src/main.cc44
-rw-r--r--src/prefs.h3
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;
+}
diff --git a/src/Util.h b/src/Util.h
index 8375c65b..e738c0ae 100644
--- a/src/Util.h
+++ b/src/Util.h
@@ -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_