浏览代码

Added --download-result option.

Added --download-result=OPT option.  This option changes the way
"Download Results" is formatted. If OPT is 'default', print GID,
status, average download speed and path/URI. If multiple files are
involved, path/URI of first requested file is printed and remaining
ones are omitted.  If OPT is 'full', print GID, status, average
download speed, percentage of progress and path/URI. The percentage of
progress and path/URI are printed for each requested file in each row.
Tatsuhiro Tsujikawa 14 年之前
父节点
当前提交
f7aeb86ccc
共有 12 个文件被更改,包括 208 次插入31 次删除
  1. 34 1
      src/BitfieldMan.cc
  2. 4 0
      src/BitfieldMan.h
  3. 22 0
      src/FileEntry.cc
  4. 10 14
      src/FileEntry.h
  5. 2 1
      src/MultiUrlRequestInfo.cc
  6. 10 0
      src/OptionHandlerFactory.cc
  7. 78 14
      src/RequestGroupMan.cc
  8. 6 1
      src/RequestGroupMan.h
  9. 3 0
      src/prefs.cc
  10. 3 0
      src/prefs.h
  11. 12 0
      src/usage_text.h
  12. 24 0
      test/BitfieldManTest.cc

+ 34 - 1
src/BitfieldMan.cc

@@ -60,7 +60,7 @@ BitfieldMan::BitfieldMan(size_t blockLength, uint64_t totalLength)
    cachedFilteredTotalLength_(0)
 {
   if(blockLength_ > 0 && totalLength_ > 0) {
-    blocks_ = totalLength_/blockLength_+(totalLength_%blockLength_ ? 1 : 0);
+    blocks_ = (totalLength_+blockLength_-1)/blockLength_;
     bitfieldLength_ = blocks_/8+(blocks_%8 ? 1 : 0);
     bitfield_ = new unsigned char[bitfieldLength_];
     useBitfield_ = new unsigned char[bitfieldLength_];
@@ -772,6 +772,39 @@ bool BitfieldMan::isBitSetOffsetRange(uint64_t offset, uint64_t length) const
   return true;
 }
 
+uint64_t BitfieldMan::getOffsetCompletedLength
+(uint64_t offset,
+ uint64_t length) const
+{
+  uint64_t res = 0;
+  if(length == 0 || totalLength_ <= offset) {
+    return 0;
+  }
+  if(totalLength_ < offset+length) {
+    length = totalLength_-offset;
+  }
+  size_t start = offset/blockLength_;
+  size_t end = (offset+length-1)/blockLength_;
+  if(start == end) {
+    if(isBitSet(start)) {
+      res = length;
+    }
+  } else {
+    if(isBitSet(start)) {
+      res += (start+1)*blockLength_-offset;
+    }
+    for(size_t i = start+1; i <= end-1; ++i) {
+      if(isBitSet(i)) {
+        res += blockLength_;
+      }
+    }
+    if(isBitSet(end)) {
+      res += offset+length-end*blockLength_;
+    }
+  }
+  return res;
+}
+
 uint64_t BitfieldMan::getMissingUnusedLength(size_t startingIndex) const
 {
   if(startingIndex < 0 || blocks_ <= startingIndex) {

+ 4 - 0
src/BitfieldMan.h

@@ -264,6 +264,10 @@ public:
 
   bool isBitSetOffsetRange(uint64_t offset, uint64_t length) const;
 
+  // Returns completed length in bytes in range [offset,
+  // offset+length). This function will not affected by filter.
+  uint64_t getOffsetCompletedLength(uint64_t offset, uint64_t length) const;
+
   uint64_t getMissingUnusedLength(size_t startingIndex) const;
 
   const unsigned char* getFilterBitfield() const

+ 22 - 0
src/FileEntry.cc

@@ -570,4 +570,26 @@ bool FileEntry::emptyRequestUri() const
   return uris_.empty() && inFlightRequests_.empty() && requestPool_.empty();
 }
 
+void writeFilePath
+(std::ostream& o,
+ const SharedHandle<FileEntry>& entry,
+ bool memory)
+{
+  if(entry->getPath().empty()) {
+    std::vector<std::string> uris;
+    entry->getUris(uris);
+    if(uris.empty()) {
+      o << "n/a";
+    } else {
+      o << uris.front();
+    }
+  } else {
+    if(memory) {
+      o << "[MEMORY]" << File(entry->getPath()).getBasename();
+    } else {
+      o << entry->getPath();
+    }
+  }
+}
+
 } // namespace aria2

+ 10 - 14
src/FileEntry.h

@@ -294,6 +294,14 @@ bool isUriSuppliedForRequsetFileEntry(InputIterator first, InputIterator last)
   return false;
 }
 
+// Writes filename to given o.  If memory is true, the output is
+// "[MEMORY]" plus the basename of the filename.  If there is no
+// FileEntry, writes "n/a" to o.
+void writeFilePath
+(std::ostream& o,
+ const SharedHandle<FileEntry>& entry,
+ bool memory);
+
 // Writes first filename to given o.  If memory is true, the output is
 // "[MEMORY]" plus the basename of the first filename.  If there is no
 // FileEntry, writes "n/a" to o.  If more than 1 FileEntry are in the
@@ -307,20 +315,8 @@ void writeFilePath
   if(!e) {
     o << "n/a";
   } else {
-    if(e->getPath().empty()) {
-      std::vector<std::string> uris;
-      e->getUris(uris);
-      if(uris.empty()) {
-        o << "n/a";
-      } else {
-        o << uris.front();
-      }
-    } else {
-      if(memory) {
-        o << "[MEMORY]" << File(e->getPath()).getBasename();
-      } else {
-        o << e->getPath();
-      }
+    writeFilePath(o, e, memory);
+    if(!e->getPath().empty()) {
       size_t count = countRequestedFileEntry(first, last);
       if(count > 1) {
         o << " (" << count-1 << "more)";

+ 2 - 1
src/MultiUrlRequestInfo.cc

@@ -251,7 +251,8 @@ error_code::Value MultiUrlRequestInfo::execute()
     if(!serverStatOf.empty()) {
       e->getRequestGroupMan()->saveServerStat(serverStatOf);
     }
-    e->getRequestGroupMan()->showDownloadResults(*summaryOut_);
+    e->getRequestGroupMan()->showDownloadResults
+      (*summaryOut_, option_->get(PREF_DOWNLOAD_RESULT) == A2_V_FULL);
     summaryOut_->flush();
 
     RequestGroupMan::DownloadStat s =

+ 10 - 0
src/OptionHandlerFactory.cc

@@ -205,6 +205,16 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
     op->hide();
     handlers.push_back(op);
   }
+  {
+    SharedHandle<OptionHandler> op(new ParameterOptionHandler
+                                   (PREF_DOWNLOAD_RESULT,
+                                    TEXT_DOWNLOAD_RESULT,
+                                    A2_V_DEFAULT,
+                                    A2_V_DEFAULT,
+                                    A2_V_FULL));
+    op->addTag(TAG_ADVANCED);
+    handlers.push_back(op);
+  }
 #ifdef ENABLE_ASYNC_DNS
   {
     SharedHandle<OptionHandler> op(new BooleanOptionHandler

+ 78 - 14
src/RequestGroupMan.cc

@@ -588,25 +588,32 @@ RequestGroupMan::DownloadStat RequestGroupMan::getDownloadStat() const
                       lastError);
 }
 
-void RequestGroupMan::showDownloadResults(OutputFile& o) const
+void RequestGroupMan::showDownloadResults(OutputFile& o, bool full) const
 {
   static const std::string MARK_OK("OK");
   static const std::string MARK_ERR("ERR");
   static const std::string MARK_INPR("INPR");
   static const std::string MARK_RM("RM");
 
-  // Download Results:
-  // idx|stat|path/length
-  // ===+====+=======================================================================
-  o.printf("\n%s"
-           "\ngid|stat|avg speed  |path/URI"
-           "\n===+====+===========+",
-           _("Download Results:"));
 #ifdef __MINGW32__
   int pathRowSize = 58;
 #else // !__MINGW32__
   int pathRowSize = 59;
 #endif // !__MINGW32__
+  // Download Results:
+  // idx|stat|path/length
+  // ===+====+=======================================================================
+  o.printf("\n%s"
+           "\ngid|stat|avg speed  |",
+           _("Download Results:"));
+  if(full) {
+    o.write("  %|path/URI"
+            "\n===+====+===========+===+");
+    pathRowSize -= 4;
+  } else {
+    o.write("path/URI"
+            "\n===+====+===========+");
+  }
   std::string line(pathRowSize, '=');
   o.printf("%s\n", line.c_str());
   int ok = 0;
@@ -633,8 +640,12 @@ void RequestGroupMan::showDownloadResults(OutputFile& o) const
       status = MARK_ERR;
       ++err;
     }
-    o.write(formatDownloadResult(status, *itr).c_str());
-    o.write("\n");
+    if(full) {
+      formatDownloadResultFull(o, status, *itr);
+    } else {
+      o.write(formatDownloadResult(status, *itr).c_str());
+      o.write("\n");
+    }
   }
   if(ok > 0 || err > 0 || inpr > 0 || rm > 0) {
     o.printf("\n%s\n", _("Status Legend:"));
@@ -654,11 +665,12 @@ void RequestGroupMan::showDownloadResults(OutputFile& o) const
   }
 }
 
-std::string RequestGroupMan::formatDownloadResult
-(const std::string& status,
- const DownloadResultHandle& downloadResult) const
+namespace {
+void formatDownloadResultCommon
+(std::ostream& o,
+ const std::string& status,
+ const DownloadResultHandle& downloadResult)
 {
-  std::stringstream o;
   o << std::setw(3) << downloadResult->gid << "|"
     << std::setw(4) << status << "|"
     << std::setw(11);
@@ -670,6 +682,58 @@ std::string RequestGroupMan::formatDownloadResult
     o << "n/a";
   }
   o << "|";
+}
+} // namespace
+
+void RequestGroupMan::formatDownloadResultFull
+(OutputFile& out,
+ const std::string& status,
+ const DownloadResultHandle& downloadResult) const
+{
+  BitfieldMan bt(downloadResult->pieceLength, downloadResult->totalLength);
+  bt.setBitfield(reinterpret_cast<const unsigned char*>
+                 (util::fromHex(downloadResult->bitfieldStr).data()),
+                 downloadResult->bitfieldStr.size()/2);
+  bool head = true;
+  const std::vector<SharedHandle<FileEntry> >& fileEntries =
+    downloadResult->fileEntries;
+  for(std::vector<SharedHandle<FileEntry> >::const_iterator i =
+        fileEntries.begin(), eoi = fileEntries.end(); i != eoi; ++i) {
+    if(!(*i)->isRequested()) {
+      continue;
+    }
+    std::stringstream o;
+    if(head) {
+      formatDownloadResultCommon(o, status, downloadResult);
+      head = false;
+    } else {
+      o << "   |    |           |";
+    }
+    if((*i)->getLength() == 0 || downloadResult->bitfieldStr.empty()) {
+      o << "  -|";
+    } else {
+      uint64_t completedLength =
+        bt.getOffsetCompletedLength((*i)->getOffset(), (*i)->getLength());
+      o << std::setw(3) << 100*completedLength/(*i)->getLength() << "|";
+    }
+    writeFilePath(o, *i, downloadResult->inMemoryDownload);
+    o << "\n";
+    out.write(o.str().c_str());
+  }
+  if(head) {
+    std::stringstream o;
+    formatDownloadResultCommon(o, status, downloadResult);
+    o << "  -|n/a\n";
+    out.write(o.str().c_str());
+  }
+}
+
+std::string RequestGroupMan::formatDownloadResult
+(const std::string& status,
+ const DownloadResultHandle& downloadResult) const
+{
+  std::stringstream o;
+  formatDownloadResultCommon(o, status, downloadResult);
   const std::vector<SharedHandle<FileEntry> >& fileEntries =
     downloadResult->fileEntries;
   writeFilePath(fileEntries.begin(), fileEntries.end(), o,

+ 6 - 1
src/RequestGroupMan.h

@@ -85,6 +85,11 @@ private:
 
   size_t maxDownloadResult_;
 
+  void formatDownloadResultFull
+  (OutputFile& out,
+   const std::string& status,
+   const DownloadResultHandle& downloadResult) const;
+
   std::string formatDownloadResult
   (const std::string& status,
    const DownloadResultHandle& downloadResult) const;
@@ -160,7 +165,7 @@ public:
 
   bool removeReservedGroup(a2_gid_t gid);
 
-  void showDownloadResults(OutputFile& o) const;
+  void showDownloadResults(OutputFile& o, bool full) const;
 
   bool isSameFileBeingDownloaded(RequestGroup* requestGroup) const;
 

+ 3 - 0
src/prefs.cc

@@ -45,6 +45,7 @@ const std::string A2_V_DEFAULT("default");
 const std::string V_NONE("none");
 const std::string V_MEM("mem");
 const std::string V_ALL("all");
+const std::string A2_V_FULL("full");
 
 /**
  * General preferences
@@ -218,6 +219,8 @@ const std::string PREF_STREAM_PIECE_SELECTOR("stream-piece-selector");
 const std::string PREF_TRUNCATE_CONSOLE_READOUT("truncate-console-readout");
 // value: true | false
 const std::string PREF_PAUSE("pause");
+// value: default | full
+const std::string PREF_DOWNLOAD_RESULT("download-result");
 
 /**
  * FTP related preferences

+ 3 - 0
src/prefs.h

@@ -49,6 +49,7 @@ extern const std::string A2_V_DEFAULT;
 extern const std::string V_NONE;
 extern const std::string V_MEM;
 extern const std::string V_ALL;
+extern const std::string A2_V_FULL;
 /**
  * General preferences
  */
@@ -221,6 +222,8 @@ extern const std::string PREF_STREAM_PIECE_SELECTOR;
 extern const std::string PREF_TRUNCATE_CONSOLE_READOUT;
 // value: true | false
 extern const std::string PREF_PAUSE;
+// value: default | full
+extern const std::string PREF_DOWNLOAD_RESULT;
 
 /**
  * FTP related preferences

+ 12 - 0
src/usage_text.h

@@ -806,3 +806,15 @@
 #define TEXT_RPC_ALLOW_ORIGIN_ALL                                       \
   _(" --rpc-allow-origin-all[=true|false] Add Access-Control-Allow-Origin header\n" \
     "                              field with value '*' to the RPC response.")
+#define TEXT_DOWNLOAD_RESULT                    \
+  _(" --download-result=OPT        This option changes the way \"Download Results\"\n" \
+    "                              is formatted. If OPT is 'default', print GID,\n" \
+    "                              status, average download speed and path/URI. If\n" \
+    "                              multiple files are involved, path/URI of first\n" \
+    "                              requested file is printed and remaining ones are\n" \
+    "                              omitted.\n"                          \
+    "                              If OPT is 'full', print GID, status, average\n" \
+    "                              download speed, percentage of progress and\n" \
+    "                              path/URI. The percentage of progress and\n" \
+    "                              path/URI are printed for each requested file in\n" \
+    "                              each row.")

+ 24 - 0
test/BitfieldManTest.cc

@@ -26,6 +26,7 @@ class BitfieldManTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testGetSparseMissingUnusedIndex_setBit);
   CPPUNIT_TEST(testGetSparseMissingUnusedIndex_withMinSplitSize);
   CPPUNIT_TEST(testIsBitSetOffsetRange);
+  CPPUNIT_TEST(testGetOffsetCompletedLength);
   CPPUNIT_TEST(testGetMissingUnusedLength);
   CPPUNIT_TEST(testSetBitRange);
   CPPUNIT_TEST(testGetAllMissingIndexes);
@@ -57,6 +58,7 @@ public:
   void testGetSparseMissingUnusedIndex_setBit();
   void testGetSparseMissingUnusedIndex_withMinSplitSize();
   void testIsBitSetOffsetRange();
+  void testGetOffsetCompletedLength();
   void testGetMissingUnusedLength();
   void testSetBitRange();
   void testCountFilteredBlock();
@@ -445,6 +447,28 @@ void BitfieldManTest::testIsBitSetOffsetRange()
   CPPUNIT_ASSERT(!bitfield.isBitSetOffsetRange(pieceLength*100, pieceLength*3));
 }
 
+void BitfieldManTest::testGetOffsetCompletedLength()
+{
+  BitfieldMan bt(1024, 1024*20);
+  // 00000|00000|00000|00000
+  CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(0, 1024));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(0, 0));
+  for(size_t i = 2; i <= 4; ++i) {
+    bt.setBit(i);
+  }
+  // 00111|00000|00000|00000
+  CPPUNIT_ASSERT_EQUAL((uint64_t)3072, bt.getOffsetCompletedLength(2048, 3072));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)3071, bt.getOffsetCompletedLength(2047, 3072));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)3071, bt.getOffsetCompletedLength(2049, 3072));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(2048, 0));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)1, bt.getOffsetCompletedLength(2048, 1));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(2047, 1));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)3072, bt.getOffsetCompletedLength(0, 1024*20));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)3072,
+                       bt.getOffsetCompletedLength(0, 1024*20+10));
+  CPPUNIT_ASSERT_EQUAL((uint64_t)0, bt.getOffsetCompletedLength(1024*20, 1));
+}
+
 void BitfieldManTest::testGetMissingUnusedLength()
 {
   uint64_t totalLength = 1024*10+10;