summaryrefslogtreecommitdiff
path: root/src/MultiDiskWriter.cc
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2006-04-12 13:55:43 +0000
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2006-04-12 13:55:43 +0000
commit2f4b3f7d023714c77a383c53cccacc862421b49a (patch)
tree390c6f6d67d76cd0e9c0b92740fbaabd9c2868da /src/MultiDiskWriter.cc
parent305aad869017e9a7ff87e5bf2e24ee0b5ec34f3a (diff)
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.
Diffstat (limited to 'src/MultiDiskWriter.cc')
-rw-r--r--src/MultiDiskWriter.cc251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/MultiDiskWriter.cc b/src/MultiDiskWriter.cc
new file mode 100644
index 00000000..7172b809
--- /dev/null
+++ b/src/MultiDiskWriter.cc
@@ -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
+}
+