Prechádzať zdrojové kódy

2007-01-08 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	To add an ability to pre-allocate file space:
	
	* src/DirectDiskAdaptor.h: Rewritten.
	* src/PieceStorage.h: DiskAdaptor -> DiskAdaptorHandle
	* src/MultiDiskAdaptor.h: Rewritten.
	* src/DefaultPieceStorage.cc
	(MultiDiskWriter.h): Removed include.
	(PreAllocationDiskWriter.h): Removed include.
	(FileAllocationMonitor.h): New include.
	(~DefaultPieceStorage): Removed deletion of diskAdaptor.
	(initStorage): Rewritten.
	(getDiskAdaptor): DiskAdaptor -> DiskAdaptorHandle
	* src/FileAllocator.h: New class.
	* src/FileAllocator.cc: New class.
	* src/FileAllocationMonitor.h: New class.
	* src/FileAllocationMonitor.cc: New class.
	* src/ConsoleFileAllocationMonitor.h: New class.
	* src/ConsoleFileAllocationMonitor.cc: New class.
	* src/AbstractSingleDiskAdaptor.h: New class.
	* src/AbstractSingleDiskAdaptor.cc: New class.
	* src/DiskAdaptor.h
	(Directory.h): Removed include.
	(DiskWriter.h): Removed include.
	(FileEntry.h): Removed include.
	(diskWriter): Removed.
	(DiskAdaptor): Removed diskWriter.
	(openFile): Made pure virtual.
	(closeFile): Made pure virtual.
	(openExistingFile): Made pure virtual.
	(initAndOpenFile): Made pure virtual.
	(writeData): Made pure virtual.
	(readData): Made pure virtual.
	(sha1Sum): Made pure virtual.
	(getStoreDir): Returns const reference of storeDir.
	(DiskAdaptorHandle): New type definition.
	* src/main.cc
	(ConsoleFileAllocationMonitor.h): New include.
	(showUsage): Added default value description of -s option.
	Added the description of --file-allocation option.
	(main): Set default value of --file-allocation option to 'none'.
	Added --file-allocation command-line option.
	Setup FileAllocationMonitorFactory.
	* src/FtpInitiateConnectionCommand.cc
	(executeInternal): Removed diskWriter related processing, which 
was
	moved to FtpNegotiationCommand.cc.
	* src/DirectDiskAdaptor.cc
	(getFilePath): Made non-const.
	* src/CopyDiskAdaptor.h
	(DiskAdaptor.h): Removed include.
	(DiskWriter.h): Removed include.
	(AbstractSingleDiskAdaptor.h): New include.
	(getFilePath): Made non-const. Added virtual keyword.
	(CopyDiskAdaptor): Removed diskWriter.
	(getTempFile): Returns const reference.
	(CopyDiskAdaptorHandle): New type definition.
	* src/ByteArrayDiskWriter.cc
	(clear): Simplified.
	(initAndOpenFile): Rewritten.
	(openFile): Call initAndOpenFile()
	* src/MultiDiskAdaptor.cc: Rewritten.
	* src/DownloadEngineFactory.cc
	(FileAllocator.h): New include.
	(FileAllocationMonitor.h): New include.
	(newConsoleEngine): Call 
DefaultDiskWriter::createNewDiskWriter() to
	create DefaultDiskWriter with file allocator.
	* src/DiskWriter.h
	(initAndOpenFile): Added totalLength argument.
	(openFile): Added totalLength argument.
	* src/prefs.h
	(PREF_FILE_ALLOCATION): New definition.
	(V_PREALLOC): New definition.
	(V_NONE): New definition.
	* src/HttpResponseCommand.cc
	(handleDefaultEncoding): Call DefaultDiskWriter::initAndOpenFile 
with
	size.
	* src/FtpNegotiateCommand.cc
	(Util.h): New include.
	(recvSize): Open file here.
	* src/Util.h
	(ullitos): New function.
	* src/CopyDiskWriter.h
	(getFilePath): Made non-const.
	* src/DefaultDiskWriter.h
	(Option.h): New include.
	(totalLength): Removed.
	(DefaultDiskWriter): Removed totalLength.
	(initAndOpenFile): Added totalLength argument.
	(DefaultDiskWriterHandle): New type definition.
	(createNewDiskWriter): New function.
	* src/Util.cc
	(ullitos): New function.
	* src/DefaultDiskWriter.cc
	(message.h): New include.
	(FileAllocator.h): New include.
	(prefs.h): New include.
	(Util.h): New include.
	(DefaultDiskWriter): Removed totalLength.
	(initAndOpenFile): Added file allocation.
	(createNewDiskWriter): New function. Just for temporary 
solution.
	It will be rewritten later.
	* src/DiskAdaptor.cc
	(DiskAdaptor): Removed diskWriter.
	* src/AbstractDiskWriter.cc
	(LogFactory.h): New include.
	(AbstractDiskWriter): Added fileAllocator, logger.
	(openFile): Added totalLength argument.
	* src/AbstractDiskWriter.h
	(FileAllocator.h): New include.
	(Logger.h): New include.
	(fileAllocator): New variable.
	(logger): New variable.
	(openFile): Added totalLength argument. Added virtual keyword
	explicitly.
	(openExistingFile): Added totalLength argument. Added virtual 
keyword
	explicitly.
	(closeFile): Added virtual keyword explicitly.
	(sha1Sum): Added virtual keyword explicitly.
	(writeData): Added virtual keyword explicitly.
	(readData): Added virtual keyword explicitly.
	(setFileAllocator): New function.
	* src/DefaultPieceStorage.h
	(FileAllocator.h): New include.
	(diskAdaptor): DiskAdaptor -> DiskAdaptorHandle
	(getDiskAdaptor): DiskAdaptor -> DiskAdaptorHandle
	* src/FileProgressMonitor.h: New class.

	To compile aria2 on PC-BSD:
	
	* src/DefaultBtContext.cc
	(libgen.h): New include.
	
	To fix memory leak:
	
	* src/Exception.h
	(~Exception): Delete cause. Fixed memory leak.
Tatsuhiro Tsujikawa 19 rokov pred
rodič
commit
3a412f89b2
51 zmenil súbory, kde vykonal 1526 pridanie a 279 odobranie
  1. 138 0
      ChangeLog
  2. 0 3
      TODO
  3. 32 31
      src/AbstractDiskWriter.cc
  4. 20 12
      src/AbstractDiskWriter.h
  5. 63 0
      src/AbstractSingleDiskAdaptor.cc
  6. 80 0
      src/AbstractSingleDiskAdaptor.h
  7. 2 1
      src/BitfieldMan.cc
  8. 12 13
      src/ByteArrayDiskWriter.cc
  9. 7 7
      src/ByteArrayDiskWriter.h
  10. 58 0
      src/ConsoleFileAllocationMonitor.cc
  11. 105 0
      src/ConsoleFileAllocationMonitor.h
  12. 1 5
      src/CopyDiskAdaptor.cc
  13. 10 7
      src/CopyDiskAdaptor.h
  14. 1 0
      src/DefaultBtContext.cc
  15. 34 6
      src/DefaultDiskWriter.cc
  16. 9 5
      src/DefaultDiskWriter.h
  17. 19 14
      src/DefaultPieceStorage.cc
  18. 4 2
      src/DefaultPieceStorage.h
  19. 1 5
      src/DirectDiskAdaptor.cc
  20. 7 5
      src/DirectDiskAdaptor.h
  21. 2 34
      src/DiskAdaptor.cc
  22. 19 20
      src/DiskAdaptor.h
  23. 10 9
      src/DiskWriter.h
  24. 3 1
      src/DownloadEngineFactory.cc
  25. 3 1
      src/Exception.h
  26. 37 0
      src/FileAllocationMonitor.cc
  27. 67 0
      src/FileAllocationMonitor.h
  28. 69 0
      src/FileAllocator.cc
  29. 59 0
      src/FileAllocator.h
  30. 56 0
      src/FileProgressMonitor.h
  31. 0 11
      src/FtpInitiateConnectionCommand.cc
  32. 10 0
      src/FtpNegotiationCommand.cc
  33. 2 1
      src/HttpResponseCommand.cc
  34. 6 4
      src/Makefile.am
  35. 38 32
      src/Makefile.in
  36. 155 14
      src/MultiDiskAdaptor.cc
  37. 112 10
      src/MultiDiskAdaptor.h
  38. 13 1
      src/MultiDiskWriter.cc
  39. 10 3
      src/MultiDiskWriter.h
  40. 1 1
      src/PieceStorage.h
  41. 4 3
      src/PreAllocationDiskWriter.cc
  42. 4 5
      src/PreAllocationDiskWriter.h
  43. 4 0
      src/Util.cc
  44. 1 0
      src/Util.h
  45. 25 2
      src/main.cc
  46. 4 0
      src/prefs.h
  47. 38 0
      test/ConsoleFileAllocationMonitorTest.cc
  48. 3 2
      test/Makefile.am
  49. 8 5
      test/Makefile.in
  50. 4 4
      test/MockPieceStorage.h
  51. 156 0
      test/MultiDiskAdaptorTest.cc

+ 138 - 0
ChangeLog

@@ -1,3 +1,141 @@
+2007-01-08  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	To add an ability to pre-allocate file space:
+	
+	* src/DirectDiskAdaptor.h: Rewritten.
+	* src/PieceStorage.h: DiskAdaptor -> DiskAdaptorHandle
+	* src/MultiDiskAdaptor.h: Rewritten.
+	* src/DefaultPieceStorage.cc
+	(MultiDiskWriter.h): Removed include.
+	(PreAllocationDiskWriter.h): Removed include.
+	(FileAllocationMonitor.h): New include.
+	(~DefaultPieceStorage): Removed deletion of diskAdaptor.
+	(initStorage): Rewritten.
+	(getDiskAdaptor): DiskAdaptor -> DiskAdaptorHandle
+	* src/FileAllocator.h: New class.
+	* src/FileAllocator.cc: New class.
+	* src/FileAllocationMonitor.h: New class.
+	* src/FileAllocationMonitor.cc: New class.
+	* src/ConsoleFileAllocationMonitor.h: New class.
+	* src/ConsoleFileAllocationMonitor.cc: New class.
+	* src/AbstractSingleDiskAdaptor.h: New class.
+	* src/AbstractSingleDiskAdaptor.cc: New class.
+	* src/DiskAdaptor.h
+	(Directory.h): Removed include.
+	(DiskWriter.h): Removed include.
+	(FileEntry.h): Removed include.
+	(diskWriter): Removed.
+	(DiskAdaptor): Removed diskWriter.
+	(openFile): Made pure virtual.
+	(closeFile): Made pure virtual.
+	(openExistingFile): Made pure virtual.
+	(initAndOpenFile): Made pure virtual.
+	(writeData): Made pure virtual.
+	(readData): Made pure virtual.
+	(sha1Sum): Made pure virtual.
+	(getStoreDir): Returns const reference of storeDir.
+	(DiskAdaptorHandle): New type definition.
+	* src/main.cc
+	(ConsoleFileAllocationMonitor.h): New include.
+	(showUsage): Added default value description of -s option.
+	Added the description of --file-allocation option.
+	(main): Set default value of --file-allocation option to 'none'.
+	Added --file-allocation command-line option.
+	Setup FileAllocationMonitorFactory.
+	* src/FtpInitiateConnectionCommand.cc
+	(executeInternal): Removed diskWriter related processing, which was
+	moved to FtpNegotiationCommand.cc.
+	* src/DirectDiskAdaptor.cc
+	(getFilePath): Made non-const.
+	* src/CopyDiskAdaptor.h
+	(DiskAdaptor.h): Removed include.
+	(DiskWriter.h): Removed include.
+	(AbstractSingleDiskAdaptor.h): New include.
+	(getFilePath): Made non-const. Added virtual keyword.
+	(CopyDiskAdaptor): Removed diskWriter.
+	(getTempFile): Returns const reference.
+	(CopyDiskAdaptorHandle): New type definition.
+	* src/ByteArrayDiskWriter.cc
+	(clear): Simplified.
+	(initAndOpenFile): Rewritten.
+	(openFile): Call initAndOpenFile()
+	* src/MultiDiskAdaptor.cc: Rewritten.
+	* src/DownloadEngineFactory.cc
+	(FileAllocator.h): New include.
+	(FileAllocationMonitor.h): New include.
+	(newConsoleEngine): Call DefaultDiskWriter::createNewDiskWriter() to
+	create DefaultDiskWriter with file allocator.
+	* src/DiskWriter.h
+	(initAndOpenFile): Added totalLength argument.
+	(openFile): Added totalLength argument.
+	* src/prefs.h
+	(PREF_FILE_ALLOCATION): New definition.
+	(V_PREALLOC): New definition.
+	(V_NONE): New definition.
+	* src/HttpResponseCommand.cc
+	(handleDefaultEncoding): Call DefaultDiskWriter::initAndOpenFile with
+	size.
+	* src/FtpNegotiateCommand.cc
+	(Util.h): New include.
+	(recvSize): Open file here.
+	* src/Util.h
+	(ullitos): New function.
+	* src/CopyDiskWriter.h
+	(getFilePath): Made non-const.
+	* src/DefaultDiskWriter.h
+	(Option.h): New include.
+	(totalLength): Removed.
+	(DefaultDiskWriter): Removed totalLength.
+	(initAndOpenFile): Added totalLength argument.
+	(DefaultDiskWriterHandle): New type definition.
+	(createNewDiskWriter): New function.
+	* src/Util.cc
+	(ullitos): New function.
+	* src/DefaultDiskWriter.cc
+	(message.h): New include.
+	(FileAllocator.h): New include.
+	(prefs.h): New include.
+	(Util.h): New include.
+	(DefaultDiskWriter): Removed totalLength.
+	(initAndOpenFile): Added file allocation.
+	(createNewDiskWriter): New function. Just for temporary solution.
+	It will be rewritten later.
+	* src/DiskAdaptor.cc
+	(DiskAdaptor): Removed diskWriter.
+	* src/AbstractDiskWriter.cc
+	(LogFactory.h): New include.
+	(AbstractDiskWriter): Added fileAllocator, logger.
+	(openFile): Added totalLength argument.
+	* src/AbstractDiskWriter.h
+	(FileAllocator.h): New include.
+	(Logger.h): New include.
+	(fileAllocator): New variable.
+	(logger): New variable.
+	(openFile): Added totalLength argument. Added virtual keyword
+	explicitly.
+	(openExistingFile): Added totalLength argument. Added virtual keyword
+	explicitly.
+	(closeFile): Added virtual keyword explicitly.
+	(sha1Sum): Added virtual keyword explicitly.
+	(writeData): Added virtual keyword explicitly.
+	(readData): Added virtual keyword explicitly.
+	(setFileAllocator): New function.
+	* src/DefaultPieceStorage.h
+	(FileAllocator.h): New include.
+	(diskAdaptor): DiskAdaptor -> DiskAdaptorHandle
+	(getDiskAdaptor): DiskAdaptor -> DiskAdaptorHandle
+	* src/FileProgressMonitor.h: New class.
+
+	To compile aria2 on PC-BSD:
+	
+	* src/DefaultBtContext.cc
+	(libgen.h): New include.
+	
+	To fix memory leak:
+	
+	* src/Exception.h
+	(~Exception): Delete cause. Fixed memory leak.
+	
 2006-12-24  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 
 	Rewritten a portion of bittorrent implementation:

+ 0 - 3
TODO

@@ -25,8 +25,5 @@
 do not start downloading and print some useful message to the user.
 * Fix Segfaults in BitfieldMan.cc:71
 https://sourceforge.net/tracker/index.php?func=detail&aid=1606060&group_id=159897&atid=813673
-* Prevent the file to be fragmented. Use PreAllocationDiskWriter
-https://sourceforge.net/tracker/index.php?func=detail&aid=1611886&group_id=159897&atid=813673
-* Add #include <libgen.h> to DefaultBtContext.h
 * int32_t
 * remove blockIndex

+ 32 - 31
src/AbstractDiskWriter.cc

@@ -37,6 +37,7 @@
 #include "File.h"
 #include "Util.h"
 #include "message.h"
+#include "LogFactory.h"
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -44,7 +45,9 @@
 #include <fcntl.h>
 
 AbstractDiskWriter::AbstractDiskWriter():
-  fd(0)
+  fd(0),
+  fileAllocator(0),
+  logger(LogFactory::getInstance())
 #ifdef ENABLE_MESSAGE_DIGEST				       
   ,ctx(DIGEST_ALGO_SHA1)
 #endif // ENABLE_MESSAGE_DIGEST
@@ -61,12 +64,12 @@ AbstractDiskWriter::~AbstractDiskWriter() {
 #endif // ENABLE_MESSAGE_DIGEST
 }
 
-void AbstractDiskWriter::openFile(const string& filename) {
+void AbstractDiskWriter::openFile(const string& filename, uint64_t totalLength) {
   File f(filename);
   if(f.exists()) {
     openExistingFile(filename);
   } else {
-    initAndOpenFile(filename);
+    initAndOpenFile(filename, totalLength);
   }
 }
 
@@ -89,7 +92,7 @@ void AbstractDiskWriter::openExistingFile(const string& filename) {
   }
 }
 
-void AbstractDiskWriter::createFile(const string& filename, int addFlags) {
+void AbstractDiskWriter::createFile(const string& filename, int32_t addFlags) {
   this->filename = filename;
   // TODO proper filename handling needed
   assert(filename.size());
@@ -101,63 +104,61 @@ void AbstractDiskWriter::createFile(const string& filename, int addFlags) {
   }  
 }
 
-void AbstractDiskWriter::writeDataInternal(const char* data, int len) {
+void AbstractDiskWriter::writeDataInternal(const char* data, uint32_t len) {
   if(write(fd, data, len) < 0) {
     throw new DlAbortEx(EX_FILE_WRITE, filename.c_str(), strerror(errno));
   }
 }
 
-int AbstractDiskWriter::readDataInternal(char* data, int len) {
-  int ret;
+int AbstractDiskWriter::readDataInternal(char* data, uint32_t len) {
+  int32_t ret;
   if((ret = read(fd, data, len)) < 0) {
     throw new DlAbortEx(EX_FILE_READ, filename.c_str(), strerror(errno));
   }
   return ret;
 }
 
-string AbstractDiskWriter::sha1Sum(long long int offset, long long int length) {
+string AbstractDiskWriter::sha1Sum(int64_t offset, uint64_t length) {
 #ifdef ENABLE_MESSAGE_DIGEST
   ctx.digestReset();
-  try {
-    int BUFSIZE = 16*1024;
-    char buf[BUFSIZE];
-    for(int i = 0; i < length/BUFSIZE; i++) {
-      if(BUFSIZE != readData(buf, BUFSIZE, offset)) {
-	throw string("error");
-      }
-      ctx.digestUpdate(buf, BUFSIZE);
-      offset += BUFSIZE;
+
+  uint32_t BUFSIZE = 16*1024;
+  char buf[BUFSIZE];
+  for(uint64_t i = 0; i < length/BUFSIZE; i++) {
+    if((int32_t)BUFSIZE != readData(buf, BUFSIZE, offset)) {
+      throw new DlAbortEx(EX_FILE_SHA1SUM, filename.c_str(), strerror(errno));
     }
-    int r = length%BUFSIZE;
-    if(r > 0) {
-      if(r != readData(buf, r, offset)) {
-	throw string("error");
-      }
-      ctx.digestUpdate(buf, r);
+    ctx.digestUpdate(buf, BUFSIZE);
+    offset += BUFSIZE;
+  }
+  uint32_t r = length%BUFSIZE;
+  if(r > 0) {
+    if((int32_t)r != readData(buf, r, offset)) {
+      throw new DlAbortEx(EX_FILE_SHA1SUM, filename.c_str(), strerror(errno));
     }
-    unsigned char hashValue[20];
-    ctx.digestFinal(hashValue);
-    return Util::toHex(hashValue, 20);
-  } catch(string ex) {
-    throw new DlAbortEx(EX_FILE_SHA1SUM, filename.c_str(), strerror(errno));
+    ctx.digestUpdate(buf, r);
   }
+  unsigned char hashValue[20];
+  ctx.digestFinal(hashValue);
+  return Util::toHex(hashValue, 20);
 #else
   return "";
 #endif // ENABLE_MESSAGE_DIGEST
 }
 
-void AbstractDiskWriter::seek(long long int offset) {
+void AbstractDiskWriter::seek(int64_t offset) {
   if(offset != lseek(fd, offset, SEEK_SET)) {
     throw new DlAbortEx(EX_FILE_SEEK, filename.c_str(), strerror(errno));
   }
 }
 
-void AbstractDiskWriter::writeData(const char* data, int len, long long int offset) {
+void AbstractDiskWriter::writeData(const char* data, uint32_t len, int64_t offset) {
   seek(offset);
   writeDataInternal(data, len);
 }
 
-int AbstractDiskWriter::readData(char* data, int len, long long int offset) {
+int AbstractDiskWriter::readData(char* data, uint32_t len, int64_t offset) {
   seek(offset);
   return readDataInternal(data, len);
 }
+

+ 20 - 12
src/AbstractDiskWriter.h

@@ -39,37 +39,45 @@
 #ifdef ENABLE_MESSAGE_DIGEST
 #include "messageDigest.h"
 #endif // ENABLE_MESSAGE_DIGEST
+#include "FileAllocator.h"
+#include "Logger.h"
 
-class AbstractDiskWriter:public DiskWriter {
+class AbstractDiskWriter : public DiskWriter {
 protected:
   string filename;
-  int fd;
+  int32_t fd;
+  FileAllocatorHandle fileAllocator;
+  const Logger* logger;
 #ifdef ENABLE_MESSAGE_DIGEST
   MessageDigestContext ctx;
 #endif // ENABLE_MESSAGE_DIGEST
 
-  void createFile(const string& filename, int addFlags = 0);
+  void createFile(const string& filename, int32_t addFlags = 0);
 
-  void writeDataInternal(const char* data, int len);
-  int readDataInternal(char* data, int len);
+  void writeDataInternal(const char* data, uint32_t len);
+  int readDataInternal(char* data, uint32_t len);
 
-  void seek(long long int offset);
+  void seek(int64_t offset);
 
 public:
   AbstractDiskWriter();
   virtual ~AbstractDiskWriter();
 
-  void openFile(const string& filename);
+  virtual void openFile(const string& filename, uint64_t totalLength = 0);
 
-  void closeFile();
+  virtual void closeFile();
 
-  void openExistingFile(const string& filename);
+  virtual void openExistingFile(const string& filename);
 
-  string sha1Sum(long long int offset, long long int length);
+  virtual string sha1Sum(int64_t offset, uint64_t length);
 
-  void writeData(const char* data, int len, long long int offset);
+  virtual void writeData(const char* data, uint32_t len, int64_t offset);
 
-  int readData(char* data, int len, long long int offset);
+  virtual int readData(char* data, uint32_t len, int64_t offset);
+
+  void setFileAllocator(const FileAllocatorHandle& fileAllocator) {
+    this->fileAllocator = fileAllocator;
+  }
 };
 
 #endif // _D_ABSTRACT_DISK_WRITER_H_

+ 63 - 0
src/AbstractSingleDiskAdaptor.cc

@@ -0,0 +1,63 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "AbstractSingleDiskAdaptor.h"
+
+void AbstractSingleDiskAdaptor::initAndOpenFile() {
+  diskWriter->initAndOpenFile(getFilePath(), totalLength);
+}
+
+void AbstractSingleDiskAdaptor::openFile() {
+  diskWriter->openFile(getFilePath(), totalLength);
+}
+
+void AbstractSingleDiskAdaptor::closeFile() {
+  diskWriter->closeFile();
+}
+
+void AbstractSingleDiskAdaptor::openExistingFile() {
+  diskWriter->openExistingFile(getFilePath());
+}
+
+void AbstractSingleDiskAdaptor::writeData(const unsigned char* data, uint32_t len, int64_t offset) {
+  diskWriter->writeData(data, len, offset);
+}
+
+int AbstractSingleDiskAdaptor::readData(unsigned char* data, uint32_t len, int64_t offset) {
+  return diskWriter->readData(data, len, offset);
+}
+
+string AbstractSingleDiskAdaptor::sha1Sum(int64_t offset, uint64_t length) {
+  return diskWriter->sha1Sum(offset, length);
+}

+ 80 - 0
src/AbstractSingleDiskAdaptor.h

@@ -0,0 +1,80 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_ABSTRACT_SINGLE_DISK_ADAPTOR_H_
+#define _D_ABSTRACT_SINGLE_DISK_ADAPTOR_H_
+
+#include "DiskAdaptor.h"
+#include "DiskWriter.h"
+
+class AbstractSingleDiskAdaptor : public DiskAdaptor {
+protected:
+  DiskWriterHandle diskWriter;
+  uint64_t totalLength;
+
+  virtual string getFilePath() = 0;
+public:
+  AbstractSingleDiskAdaptor():diskWriter(0), totalLength(0) {}
+
+  virtual ~AbstractSingleDiskAdaptor() {}
+
+  virtual void initAndOpenFile();
+
+  virtual void openFile();
+
+  virtual void closeFile();
+
+  virtual void openExistingFile();
+
+  virtual void writeData(const unsigned char* data, uint32_t len,
+			 int64_t offset);
+
+  virtual int readData(unsigned char* data, uint32_t len, int64_t offset);
+
+  virtual string sha1Sum(int64_t offset, uint64_t length);
+ 
+  void setDiskWriter(const DiskWriterHandle diskWriter) {
+    this->diskWriter = diskWriter;
+  }
+
+  DiskWriterHandle getDiskWriter() const { return diskWriter; }
+
+  void setTotalLength(const uint64_t& totalLength) {
+    this->totalLength = totalLength;
+  }
+
+  uint64_t getTotalLength() const { return totalLength; }
+};
+
+#endif // _D_ABSTRACT_SINGLE_DISK_ADAPTOR_H_

+ 2 - 1
src/BitfieldMan.cc

@@ -110,9 +110,10 @@ BitfieldMan::getMissingIndexRandomly(const unsigned char* bitfield,
 			   (randomizer->getMaxRandomNumber()+1.0));
 
   unsigned char lastMask = 0;
+  // the number of bytes in the last byte of bitfield
   uint32_t lastByteLength = totalLength%(blockLength*8);
+  // the number of block in the last byte of bitfield
   uint32_t lastBlockCount = DIV_FLOOR(lastByteLength, blockLength);
-  // TODO test this
   for(uint32_t i = 0; i < lastBlockCount; i++) {
     lastMask >>= 1;
     lastMask |= 0x80;

+ 12 - 13
src/ByteArrayDiskWriter.cc

@@ -35,7 +35,7 @@
 #include "ByteArrayDiskWriter.h"
 #include "Util.h"
 
-ByteArrayDiskWriter::ByteArrayDiskWriter():buf(NULL) {
+ByteArrayDiskWriter::ByteArrayDiskWriter():buf(0) {
 }
 
 ByteArrayDiskWriter::~ByteArrayDiskWriter() {
@@ -43,10 +43,7 @@ ByteArrayDiskWriter::~ByteArrayDiskWriter() {
 }
 
 void ByteArrayDiskWriter::clear() {
-  if(buf != NULL) {
-    delete [] buf;
-    buf = NULL;
-  }
+  delete [] buf;
 }
 
 void ByteArrayDiskWriter::init() {
@@ -55,15 +52,17 @@ void ByteArrayDiskWriter::init() {
   bufLength = 0;
 }
 
-void ByteArrayDiskWriter::initAndOpenFile(const string& filename) {
-  openFile(filename);
-}
-
-void ByteArrayDiskWriter::openFile(const string& filename) {
+void ByteArrayDiskWriter::initAndOpenFile(const string& filename,
+					  uint64_t totalLength) {
   clear();
   init();
 }
 
+void ByteArrayDiskWriter::openFile(const string& filename,
+				   uint64_t totalLength) {
+  initAndOpenFile(filename);
+}
+
 void ByteArrayDiskWriter::closeFile() {
   clear();
 }
@@ -72,7 +71,7 @@ void ByteArrayDiskWriter::openExistingFile(const string& filename) {
   openFile(filename);
 }
 
-void ByteArrayDiskWriter::writeData(const char* data, int dataLength, long long int position) {
+void ByteArrayDiskWriter::writeData(const char* data, uint32_t dataLength, int64_t position) {
   if(bufLength+dataLength >= maxBufLength) {
     maxBufLength = Util::expandBuffer(&buf, bufLength, bufLength+dataLength);
   }
@@ -80,11 +79,11 @@ void ByteArrayDiskWriter::writeData(const char* data, int dataLength, long long
   bufLength += dataLength;
 }
 
-int ByteArrayDiskWriter::readData(char* data, int len, long long int position) {
+int ByteArrayDiskWriter::readData(char* data, uint32_t len, int64_t position) {
   if(position >= bufLength) {
     return 0;
   }
-  int readLength;
+  uint32_t readLength;
   if(position+len <= bufLength) {
     readLength = len;
   } else {

+ 7 - 7
src/ByteArrayDiskWriter.h

@@ -40,8 +40,8 @@
 class ByteArrayDiskWriter : public DiskWriter {
 private:
   char* buf;
-  int maxBufLength;
-  int bufLength;
+  uint32_t maxBufLength;
+  uint32_t bufLength;
 
   void init();
   void clear();
@@ -49,19 +49,19 @@ public:
   ByteArrayDiskWriter();
   virtual ~ByteArrayDiskWriter();
 
-  virtual void initAndOpenFile(const string& filename);
+  virtual void initAndOpenFile(const string& filename, uint64_t totalLength = 0);
 
-  virtual void openFile(const string& filename);
+  virtual void openFile(const string& filename, uint64_t totalLength = 0);
 
   virtual void closeFile();
 
   virtual void openExistingFile(const string& filename);
 
   // position is ignored
-  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 void writeData(const char* data, uint32_t len, int64_t position = 0);
+  virtual int readData(char* data, uint32_t len, int64_t position);
   // not implemented yet
-  virtual string sha1Sum(long long int offset, long long int length) { return ""; }
+  virtual string sha1Sum(int64_t offset, uint64_t length) { return ""; }
 
   const char* getByteArray() const {
     return buf;

+ 58 - 0
src/ConsoleFileAllocationMonitor.cc

@@ -0,0 +1,58 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "ConsoleFileAllocationMonitor.h"
+#include "Util.h"
+
+void ConsoleFileAllocationMonitor::showProgress() {
+  uint32_t progressPercentage = (uint32_t)(((current-min)*1.0/(max-min))*100);
+  uint32_t numOfStar = progressPercentage/10*2;
+
+  cout << "\r                                                                                ";
+  cout << "\r";
+  cout << "|";
+  for(uint32_t i = 0; i < numOfStar; i++) {
+    cout << "*";
+  }
+  for(uint32_t i = 0; i < 20-numOfStar; i++) {
+    cout << " ";
+  }
+  cout << "|";
+  cout << progressPercentage << "%";
+  cout << "(";
+  cout << Util::ullitos(current, true) << "/" << Util::ullitos(max, true);
+  cout << ") done";
+  cout << flush;
+  // |******************* | 95%(1,333,3256/1,553,3232 bytes) done
+}

+ 105 - 0
src/ConsoleFileAllocationMonitor.h

@@ -0,0 +1,105 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_CONSOLE_FILE_ALLOCATION_MONITOR_H_
+#define _D_CONSOLE_FILE_ALLOCATION_MONITOR_H_
+
+#include "FileAllocationMonitor.h"
+
+class ConsoleFileAllocationMonitor : public FileAllocationMonitor {
+private:
+  string filename;
+  uint64_t min;
+  uint64_t max;
+  uint64_t current;
+public:
+  ConsoleFileAllocationMonitor():min(0), max(0), current(0) {}
+
+  virtual ~ConsoleFileAllocationMonitor() {}
+
+  virtual void setFilename(const string& filename) {
+    this->filename = filename;
+  }
+
+  virtual void setMinValue(const uint64_t& min) {
+    if(max < min) {
+      this->min = max;
+    } else {
+      this->min = min;
+    }
+  }
+  
+  uint64_t getMinValue() const {
+    return min;
+  }
+
+  virtual void setMaxValue(const uint64_t& max) {
+    if(max < min) {
+      this->max = min;
+    } else {
+      this->max = max;
+    }
+  }
+
+  uint64_t getMaxValue() const {
+    return max;
+  }
+
+  virtual void setCurrentValue(const uint64_t& current) {
+    if(current > max) {
+      this->current = max;
+    } else {
+      this->current = current;
+    }
+  }
+
+  uint64_t getCurrentValue() const {
+    return current;
+  }
+
+  virtual void showProgress();
+};
+
+class ConsoleFileAllocationMonitorFactory : public FileAllocationMonitorFactory {
+public:
+  ConsoleFileAllocationMonitorFactory() {}
+
+  virtual FileAllocationMonitorHandle createNewMonitor() {
+    return new ConsoleFileAllocationMonitor();
+  }
+};
+
+typedef SharedHandle<ConsoleFileAllocationMonitorFactory> ConsoleFileAllocationMonitorFactoryHandle;
+
+#endif // _D_CONSOLE_FILE_ALLOCATION_MONITOR_H_

+ 1 - 5
src/CopyDiskAdaptor.cc

@@ -35,10 +35,6 @@
 #include "CopyDiskAdaptor.h"
 #include "Util.h"
 
-CopyDiskAdaptor::CopyDiskAdaptor(DiskWriter* diskWriter):DiskAdaptor(diskWriter) {}
-
-CopyDiskAdaptor::~CopyDiskAdaptor() {}
-
 void CopyDiskAdaptor::onDownloadComplete() {
   closeFile();
   fixFilename();
@@ -62,6 +58,6 @@ void CopyDiskAdaptor::fixFilename() {
   }
 }
 
-string CopyDiskAdaptor::getFilePath() const {
+string CopyDiskAdaptor::getFilePath() {
   return storeDir+"/"+tempFilename;
 }

+ 10 - 7
src/CopyDiskAdaptor.h

@@ -35,20 +35,20 @@
 #ifndef _D_COPY_DISK_ADAPTOR_H_
 #define _D_COPY_DISK_ADAPTOR_H_
 
-#include "DiskAdaptor.h"
-#include "DiskWriter.h"
+#include "AbstractSingleDiskAdaptor.h"
 
-class CopyDiskAdaptor : public DiskAdaptor {
+class CopyDiskAdaptor : public AbstractSingleDiskAdaptor {
 private:
   string tempFilename;
   string topDir;
 
   void fixFilename();
 protected:
-  string getFilePath() const;
+  virtual string getFilePath();
 public:
-  CopyDiskAdaptor(DiskWriter* diskWriter);
-  ~CopyDiskAdaptor();
+  CopyDiskAdaptor() {}
+
+  virtual ~CopyDiskAdaptor() {}
 
   virtual void onDownloadComplete();
 
@@ -56,7 +56,8 @@ public:
   void setTempFilename(const string& tempFilename) {
     this->tempFilename = tempFilename;
   }
-  string getTempFile() const { return this->tempFilename; }
+
+  const string& getTempFile() const { return this->tempFilename; }
 
   void setTopDir(const string& topDir) {
     this->topDir = topDir;
@@ -67,4 +68,6 @@ public:
   }
 };
 
+typedef SharedHandle<CopyDiskAdaptor> CopyDiskAdaptorHandle;
+
 #endif // _D_COPY_DISK_ADAPTOR_H_

+ 1 - 0
src/DefaultBtContext.cc

@@ -40,6 +40,7 @@
 #include "DlAbortEx.h"
 #include "ShaVisitor.h"
 #include "Util.h"
+#include <libgen.h>
 
 DefaultBtContext::DefaultBtContext() {}
 

+ 34 - 6
src/DefaultDiskWriter.cc

@@ -34,18 +34,46 @@
 /* copyright --> */
 #include "DefaultDiskWriter.h"
 #include "DlAbortEx.h"
+#include "message.h"
+#include "FileAllocator.h"
+#include "prefs.h"
+#include "Util.h"
 #include <errno.h>
 #include <unistd.h>
 
-DefaultDiskWriter::DefaultDiskWriter():AbstractDiskWriter(), totalLength(0) {}
-
-DefaultDiskWriter::DefaultDiskWriter(long long int totalLength):AbstractDiskWriter(), totalLength(totalLength) {}
+DefaultDiskWriter::DefaultDiskWriter():AbstractDiskWriter() {}
 
 DefaultDiskWriter::~DefaultDiskWriter() {}
 
-void DefaultDiskWriter::initAndOpenFile(const string& filename) {
+void DefaultDiskWriter::initAndOpenFile(const string& filename,
+					uint64_t totalLength)
+{
   createFile(filename);
-  if(totalLength > 0) {
-    ftruncate(fd, totalLength);
+  try {
+    if(totalLength > 0) {
+      if(fileAllocator.isNull()) {
+	ftruncate(fd, totalLength);
+      } else {
+	logger->notice("Allocating file %s, %s bytes",
+		       filename.c_str(),
+		       Util::ullitos(totalLength).c_str());
+	fileAllocator->allocate(fd, totalLength);
+      }
+    }
+  } catch(Exception *e) {
+    throw new DlAbortEx(e, EX_FILE_WRITE, filename.c_str(), strerror(errno));
+  }
+}
+
+DefaultDiskWriter* DefaultDiskWriter::createNewDiskWriter(const Option* option)
+{
+  DefaultDiskWriter* diskWriter = new DefaultDiskWriter();
+  if(option->get(PREF_FILE_ALLOCATION) == V_PREALLOC) {
+    FileAllocatorHandle allocator = new FileAllocator();
+    allocator->setFileAllocationMonitor(FileAllocationMonitorFactory::getFactory()->createNewMonitor());
+    diskWriter->setFileAllocator(allocator);
+  } else {
+    diskWriter->setFileAllocator(0);
   }
+  return diskWriter;
 }

+ 9 - 5
src/DefaultDiskWriter.h

@@ -36,16 +36,20 @@
 #define _D_DEFAULT_DISK_WRITER_H_
 
 #include "AbstractDiskWriter.h"
+#include "Option.h"
 
 class DefaultDiskWriter:public AbstractDiskWriter {
-private:
-  long long int totalLength;
 public:
   DefaultDiskWriter();
-  DefaultDiskWriter(long long int totalLength);
-  ~DefaultDiskWriter();
 
-  void initAndOpenFile(const string& filename);
+  virtual ~DefaultDiskWriter();
+
+  virtual void initAndOpenFile(const string& filename,
+			       uint64_t totalLength = 0);
+
+  static DefaultDiskWriter* createNewDiskWriter(const Option* option);
 };
 
+typedef SharedHandle<DefaultDiskWriter> DefaultDiskWriterHandle;
+
 #endif // _D_DEFAULT_DISK_WRITER_H_

+ 19 - 14
src/DefaultPieceStorage.cc

@@ -39,10 +39,9 @@
 #include "MultiDiskAdaptor.h"
 #include "CopyDiskAdaptor.h"
 #include "DefaultDiskWriter.h"
-#include "MultiDiskWriter.h"
-#include "PreAllocationDiskWriter.h"
 #include "DlAbortEx.h"
 #include "BitfieldManFactory.h"
+#include "FileAllocationMonitor.h"
 
 DefaultPieceStorage::DefaultPieceStorage(BtContextHandle btContext, const Option* option):
   btContext(btContext),
@@ -59,7 +58,6 @@ DefaultPieceStorage::DefaultPieceStorage(BtContextHandle btContext, const Option
 
 DefaultPieceStorage::~DefaultPieceStorage() {
   delete bitfieldMan;
-  delete diskAdaptor;
 }
 
 bool DefaultPieceStorage::hasMissingPiece(const PeerHandle& peer) {
@@ -331,23 +329,30 @@ bool DefaultPieceStorage::downloadFinished() {
 
 // not unittested
 void DefaultPieceStorage::initStorage() {
-  if(diskAdaptor) {
-    delete diskAdaptor;
-    diskAdaptor = 0;
-  }
   if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE) {
     if(btContext->getFileMode() == BtContext::SINGLE) {
-      diskAdaptor = new DirectDiskAdaptor(new DefaultDiskWriter(btContext->getTotalLength()));
+      DefaultDiskWriterHandle writer = DefaultDiskWriter::createNewDiskWriter(option);
+      DirectDiskAdaptorHandle directDiskAdaptor = new DirectDiskAdaptor();
+      directDiskAdaptor->setDiskWriter(writer);
+      directDiskAdaptor->setTotalLength(btContext->getTotalLength());
+      this->diskAdaptor = directDiskAdaptor;
     } else {
-      diskAdaptor = new MultiDiskAdaptor(new MultiDiskWriter(btContext->getPieceLength()));
-      ((MultiDiskAdaptor*)diskAdaptor)->setTopDir(btContext->getName());
+      MultiDiskAdaptorHandle multiDiskAdaptor = new MultiDiskAdaptor();
+      multiDiskAdaptor->setPieceLength(btContext->getPieceLength());
+      multiDiskAdaptor->setTopDir(btContext->getName());
+      multiDiskAdaptor->setOption(option);
+      this->diskAdaptor = multiDiskAdaptor;
     }
   } else {
-    diskAdaptor = new CopyDiskAdaptor(new PreAllocationDiskWriter(btContext->getTotalLength()));
-    ((CopyDiskAdaptor*)diskAdaptor)->setTempFilename(btContext->getName()+".a2tmp");
+    DefaultDiskWriterHandle writer = DefaultDiskWriter::createNewDiskWriter(option);
+    CopyDiskAdaptorHandle copyDiskAdaptor = new CopyDiskAdaptor();
+    copyDiskAdaptor->setDiskWriter(writer);
+    copyDiskAdaptor->setTempFilename(btContext->getName()+".a2tmp");
+    copyDiskAdaptor->setTotalLength(btContext->getTotalLength());
     if(btContext->getFileMode() == BtContext::MULTI) {
-      ((CopyDiskAdaptor*)diskAdaptor)->setTopDir(btContext->getName());
+      copyDiskAdaptor->setTopDir(btContext->getName());
     }
+    this->diskAdaptor = copyDiskAdaptor;
   }
   string storeDir = option->get(PREF_DIR);
   if(storeDir == "") {
@@ -370,7 +375,7 @@ const unsigned char* DefaultPieceStorage::getBitfield() {
   return bitfieldMan->getBitfield();
 }
 
-DiskAdaptor* DefaultPieceStorage::getDiskAdaptor() {
+DiskAdaptorHandle DefaultPieceStorage::getDiskAdaptor() {
   return diskAdaptor;
 }
 

+ 4 - 2
src/DefaultPieceStorage.h

@@ -42,6 +42,7 @@
 #include "Logger.h"
 #include "Option.h"
 #include "Piece.h"
+#include "FileAllocator.h"
 
 #define END_GAME_PIECE_NUM 20
 
@@ -68,12 +69,13 @@ class DefaultPieceStorage : public PieceStorage {
 private:
   BtContextHandle btContext;
   BitfieldMan* bitfieldMan;
-  DiskAdaptor* diskAdaptor;
+  DiskAdaptorHandle diskAdaptor;
   Pieces usedPieces;
   uint32_t endGamePieceNum;
   Logger* logger;
   const Option* option;
   Haves haves;
+  FileAllocatorHandle createFileAllocator();
 
   int getMissingPieceIndex(const PeerHandle& peer);
   int getMissingFastPieceIndex(const PeerHandle& peer);
@@ -139,7 +141,7 @@ public:
 
   virtual bool isEndGame();
   
-  virtual DiskAdaptor* getDiskAdaptor();
+  virtual DiskAdaptorHandle getDiskAdaptor();
 
   virtual int getPieceLength(int index);
 

+ 1 - 5
src/DirectDiskAdaptor.cc

@@ -34,11 +34,7 @@
 /* copyright --> */
 #include "DirectDiskAdaptor.h"
 
-DirectDiskAdaptor::DirectDiskAdaptor(DiskWriter* diskWriter):DiskAdaptor(diskWriter) {}
-
-DirectDiskAdaptor::~DirectDiskAdaptor() {}
-
-string DirectDiskAdaptor::getFilePath() const {
+string DirectDiskAdaptor::getFilePath() {
   return storeDir+"/"+fileEntries.front()->getPath();
 }
 

+ 7 - 5
src/DirectDiskAdaptor.h

@@ -35,16 +35,18 @@
 #ifndef _D_DIRECT_DISK_ADAPTOR_H_
 #define _D_DIRECT_DISK_ADAPTOR_H_
 
-#include "DiskAdaptor.h"
+#include "AbstractSingleDiskAdaptor.h"
 
-class DirectDiskAdaptor : public DiskAdaptor {
+class DirectDiskAdaptor : public AbstractSingleDiskAdaptor {
 protected:
-  string getFilePath() const;
+  virtual string getFilePath();
 public:
-  DirectDiskAdaptor(DiskWriter* diskWriter);
-  virtual ~DirectDiskAdaptor();
+  DirectDiskAdaptor() {};
+  virtual ~DirectDiskAdaptor() {};
 
   virtual void onDownloadComplete();
 };
 
+typedef SharedHandle<DirectDiskAdaptor> DirectDiskAdaptorHandle;
+
 #endif // _D_DIRECT_DISK_ADAPTOR_H_

+ 2 - 34
src/DiskAdaptor.cc

@@ -36,41 +36,9 @@
 #include "DlAbortEx.h"
 #include "LogFactory.h"
 
-DiskAdaptor::DiskAdaptor(DiskWriter* diskWriter):diskWriter(diskWriter) {
-  logger = LogFactory::getInstance();
-}
-
-DiskAdaptor::~DiskAdaptor() {
-  delete diskWriter;
-}
-
-void DiskAdaptor::openFile() {
-  diskWriter->openFile(getFilePath());
-}
-
-void DiskAdaptor::closeFile() {
-  diskWriter->closeFile();
-}
-
-void DiskAdaptor::openExistingFile() {
-  diskWriter->openExistingFile(getFilePath());
-}
-
-void DiskAdaptor::initAndOpenFile() {
-  diskWriter->initAndOpenFile(getFilePath());
-}
-
-void DiskAdaptor::writeData(const unsigned char* data, uint32_t len, int64_t offset) {
-  diskWriter->writeData(data, len, offset);
-}
-
-int DiskAdaptor::readData(unsigned char* data, uint32_t len, int64_t offset) {
-  return diskWriter->readData(data, len, offset);
-}
+DiskAdaptor::DiskAdaptor():logger(LogFactory::getInstance()) {}
 
-string DiskAdaptor::sha1Sum(int64_t offset, uint64_t length) {
-  return diskWriter->sha1Sum(offset, length);
-}
+DiskAdaptor::~DiskAdaptor() {}
 
 FileEntryHandle DiskAdaptor::getFileEntryFromPath(const string& fileEntryPath) const {
   for(FileEntries::const_iterator itr = fileEntries.begin();

+ 19 - 20
src/DiskAdaptor.h

@@ -37,36 +37,30 @@
 
 #include "common.h"
 #include "FileEntry.h"
-#include "Directory.h"
-#include "DiskWriter.h"
 #include "Logger.h"
-#include "FileEntry.h"
 
 class DiskAdaptor {
 protected:
-  DiskWriter* diskWriter;
   string storeDir;
   FileEntries fileEntries;
   const Logger* logger;
-  virtual string getFilePath() const = 0;
 public:
-  DiskAdaptor(DiskWriter* diskWriter);
+  DiskAdaptor();
   virtual ~DiskAdaptor();
 
-  virtual void openFile();
-  virtual void closeFile();
-  virtual void openExistingFile();
-  virtual void initAndOpenFile();
-  void writeData(const unsigned char* data, uint32_t len, int64_t offset);
-  void writeData(const char* data, uint32_t len, int64_t offset) {
-    writeData((const unsigned char*)data, len, offset);
-  }
-  int readData(unsigned char* data, uint32_t len, int64_t offset);
-  int readData(char* data, uint32_t len, int64_t offset) {
-    return readData((unsigned char*)data, len, offset);
-  }
+  virtual void openFile() = 0;
+
+  virtual void closeFile() = 0;
+
+  virtual void openExistingFile() = 0;
+
+  virtual void initAndOpenFile() = 0;
 
-  string sha1Sum(int64_t offset, uint64_t length);
+  virtual void writeData(const unsigned char* data, uint32_t len, int64_t offset) = 0;
+
+  virtual int readData(unsigned char* data, uint32_t len, int64_t offset) = 0;
+
+  virtual string sha1Sum(int64_t offset, uint64_t length) = 0;
 
   virtual void onDownloadComplete() = 0;  
 
@@ -79,13 +73,18 @@ public:
   const FileEntries& getFileEntries() const { return fileEntries; }
 
   bool addDownloadEntry(const string& fileEntryPath);
+
   bool addDownloadEntry(int index);
+
   void addAllDownloadEntry();
+
   void removeAllDownloadEntry();
 
   void setStoreDir(const string& storeDir) { this->storeDir = storeDir; }
 
-  string getStoreDir() const { return this->storeDir; }
+  const string& getStoreDir() const { return this->storeDir; }
 };
 
+typedef SharedHandle<DiskAdaptor> DiskAdaptorHandle;
+
 #endif // _D_DISK_ADAPTOR_H_

+ 10 - 9
src/DiskWriter.h

@@ -52,9 +52,9 @@ public:
    * If the file exists, then it is truncated to 0 length.
    * @param filename the file name to be opened.
    */
-  virtual void initAndOpenFile(const string& filename) = 0;
-
-  virtual void openFile(const string& filename) = 0;
+  virtual void initAndOpenFile(const string& filename, uint64_t totalLength = 0) = 0;
+  
+  virtual void openFile(const string& filename, uint64_t totalLength = 0) = 0;
 
   /**
    * Closes this output stream.
@@ -78,19 +78,20 @@ public:
    * @param len the number of bytes to write
    * @param position the offset of this binary stream
    */
-  virtual void writeData(const char* data, int len, long long int position = 0) = 0;
-  virtual void writeData(const unsigned char* data, int len, long long int position = 0)
+  virtual void writeData(const char* data, uint32_t len, int64_t position = 0) = 0;
+  virtual void writeData(const unsigned char* data, uint32_t len, int64_t position = 0)
   {
     writeData((const char*)data, len, position);
   }
 
-  virtual int readData(char* data, int len, long long int position) = 0;
-  virtual int readData(unsigned char* data, int len, long long int position) {
+  virtual int readData(char* data, uint32_t len, int64_t position) = 0;
+  virtual int readData(unsigned char* data, uint32_t len, int64_t position) {
     return readData((char*)data, len, position);
   }
 
-  virtual string sha1Sum(long long int offset, long long int length) = 0;
-
+  virtual string sha1Sum(int64_t offset, uint64_t length) = 0;
 };
 
+typedef SharedHandle<DiskWriter> DiskWriterHandle;
+
 #endif // _D_DISK_WRITER_H_

+ 3 - 1
src/DownloadEngineFactory.cc

@@ -38,6 +38,8 @@
 #include "InitiateConnectionCommandFactory.h"
 #include "ByteArrayDiskWriter.h"
 #include "Util.h"
+#include "FileAllocator.h"
+#include "FileAllocationMonitor.h"
 #ifdef ENABLE_BITTORRENT
 # include "PeerListenCommand.h"
 # include "TrackerWatcherCommand.h"
@@ -63,7 +65,7 @@ DownloadEngineFactory::newConsoleEngine(const Option* op,
   ConsoleDownloadEngine* e = new ConsoleDownloadEngine();
   e->option = op;
   e->segmentMan = new SegmentMan();
-  e->segmentMan->diskWriter = new DefaultDiskWriter();
+  e->segmentMan->diskWriter = DefaultDiskWriter::createNewDiskWriter(op);
   e->segmentMan->dir = op->get(PREF_DIR);
   e->segmentMan->ufilename = op->get(PREF_OUT);
   e->segmentMan->option = op;

+ 3 - 1
src/Exception.h

@@ -56,7 +56,9 @@ protected:
 public:
   Exception(Exception* cause = 0):cause(cause) {}
 
-  virtual ~Exception() {}
+  virtual ~Exception() {
+    delete cause;
+  }
 
   const string& getMsg() const { return msg; }
 

+ 37 - 0
src/FileAllocationMonitor.cc

@@ -0,0 +1,37 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "FileAllocationMonitor.h"
+
+FileAllocationMonitorFactoryHandle FileAllocationMonitorFactory::factory = 0;

+ 67 - 0
src/FileAllocationMonitor.h

@@ -0,0 +1,67 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_FILE_ALLOCATION_MONITOR_H_
+#define _D_FILE_ALLOCATION_MONITOR_H_
+
+#include "FileProgressMonitor.h"
+
+typedef FileProgressMonitor<uint64_t> FileAllocationMonitor;
+typedef SharedHandle<FileAllocationMonitor> FileAllocationMonitorHandle;
+
+class FileAllocationMonitorFactory;
+
+typedef SharedHandle<FileAllocationMonitorFactory> FileAllocationMonitorFactoryHandle;
+
+class FileAllocationMonitorFactory {
+private:
+  static FileAllocationMonitorFactoryHandle factory;
+
+protected:
+  FileAllocationMonitorFactory() {}
+public:
+  static FileAllocationMonitorFactoryHandle getFactory() {
+    return factory;
+  }
+
+  virtual ~FileAllocationMonitorFactory() {}
+
+  static void setFactory(const FileAllocationMonitorFactoryHandle& factory) {
+    FileAllocationMonitorFactory::factory = factory;
+  }
+
+  virtual FileAllocationMonitorHandle createNewMonitor() = 0;
+};
+
+#endif // _D_FILE_ALLOCATION_MONITOR_H_

+ 69 - 0
src/FileAllocator.cc

@@ -0,0 +1,69 @@
+
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "FileAllocator.h"
+#include "DlAbortEx.h"
+#include "TimeA2.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+void FileAllocator::allocate(int fd, uint64_t totalLength)
+{
+  if(0 != lseek(fd, 0, SEEK_SET)) {
+    throw new DlAbortEx("Seek failed: %s", strerror(errno));
+  }
+  uint32_t bufSize = 4096;
+  char buf[4096];
+  memset(buf, 0, bufSize);
+  uint64_t x = (totalLength+bufSize-1)/bufSize;
+  fileAllocationMonitor->setMinValue(0);
+  fileAllocationMonitor->setMaxValue(totalLength);
+  fileAllocationMonitor->setCurrentValue(0);
+  fileAllocationMonitor->showProgress();
+  Time cp;
+  for(uint64_t i = 0; i < x; i++) {
+    if(write(fd, buf, bufSize) < 0) {
+      throw new DlAbortEx("Allocation failed: %s", strerror(errno));
+    }
+    if(cp.elapsedInMillis(500)) {
+      fileAllocationMonitor->setCurrentValue(x*bufSize);
+      fileAllocationMonitor->showProgress();
+    }
+  }
+  fileAllocationMonitor->setCurrentValue(totalLength);
+  fileAllocationMonitor->showProgress();
+  ftruncate(fd, totalLength);
+}

+ 59 - 0
src/FileAllocator.h

@@ -0,0 +1,59 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_FILE_ALLOCATOR_H_
+#define _D_FILE_ALLOCATOR_H_
+
+#include "common.h"
+#include "FileAllocationMonitor.h"
+
+class FileAllocator {
+private:
+  FileAllocationMonitorHandle fileAllocationMonitor;
+public:
+  FileAllocator():fileAllocationMonitor(0) {}
+
+  ~FileAllocator() {}
+
+  void allocate(int fd, uint64_t totalLength);
+
+  void setFileAllocationMonitor(const FileAllocationMonitorHandle& monitor)
+  {
+    this->fileAllocationMonitor = monitor;
+  }
+};
+
+typedef SharedHandle<FileAllocator> FileAllocatorHandle;
+
+#endif // _D_FILE_ALLOCATOR_H_

+ 56 - 0
src/FileProgressMonitor.h

@@ -0,0 +1,56 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_FILE_PROGRESS_MONITOR_H_
+#define _D_FILE_PROGRESS_MONITOR_H_
+
+#include "common.h"
+
+template <typename T>
+class FileProgressMonitor {
+public:
+  virtual ~FileProgressMonitor() {}
+
+  virtual void setFilename(const string& filename) = 0;
+
+  virtual void setMinValue(const T& min) = 0;
+  
+  virtual void setMaxValue(const T& max) = 0;
+
+  virtual void setCurrentValue(const T& current) = 0;
+
+  virtual void showProgress() = 0;
+};
+
+#endif // _D_FILE_PROGRESS_MONITOR_H_

+ 0 - 11
src/FtpInitiateConnectionCommand.cc

@@ -74,17 +74,6 @@ bool FtpInitiateConnectionCommand::executeInternal(Segment& segment) {
     }
   }
 #endif // ENABLE_ASYNC_DNS
-  if(!e->segmentMan->downloadStarted) {
-    e->segmentMan->filename = Util::urldecode(req->getFile());
-    bool segFileExists = e->segmentMan->segmentFileExists();
-    if(segFileExists) {
-      e->segmentMan->load();
-      e->segmentMan->diskWriter->openExistingFile(e->segmentMan->getFilePath());
-      e->segmentMan->downloadStarted = true;
-    } else {
-      e->segmentMan->diskWriter->initAndOpenFile(e->segmentMan->getFilePath());
-    }
-  }
   Command* command;
   if(useHttpProxy()) {
     logger->info(MSG_CONNECTING_TO_SERVER, cuid,

+ 10 - 0
src/FtpNegotiationCommand.cc

@@ -38,6 +38,7 @@
 #include "DlRetryEx.h"
 #include "message.h"
 #include "prefs.h"
+#include "Util.h"
 
 FtpNegotiationCommand::FtpNegotiationCommand(int cuid, const RequestHandle req,
 					     DownloadEngine* e,
@@ -185,6 +186,15 @@ bool FtpNegotiationCommand::recvSize() {
     e->segmentMan->totalSize = size;
     e->segmentMan->initBitfield(e->option->getAsInt(PREF_SEGMENT_SIZE),
 				e->segmentMan->totalSize);
+
+    e->segmentMan->filename = Util::urldecode(req->getFile());
+    bool segFileExists = e->segmentMan->segmentFileExists();
+    if(segFileExists) {
+      e->segmentMan->load();
+      e->segmentMan->diskWriter->openExistingFile(e->segmentMan->getFilePath());
+    } else {
+      e->segmentMan->diskWriter->initAndOpenFile(e->segmentMan->getFilePath(), size);
+    }
   } else if(e->segmentMan->totalSize != size) {
     throw new DlAbortEx(EX_SIZE_MISMATCH, e->segmentMan->totalSize, size);
   }

+ 2 - 1
src/HttpResponseCommand.cc

@@ -162,7 +162,8 @@ bool HttpResponseCommand::handleDefaultEncoding(const HttpHeader& headers) {
     e->segmentMan->totalSize = size;
     e->segmentMan->initBitfield(e->option->getAsInt(PREF_SEGMENT_SIZE),
 				e->segmentMan->totalSize);
-    e->segmentMan->diskWriter->initAndOpenFile(e->segmentMan->getFilePath());
+    e->segmentMan->diskWriter->initAndOpenFile(e->segmentMan->getFilePath(),
+					       size);
     return prepareForRetry(0);
   }
 }

+ 6 - 4
src/Makefile.am

@@ -37,9 +37,8 @@ SRCS =  Socket.h\
 	TransferEncoding.h\
 	ChunkedEncoding.cc ChunkedEncoding.h\
 	DiskWriter.h\
-	DefaultDiskWriter.cc DefaultDiskWriter.h\
-	PreAllocationDiskWriter.cc PreAllocationDiskWriter.h\
 	AbstractDiskWriter.cc AbstractDiskWriter.h\
+	DefaultDiskWriter.cc DefaultDiskWriter.h\
 	File.cc File.h\
 	Option.cc Option.h\
 	Base64.cc Base64.h\
@@ -59,7 +58,10 @@ SRCS =  Socket.h\
 	BitfieldMan.cc BitfieldMan.h\
 	BitfieldManFactory.cc BitfieldManFactory.h\
 	Randomizer.h\
-	SimpleRandomizer.cc SimpleRandomizer.h
+	SimpleRandomizer.cc SimpleRandomizer.h\
+	FileAllocator.cc FileAllocator.h\
+	FileAllocationMonitor.cc FileAllocationMonitor.h\
+	ConsoleFileAllocationMonitor.cc ConsoleFileAllocationMonitor.h
 #	debug_new.cpp
 
 if ENABLE_ASYNC_DNS
@@ -88,8 +90,8 @@ SRCS += MetaEntry.h\
 	TorrentAutoSaveCommand.cc TorrentAutoSaveCommand.h\
 	Directory.cc Directory.h\
 	TrackerWatcherCommand.cc TrackerWatcherCommand.h\
-	MultiDiskWriter.cc MultiDiskWriter.h\
 	DiskAdaptor.cc DiskAdaptor.h\
+	AbstractSingleDiskAdaptor.cc AbstractSingleDiskAdaptor.h\
 	CopyDiskAdaptor.cc CopyDiskAdaptor.h\
 	DirectDiskAdaptor.cc DirectDiskAdaptor.h\
 	MultiDiskAdaptor.cc MultiDiskAdaptor.h\

+ 38 - 32
src/Makefile.in

@@ -61,8 +61,8 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_BITTORRENT_TRUE@	TorrentAutoSaveCommand.cc TorrentAutoSaveCommand.h\
 @ENABLE_BITTORRENT_TRUE@	Directory.cc Directory.h\
 @ENABLE_BITTORRENT_TRUE@	TrackerWatcherCommand.cc TrackerWatcherCommand.h\
-@ENABLE_BITTORRENT_TRUE@	MultiDiskWriter.cc MultiDiskWriter.h\
 @ENABLE_BITTORRENT_TRUE@	DiskAdaptor.cc DiskAdaptor.h\
+@ENABLE_BITTORRENT_TRUE@	AbstractSingleDiskAdaptor.cc AbstractSingleDiskAdaptor.h\
 @ENABLE_BITTORRENT_TRUE@	CopyDiskAdaptor.cc CopyDiskAdaptor.h\
 @ENABLE_BITTORRENT_TRUE@	DirectDiskAdaptor.cc DirectDiskAdaptor.h\
 @ENABLE_BITTORRENT_TRUE@	MultiDiskAdaptor.cc MultiDiskAdaptor.h\
@@ -199,9 +199,8 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	message.h Exception.h DlAbortEx.h DlRetryEx.h Logger.h \
 	SimpleLogger.cc SimpleLogger.h TransferEncoding.h \
 	ChunkedEncoding.cc ChunkedEncoding.h DiskWriter.h \
-	DefaultDiskWriter.cc DefaultDiskWriter.h \
-	PreAllocationDiskWriter.cc PreAllocationDiskWriter.h \
-	AbstractDiskWriter.cc AbstractDiskWriter.h File.cc File.h \
+	AbstractDiskWriter.cc AbstractDiskWriter.h \
+	DefaultDiskWriter.cc DefaultDiskWriter.h File.cc File.h \
 	Option.cc Option.h Base64.cc Base64.h CookieBox.cc CookieBox.h \
 	messageDigest.h LogFactory.cc LogFactory.h NullLogger.h \
 	TimeA2.cc TimeA2.h SharedHandle.h HandleRegistry.h \
@@ -210,12 +209,15 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	UrlRequestInfo.h SpeedCalc.cc SpeedCalc.h PeerStat.h \
 	BitfieldMan.cc BitfieldMan.h BitfieldManFactory.cc \
 	BitfieldManFactory.h Randomizer.h SimpleRandomizer.cc \
-	SimpleRandomizer.h NameResolver.cc NameResolver.h MetaEntry.h \
-	Data.cc Data.h Dictionary.cc Dictionary.h List.cc List.h \
-	MetaFileUtil.cc MetaFileUtil.h MetaEntryVisitor.h \
-	ShaVisitor.cc ShaVisitor.h PeerConnection.cc PeerConnection.h \
-	PeerMessageUtil.cc PeerMessageUtil.h PeerAbstractCommand.cc \
-	PeerAbstractCommand.h PeerInitiateConnectionCommand.cc \
+	SimpleRandomizer.h FileAllocator.cc FileAllocator.h \
+	FileAllocationMonitor.cc FileAllocationMonitor.h \
+	ConsoleFileAllocationMonitor.cc ConsoleFileAllocationMonitor.h \
+	NameResolver.cc NameResolver.h MetaEntry.h Data.cc Data.h \
+	Dictionary.cc Dictionary.h List.cc List.h MetaFileUtil.cc \
+	MetaFileUtil.h MetaEntryVisitor.h ShaVisitor.cc ShaVisitor.h \
+	PeerConnection.cc PeerConnection.h PeerMessageUtil.cc \
+	PeerMessageUtil.h PeerAbstractCommand.cc PeerAbstractCommand.h \
+	PeerInitiateConnectionCommand.cc \
 	PeerInitiateConnectionCommand.h PeerInteractionCommand.cc \
 	PeerInteractionCommand.h Peer.cc Peer.h \
 	TorrentDownloadEngine.cc TorrentDownloadEngine.h \
@@ -224,11 +226,11 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	RequestSlot.cc RequestSlot.h TorrentAutoSaveCommand.cc \
 	TorrentAutoSaveCommand.h Directory.cc Directory.h \
 	TrackerWatcherCommand.cc TrackerWatcherCommand.h \
-	MultiDiskWriter.cc MultiDiskWriter.h DiskAdaptor.cc \
-	DiskAdaptor.h CopyDiskAdaptor.cc CopyDiskAdaptor.h \
-	DirectDiskAdaptor.cc DirectDiskAdaptor.h MultiDiskAdaptor.cc \
-	MultiDiskAdaptor.h FileEntry.cc FileEntry.h \
-	TrackerUpdateCommand.cc TrackerUpdateCommand.h \
+	DiskAdaptor.cc DiskAdaptor.h AbstractSingleDiskAdaptor.cc \
+	AbstractSingleDiskAdaptor.h CopyDiskAdaptor.cc \
+	CopyDiskAdaptor.h DirectDiskAdaptor.cc DirectDiskAdaptor.h \
+	MultiDiskAdaptor.cc MultiDiskAdaptor.h FileEntry.cc \
+	FileEntry.h TrackerUpdateCommand.cc TrackerUpdateCommand.h \
 	ByteArrayDiskWriter.cc ByteArrayDiskWriter.h \
 	PeerChokeCommand.cc PeerChokeCommand.h HaveEraseCommand.cc \
 	HaveEraseCommand.h TorrentRequestInfo.cc TorrentRequestInfo.h \
@@ -297,8 +299,8 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 @ENABLE_BITTORRENT_TRUE@	TorrentAutoSaveCommand.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	Directory.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	TrackerWatcherCommand.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	MultiDiskWriter.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DiskAdaptor.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	AbstractSingleDiskAdaptor.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	CopyDiskAdaptor.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DirectDiskAdaptor.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	MultiDiskAdaptor.$(OBJEXT) \
@@ -365,15 +367,16 @@ am__objects_4 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
 	DownloadEngine.$(OBJEXT) ConsoleDownloadEngine.$(OBJEXT) \
 	Segment.$(OBJEXT) SegmentMan.$(OBJEXT) Util.$(OBJEXT) \
 	Request.$(OBJEXT) SimpleLogger.$(OBJEXT) \
-	ChunkedEncoding.$(OBJEXT) DefaultDiskWriter.$(OBJEXT) \
-	PreAllocationDiskWriter.$(OBJEXT) AbstractDiskWriter.$(OBJEXT) \
-	File.$(OBJEXT) Option.$(OBJEXT) Base64.$(OBJEXT) \
-	CookieBox.$(OBJEXT) LogFactory.$(OBJEXT) TimeA2.$(OBJEXT) \
-	FeatureConfig.$(OBJEXT) DownloadEngineFactory.$(OBJEXT) \
-	UrlRequestInfo.$(OBJEXT) SpeedCalc.$(OBJEXT) \
-	BitfieldMan.$(OBJEXT) BitfieldManFactory.$(OBJEXT) \
-	SimpleRandomizer.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
-	$(am__objects_3)
+	ChunkedEncoding.$(OBJEXT) AbstractDiskWriter.$(OBJEXT) \
+	DefaultDiskWriter.$(OBJEXT) File.$(OBJEXT) Option.$(OBJEXT) \
+	Base64.$(OBJEXT) CookieBox.$(OBJEXT) LogFactory.$(OBJEXT) \
+	TimeA2.$(OBJEXT) FeatureConfig.$(OBJEXT) \
+	DownloadEngineFactory.$(OBJEXT) UrlRequestInfo.$(OBJEXT) \
+	SpeedCalc.$(OBJEXT) BitfieldMan.$(OBJEXT) \
+	BitfieldManFactory.$(OBJEXT) SimpleRandomizer.$(OBJEXT) \
+	FileAllocator.$(OBJEXT) FileAllocationMonitor.$(OBJEXT) \
+	ConsoleFileAllocationMonitor.$(OBJEXT) $(am__objects_1) \
+	$(am__objects_2) $(am__objects_3)
 am_libaria2c_a_OBJECTS = $(am__objects_4)
 libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS)
 am__installdirs = "$(DESTDIR)$(bindir)"
@@ -566,9 +569,8 @@ SRCS = Socket.h SocketCore.cc SocketCore.h Command.cc Command.h \
 	message.h Exception.h DlAbortEx.h DlRetryEx.h Logger.h \
 	SimpleLogger.cc SimpleLogger.h TransferEncoding.h \
 	ChunkedEncoding.cc ChunkedEncoding.h DiskWriter.h \
-	DefaultDiskWriter.cc DefaultDiskWriter.h \
-	PreAllocationDiskWriter.cc PreAllocationDiskWriter.h \
-	AbstractDiskWriter.cc AbstractDiskWriter.h File.cc File.h \
+	AbstractDiskWriter.cc AbstractDiskWriter.h \
+	DefaultDiskWriter.cc DefaultDiskWriter.h File.cc File.h \
 	Option.cc Option.h Base64.cc Base64.h CookieBox.cc CookieBox.h \
 	messageDigest.h LogFactory.cc LogFactory.h NullLogger.h \
 	TimeA2.cc TimeA2.h SharedHandle.h HandleRegistry.h \
@@ -577,8 +579,10 @@ SRCS = Socket.h SocketCore.cc SocketCore.h Command.cc Command.h \
 	UrlRequestInfo.h SpeedCalc.cc SpeedCalc.h PeerStat.h \
 	BitfieldMan.cc BitfieldMan.h BitfieldManFactory.cc \
 	BitfieldManFactory.h Randomizer.h SimpleRandomizer.cc \
-	SimpleRandomizer.h $(am__append_1) $(am__append_2) \
-	$(am__append_3)
+	SimpleRandomizer.h FileAllocator.cc FileAllocator.h \
+	FileAllocationMonitor.cc FileAllocationMonitor.h \
+	ConsoleFileAllocationMonitor.cc ConsoleFileAllocationMonitor.h \
+	$(am__append_1) $(am__append_2) $(am__append_3)
 noinst_LIBRARIES = libaria2c.a
 libaria2c_a_SOURCES = $(SRCS)
 aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\
@@ -668,6 +672,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/alloca.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AbstractCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AbstractDiskWriter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AbstractSingleDiskAdaptor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AnnounceList.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Base64.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitfieldMan.Po@am__quote@
@@ -696,6 +701,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Command.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CompactPeerListProcessor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConsoleDownloadEngine.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConsoleFileAllocationMonitor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieBox.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CopyDiskAdaptor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Data.Po@am__quote@
@@ -721,6 +727,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadEngineFactory.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeatureConfig.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/File.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileAllocationMonitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileAllocator.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileEntry.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FtpConnection.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FtpDownloadCommand.Po@am__quote@
@@ -746,7 +754,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkResource.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Metalinker.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskAdaptor.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskWriter.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NameResolver.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@
@@ -758,7 +765,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PeerListenCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PeerMessageUtil.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Piece.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PreAllocationDiskWriter.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Request.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestSlot.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SeedCheckCommand.Po@am__quote@

+ 155 - 14
src/MultiDiskAdaptor.cc

@@ -33,44 +33,185 @@
  */
 /* copyright --> */
 #include "MultiDiskAdaptor.h"
+#include "DefaultDiskWriter.h"
+#include "DlAbortEx.h"
+#include "message.h"
+#include "Util.h"
+#include <errno.h>
 
-MultiDiskAdaptor::MultiDiskAdaptor(MultiDiskWriter* diskWriter):DiskAdaptor(diskWriter) {}
-
-MultiDiskAdaptor::~MultiDiskAdaptor() {}
-
-void MultiDiskAdaptor::setDiskWriterFileEntries() {
-  ((MultiDiskWriter*)diskWriter)->setFileEntries(fileEntries);
+void MultiDiskAdaptor::resetDiskWriterEntries() {
+  diskWriterEntries.clear();
+  for(FileEntries::const_iterator itr = fileEntries.begin();
+      itr != fileEntries.end(); itr++) {
+    DiskWriterEntryHandle entry = new DiskWriterEntry(*itr);
+    if((*itr)->isRequested()) {
+      entry->setDiskWriter(DefaultDiskWriter::createNewDiskWriter(option));
+    } else {
+      entry->setDiskWriter(new DefaultDiskWriter());
+    }
+    diskWriterEntries.push_back(entry);
+  }
 }
 
-string MultiDiskAdaptor::getFilePath() const {
+string MultiDiskAdaptor::getTopDirPath() const {
   return storeDir+"/"+topDir;
 }
 
 void MultiDiskAdaptor::mkdir() const {
   for(FileEntries::const_iterator itr = fileEntries.begin();
       itr != fileEntries.end(); itr++) {
-    (*itr)->setupDir(getFilePath());
+    (*itr)->setupDir(getTopDirPath());
   }
 }
 
 void MultiDiskAdaptor::openFile() {
   mkdir();
-  setDiskWriterFileEntries();
-  DiskAdaptor::openFile();
+  resetDiskWriterEntries();
+  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
+      itr != diskWriterEntries.end(); itr++) {
+    (*itr)->openFile(getTopDirPath());
+  }
 }
 
 void MultiDiskAdaptor::initAndOpenFile() {
   mkdir();
-  setDiskWriterFileEntries();
-  DiskAdaptor::initAndOpenFile();
+  resetDiskWriterEntries();
+  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
+      itr != diskWriterEntries.end(); itr++) {
+    (*itr)->initAndOpenFile(getTopDirPath());
+  }
 }
 
 void MultiDiskAdaptor::openExistingFile() {
-  setDiskWriterFileEntries();
-  DiskAdaptor::openExistingFile();
+  resetDiskWriterEntries();
+  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
+      itr != diskWriterEntries.end(); itr++) {
+    (*itr)->openExistingFile(getTopDirPath());
+  }
+}
+
+void MultiDiskAdaptor::closeFile() {
+  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
+      itr != diskWriterEntries.end(); itr++) {
+    (*itr)->closeFile();
+  }
 }
 
 void MultiDiskAdaptor::onDownloadComplete() {
   closeFile();
   openFile();
 }
+
+void MultiDiskAdaptor::writeData(const unsigned char* data, uint32_t len,
+				 int64_t offset)
+{
+  int64_t fileOffset = offset;
+  bool writing = false;
+  uint32_t rem = len;
+  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
+      itr != diskWriterEntries.end() && rem != 0; itr++) {
+    if(isInRange(*itr, offset) || writing) {
+      uint32_t writeLength = calculateLength(*itr, fileOffset, rem);
+      (*itr)->getDiskWriter()->writeData(data+(len-rem), writeLength, fileOffset);
+      rem -= writeLength;
+      writing = true;
+      fileOffset = 0;
+    } else {
+      fileOffset -= (*itr)->fileEntry->getLength();
+    }
+  }
+  if(!writing) {
+    throw new DlAbortEx(EX_FILE_OFFSET_OUT_OF_RANGE, offset);
+  }
+}
+
+bool MultiDiskAdaptor::isInRange(const DiskWriterEntryHandle entry,
+				 int64_t offset) const
+{
+  return entry->fileEntry->getOffset() <= offset &&
+    offset < entry->fileEntry->getOffset()+entry->fileEntry->getLength();
+}
+
+uint32_t MultiDiskAdaptor::calculateLength(const DiskWriterEntryHandle entry,
+					   int64_t fileOffset,
+					   uint32_t rem) const
+{
+  uint32_t length;
+  if(entry->fileEntry->getLength() < fileOffset+rem) {
+    length = entry->fileEntry->getLength()-fileOffset;
+  } else {
+    length = rem;
+  }
+  return length;
+}
+
+int MultiDiskAdaptor::readData(unsigned char* data, uint32_t len, int64_t offset)
+{
+  int64_t fileOffset = offset;
+  bool reading = false;
+  uint32_t rem = len;
+  uint32_t totalReadLength = 0;
+  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
+      itr != diskWriterEntries.end() && rem != 0; itr++) {
+    if(isInRange(*itr, offset) || reading) {
+      uint32_t readLength = calculateLength((*itr), fileOffset, rem);
+      totalReadLength += (*itr)->getDiskWriter()->readData(data+(len-rem), readLength, fileOffset);
+      rem -= readLength;
+      reading = true;
+      fileOffset = 0;
+    } else {
+      fileOffset -= (*itr)->fileEntry->getLength();
+    }
+  }
+  if(!reading) {
+    throw new DlAbortEx(EX_FILE_OFFSET_OUT_OF_RANGE, offset);
+  }
+  return totalReadLength;
+}
+
+void MultiDiskAdaptor::hashUpdate(const DiskWriterEntryHandle entry,
+				  int64_t offset, uint64_t length)
+{
+  uint32_t BUFSIZE = 16*1024;
+  unsigned char buf[BUFSIZE];
+  for(uint64_t i = 0; i < length/BUFSIZE; i++) {
+    if((int32_t)BUFSIZE != entry->getDiskWriter()->readData(buf, BUFSIZE, offset)) {
+      throw new DlAbortEx(EX_FILE_SHA1SUM, "", strerror(errno));
+    }
+    ctx.digestUpdate(buf, BUFSIZE);
+    offset += BUFSIZE;
+  }
+  uint32_t r = length%BUFSIZE;
+  if(r > 0) {
+    if((int32_t)r != entry->getDiskWriter()->readData(buf, r, offset)) {
+      throw new DlAbortEx(EX_FILE_SHA1SUM, "", strerror(errno));
+    }
+    ctx.digestUpdate(buf, r);
+  }
+}
+
+string MultiDiskAdaptor::sha1Sum(int64_t offset, uint64_t length) {
+  int64_t fileOffset = offset;
+  bool reading = false;
+  uint32_t rem = length;
+  ctx.digestReset();
+
+  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
+      itr != diskWriterEntries.end() && rem != 0; itr++) {
+    if(isInRange(*itr, offset) || reading) {
+      uint32_t readLength = calculateLength((*itr), fileOffset, rem);
+      hashUpdate(*itr, fileOffset, readLength);
+      rem -= readLength;
+      reading = true;
+      fileOffset = 0;
+    } else {
+      fileOffset -= (*itr)->fileEntry->getLength();
+    }
+  }
+  if(!reading) {
+    throw new DlAbortEx(EX_FILE_OFFSET_OUT_OF_RANGE, offset);
+  }
+  unsigned char hashValue[20];
+  ctx.digestFinal(hashValue);
+  return Util::toHex(hashValue, 20);
+}

+ 112 - 10
src/MultiDiskAdaptor.h

@@ -36,19 +36,108 @@
 #define _D_MULTI_DISK_ADAPTOR_H_
 
 #include "DiskAdaptor.h"
-#include "MultiDiskWriter.h"
+#include "Option.h"
+#include "DiskWriter.h"
+#include "messageDigest.h"
+
+class DiskWriterEntry {
+public:
+  FileEntryHandle fileEntry;
+private:
+  DiskWriterHandle diskWriter;
+public:
+  DiskWriterEntry(const FileEntryHandle& fileEntry):
+    fileEntry(fileEntry),
+    diskWriter(0) {}
+
+  ~DiskWriterEntry() {}
+
+  string getFilePath(const string& topDir) const
+  {
+    return topDir+"/"+fileEntry->getPath();
+  }
+
+  void initAndOpenFile(const string& topDir)
+  {
+    diskWriter->initAndOpenFile(getFilePath(topDir), fileEntry->getLength());
+  }
+
+  void openFile(const string& topDir)
+  {
+    diskWriter->openFile(getFilePath(topDir), fileEntry->getLength());
+  }
+
+  void openExistingFile(const string& topDir)
+  {
+    diskWriter->openExistingFile(getFilePath(topDir));
+  }
+
+  void closeFile()
+  {
+    diskWriter->closeFile();
+  }
+
+  void setDiskWriter(const DiskWriterHandle& diskWriter) {
+    this->diskWriter = diskWriter;
+  }
+
+  DiskWriterHandle getDiskWriter() const {
+    return diskWriter;
+  }
+};
+
+typedef SharedHandle<DiskWriterEntry> DiskWriterEntryHandle;
+
+typedef deque<DiskWriterEntryHandle> DiskWriterEntries;
 
 class MultiDiskAdaptor : public DiskAdaptor {
 private:
   string topDir;
+  uint32_t pieceLength;
+  MessageDigestContext ctx;
+  DiskWriterEntries diskWriterEntries;
+  const Option* option;
+
+  void resetDiskWriterEntries();
 
-  void setDiskWriterFileEntries();
   void mkdir() const;
-protected:
-  virtual string getFilePath() const;
+
+  bool isInRange(const DiskWriterEntryHandle entry, int64_t offset) const;
+
+  uint32_t calculateLength(const DiskWriterEntryHandle entry,
+			   int64_t fileOffset,
+			   uint32_t rem) const;
+
+  void hashUpdate(const DiskWriterEntryHandle entry,
+		  int64_t offset, uint64_t length);
+
+  string getTopDirPath() const;
 public:
-  MultiDiskAdaptor(MultiDiskWriter* diskWriter);
-  virtual ~MultiDiskAdaptor();
+  MultiDiskAdaptor():pieceLength(0),
+		     ctx(DIGEST_ALGO_SHA1),
+		     option(0)
+  {
+    ctx.digestInit();
+  }
+
+  virtual ~MultiDiskAdaptor() {}
+
+  virtual void initAndOpenFile();
+
+  virtual void openFile();
+
+  virtual void openExistingFile();
+
+  virtual void closeFile();
+
+  virtual void onDownloadComplete();
+
+  virtual void writeData(const unsigned char* data, uint32_t len,
+			 int64_t offset);
+
+  virtual int readData(unsigned char* data, uint32_t len, int64_t offset);
+
+  virtual string sha1Sum(int64_t offset, uint64_t length);
 
   void setTopDir(const string& topDir) {
     this->topDir = topDir;
@@ -58,10 +147,23 @@ public:
     return topDir;
   }
 
-  virtual void openFile();
-  virtual void initAndOpenFile();
-  virtual void openExistingFile();
-  virtual void onDownloadComplete();
+  void setPieceLength(uint32_t pieceLength) {
+    this->pieceLength = pieceLength;
+  }
+
+  uint32_t getPieceLength() const {
+    return pieceLength;
+  }
+
+  void setOption(const Option* option) {
+    this->option = option;
+  }
+
+  const Option* getOption() const {
+    return option;
+  }
 };
 
+typedef SharedHandle<MultiDiskAdaptor> MultiDiskAdaptorHandle;
+
 #endif // _D_MULTI_DISK_ADAPTOR_H_

+ 13 - 1
src/MultiDiskWriter.cc

@@ -40,7 +40,8 @@
 
 MultiDiskWriter::MultiDiskWriter(int pieceLength):
   pieceLength(pieceLength),
-  ctx(DIGEST_ALGO_SHA1) {
+  ctx(DIGEST_ALGO_SHA1),
+  fileAllocator(0) {
   ctx.digestInit();
 }
 
@@ -65,9 +66,18 @@ void MultiDiskWriter::setFileEntries(const FileEntries& fileEntries) {
   }
 }
 
+void MultiDiskWriter::configureFileAllocator(DiskWriterEntry* entry) {
+  if(entry->fileEntry->isRequested()) {
+    entry->diskWriter->setFileAllocator(fileAllocator);
+  } else {
+    entry->diskWriter->setFileAllocator(0);
+  }
+}
+
 void MultiDiskWriter::openFile(const string& filename) {
   for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
       itr != diskWriterEntries.end(); itr++) {
+    configureFileAllocator(*itr);
     (*itr)->diskWriter->openFile(filename+"/"+(*itr)->fileEntry->getPath());
   }
 }
@@ -76,6 +86,7 @@ void MultiDiskWriter::openFile(const string& filename) {
 void MultiDiskWriter::initAndOpenFile(const string& filename) {
   for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
       itr != diskWriterEntries.end(); itr++) {
+    configureFileAllocator(*itr);
     (*itr)->diskWriter->initAndOpenFile(filename+"/"+(*itr)->fileEntry->getPath());
   }
 }
@@ -90,6 +101,7 @@ void MultiDiskWriter::closeFile() {
 void MultiDiskWriter::openExistingFile(const string& filename) {
   for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
       itr != diskWriterEntries.end(); itr++) {
+    (*itr)->diskWriter->setFileAllocator(0);
     (*itr)->diskWriter->openExistingFile(filename+"/"+(*itr)->fileEntry->getPath());
   }
 }

+ 10 - 3
src/MultiDiskWriter.h

@@ -38,11 +38,12 @@
 #include "DefaultDiskWriter.h"
 #include "messageDigest.h"
 #include "FileEntry.h"
+#include "FileAllocator.h"
 
 class DiskWriterEntry {
 public:
   FileEntryHandle fileEntry;
-  DiskWriter* diskWriter;
+  DefaultDiskWriter* diskWriter;
 public:
   DiskWriterEntry(const FileEntryHandle& fileEntry):fileEntry(fileEntry) {
     diskWriter = new DefaultDiskWriter(this->fileEntry->getLength());
@@ -58,12 +59,14 @@ class MultiDiskWriter : public DiskWriter {
 private:
   DiskWriterEntries diskWriterEntries;
   int pieceLength;
+  MessageDigestContext ctx;
+  FileAllocatorHandle fileAllocator;
+
   bool isInRange(const DiskWriterEntry* entry, long long int offset) const;
   int calculateLength(const DiskWriterEntry* entry, long long int fileOffset, int rem) const;
   void clearEntries();
-  MessageDigestContext ctx;
   void hashUpdate(DiskWriterEntry* entry, long long int offset, long long int length);
-
+  void configureFileAllocator(DiskWriterEntry* entry);
 public:
   MultiDiskWriter(int pieceLength);
   virtual ~MultiDiskWriter();
@@ -77,6 +80,10 @@ public:
   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);
+
+  void setFileAllocator(const FileAllocatorHandle& fileAllocator) {
+    this->fileAllocator = fileAllocator;
+  }
 };
 
 #endif // _D_MULTI_DISK_WRITER_H_

+ 1 - 1
src/PieceStorage.h

@@ -123,7 +123,7 @@ public:
 
   virtual bool isEndGame() = 0;
 
-  virtual DiskAdaptor* getDiskAdaptor() = 0;
+  virtual DiskAdaptorHandle getDiskAdaptor() = 0;
   
   virtual int getPieceLength(int index) = 0;
 

+ 4 - 3
src/PreAllocationDiskWriter.cc

@@ -42,12 +42,13 @@
 #include <errno.h>
 #include <string.h>
 
-PreAllocationDiskWriter::PreAllocationDiskWriter(long long int totalLength)
-  :AbstractDiskWriter(),totalLength(totalLength) {}
+PreAllocationDiskWriter::PreAllocationDiskWriter(uint64_t totalLength)
+  :AbstractDiskWriter(totalLength) {}
 
 PreAllocationDiskWriter::~PreAllocationDiskWriter() {}
 
-void PreAllocationDiskWriter::initAndOpenFile(const string& filename) {
+void PreAllocationDiskWriter::initAndOpenFile(const string& filename)
+{
   createFile(filename);
   int bufSize = 4096;
   char buf[4096];

+ 4 - 5
src/PreAllocationDiskWriter.h

@@ -38,13 +38,12 @@
 #include "AbstractDiskWriter.h"
 
 class PreAllocationDiskWriter:public AbstractDiskWriter {
-private:
-  long long int totalLength;
 public:
-  PreAllocationDiskWriter(long long int totalLength);
-  ~PreAllocationDiskWriter();
+  PreAllocationDiskWriter(uint64_t totalLength);
 
-  void initAndOpenFile(const string& filename);
+  virtual ~PreAllocationDiskWriter();
+
+  virtual void initAndOpenFile(const string& filename);
 };
 
 #endif // _D_PRE_ALLOCATION_DISK_WRITER_H_

+ 4 - 0
src/Util.cc

@@ -101,6 +101,10 @@ string Util::itos(int32_t value, bool comma) {
   return int2str<int32_t>(value, comma);
 }
 
+string Util::ullitos(uint64_t value, bool comma) {
+  return uint2str<uint64_t>(value, comma);
+}
+
 string Util::llitos(int64_t value, bool comma)
 {
   return int2str<int64_t>(value, comma);

+ 1 - 0
src/Util.h

@@ -53,6 +53,7 @@ class Util {
 public:
   static void split(pair<string, string>& hp, const string& src, char delim);
   static string llitos(int64_t value, bool comma = false);
+  static string ullitos(uint64_t value, bool comma = false);
   static string itos(int32_t value, bool comma = false);
   static string uitos(uint32_t value, bool comma = false);
   static string itos(int16_t value, bool comma = false);

+ 25 - 2
src/main.cc

@@ -47,6 +47,7 @@
 #include "TorrentRequestInfo.h"
 #include "BitfieldManFactory.h"
 #include "SimpleRandomizer.h"
+#include "ConsoleFileAllocationMonitor.h"
 #include <deque>
 #include <algorithm>
 #include <time.h>
@@ -119,7 +120,8 @@ void showUsage() {
   cout << _(" -s, --split=N                Download a file using N connections. N must be\n"
 	    "                              between 1 and 5. This option affects all URLs.\n"
 	    "                              Thus, aria2 connects to each URL with\n"
-	    "                              N connections.") << endl;
+	    "                              N connections.\n"
+	    "                              Default: 1") << endl;
   cout << _(" --retry-wait=SEC             Set amount of time in second between requests\n"
 	    "                              for errors. Specify a value between 0 and 60.\n"
 	    "                              Default: 5") << endl;
@@ -167,6 +169,13 @@ void showUsage() {
 	    "                              0 means unrestricted.\n"
 	    "                              You can append K or M(1K = 1024, 1M = 1024K).\n"
 	    "                              Default: 0") << endl;
+  cout << _(" --file-allocation=METHOD     Specify file allocation method. METHOD is either\n"
+	    "                              'none' or 'prealloc'.\n"
+	    "                              'none' doesn't pre-allocate file space. 'prealloc'\n"
+	    "                              pre-allocates file space before download begins.\n"
+	    "                              This may take some time depending on the size of\n"
+	    "                              file.\n"
+	    "                              Default: 'none'") << endl;
 #ifdef ENABLE_BITTORRENT
   cout << _(" -T, --torrent-file=TORRENT_FILE  The file path to .torrent file.") << endl;
   cout << _(" --follow-torrent=true|false  Setting this option to false prevents aria2 to\n"
@@ -327,6 +336,7 @@ int main(int argc, char* argv[]) {
   op->put(PREF_MAX_UPLOAD_LIMIT, "0");
   op->put(PREF_STARTUP_IDLE_TIME, "10");
   op->put(PREF_TRACKER_MAX_TRIES, "10");
+  op->put(PREF_FILE_ALLOCATION, V_NONE);
   while(1) {
     int optIndex = 0;
     int lopt;
@@ -355,6 +365,7 @@ int main(int argc, char* argv[]) {
       { "http-proxy-method", required_argument, &lopt, 14 },
       { "lowest-speed-limit", required_argument, &lopt, 200 },
       { "max-download-limit", required_argument, &lopt, 201 },
+      { "file-allocation", required_argument, 0, 'a' },
 #ifdef ENABLE_BITTORRENT
       { "torrent-file", required_argument, NULL, 'T' },
       { "listen-port", required_argument, &lopt, 15 },
@@ -382,7 +393,7 @@ int main(int argc, char* argv[]) {
       { "help", no_argument, NULL, 'h' },
       { 0, 0, 0, 0 }
     };
-    c = getopt_long(argc, argv, "Dd:o:l:s:pt:m:vhST:M:C:", longOpts, &optIndex);
+    c = getopt_long(argc, argv, "Dd:o:l:s:pt:m:vhST:M:C:a:", longOpts, &optIndex);
     if(c == -1) {
       break;
     }
@@ -677,6 +688,17 @@ int main(int argc, char* argv[]) {
       op->put(PREF_METALINK_SERVERS, Util::itos(metalinkServers));
       break;
     }
+    case 'a': {
+      string value = string(optarg);
+      if(value == V_NONE || value == V_PREALLOC) {
+	op->put(PREF_FILE_ALLOCATION, value);
+      } else {
+	cerr << _("file-allocation must be either 'none' or 'prealloc'.") << endl;
+	showUsage();
+	exit(EXIT_FAILURE);
+      }
+      break;
+    }
     case 'v':
       showVersion();
       exit(EXIT_SUCCESS);
@@ -716,6 +738,7 @@ int main(int argc, char* argv[]) {
 #endif // ENABLE_METALINK
   SimpleRandomizer::init();
   BitfieldManFactory::setDefaultRandomizer(SimpleRandomizer::getInstance());
+  FileAllocationMonitorFactory::setFactory(new ConsoleFileAllocationMonitorFactory());
   if(op->getAsBool(PREF_STDOUT_LOG)) {
     LogFactory::setLogFile("/dev/stdout");
   } else if(op->get(PREF_LOG).size()) {

+ 4 - 0
src/prefs.h

@@ -42,6 +42,7 @@
  */
 #define V_TRUE "true"
 #define V_FALSE "false"
+#define V_NONE "none"
 
 /**
  * General preferences
@@ -80,6 +81,9 @@
 #define PREF_MAX_DOWNLOAD_LIMIT "max_download_limit"
 // value: 1*digit
 #define PREF_STARTUP_IDLE_TIME "startup_idle_time"
+// value: prealloc | none
+#define PREF_FILE_ALLOCATION "file_allocation"
+#  define V_PREALLOC "prealloc"
 
 /**
  * FTP related preferences

+ 38 - 0
test/ConsoleFileAllocationMonitorTest.cc

@@ -0,0 +1,38 @@
+#include "ConsoleFileAllocationMonitor.h"
+#include <string>
+#include <cppunit/extensions/HelperMacros.h>
+
+using namespace std;
+
+class ConsoleFileAllocationMonitorTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(ConsoleFileAllocationMonitorTest);
+  CPPUNIT_TEST(testShowProgress);
+  CPPUNIT_TEST_SUITE_END();
+private:
+
+public:
+  void setUp() {
+  }
+
+  void testShowProgress();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION( ConsoleFileAllocationMonitorTest );
+
+void ConsoleFileAllocationMonitorTest::testShowProgress() {
+  ConsoleFileAllocationMonitor monitor;
+  monitor.setMinValue(0);
+  monitor.setMaxValue(1000000000);
+  monitor.setCurrentValue(0);
+
+  cout << endl;
+  for(uint64_t i = monitor.getMinValue(); i <= monitor.getMaxValue(); i += 1234343) {
+    monitor.setCurrentValue(i);
+    monitor.showProgress();
+    usleep(5);
+  }
+  monitor.setCurrentValue(monitor.getMaxValue());
+  monitor.showProgress();
+}

+ 3 - 2
test/Makefile.am

@@ -15,7 +15,7 @@ aria2c_SOURCES = AllTest.cc\
 	ShaVisitorTest.cc\
 	PeerMessageUtilTest.cc\
 	DefaultDiskWriterTest.cc\
-	MultiDiskWriterTest.cc\
+	MultiDiskAdaptorTest.cc\
 	BitfieldManTest.cc\
 	Xml2MetalinkProcessorTest.cc\
 	MetalinkerTest.cc\
@@ -57,7 +57,8 @@ aria2c_SOURCES = AllTest.cc\
 	MockBtMessageDispatcher.h\
 	FixedNumberRandomizer.h\
 	MockBtMessageFactory.h\
-	MockBtMessage.h
+	MockBtMessage.h\
+	ConsoleFileAllocationMonitorTest.cc
 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
 #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
 

+ 8 - 5
test/Makefile.in

@@ -64,7 +64,7 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) RequestTest.$(OBJEXT) \
 	DictionaryTest.$(OBJEXT) ListTest.$(OBJEXT) \
 	MetaFileUtilTest.$(OBJEXT) ShaVisitorTest.$(OBJEXT) \
 	PeerMessageUtilTest.$(OBJEXT) DefaultDiskWriterTest.$(OBJEXT) \
-	MultiDiskWriterTest.$(OBJEXT) BitfieldManTest.$(OBJEXT) \
+	MultiDiskAdaptorTest.$(OBJEXT) BitfieldManTest.$(OBJEXT) \
 	Xml2MetalinkProcessorTest.$(OBJEXT) MetalinkerTest.$(OBJEXT) \
 	MetalinkEntryTest.$(OBJEXT) FeatureConfigTest.$(OBJEXT) \
 	ShareRatioSeedCriteriaTest.$(OBJEXT) \
@@ -88,7 +88,8 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) RequestTest.$(OBJEXT) \
 	BtRejectMessageTest.$(OBJEXT) BtRequestMessageTest.$(OBJEXT) \
 	BtSuggestPieceMessageTest.$(OBJEXT) \
 	BtUnchokeMessageTest.$(OBJEXT) \
-	BtHandshakeMessageTest.$(OBJEXT)
+	BtHandshakeMessageTest.$(OBJEXT) \
+	ConsoleFileAllocationMonitorTest.$(OBJEXT)
 aria2c_OBJECTS = $(am_aria2c_OBJECTS)
 am__DEPENDENCIES_1 =
 aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1)
@@ -267,7 +268,7 @@ aria2c_SOURCES = AllTest.cc\
 	ShaVisitorTest.cc\
 	PeerMessageUtilTest.cc\
 	DefaultDiskWriterTest.cc\
-	MultiDiskWriterTest.cc\
+	MultiDiskAdaptorTest.cc\
 	BitfieldManTest.cc\
 	Xml2MetalinkProcessorTest.cc\
 	MetalinkerTest.cc\
@@ -309,7 +310,8 @@ aria2c_SOURCES = AllTest.cc\
 	MockBtMessageDispatcher.h\
 	FixedNumberRandomizer.h\
 	MockBtMessageFactory.h\
-	MockBtMessage.h
+	MockBtMessage.h\
+	ConsoleFileAllocationMonitorTest.cc
 
 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
 #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
@@ -395,6 +397,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtSuggestPieceMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtUnchokeMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChunkedEncodingTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConsoleFileAllocationMonitorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieBoxTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DataTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtAnnounceTest.Po@am__quote@
@@ -412,7 +415,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetaFileUtilTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkEntryTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkerTest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskWriterTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskAdaptorTest.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)/PeerTest.Po@am__quote@

+ 4 - 4
test/MockPieceStorage.h

@@ -13,10 +13,10 @@ private:
   BitfieldMan* bitfieldMan;
   bool selectiveDownloadingMode;
   bool endGame;
-  DiskAdaptor* diskAdaptor;
+  DiskAdaptorHandle diskAdaptor;
   Integers pieceLengthList;
 public:
-  MockPieceStorage() {}
+  MockPieceStorage():diskAdaptor(0) {}
   virtual ~MockPieceStorage() {}
 
   virtual bool hasMissingPiece(const PeerHandle& peer) {
@@ -122,11 +122,11 @@ public:
     this->endGame = flag;
   }
 
-  virtual DiskAdaptor* getDiskAdaptor() {
+  virtual DiskAdaptorHandle getDiskAdaptor() {
     return diskAdaptor;
   }
 
-  void setDiskAdaptor(DiskAdaptor* adaptor) {
+  void setDiskAdaptor(const DiskAdaptorHandle adaptor) {
     this->diskAdaptor = adaptor;
   }
   

+ 156 - 0
test/MultiDiskAdaptorTest.cc

@@ -0,0 +1,156 @@
+#include "MultiDiskAdaptor.h"
+#include <string>
+#include <cppunit/extensions/HelperMacros.h>
+
+using namespace std;
+
+class MultiDiskAdaptorTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(MultiDiskAdaptorTest);
+  CPPUNIT_TEST(testWriteData);
+  CPPUNIT_TEST(testReadData);
+  CPPUNIT_TEST(testSha1Sum);
+  CPPUNIT_TEST_SUITE_END();
+private:
+  Option* option;
+  MultiDiskAdaptorHandle adaptor;
+public:
+  MultiDiskAdaptorTest():option(0), adaptor(0) {}
+
+  void setUp() {
+    delete option;
+    option = new Option();
+
+    adaptor = new MultiDiskAdaptor();
+    adaptor->setPieceLength(2);
+    adaptor->setOption(new Option());
+    adaptor->setStoreDir(".");
+    adaptor->setTopDir(".");
+  }
+
+  void testWriteData();
+  void testReadData();
+  void testSha1Sum();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION( MultiDiskAdaptorTest );
+
+FileEntries createEntries() {
+  FileEntryHandle entry1(new FileEntry("file1.txt", 15, 0));
+  FileEntryHandle entry2(new FileEntry("file2.txt", 7, 15));
+  FileEntryHandle entry3(new FileEntry("file3.txt", 3, 22));
+  unlink("file1.txt");
+  unlink("file2.txt");
+  unlink("file3.txt");
+  FileEntries 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 MultiDiskAdaptorTest::testWriteData() {
+  try {
+  adaptor->setFileEntries(createEntries());
+
+  adaptor->openFile();
+  string msg = "12345";
+  adaptor->writeData((const unsigned char*)msg.c_str(), msg.size(), 0);
+  adaptor->closeFile();
+
+  char buf[128];
+  readFile("file1.txt", buf, 5);
+  buf[5] = '\0';
+  CPPUNIT_ASSERT_EQUAL(msg, string(buf));
+
+  adaptor->openFile();
+  string msg2 = "67890ABCDEF";
+  adaptor->writeData((const unsigned char*)msg2.c_str(), msg2.size(), 5);
+  adaptor->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));
+
+  adaptor->openFile();
+  string msg3 = "12345123456712";
+  adaptor->writeData((const unsigned char*)msg3.c_str(), msg3.size(), 10);
+  adaptor->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));
+  } catch(Exception* e) {
+    CPPUNIT_FAIL(e->getMsg());
+  }
+}
+
+void MultiDiskAdaptorTest::testReadData() {
+  FileEntryHandle entry1(new FileEntry("file1r.txt", 15, 0));
+  FileEntryHandle entry2(new FileEntry("file2r.txt", 7, 15));
+  FileEntryHandle entry3(new FileEntry("file3r.txt", 3, 22));
+  FileEntries entries;
+  entries.push_back(entry1);
+  entries.push_back(entry2);
+  entries.push_back(entry3);
+
+  adaptor->setFileEntries(entries);
+
+  adaptor->openFile();
+  unsigned char buf[128];
+  adaptor->readData(buf, 15, 0);
+  buf[15] = '\0';
+  CPPUNIT_ASSERT_EQUAL(string("1234567890ABCDE"), string((char*)buf));
+  adaptor->readData(buf, 10, 6);
+  buf[10] = '\0';
+  CPPUNIT_ASSERT_EQUAL(string("7890ABCDEF"), string((char*)buf));
+  adaptor->readData(buf, 4, 20);
+  buf[4] = '\0';
+  CPPUNIT_ASSERT_EQUAL(string("KLMN"), string((char*)buf));
+  adaptor->readData(buf, 25, 0);
+  buf[25] = '\0';
+  CPPUNIT_ASSERT_EQUAL(string("1234567890ABCDEFGHIJKLMNO"), string((char*)buf));
+}
+
+void MultiDiskAdaptorTest::testSha1Sum() {
+  FileEntryHandle entry1(new FileEntry("file1r.txt", 15, 0));
+  FileEntryHandle entry2(new FileEntry("file2r.txt", 7, 15));
+  FileEntryHandle entry3(new FileEntry("file3r.txt", 3, 22));
+  FileEntries entries;
+  entries.push_back(entry1);
+  entries.push_back(entry2);
+  entries.push_back(entry3);
+
+  adaptor->setFileEntries(entries);
+
+  adaptor->openFile();
+  string sha1sum = adaptor->sha1Sum(0, 25);
+  CPPUNIT_ASSERT_EQUAL(string("76495faf71ca63df66dce99547d2c58da7266d9e"), sha1sum);
+  sha1sum = adaptor->sha1Sum(15, 7);
+  CPPUNIT_ASSERT_EQUAL(string("737660d816fb23c2d5bc74f62d9b01b852b2aaca"), sha1sum);
+  sha1sum = adaptor->sha1Sum(10, 14);
+  CPPUNIT_ASSERT_EQUAL(string("6238bf61dd8df8f77156b2378e9e39cd3939680c"), sha1sum);
+  adaptor->closeFile();
+}