Browse Source

2009-11-29 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

	Added option --bt-prioritize-piece.  This option instruct aria2 to
	try to download first and last pieces of each file first. The
	argument can contain 2 keywords:head and tail. To include both
	keywords, they must be separated by comma. These keywords can take
	one parameter, SIZE. For example , if head=SIZE is specified,
	pieces in the range of first SIZE bytes of each file get higher
	priority. tail=SIZE means the range of last SIZE bytes of each
	file. SIZE can include K or M(1K = 1024, 1M = 1024K).
	* src/DefaultPieceStorage.h
	* src/Makefile.am
	* src/OptionHandlerFactory.cc
	* src/OptionHandlerImpl.h
	* src/PriorityPieceSelector.cc
	* src/PriorityPieceSelector.h
	* src/RequestGroup.cc
	* src/prefs.cc
	* src/prefs.h
	* src/usage_text.h
	* src/util.cc
	* src/util.h
	* test/Makefile.am
	* test/MockPieceSelector.h
	* test/PriorityPieceSelectorTest.cc
	* test/UtilTest.cc
Tatsuhiro Tsujikawa 16 năm trước cách đây
mục cha
commit
e208302947

+ 27 - 0
ChangeLog

@@ -1,3 +1,30 @@
+2009-11-29  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Added option --bt-prioritize-piece.  This option instruct aria2 to
+	try to download first and last pieces of each file first. The
+	argument can contain 2 keywords:head and tail. To include both
+	keywords, they must be separated by comma. These keywords can take
+	one parameter, SIZE. For example , if head=SIZE is specified,
+	pieces in the range of first SIZE bytes of each file get higher
+	priority. tail=SIZE means the range of last SIZE bytes of each
+	file. SIZE can include K or M(1K = 1024, 1M = 1024K).
+	* src/DefaultPieceStorage.h
+	* src/Makefile.am
+	* src/OptionHandlerFactory.cc
+	* src/OptionHandlerImpl.h
+	* src/PriorityPieceSelector.cc
+	* src/PriorityPieceSelector.h
+	* src/RequestGroup.cc
+	* src/prefs.cc
+	* src/prefs.h
+	* src/usage_text.h
+	* src/util.cc
+	* src/util.h
+	* test/Makefile.am
+	* test/MockPieceSelector.h
+	* test/PriorityPieceSelectorTest.cc
+	* test/UtilTest.cc
+
 2009-11-29  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	Fixed typo

+ 6 - 0
src/DefaultPieceStorage.h

@@ -239,6 +239,12 @@ public:
   {
     _pieceSelector = pieceSelector;
   }
+
+  SharedHandle<PieceSelector> getPieceSelector() const
+  {
+    return _pieceSelector;
+  }
+    
 };
 
 typedef SharedHandle<DefaultPieceStorage> DefaultPieceStorageHandle;

+ 2 - 1
src/Makefile.am

@@ -442,7 +442,8 @@ SRCS += PeerAbstractCommand.cc PeerAbstractCommand.h\
 	ExtensionMessageRegistry.h\
 	bencode.cc bencode.h\
 	bittorrent_helper.cc bittorrent_helper.h\
-	BtStopDownloadCommand.cc BtStopDownloadCommand.h
+	BtStopDownloadCommand.cc BtStopDownloadCommand.h\
+	PriorityPieceSelector.cc PriorityPieceSelector.h
 endif # ENABLE_BITTORRENT
 
 if ENABLE_METALINK

+ 7 - 3
src/Makefile.in

@@ -240,7 +240,8 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_BITTORRENT_TRUE@	ExtensionMessageRegistry.h\
 @ENABLE_BITTORRENT_TRUE@	bencode.cc bencode.h\
 @ENABLE_BITTORRENT_TRUE@	bittorrent_helper.cc bittorrent_helper.h\
-@ENABLE_BITTORRENT_TRUE@	BtStopDownloadCommand.cc BtStopDownloadCommand.h
+@ENABLE_BITTORRENT_TRUE@	BtStopDownloadCommand.cc BtStopDownloadCommand.h\
+@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelector.cc PriorityPieceSelector.h
 
 @ENABLE_METALINK_TRUE@am__append_14 = Metalinker.cc Metalinker.h\
 @ENABLE_METALINK_TRUE@	MetalinkEntry.cc MetalinkEntry.h\
@@ -568,7 +569,8 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	ZeroBtMessage.h RangeBtMessageValidator.h \
 	IndexBtMessageValidator.h ExtensionMessageRegistry.h \
 	bencode.cc bencode.h bittorrent_helper.cc bittorrent_helper.h \
-	BtStopDownloadCommand.cc BtStopDownloadCommand.h Metalinker.cc \
+	BtStopDownloadCommand.cc BtStopDownloadCommand.h \
+	PriorityPieceSelector.cc PriorityPieceSelector.h Metalinker.cc \
 	Metalinker.h MetalinkEntry.cc MetalinkEntry.h \
 	MetalinkResource.cc MetalinkResource.h MetalinkProcessor.h \
 	MetalinkParserController.cc MetalinkParserController.h \
@@ -730,7 +732,8 @@ am__objects_6 =
 @ENABLE_BITTORRENT_TRUE@	ZeroBtMessage.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	bencode.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	bittorrent_helper.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	BtStopDownloadCommand.$(OBJEXT)
+@ENABLE_BITTORRENT_TRUE@	BtStopDownloadCommand.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelector.$(OBJEXT)
 @ENABLE_METALINK_TRUE@am__objects_14 = Metalinker.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkEntry.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkResource.$(OBJEXT) \
@@ -1507,6 +1510,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PieceStatMan.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PiecedSegment.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PriorityPieceSelector.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ProtocolDetector.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RangeBtMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RarestPieceSelector.Po@am__quote@

+ 7 - 0
src/OptionHandlerFactory.cc

@@ -969,6 +969,13 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
     op->addTag(TAG_BITTORRENT);
     handlers.push_back(op);
   }
+  {
+    SharedHandle<OptionHandler> op(new PrioritizePieceOptionHandler
+				   (PREF_BT_PRIORITIZE_PIECE,
+				    TEXT_BT_PRIORITIZE_PIECE));
+    op->addTag(TAG_BITTORRENT);
+    handlers.push_back(op);
+  }
   {
     SharedHandle<OptionHandler> op(new UnitNumberOptionHandler
 				   (PREF_BT_REQUEST_PEER_SPEED_LIMIT,

+ 27 - 0
src/OptionHandlerImpl.h

@@ -55,6 +55,7 @@
 #include "a2functional.h"
 #include "message.h"
 #include "File.h"
+#include "FileEntry.h"
 
 namespace aria2 {
 
@@ -601,6 +602,32 @@ public:
   }
 };
 
+class PrioritizePieceOptionHandler:public NameMatchOptionHandler {
+public:
+  PrioritizePieceOptionHandler
+  (const std::string& optName,
+   const std::string& description = NO_DESCRIPTION,
+   const std::string& defaultValue = NO_DEFAULT_VALUE,
+   char shortName = 0):
+    NameMatchOptionHandler(optName, description, defaultValue,
+			   OptionHandler::REQ_ARG, shortName) {}
+
+  virtual void parseArg(Option& option, const std::string& optarg)
+  {
+    // Parse optarg against empty FileEntry list to detect syntax
+    // error.
+    std::vector<size_t> result;
+    util::parsePrioritizePieceRange
+      (result, optarg, std::vector<SharedHandle<FileEntry> >(), 1024);
+    option.put(_optName, optarg);
+  }
+
+  virtual std::string createPossibleValuesString() const
+  {
+    return "head[=SIZE],tail[=SIZE]";
+  }
+};
+
 } // namespace aria2
 
 #endif // _D_OPTION_HANDLER_IMPL_H_

+ 57 - 0
src/PriorityPieceSelector.cc

@@ -0,0 +1,57 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2009 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 "PriorityPieceSelector.h"
+#include "bitfield.h"
+
+namespace aria2 {
+
+PriorityPieceSelector::PriorityPieceSelector
+(const SharedHandle<PieceSelector>& selector):
+  _selector(selector) {}
+
+bool PriorityPieceSelector::select
+(size_t& index, const unsigned char* bitfield, size_t nbits) const
+{
+  for(std::vector<size_t>::const_iterator i = _prioritizedPieces.begin();
+      i != _prioritizedPieces.end(); ++i) {
+    if(bitfield::test(bitfield, nbits, *i)) {
+      index = *i;
+      return true;
+    }
+  }
+  return _selector->select(index, bitfield, nbits);
+}
+
+} // namespace aria2

+ 67 - 0
src/PriorityPieceSelector.h

@@ -0,0 +1,67 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2009 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_PRIORITY_PIECE_SELECTOR_H_
+#define _D_PRIORITY_PIECE_SELECTOR_H_
+
+#include "PieceSelector.h"
+
+#include <vector>
+
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class PriorityPieceSelector:public PieceSelector {
+private:
+  std::vector<size_t> _prioritizedPieces;
+
+  SharedHandle<PieceSelector> _selector;
+public:
+  PriorityPieceSelector(const SharedHandle<PieceSelector>& selector);
+
+  virtual bool select
+  (size_t& index, const unsigned char* bitfield, size_t nbits) const;
+
+  template<typename InputIterator>
+  void setPriorityPiece(InputIterator first, InputIterator last)
+  {
+    std::vector<size_t> t(first, last);
+    _prioritizedPieces.swap(t);
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_PRIORITY_PIECE_SELECTOR_H_

+ 28 - 9
src/RequestGroup.cc

@@ -76,6 +76,7 @@
 #include "PieceSelector.h"
 #include "a2functional.h"
 #include "SocketCore.h"
+#include "SimpleRandomizer.h"
 #ifdef ENABLE_MESSAGE_DIGEST
 # include "CheckIntegrityCommand.h"
 #endif // ENABLE_MESSAGE_DIGEST
@@ -100,6 +101,7 @@
 # include "DHTPeerAnnounceStorage.h"
 # include "DHTEntryPointNameResolveCommand.h"
 # include "LongestSequencePieceSelector.h"
+# include "PriorityPieceSelector.h"
 #endif // ENABLE_BITTORRENT
 #ifdef ENABLE_METALINK
 # include "MetalinkPostDownloadHandler.h"
@@ -440,15 +442,32 @@ void RequestGroup::initPieceStorage()
 #ifdef ENABLE_BITTORRENT
     SharedHandle<DefaultPieceStorage> ps
       (new DefaultPieceStorage(_downloadContext, _option.get()));
-    // Use LongestSequencePieceSelector when HTTP/FTP/BitTorrent integrated
-    // downloads. Currently multi-file integrated download is not supported.
-    if(_downloadContext->hasAttribute(bittorrent::BITTORRENT) &&
-       isUriSuppliedForRequsetFileEntry
-       (_downloadContext->getFileEntries().begin(),
-	_downloadContext->getFileEntries().end())) {
-      _logger->debug("Using LongestSequencePieceSelector");
-      ps->setPieceSelector
-	(SharedHandle<PieceSelector>(new LongestSequencePieceSelector()));
+    if(_downloadContext->hasAttribute(bittorrent::BITTORRENT)) {
+      if(isUriSuppliedForRequsetFileEntry
+	 (_downloadContext->getFileEntries().begin(),
+	  _downloadContext->getFileEntries().end())) {
+	// Use LongestSequencePieceSelector when HTTP/FTP/BitTorrent
+	// integrated downloads. Currently multi-file integrated
+	// download is not supported.
+	_logger->debug("Using LongestSequencePieceSelector");
+	ps->setPieceSelector
+	  (SharedHandle<PieceSelector>(new LongestSequencePieceSelector()));
+      }
+      if(_option->defined(PREF_BT_PRIORITIZE_PIECE)) {
+	std::vector<size_t> result;
+	util::parsePrioritizePieceRange
+	  (result, _option->get(PREF_BT_PRIORITIZE_PIECE),
+	   _downloadContext->getFileEntries(),
+	   _downloadContext->getPieceLength());
+	if(!result.empty()) {
+	  std::random_shuffle(result.begin(), result.end(),
+			      *(SimpleRandomizer::getInstance().get()));
+	  SharedHandle<PriorityPieceSelector> priSelector
+	    (new PriorityPieceSelector(ps->getPieceSelector()));
+	  priSelector->setPriorityPiece(result.begin(), result.end());
+	  ps->setPieceSelector(priSelector);
+	}
+      }
     }
 #else // !ENABLE_BITTORRENT
     SharedHandle<DefaultPieceStorage> ps

+ 2 - 0
src/prefs.cc

@@ -305,6 +305,8 @@ const std::string PREF_INDEX_OUT("index-out");
 const std::string PREF_BT_TRACKER_INTERVAL("bt-tracker-interval");
 // values: 1*digit
 const std::string PREF_BT_STOP_TIMEOUT("bt-stop-timeout");
+// values: head[=SIZE]|tail[=SIZE], ...
+const std::string PREF_BT_PRIORITIZE_PIECE("bt-prioritize-piece");
 
 /**
  * Metalink related preferences

+ 2 - 0
src/prefs.h

@@ -309,6 +309,8 @@ extern const std::string PREF_INDEX_OUT;
 extern const std::string PREF_BT_TRACKER_INTERVAL;
 // values: 1*digit
 extern const std::string PREF_BT_STOP_TIMEOUT;
+// values: head[=SIZE]|tail[=SIZE], ...
+extern const std::string PREF_BT_PRIORITIZE_PIECE;
 
 /**
  * Metalink related preferences

+ 11 - 0
src/usage_text.h

@@ -567,3 +567,14 @@ _(" --bt-stop-timeout=SEC        Stop BitTorrent download if download speed is 0
 _(" --xml-rpc-listen-all[=true|false] Listen incoming XML-RPC requests on all\n"\
   "                              network interfaces. If false is given, listen only\n"\
   "                              on local loopback interface.")
+#define TEXT_BT_PRIORITIZE_PIECE \
+_(" --bt-prioritize-piece=head[=SIZE],tail[=SIZE] Try to download first and last\n"\
+  "                              pieces of each file first. The argument can\n"\
+  "                              contain 2 keywords:head and tail. To include both\n"\
+  "                              keywords, they must be separated by comma. These\n"\
+  "                              keywords can take one parameter, SIZE. For example\n"\
+  "                              , if head=SIZE is specified, pieces in the range\n"\
+  "                              of first SIZE bytes of each file get higher\n"\
+  "                              priority. tail=SIZE means the range of last SIZE\n"\
+  "                              bytes of each file. SIZE can include K or M(1K =\n"\
+  "                              1024, 1M = 1024K).")

+ 64 - 0
src/util.cc

@@ -525,6 +525,70 @@ IntSequence parseIntRange(const std::string& src)
   return values;
 }
 
+void parsePrioritizePieceRange
+(std::vector<size_t>& result, const std::string& src,
+ const std::vector<SharedHandle<FileEntry> >& fileEntries,
+ size_t pieceLength)
+{
+  std::vector<size_t> indexes;
+  std::vector<std::string> parts;
+  split(src, std::back_inserter(parts), ",", true);
+  for(std::vector<std::string>::const_iterator i = parts.begin();
+      i != parts.end(); ++i) {
+    if((*i) == "head") {
+      for(std::vector<SharedHandle<FileEntry> >::const_iterator fi = 
+	    fileEntries.begin(); fi != fileEntries.end(); ++fi) {
+	indexes.push_back((*fi)->getOffset()/pieceLength);
+      }
+    } else if(util::startsWith(*i, "head=")) {
+      std::string sizestr = std::string((*i).begin()+(*i).find("=")+1,
+					(*i).end());
+      uint64_t head = std::max((int64_t)0, getRealSize(sizestr));
+      for(std::vector<SharedHandle<FileEntry> >::const_iterator fi =
+	    fileEntries.begin(); fi != fileEntries.end(); ++fi) {
+	if((*fi)->getLength() == 0) {
+	  continue;
+	}
+	size_t lastIndex =
+	  ((*fi)->getOffset()+std::min(head, (*fi)->getLength())-1)/pieceLength;
+	for(size_t index = (*fi)->getOffset()/pieceLength;
+	    index <= lastIndex; ++index) {
+	  indexes.push_back(index);
+	}
+      }
+    } else if((*i) == "tail") {
+      for(std::vector<SharedHandle<FileEntry> >::const_iterator fi =
+	    fileEntries.begin(); fi != fileEntries.end(); ++fi) {
+	indexes.push_back
+	  (((*fi)->getOffset()+(*fi)->getLength()-1)/pieceLength);
+      }
+    } else if(util::startsWith(*i, "tail=")) {
+      std::string sizestr = std::string((*i).begin()+(*i).find("=")+1,
+					(*i).end());
+      size_t tail = std::max((int64_t)0, getRealSize(sizestr));
+      for(std::vector<SharedHandle<FileEntry> >::const_iterator fi =
+	    fileEntries.begin(); fi != fileEntries.end(); ++fi) {
+	if((*fi)->getLength() == 0) {
+	  continue;
+	}
+	uint64_t endOffset = (*fi)->getLastOffset();
+	size_t fromIndex =
+	  (endOffset-1-(std::min(tail, (*fi)->getLength())-1))/pieceLength;
+	for(size_t index = fromIndex; index <= (endOffset-1)/pieceLength;
+	    ++index) {
+	  indexes.push_back(index);
+	}
+      }
+    } else {
+      throw DL_ABORT_EX
+	(StringFormat("Unrecognized token %s", (*i).c_str()).str());
+    }
+  }
+  std::sort(indexes.begin(), indexes.end());
+  indexes.erase(std::unique(indexes.begin(), indexes.end()), indexes.end());
+  result.insert(result.end(), indexes.begin(), indexes.end());
+}
+
 std::string getContentDispositionFilename(const std::string& header) {
   static const std::string keyName = "filename=";
   std::string::size_type attributesp = header.find(keyName);

+ 18 - 1
src/util.h

@@ -48,6 +48,7 @@
 #include <map>
 #include <iomanip>
 #include <algorithm>
+#include <vector>
 
 #include "SharedHandle.h"
 #include "IntSequence.h"
@@ -193,6 +194,22 @@ uint64_t parseULLInt(const std::string& s, int base = 10);
 
 IntSequence parseIntRange(const std::string& src);
 
+// Parses string which specifies the range of piece index for higher
+// priority and appends those indexes into result.  The input string
+// src can contain 2 keywords "head" and "tail".  To include both
+// keywords, they must be separated by comma.  "head" means the pieces
+// where the first byte of each file sits.  "tail" means the pieces
+// where the last byte of each file sits.  These keywords can take one
+// parameter, SIZE. For example, if "head=SIZE" is specified, pieces
+// in the range of first SIZE bytes of each file get higher
+// priority. SIZE can include K or M(1K = 1024, 1M = 1024K).
+//
+// sample: head=512K,tail=512K
+void parsePrioritizePieceRange
+(std::vector<size_t>& result, const std::string& src,
+ const std::vector<SharedHandle<FileEntry> >& fileEntries,
+ size_t pieceLength);
+
 // this function temporarily put here
 std::string getContentDispositionFilename(const std::string& header);
 
@@ -293,7 +310,7 @@ parseIndexPath(const std::string& line);
 std::map<size_t, std::string> createIndexPathMap(std::istream& i);
 
 /**
- * Take a string src which is a deliminated list and add its elements
+ * Take a string src which is a delimited list and add its elements
  * into result. result is stored in out.
  */
 template<typename OutputIterator>

+ 3 - 1
test/Makefile.am

@@ -187,7 +187,9 @@ aria2c_SOURCES += BtAllowedFastMessageTest.cc\
 	MockExtensionMessageFactory.h\
 	MockPieceStorage.h\
 	BencodeTest.cc\
-	BittorrentHelperTest.cc
+	BittorrentHelperTest.cc\
+	PriorityPieceSelectorTest.cc\
+	MockPieceSelector.h
 endif # ENABLE_BITTORRENT
 
 if ENABLE_METALINK

+ 7 - 2
test/Makefile.in

@@ -136,7 +136,9 @@ check_PROGRAMS = $(am__EXEEXT_1)
 @ENABLE_BITTORRENT_TRUE@	MockExtensionMessageFactory.h\
 @ENABLE_BITTORRENT_TRUE@	MockPieceStorage.h\
 @ENABLE_BITTORRENT_TRUE@	BencodeTest.cc\
-@ENABLE_BITTORRENT_TRUE@	BittorrentHelperTest.cc
+@ENABLE_BITTORRENT_TRUE@	BittorrentHelperTest.cc\
+@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelectorTest.cc\
+@ENABLE_BITTORRENT_TRUE@	MockPieceSelector.h
 
 @ENABLE_METALINK_TRUE@am__append_7 = MetalinkerTest.cc\
 @ENABLE_METALINK_TRUE@	MetalinkEntryTest.cc\
@@ -259,6 +261,7 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 	MockDHTTask.h MockDHTTaskFactory.h MockDHTTaskQueue.h \
 	MockExtensionMessage.h MockExtensionMessageFactory.h \
 	MockPieceStorage.h BencodeTest.cc BittorrentHelperTest.cc \
+	PriorityPieceSelectorTest.cc MockPieceSelector.h \
 	MetalinkerTest.cc MetalinkEntryTest.cc \
 	Metalink2RequestGroupTest.cc \
 	MetalinkPostDownloadHandlerTest.cc MetalinkHelperTest.cc \
@@ -347,7 +350,8 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 @ENABLE_BITTORRENT_TRUE@	ARC4Test.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	MSEHandshakeTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	BencodeTest.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	BittorrentHelperTest.$(OBJEXT)
+@ENABLE_BITTORRENT_TRUE@	BittorrentHelperTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelectorTest.$(OBJEXT)
 @ENABLE_METALINK_TRUE@am__objects_7 = MetalinkerTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkEntryTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	Metalink2RequestGroupTest.$(OBJEXT) \
@@ -840,6 +844,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PeerTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PieceStatManTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PieceTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PriorityPieceSelectorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ProtocolDetectorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RarestPieceSelectorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestGroupManTest.Po@am__quote@

+ 19 - 0
test/MockPieceSelector.h

@@ -0,0 +1,19 @@
+#ifndef _D_MOCK_PIECE_SELECTOR_H_
+#define _D_MOCK_PIECE_SELECTOR_H_
+
+#include "PieceSelector.h"
+
+namespace aria2 {
+
+class MockPieceSelector:public PieceSelector {
+public:
+  virtual bool select
+  (size_t& index, const unsigned char* bitfield, size_t nbits) const
+  {
+    return false;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_MOCK_PIECE_SELECTOR_H_

+ 45 - 0
test/PriorityPieceSelectorTest.cc

@@ -0,0 +1,45 @@
+#include "PriorityPieceSelector.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "array_fun.h"
+#include "BitfieldMan.h"
+#include "MockPieceSelector.h"
+
+namespace aria2 {
+
+class PriorityPieceSelectorTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(PriorityPieceSelectorTest);
+  CPPUNIT_TEST(testSelect);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testSelect();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PriorityPieceSelectorTest);
+
+void PriorityPieceSelectorTest::testSelect()
+{
+  size_t pieceLength = 1024;
+  size_t A[] = { 1,200};
+  BitfieldMan bf(pieceLength, pieceLength*256);
+  for(size_t i = 0; i < arrayLength(A); ++i) {
+    bf.setBit(A[i]);
+  }
+  PriorityPieceSelector selector
+    (SharedHandle<PieceSelector>(new MockPieceSelector()));
+  selector.setPriorityPiece(&A[0], &A[arrayLength(A)]);
+
+  size_t index;
+  CPPUNIT_ASSERT(selector.select(index, bf.getBitfield(), bf.countBlock()));
+  CPPUNIT_ASSERT_EQUAL((size_t)1, index);
+  bf.unsetBit(1);
+  CPPUNIT_ASSERT(selector.select(index, bf.getBitfield(), bf.countBlock()));
+  CPPUNIT_ASSERT_EQUAL((size_t)200, index);
+  bf.unsetBit(200);
+  CPPUNIT_ASSERT(!selector.select(index, bf.getBitfield(), bf.countBlock()));
+}
+
+} // namespace aria2

+ 65 - 0
test/UtilTest.cc

@@ -57,6 +57,7 @@ class UtilTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testCreateIndexPathMap);
   CPPUNIT_TEST(testGenerateRandomData);
   CPPUNIT_TEST(testFromHex);
+  CPPUNIT_TEST(testParsePrioritizePieceRange);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -102,6 +103,7 @@ public:
   void testCreateIndexPathMap();
   void testGenerateRandomData();
   void testFromHex();
+  void testParsePrioritizePieceRange();
 };
 
 
@@ -821,4 +823,67 @@ void UtilTest::testFromHex()
   CPPUNIT_ASSERT(util::fromHex(src).empty());
 }
 
+void UtilTest::testParsePrioritizePieceRange()
+{
+  // piece index
+  // 0     1     2     3     4     5     6     7
+  // |     |              |                    |
+  // file1 |              |                    |
+  //       |              |                    |
+  //       file2          |                    |
+  //                    file3                  |
+  //                      |                    |
+  //                      file4                |
+  size_t pieceLength = 1024;
+  std::vector<SharedHandle<FileEntry> > entries(4, SharedHandle<FileEntry>());
+  entries[0].reset(new FileEntry("file1", 1024, 0));
+  entries[1].reset(new FileEntry("file2",2560,entries[0]->getLastOffset()));
+  entries[2].reset(new FileEntry("file3",0,entries[1]->getLastOffset()));
+  entries[3].reset(new FileEntry("file4",3584,entries[2]->getLastOffset()));
+
+  std::vector<size_t> result;
+  util::parsePrioritizePieceRange(result, "head", entries, pieceLength);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, result.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)0, result[0]);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, result[1]);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, result[2]);
+  result.clear();
+  util::parsePrioritizePieceRange(result, "tail", entries, pieceLength);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, result.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)0, result[0]);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, result[1]);
+  CPPUNIT_ASSERT_EQUAL((size_t)6, result[2]);
+  result.clear();
+  util::parsePrioritizePieceRange(result, "head=1K", entries, pieceLength);
+  CPPUNIT_ASSERT_EQUAL((size_t)4, result.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)0, result[0]);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, result[1]);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, result[2]);
+  CPPUNIT_ASSERT_EQUAL((size_t)4, result[3]);
+  result.clear();
+  util::parsePrioritizePieceRange(result, "tail=1K", entries, pieceLength);
+  CPPUNIT_ASSERT_EQUAL((size_t)4, result.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)0, result[0]);
+  CPPUNIT_ASSERT_EQUAL((size_t)2, result[1]);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, result[2]);
+  CPPUNIT_ASSERT_EQUAL((size_t)6, result[3]);
+  result.clear();
+  util::parsePrioritizePieceRange(result, "head,tail", entries, pieceLength);
+  CPPUNIT_ASSERT_EQUAL((size_t)4, result.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)0, result[0]);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, result[1]);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, result[2]);
+  CPPUNIT_ASSERT_EQUAL((size_t)6, result[3]);
+  result.clear();
+  util::parsePrioritizePieceRange
+    (result, "head=300M,tail=300M", entries, pieceLength);
+  CPPUNIT_ASSERT_EQUAL((size_t)7, result.size());
+  for(size_t i = 0; i < 7; ++i) {
+    CPPUNIT_ASSERT_EQUAL(i, result[i]);
+  }
+  result.clear();
+  util::parsePrioritizePieceRange(result, "", entries, pieceLength);
+  CPPUNIT_ASSERT(result.empty());
+}
+
 } // namespace aria2