2006-04-12 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

To add the ability to download multi torrent into respective 
files
	directly:
	
	* src/DiskWriter.h (openFile): New function.
	(seek): Removed.
	* src/MultiDiskWriter.h: New class.
	* src/MultiDiskWriter.cc: New class.
	* src/AbstractDiskWriter.h (seek): Changed its scope from public 
to
	protected.
	(openFile): New function.
	* src/AbstractDiskWriter.cc (openFile): New function.
	* src/prefs.h (V_FALSE): New definition.
	(PREF_DIRECT_FILE_MAPPING): New definition.
	* src/TorrentMan.h (setupDiskWriter): New function.
	(setAllMultiFileRequestedState): New function.
	(onDownloadComplete): New function.
	* src/TorrentMan.cc : Included MultiDiskWriter.h
	(setupDiskWriter): New function.
	(getFilePath): Updated.
	(getTempFilePath): Updated.
	(getSegmentFilePath): Updated.
	(fixFilename): Updated.
	(deleteTempFile): Updated.
	(setAllMultiFileRequestedState): New function.
	(setFileEntriesToDownload): Use setAllMultiFileRequestedState().
	(finishPartialDownloadingMode): Reset requested flags.
	(onDownloadComplete): New function.
	* src/main.cc: Added --direct-file-mapping option.
	Use TorretMan::setupDiskWriter().
	* src/TorrentDownloadEngine.cc (afterEachIteration): Use 
TorrentMan::
	onDownloadComplete().
	
	
	To fix ETA bug:

	* src/Util.h (difftvsec): New function.
	* src/Util.cc (difftvsec): New function.
	* src/TorrentConsoleDownloadEngine.cc (calculateSpeed): Use int 
for the
	type of "elapsed" instead of long long int.
	(calculateStatistics): Use Util::difftvsec instead of 
Util::difftv.
	The updates of statistics takes place every 1 seconds.
	* src/TorrentConsoleDownloadEngine.h (lastElapsed): Changed its 
type.
	(calculateSpeed): Changed its argument signature.
	
	* src/PeerMessage.cc (toString): Fixed message.
pull/1/head
Tatsuhiro Tsujikawa 2006-04-12 13:55:43 +00:00
parent 305aad8690
commit 2f4b3f7d02
25 changed files with 674 additions and 56 deletions

View File

@ -1,3 +1,51 @@
2006-04-12 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
To add the ability to download multi torrent into respective files
directly:
* src/DiskWriter.h (openFile): New function.
(seek): Removed.
* src/MultiDiskWriter.h: New class.
* src/MultiDiskWriter.cc: New class.
* src/AbstractDiskWriter.h (seek): Changed its scope from public to
protected.
(openFile): New function.
* src/AbstractDiskWriter.cc (openFile): New function.
* src/prefs.h (V_FALSE): New definition.
(PREF_DIRECT_FILE_MAPPING): New definition.
* src/TorrentMan.h (setupDiskWriter): New function.
(setAllMultiFileRequestedState): New function.
(onDownloadComplete): New function.
* src/TorrentMan.cc : Included MultiDiskWriter.h
(setupDiskWriter): New function.
(getFilePath): Updated.
(getTempFilePath): Updated.
(getSegmentFilePath): Updated.
(fixFilename): Updated.
(deleteTempFile): Updated.
(setAllMultiFileRequestedState): New function.
(setFileEntriesToDownload): Use setAllMultiFileRequestedState().
(finishPartialDownloadingMode): Reset requested flags.
(onDownloadComplete): New function.
* src/main.cc: Added --direct-file-mapping option.
Use TorretMan::setupDiskWriter().
* src/TorrentDownloadEngine.cc (afterEachIteration): Use TorrentMan::
onDownloadComplete().
To fix ETA bug:
* src/Util.h (difftvsec): New function.
* src/Util.cc (difftvsec): New function.
* src/TorrentConsoleDownloadEngine.cc (calculateSpeed): Use int for the
type of "elapsed" instead of long long int.
(calculateStatistics): Use Util::difftvsec instead of Util::difftv.
The updates of statistics takes place every 1 seconds.
* src/TorrentConsoleDownloadEngine.h (lastElapsed): Changed its type.
(calculateSpeed): Changed its argument signature.
* src/PeerMessage.cc (toString): Fixed message.
2006-04-06 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
To print ETA:

1
TODO
View File

@ -13,3 +13,4 @@
* Distinguish seeder from leecher
* file selection in multi-file mode
* try to use ftruncate to allocate file.
* fix TorrentMan::getFilePath()

View File

@ -44,6 +44,12 @@ AbstractDiskWriter::~AbstractDiskWriter() {
#endif // ENABLE_SHA1DIGEST
}
void AbstractDiskWriter::openFile(const string& filename) {
if((fd = open(filename.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
throw new DlAbortEx(strerror(errno));
}
}
void AbstractDiskWriter::closeFile() {
if(fd != 0) {
close(fd);

View File

@ -39,18 +39,21 @@ protected:
void writeDataInternal(const char* data, int len);
int readDataInternal(char* data, int len);
void seek(long long int offset);
public:
AbstractDiskWriter();
virtual ~AbstractDiskWriter();
void openFile(const string& filename);
void closeFile();
void openExistingFile(string filename);
string sha1Sum(long long int offset, long long int length);
void seek(long long int offset);
void writeData(const char* data, int len, long long int offset);
int readData(char* data, int len, long long int offset);

View File

@ -41,6 +41,8 @@ public:
*/
virtual void initAndOpenFile(string filename) = 0;
virtual void openFile(const string& filename) = 0;
/**
* Closes this output stream.
*/
@ -69,7 +71,6 @@ public:
virtual string sha1Sum(long long int offset, long long int length) = 0;
virtual void seek(long long int offset) = 0;
};
#endif // _D_DISK_WRITER_H_

View File

@ -78,7 +78,8 @@ SRCS = Socket.cc Socket.h\
Directory.cc Directory.h\
TrackerWatcherCommand.cc TrackerWatcherCommand.h\
messageDigest.h\
SendMessageQueue.cc SendMessageQueue.h
SendMessageQueue.cc SendMessageQueue.h\
MultiDiskWriter.cc MultiDiskWriter.h
noinst_LIBRARIES = libaria2c.a
libaria2c_a_SOURCES = $(SRCS)
aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\

View File

@ -98,7 +98,7 @@ am__objects_1 = Socket.$(OBJEXT) SocketCore.$(OBJEXT) \
PeerMessage.$(OBJEXT) Piece.$(OBJEXT) RequestSlot.$(OBJEXT) \
RequestSlotMan.$(OBJEXT) TorrentAutoSaveCommand.$(OBJEXT) \
Directory.$(OBJEXT) TrackerWatcherCommand.$(OBJEXT) \
SendMessageQueue.$(OBJEXT)
SendMessageQueue.$(OBJEXT) MultiDiskWriter.$(OBJEXT)
am_libaria2c_a_OBJECTS = $(am__objects_1)
libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS)
am__installdirs = "$(DESTDIR)$(bindir)"
@ -326,7 +326,8 @@ SRCS = Socket.cc Socket.h\
Directory.cc Directory.h\
TrackerWatcherCommand.cc TrackerWatcherCommand.h\
messageDigest.h\
SendMessageQueue.cc SendMessageQueue.h
SendMessageQueue.cc SendMessageQueue.h\
MultiDiskWriter.cc MultiDiskWriter.h
noinst_LIBRARIES = libaria2c.a
libaria2c_a_SOURCES = $(SRCS)
@ -443,6 +444,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InitiateConnectionCommandFactory.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/List.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetaFileUtil.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskWriter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Option.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Peer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PeerAbstractCommand.Po@am__quote@

251
src/MultiDiskWriter.cc Normal file
View File

@ -0,0 +1,251 @@
/* <!-- copyright */
/*
* aria2 - a simple utility for downloading files faster
*
* Copyright (C) 2006 Tatsuhiro Tsujikawa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* copyright --> */
#include "MultiDiskWriter.h"
#include "DlAbortEx.h"
#include "Util.h"
#include <errno.h>
MultiDiskWriter::MultiDiskWriter() {
#ifdef ENABLE_SHA1DIGEST
sha1DigestInit(ctx);
#endif // ENABLE_SHA1DIGEST
}
MultiDiskWriter::~MultiDiskWriter() {
clearEntries();
#ifdef ENABLE_SHA1DIGEST
sha1DigestFree(ctx);
#endif // ENABLE_SHA1DIGEST
}
typedef struct {
long long int blockOffset;
long long int length;
} Range;
typedef deque<Range> Ranges;
void MultiDiskWriter::clearEntries() {
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end(); itr++) {
if((*itr)->enabled) {
(*itr)->diskWriter->closeFile();
}
delete *itr;
}
diskWriterEntries.clear();
}
void MultiDiskWriter::setMultiFileEntries(const MultiFileEntries& multiFileEntries, int pieceLength) {
clearEntries();
Ranges ranges;
for(MultiFileEntries::const_iterator itr = multiFileEntries.begin();
itr != multiFileEntries.end(); itr++) {
if(itr->requested) {
Range range;
range.blockOffset = (itr->offset/pieceLength)*pieceLength;
range.length = ((itr->offset+itr->length)/pieceLength+
((itr->offset+itr->length)%pieceLength ? 1 : 0))*pieceLength-range.blockOffset;
ranges.push_back(range);
}
}
Ranges::const_iterator ritr = ranges.begin();
for(MultiFileEntries::const_iterator itr = multiFileEntries.begin();
itr != multiFileEntries.end(); itr++) {
DiskWriterEntry* entry;
if(ritr != ranges.end() &&
// !(ritr->blockOffset+ritr->length-1 < itr->offset ||
// itr->offset+itr->length-1 < ritr->blockOffset)) {
itr->offset < ritr->blockOffset+ritr->length &&
ritr->blockOffset < itr->offset+itr->length) {
entry = new DiskWriterEntry(*itr, true);
for(;ritr->blockOffset+ritr->length <= itr->offset+itr->length &&
ritr != ranges.end(); ritr++);
} else {
entry = new DiskWriterEntry(*itr, false);
}
diskWriterEntries.push_back(entry);
}
}
void MultiDiskWriter::openFile(const string& filename) {
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end(); itr++) {
if((*itr)->enabled) {
(*itr)->diskWriter->openFile(filename+"/"+(*itr)->fileEntry.path);
}
}
}
// filename is a directory which is specified by the user in the option.
void MultiDiskWriter::initAndOpenFile(string filename) {
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end(); itr++) {
if((*itr)->enabled) {
(*itr)->diskWriter->initAndOpenFile(filename+"/"+(*itr)->fileEntry.path);
}
}
}
void MultiDiskWriter::closeFile() {
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end(); itr++) {
if((*itr)->enabled) {
(*itr)->diskWriter->closeFile();
}
}
}
void MultiDiskWriter::openExistingFile(string filename) {
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end(); itr++) {
if((*itr)->enabled) {
(*itr)->diskWriter->openExistingFile(filename+"/"+(*itr)->fileEntry.path);
}
}
}
void MultiDiskWriter::writeData(const char* data, int len, long long int position) {
long long int offset = position;
long long int fileOffset = offset;
bool writing = false;
int rem = len;
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end() && rem != 0; itr++) {
if(isInRange(*itr, offset) || writing) {
if(!(*itr)->enabled) {
throw new DlAbortEx("invalid offset or length. offset = %lld", offset);
}
int writeLength = calculateLength(*itr, fileOffset, rem);
(*itr)->diskWriter->writeData(data+(len-rem), writeLength, fileOffset);
rem -= writeLength;
writing = true;
fileOffset = 0;
} else {
fileOffset -= (*itr)->fileEntry.length;
}
}
if(!writing) {
throw new DlAbortEx("offset out of range");
}
}
bool MultiDiskWriter::isInRange(const DiskWriterEntry* entry, long long int offset) const {
return entry->fileEntry.offset <= offset &&
offset < entry->fileEntry.offset+entry->fileEntry.length;
}
int MultiDiskWriter::calculateLength(const DiskWriterEntry* entry, long long int fileOffset, int rem) const {
int length;
if(entry->fileEntry.length < fileOffset+rem) {
length = entry->fileEntry.length-fileOffset;
} else {
length = rem;
}
return length;
}
int MultiDiskWriter::readData(char* data, int len, long long int position) {
long long int offset = position;
long long int fileOffset = offset;
bool reading = false;
int rem = len;
int totalReadLength = 0;
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end() && rem != 0; itr++) {
if(isInRange(*itr, offset) || reading) {
if(!(*itr)->enabled) {
throw new DlAbortEx("invalid offset or length. offset = %lld", offset);
}
int readLength = calculateLength((*itr), fileOffset, rem);
totalReadLength += (*itr)->diskWriter->readData(data+(len-rem), readLength, fileOffset);
rem -= readLength;
reading = true;
fileOffset = 0;
} else {
fileOffset -= (*itr)->fileEntry.length;
}
}
if(!reading) {
throw new DlAbortEx("offset out of range");
}
return totalReadLength;
}
#ifdef ENABLE_SHA1DIGEST
void MultiDiskWriter::hashUpdate(const DiskWriterEntry* entry, long long int offset, long long int length) const {
int BUFSIZE = 16*1024;
char buf[BUFSIZE];
for(int i = 0; i < length/BUFSIZE; i++) {
if(BUFSIZE != entry->diskWriter->readData(buf, BUFSIZE, offset)) {
throw "error";
}
sha1DigestUpdate(ctx, buf, BUFSIZE);
offset += BUFSIZE;
}
int r = length%BUFSIZE;
if(r > 0) {
if(r != entry->diskWriter->readData(buf, r, offset)) {
throw "error";
}
sha1DigestUpdate(ctx, buf, r);
}
}
#endif // ENABLE_SHA1DIGEST
string MultiDiskWriter::sha1Sum(long long int offset, long long int length) {
#ifdef ENABLE_SHA1DIGEST
long long int fileOffset = offset;
bool reading = false;
int rem = length;
sha1DigestReset(ctx);
try {
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
itr != diskWriterEntries.end() && rem != 0; itr++) {
if(isInRange(*itr, offset) || reading) {
if(!(*itr)->enabled) {
throw new DlAbortEx("invalid offset or length. offset = %lld", offset);
}
int readLength = calculateLength((*itr), fileOffset, rem);
hashUpdate(*itr, fileOffset, readLength);
rem -= readLength;
reading = true;
fileOffset = 0;
} else {
fileOffset -= (*itr)->fileEntry.length;
}
}
if(!reading) {
throw new DlAbortEx("offset out of range");
}
unsigned char hashValue[20];
sha1DigestFinal(ctx, hashValue);
return Util::toHex(hashValue, 20);
} catch(string ex) {
throw new DlAbortEx(strerror(errno));
}
#else
return "";
#endif // ENABLE_SHA1DIGEST
}

76
src/MultiDiskWriter.h Normal file
View File

@ -0,0 +1,76 @@
/* <!-- copyright */
/*
* aria2 - a simple utility for downloading files faster
*
* Copyright (C) 2006 Tatsuhiro Tsujikawa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* copyright --> */
#ifndef _D_MULTI_DISK_WRITER_H_
#define _D_MULTI_DISK_WRITER_H_
#include "DefaultDiskWriter.h"
#include "TorrentMan.h"
#include "messageDigest.h"
class DiskWriterEntry {
public:
FileEntry fileEntry;
DiskWriter* diskWriter;
bool enabled;
public:
DiskWriterEntry(const FileEntry& fileEntry, bool enabled):fileEntry(fileEntry), enabled(enabled) {
if(enabled) {
diskWriter = new DefaultDiskWriter();
}
}
~DiskWriterEntry() {
if(enabled) {
delete diskWriter;
}
}
};
typedef deque<DiskWriterEntry*> DiskWriterEntries;
class MultiDiskWriter : public DiskWriter {
private:
DiskWriterEntries diskWriterEntries;
bool isInRange(const DiskWriterEntry* entry, long long int offset) const;
int calculateLength(const DiskWriterEntry* entry, long long int fileOffset, int rem) const;
void clearEntries();
#ifdef ENABLE_SHA1DIGEST
MessageDigestContext ctx;
void hashUpdate(const DiskWriterEntry* entry, long long int offset, long long int length) const;
#endif // ENABLE_SHA1DIGEST
public:
MultiDiskWriter();
virtual ~MultiDiskWriter();
void setMultiFileEntries(const MultiFileEntries& multiFileEntries, int pieceLength);
virtual void openFile(const string& filename);
virtual void initAndOpenFile(string filename);
virtual void closeFile();
virtual void openExistingFile(string filename);
virtual void writeData(const char* data, int len, long long int position = 0);
virtual int readData(char* data, int len, long long int position);
virtual string sha1Sum(long long int offset, long long int length);
};
#endif // _D_MULTI_DISK_WRITER_H_

View File

@ -61,7 +61,7 @@ string PeerMessage::toString() const {
return "piece index="+Util::itos(index)+", begin="+Util::itos(begin)+
", length="+Util::itos(blockLength);
case CANCEL:
return "calcel index="+Util::itos(index)+", begin="+Util::itos(begin)+
return "cancel index="+Util::itos(index)+", begin="+Util::itos(begin)+
", length="+Util::itos(length);
case KEEP_ALIVE:
return "keep alive";

View File

@ -74,15 +74,15 @@ void TorrentConsoleDownloadEngine::initStatistics() {
}
}
int TorrentConsoleDownloadEngine::calculateSpeed(long long int sessionLength, long long int elapsed) {
int nowSpeed = (int)(sessionLength/(elapsed/1000000.0));
int TorrentConsoleDownloadEngine::calculateSpeed(long long int sessionLength, int elapsed) {
int nowSpeed = (int)(sessionLength/(elapsed*1.0));
return nowSpeed;
}
void TorrentConsoleDownloadEngine::calculateStatistics() {
struct timeval now;
gettimeofday(&now, NULL);
long long int elapsed = Util::difftv(now, cp[currentCp]);
int elapsed = Util::difftvsec(now, cp[currentCp]);
sessionDownloadLengthArray[0] += torrentMan->getDeltaDownloadLength();
sessionUploadLengthArray[0] += torrentMan->getDeltaUploadLength();
@ -91,8 +91,6 @@ void TorrentConsoleDownloadEngine::calculateStatistics() {
sessionDownloadLength += torrentMan->getDeltaDownloadLength();
downloadSpeed = calculateSpeed(sessionDownloadLengthArray[currentCp], elapsed);
uploadSpeed = calculateSpeed(sessionUploadLengthArray[currentCp], elapsed);
torrentMan->resetDeltaDownloadLength();
torrentMan->resetDeltaUploadLength();
@ -105,18 +103,23 @@ void TorrentConsoleDownloadEngine::calculateStatistics() {
totalLength = torrentMan->getTotalLength();
}
if(elapsed-lastElapsed >= 1) {
downloadSpeed = calculateSpeed(sessionDownloadLengthArray[currentCp], elapsed);
uploadSpeed = calculateSpeed(sessionUploadLengthArray[currentCp], elapsed);
avgSpeed = calculateSpeed(sessionDownloadLength,
Util::difftv(now, startup));
if(avgSpeed != 0) {
Util::difftvsec(now, startup));
if(avgSpeed < 0) {
avgSpeed = 0;
} else if(avgSpeed != 0) {
eta = (totalLength-downloadLength)/avgSpeed;
}
if(elapsed-lastElapsed >= 1000000) {
printStatistics();
lastElapsed = elapsed;
}
if(elapsed > 15*1000000) {
if(elapsed > 15) {
sessionDownloadLengthArray[currentCp] = 0;
sessionUploadLengthArray[currentCp] = 0;
cp[currentCp] = now;

View File

@ -38,7 +38,7 @@ private:
int downloadSpeed;
int uploadSpeed;
long long int lastElapsed;
int lastElapsed;
long long int partialDownloadLengthDiff;
long long int partialTotalLength;
struct timeval startup;
@ -49,7 +49,7 @@ private:
long long int totalLength;
void printStatistics();
int calculateSpeed(long long int sessionLength, long long int elapsed);
int calculateSpeed(long long int sessionLength, int elapsed);
protected:
void initStatistics();
void calculateStatistics();

View File

@ -32,18 +32,12 @@ void TorrentDownloadEngine::onEndOfRun() {
void TorrentDownloadEngine::afterEachIteration() {
if(!filenameFixed && torrentMan->downloadComplete()) {
torrentMan->diskWriter->closeFile();
torrentMan->save();
torrentMan->fixFilename();
if(torrentMan->isPartialDownloadingMode()) {
torrentMan->finishPartialDownloadingMode();
onPartialDownloadingCompletes();
}
torrentMan->onDownloadComplete();
if(torrentMan->downloadComplete()) {
filenameFixed = true;
}
} else {
filenameFixed = true;
}
torrentMan->diskWriter->openExistingFile(torrentMan->getTempFilePath());
}
}

View File

@ -30,6 +30,7 @@
#include "message.h"
#include "PreAllocationDiskWriter.h"
#include "DefaultDiskWriter.h"
#include "MultiDiskWriter.h"
#include "prefs.h"
#include <errno.h>
#include <libgen.h>
@ -379,7 +380,22 @@ void TorrentMan::setup(string metaInfoFile) {
initBitfield();
delete topDic;
}
void TorrentMan::setupDiskWriter() {
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE) {
if(segmentFileExists()) {
load();
}
if(fileMode == SINGLE) {
diskWriter = new DefaultDiskWriter();
} else {
diskWriter = new MultiDiskWriter();
((MultiDiskWriter*)diskWriter)->setMultiFileEntries(multiFileEntries, pieceLength);
multiFileTopDir->createDir(storeDir, true);
}
diskWriter->openFile(getFilePath());
} else {
if(option->get(PREF_NO_PREALLOCATION) == V_TRUE) {
diskWriter = new DefaultDiskWriter();
} else {
@ -391,6 +407,7 @@ void TorrentMan::setup(string metaInfoFile) {
} else {
diskWriter->initAndOpenFile(getTempFilePath());
}
}
setupComplete = true;
}
@ -417,15 +434,27 @@ string TorrentMan::getPieceHash(int index) const {
}
string TorrentMan::getFilePath() const {
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE && fileMode == MULTI) {
return storeDir;
} else {
return storeDir+"/"+name;
}
}
string TorrentMan::getTempFilePath() const {
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE) {
return getFilePath();
} else {
return getFilePath()+".a2tmp";
}
}
string TorrentMan::getSegmentFilePath() const {
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE && fileMode == MULTI) {
return storeDir+"/"+name+".aria2";
} else {
return getFilePath()+".aria2";
}
}
bool TorrentMan::segmentFileExists() const {
@ -518,11 +547,15 @@ void TorrentMan::remove() const {
}
void TorrentMan::fixFilename() {
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE) {
// nothing to do here
} else {
if(fileMode == SINGLE) {
copySingleFile();
} else {
splitMultiFile();
}
}
}
void TorrentMan::copySingleFile() const {
@ -547,7 +580,11 @@ void TorrentMan::splitMultiFile() {
}
void TorrentMan::deleteTempFile() const {
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE) {
// nothing to do here
} else {
unlink(getTempFilePath().c_str());
}
}
// bool TorrentMan::unextractedFileEntryExists() const {
@ -567,10 +604,7 @@ void TorrentMan::setFileEntriesToDownload(const Strings& filePaths) {
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;
}
setAllMultiFileRequestedState(false);
for(Strings::const_iterator pitr = filePaths.begin();
pitr != filePaths.end(); pitr++) {
bool found = false;
@ -594,8 +628,19 @@ bool TorrentMan::isPartialDownloadingMode() const {
return bitfield->isFilterEnabled();
}
void TorrentMan::setAllMultiFileRequestedState(bool state) {
for(MultiFileEntries::iterator itr = multiFileEntries.begin();
itr != multiFileEntries.end(); itr++) {
itr->requested = state;
}
}
void TorrentMan::finishPartialDownloadingMode() {
bitfield->clearFilter();
setAllMultiFileRequestedState(true);
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE && fileMode == MULTI) {
((MultiDiskWriter*)diskWriter)->setMultiFileEntries(multiFileEntries, pieceLength);
}
}
long long int TorrentMan::getCompletedLength() const {
@ -605,3 +650,13 @@ long long int TorrentMan::getCompletedLength() const {
long long int TorrentMan::getPartialTotalLength() const {
return bitfield->getFilteredTotalLength();
}
void TorrentMan::onDownloadComplete() {
diskWriter->closeFile();
save();
fixFilename();
if(isPartialDownloadingMode()) {
finishPartialDownloadingMode();
}
diskWriter->openFile(getTempFilePath());
}

View File

@ -151,6 +151,7 @@ public:
}
void setup(string metaInfoFile);
void setupDiskWriter();
string getPieceHash(int index) const;
@ -238,6 +239,7 @@ public:
//bool unextractedFileEntryExists() const;
void setAllMultiFileRequestedState(bool state);
void finishPartialDownloadingMode();
bool isPartialDownloadingMode() const;
@ -246,6 +248,8 @@ public:
long long int getCompletedLength() const;
long long int getPartialTotalLength() const;
void onDownloadComplete();
enum FILE_MODE {
SINGLE,
MULTI

View File

@ -90,6 +90,13 @@ long long int Util::difftv(struct timeval tv1, struct timeval tv2) {
tv1.tv_usec-tv2.tv_usec);
}
int Util::difftvsec(struct timeval tv1, struct timeval tv2) {
if(tv1.tv_sec < tv2.tv_sec) {
return 0;
}
return tv1.tv_sec-tv2.tv_sec;
}
void Util::slice(Strings& result, const string& src, char delim) {
string::size_type p = 0;
while(1) {

View File

@ -43,6 +43,7 @@ public:
* If tv1 is older than tv2, then this method returns 0.
*/
static long long int difftv(struct timeval tv1, struct timeval tv2);
static int difftvsec(struct timeval tv1, struct timeval tv2);
/**
* Take a string src which is a deliminated list and add its elements
* into result. result is not cleared before conversion begins.

View File

@ -252,6 +252,7 @@ int main(int argc, char* argv[]) {
op->put(PREF_FTP_TYPE, V_BINARY);
op->put(PREF_FTP_VIA_HTTP_PROXY, V_TUNNEL);
op->put(PREF_AUTO_SAVE_INTERVAL, "60");
op->put(PREF_DIRECT_FILE_MAPPING, V_TRUE);
while(1) {
int optIndex = 0;
@ -284,6 +285,7 @@ int main(int argc, char* argv[]) {
{ "follow-torrent", required_argument, &lopt, 16 },
{ "torrent-show-files", no_argument, &lopt, 17 },
{ "no-preallocation", no_argument, &lopt, 18 },
{ "direct-file-mapping", required_argument, &lopt, 19 },
#endif // ENABLE_BITTORRENT
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
@ -417,6 +419,16 @@ int main(int argc, char* argv[]) {
case 18:
op->put(PREF_NO_PREALLOCATION, V_TRUE);
break;
case 19:
if(string(optarg) == "true") {
op->put(PREF_DIRECT_FILE_MAPPING, V_TRUE);
} else if(string(optarg) == "false") {
op->put(PREF_DIRECT_FILE_MAPPING, V_FALSE);
} else {
cerr << "direct-file-mapping must be either 'true' or 'false'." << endl;
showUsage();
exit(1);
}
}
break;
}
@ -621,6 +633,7 @@ int main(int argc, char* argv[]) {
te->torrentMan->getFileMode() == TorrentMan::MULTI) {
te->torrentMan->setFileEntriesToDownload(args);
}
te->torrentMan->setupDiskWriter();
}
PeerListenCommand* listenCommand =
new PeerListenCommand(te->torrentMan->getNewCuid(), te);

View File

@ -28,7 +28,7 @@
* Constants
*/
#define V_TRUE "true"
//#define V_FALSE "false"
#define V_FALSE "false"
/**
* General preferences
@ -92,4 +92,7 @@
#define PREF_TORRENT_SHOW_FILES "torrent_show_files"
// values: true | false
#define PREF_NO_PREALLOCATION "no_preallocation"
// values: true | false
#define PREF_DIRECT_FILE_MAPPING "direct_file_mapping"
#endif // _D_PREFS_H_

View File

@ -16,7 +16,8 @@ aria2c_SOURCES = AllTest.cc\
TorrentManTest.cc\
PeerMessageUtilTest.cc\
BitfieldManTest.cc\
DefaultDiskWriterTest.cc
DefaultDiskWriterTest.cc\
MultiDiskWriterTest.cc
#aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
#aria2c_LDFLAGS = ${CPPUNIT_LIBS}

View File

@ -63,7 +63,8 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) RequestTest.$(OBJEXT) \
DictionaryTest.$(OBJEXT) ListTest.$(OBJEXT) \
MetaFileUtilTest.$(OBJEXT) ShaVisitorTest.$(OBJEXT) \
TorrentManTest.$(OBJEXT) PeerMessageUtilTest.$(OBJEXT) \
BitfieldManTest.$(OBJEXT) DefaultDiskWriterTest.$(OBJEXT)
BitfieldManTest.$(OBJEXT) DefaultDiskWriterTest.$(OBJEXT) \
MultiDiskWriterTest.$(OBJEXT)
aria2c_OBJECTS = $(am_aria2c_OBJECTS)
am__DEPENDENCIES_1 =
aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1)
@ -220,7 +221,8 @@ aria2c_SOURCES = AllTest.cc\
TorrentManTest.cc\
PeerMessageUtilTest.cc\
BitfieldManTest.cc\
DefaultDiskWriterTest.cc
DefaultDiskWriterTest.cc\
MultiDiskWriterTest.cc
#aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
#aria2c_LDFLAGS = ${CPPUNIT_LIBS}
@ -292,6 +294,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ListTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetaFileUtilTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskWriterTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OptionTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PeerMessageUtilTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestTest.Po@am__quote@

142
test/MultiDiskWriterTest.cc Normal file
View File

@ -0,0 +1,142 @@
#include "MultiDiskWriter.h"
#include <string>
#include <cppunit/extensions/HelperMacros.h>
using namespace std;
class MultiDiskWriterTest:public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(MultiDiskWriterTest);
CPPUNIT_TEST(testWriteData);
CPPUNIT_TEST(testReadData);
CPPUNIT_TEST(testSha1Sum);
CPPUNIT_TEST_SUITE_END();
private:
public:
void setUp() {
}
void testWriteData();
void testReadData();
void testSha1Sum();
};
CPPUNIT_TEST_SUITE_REGISTRATION( MultiDiskWriterTest );
MultiFileEntries createEntries() {
FileEntry entry1("file1.txt", 15, 0);
FileEntry entry2("file2.txt", 7, 15);
FileEntry entry3("file3.txt", 3, 22);
unlink("file1.txt");
unlink("file2.txt");
unlink("file3.txt");
MultiFileEntries entries;
entries.push_back(entry1);
entries.push_back(entry2);
entries.push_back(entry3);
return entries;
}
void readFile(const string& filename, char* buf, int bufLength) {
FILE* f = fopen(filename.c_str(), "r");
if(f == NULL) {
abort();
}
int retval = fread(buf, bufLength, 1, f);
fclose(f);
if(retval != 1) {
abort();
}
}
void MultiDiskWriterTest::testWriteData() {
MultiDiskWriter dw;
dw.setMultiFileEntries(createEntries(), 2);
dw.openFile(".");
string msg = "12345";
dw.writeData(msg.c_str(), msg.size(), 0);
dw.closeFile();
char buf[128];
readFile("file1.txt", buf, 5);
buf[5] = '\0';
CPPUNIT_ASSERT_EQUAL(msg, string(buf));
dw.openFile(".");
string msg2 = "67890ABCDEF";
dw.writeData(msg2.c_str(), msg2.size(), 5);
dw.closeFile();
readFile("file1.txt", buf, 15);
buf[15] = '\0';
CPPUNIT_ASSERT_EQUAL(string("1234567890ABCDE"), string(buf));
readFile("file2.txt", buf, 1);
buf[1] = '\0';
CPPUNIT_ASSERT_EQUAL(string("F"), string(buf));
dw.openFile(".");
string msg3 = "12345123456712";
dw.writeData(msg3.c_str(), msg3.size(), 10);
dw.closeFile();
readFile("file1.txt", buf, 15);
buf[15] = '\0';
CPPUNIT_ASSERT_EQUAL(string("123456789012345"), string(buf));
readFile("file2.txt", buf, 7);
buf[7] = '\0';
CPPUNIT_ASSERT_EQUAL(string("1234567"), string(buf));
readFile("file3.txt", buf, 2);
buf[2] = '\0';
CPPUNIT_ASSERT_EQUAL(string("12"), string(buf));
}
void MultiDiskWriterTest::testReadData() {
FileEntry entry1("file1r.txt", 15, 0);
FileEntry entry2("file2r.txt", 7, 15);
FileEntry entry3("file3r.txt", 3, 22);
MultiFileEntries entries;
entries.push_back(entry1);
entries.push_back(entry2);
entries.push_back(entry3);
MultiDiskWriter dw;
dw.setMultiFileEntries(entries, 2);
dw.openFile(".");
char buf[128];
dw.readData(buf, 15, 0);
buf[15] = '\0';
CPPUNIT_ASSERT_EQUAL(string("1234567890ABCDE"), string(buf));
dw.readData(buf, 10, 6);
buf[10] = '\0';
CPPUNIT_ASSERT_EQUAL(string("7890ABCDEF"), string(buf));
dw.readData(buf, 4, 20);
buf[4] = '\0';
CPPUNIT_ASSERT_EQUAL(string("KLMN"), string(buf));
dw.readData(buf, 25, 0);
buf[25] = '\0';
CPPUNIT_ASSERT_EQUAL(string("1234567890ABCDEFGHIJKLMNO"), string(buf));
}
void MultiDiskWriterTest::testSha1Sum() {
FileEntry entry1("file1r.txt", 15, 0);
FileEntry entry2("file2r.txt", 7, 15);
FileEntry entry3("file3r.txt", 3, 22);
MultiFileEntries entries;
entries.push_back(entry1);
entries.push_back(entry2);
entries.push_back(entry3);
MultiDiskWriter dw;
dw.setMultiFileEntries(entries, 2);
dw.openFile(".");
string sha1sum = dw.sha1Sum(0, 25);
CPPUNIT_ASSERT_EQUAL(string("76495faf71ca63df66dce99547d2c58da7266d9e"), sha1sum);
sha1sum = dw.sha1Sum(15, 7);
CPPUNIT_ASSERT_EQUAL(string("737660d816fb23c2d5bc74f62d9b01b852b2aaca"), sha1sum);
sha1sum = dw.sha1Sum(10, 14);
CPPUNIT_ASSERT_EQUAL(string("6238bf61dd8df8f77156b2378e9e39cd3939680c"), sha1sum);
dw.closeFile();
}

1
test/file1r.txt Normal file
View File

@ -0,0 +1 @@
1234567890ABCDE

1
test/file2r.txt Normal file
View File

@ -0,0 +1 @@
FGHIJKL

1
test/file3r.txt Normal file
View File

@ -0,0 +1 @@
MNO