Browse Source

Faster deletion of RequestGroup and DownloadResult lists

Tatsuhiro Tsujikawa 13 years ago
parent
commit
07bb779eb0

+ 13 - 12
src/ConsoleStatCalc.cc

@@ -138,16 +138,16 @@ void printProgressCompact(std::ostream& o, const DownloadEngine* e,
     o << "[DL:" << sizeFormatter(dl) << "B UL:" << sizeFormatter(ul) << "B]";
   }
 
-  const std::deque<SharedHandle<RequestGroup> >& groups =
+  const RequestGroupList& groups =
     e->getRequestGroupMan()->getRequestGroups();
   size_t cnt = 0;
   const size_t MAX_ITEM = 5;
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator
-        i = groups.begin(), eoi = groups.end(); i != eoi && cnt < MAX_ITEM;
-      ++i, ++cnt) {
-    TransferStat stat = (*i)->calculateStat();
-    o << "[#" << GroupId::toAbbrevHex((*i)->getGID()) << " ";
-    printSizeProgress(o, *i, stat, sizeFormatter);
+  for(RequestGroupList::SeqType::const_iterator i = groups.begin(),
+        eoi = groups.end(); i != eoi && cnt < MAX_ITEM; ++i, ++cnt) {
+    const SharedHandle<RequestGroup>& rg = (*i).second;
+    TransferStat stat = rg->calculateStat();
+    o << "[#" << GroupId::toAbbrevHex(rg->getGID()) << " ";
+    printSizeProgress(o, rg, stat, sizeFormatter);
     o << "]";
   }
   if(cnt < groups.size()) {
@@ -210,8 +210,9 @@ public:
    const SizeFormatter& sizeFormatter):
     cols_(cols), e_(e), sizeFormatter_(sizeFormatter) {}
 
-  void operator()(const SharedHandle<RequestGroup>& rg)
+  void operator()(const RequestGroupList::SeqType::value_type& val)
   {
+    const SharedHandle<RequestGroup>& rg = val.second;
     const char SEP_CHAR = '-';
     std::stringstream o;
     printProgress(o, rg, e_, sizeFormatter_);
@@ -229,8 +230,7 @@ public:
 
 namespace {
 void printProgressSummary
-(const std::deque<SharedHandle<RequestGroup> >& groups, size_t cols,
- const DownloadEngine* e,
+(const RequestGroupList& groups, size_t cols, const DownloadEngine* e,
  const SizeFormatter& sizeFormatter)
 {
   const char SEP_CHAR = '=';
@@ -318,8 +318,9 @@ ConsoleStatCalc::calculateStat(const DownloadEngine* e)
   }
   size_t numGroup = e->getRequestGroupMan()->countRequestGroup();
   if(numGroup == 1) {
-    printProgress(o, e->getRequestGroupMan()->getRequestGroup(0), e,
-                  sizeFormatter);
+    const SharedHandle<RequestGroup>& rg =
+      (*e->getRequestGroupMan()->getRequestGroups().begin()).second;
+    printProgress(o, rg, e, sizeFormatter);
   } else if(numGroup > 1) {
     // For more than 2 RequestGroups, use compact readout form
     printProgressCompact(o, e, sizeFormatter);

+ 5 - 6
src/HaveEraseCommand.cc

@@ -55,12 +55,11 @@ void HaveEraseCommand::preProcess()
 
 void HaveEraseCommand::process()
 {
-  size_t numLoop =
-    getDownloadEngine()->getRequestGroupMan()->countRequestGroup();
-  for(size_t i = 0; i < numLoop; ++i) {
-    SharedHandle<PieceStorage> ps =
-      getDownloadEngine()->getRequestGroupMan()->getRequestGroup(i)->
-      getPieceStorage();
+  const RequestGroupList& groups =
+    getDownloadEngine()->getRequestGroupMan()->getRequestGroups();
+  for(RequestGroupList::SeqType::const_iterator i = groups.begin(),
+        eoi = groups.end(); i != eoi; ++i) {
+    const SharedHandle<PieceStorage>& ps = (*i).second->getPieceStorage();
     if(ps) {
       ps->removeAdvertisedPiece(5);
     }

+ 293 - 0
src/IndexedList.h

@@ -0,0 +1,293 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 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_INDEXED_LIST_H
+#define D_INDEXED_LIST_H
+
+#include "common.h"
+
+#include <list>
+#include <map>
+
+namespace aria2 {
+
+enum A2_HOW {
+  A2_POS_SET,
+  A2_POS_CUR,
+  A2_POS_END
+};
+
+// List with O(logN) look-up using std::map as an index.
+template<typename KeyType, typename ValuePtrType>
+class IndexedList {
+public:
+  IndexedList() {}
+  ~IndexedList() {}
+
+  typedef std::list<std::pair<KeyType, ValuePtrType> > SeqType;
+  typedef std::map<KeyType, typename SeqType::iterator> IndexType;
+
+  // Inserts (|key|, |value|) to the end of the list. If the same key
+  // has been already added, this function fails. This function
+  // returns true if it succeeds. Complexity: O(logN)
+  bool push_back(KeyType key, ValuePtrType value)
+  {
+    typename IndexType::iterator i = index_.lower_bound(key);
+    if(i == index_.end() || (*i).first != key) {
+      seq_.push_back(std::make_pair(key, value));
+      typename SeqType::iterator j = seq_.end();
+      --j;
+      index_.insert(i, std::make_pair(key, j));
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  // Inserts (|key|, |value|) to the front of the list. If the same
+  // key has been already added, this function fails. This function
+  // returns true if it succeeds. Complexity: O(logN)
+  bool push_front(KeyType key, ValuePtrType value)
+  {
+    typename IndexType::iterator i = index_.lower_bound(key);
+    if(i == index_.end() || (*i).first != key) {
+      seq_.push_front(std::make_pair(key, value));
+      typename SeqType::iterator j = seq_.begin();
+      index_.insert(i, std::make_pair(key, j));
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  // Inserts (|key|, |value|) to the position |dest|. If the same key
+  // has been already added, this function fails. This function
+  // returns the iterator to the newly added element if it is
+  // succeeds, or end(). Complexity: O(N)
+  typename SeqType::iterator insert(size_t dest, KeyType key,
+                                    ValuePtrType value)
+  {
+    if(dest > size()) {
+      return seq_.end();
+    }
+    typename IndexType::iterator i = index_.lower_bound(key);
+    if(i == index_.end() || (*i).first != key) {
+      typename SeqType::iterator j = seq_.begin();
+      std::advance(j, dest);
+      j = seq_.insert(j, std::make_pair(key, value));
+      index_.insert(i, std::make_pair(key, j));
+      return j;
+    } else {
+      return seq_.end();
+    }
+  }
+
+  // Inserts (|key|, |value|) to the position |dest|. If the same key
+  // has been already added, this function fails. This function
+  // returns the iterator to the newly added element if it is
+  // succeeds, or end(). Complexity: O(logN)
+  typename SeqType::iterator insert(typename SeqType::iterator dest,
+                                    KeyType key,
+                                    ValuePtrType value)
+  {
+    typename IndexType::iterator i = index_.lower_bound(key);
+    if(i == index_.end() || (*i).first != key) {
+      dest = seq_.insert(dest, std::make_pair(key, value));
+      index_.insert(i, std::make_pair(key, dest));
+      return dest;
+    } else {
+      return seq_.end();
+    }
+  }
+
+  // Removes |key| from the list. If the element is not found, this
+  // function fails. This function returns true if it
+  // succeeds. Complexity: O(logN)
+  bool erase(KeyType key)
+  {
+    typename IndexType::iterator i = index_.find(key);
+    if(i == index_.end()) {
+      return false;
+    }
+    seq_.erase((*i).second);
+    index_.erase(i);
+    return true;
+  }
+
+  // Removes element at the front of the list. If the list is empty,
+  // this function fails. This function returns true if it
+  // succeeds. Complexity: O(logN)
+  bool pop_front()
+  {
+    if(seq_.empty()) {
+      return false;
+    }
+    KeyType key = seq_.front().first;
+    index_.erase(key);
+    seq_.pop_front();
+    return true;
+  }
+
+  // Moves element with |key| to the specified position. If |how| is
+  // A2_POS_CUR, the element is moved to the position |offset|
+  // relative to the current position. If |how| is A2_POS_SET, the
+  // element is moved to the position |offset|. If |how| is
+  // A2_POS_END, the element is moved to the position |offset|
+  // relative to the end of the list.  This function returns the
+  // position the elment is moved to if it succeeds, or -1 if no
+  // element with |key| is found or |how| is invalid.  Complexity:
+  // O(N)
+  ssize_t move(KeyType key, ssize_t offset, A2_HOW how)
+  {
+    typename IndexType::iterator idxent = index_.find(key);
+    if(idxent == index_.end()) {
+      return -1;
+    }
+    ssize_t dest;
+    typename SeqType::iterator x = (*idxent).second;
+    typename SeqType::iterator d;
+    if(how == A2_POS_CUR) {
+      // Because aria2.changePosition() RPC method must return the
+      // absolute position after move, we have to calculate absolute
+      // position here.
+      if(offset > 0) {
+        d = x;
+        for(; offset >= 0 && d != seq_.end(); --offset, ++d);
+        dest = std::distance(seq_.begin(), d)-1;
+      } else {
+        d = x;
+        for(; offset < 0 && d != seq_.begin(); ++offset, --d);
+        dest = std::distance(seq_.begin(), d);
+      }
+    } else {
+      ssize_t size = index_.size();
+      if(how == A2_POS_END) {
+        dest = std::min(size-1, size-1+offset);
+      } else if(how == A2_POS_SET) {
+        dest = std::min(size-1, offset);
+      } else {
+        return -1;
+      }
+      dest = std::max(dest, static_cast<ssize_t>(0));
+      d = seq_.begin();
+      for(ssize_t i = 0; i < dest; ++i, ++d) {
+        if(d == x) {
+          ++d;
+        }
+      }
+    }
+    seq_.splice(d, seq_, x);
+    return dest;
+  }
+
+  // Returns the value associated by |key|. If it is not found,
+  // returns ValuePtrType().  Complexity: O(logN)
+  ValuePtrType get(KeyType key) const
+  {
+    typename IndexType::const_iterator idxent = index_.find(key);
+    if(idxent == index_.end()) {
+      return ValuePtrType();
+    } else {
+      return (*(*idxent).second).second;
+    }
+  }
+
+  // Returns the iterator to the element associated by |key|. If it is
+  // not found, end() is returned. Complexity: O(logN)
+  typename SeqType::iterator find(KeyType key)
+  {
+    typename IndexType::iterator idxent = index_.find(key);
+    if(idxent == index_.end()) {
+      return seq_.end();
+    } else {
+      return (*idxent).second;
+    }
+  }
+
+  // Returns the iterator to the element associated by |key|. If it is
+  // not found, end() is returned. Complexity: O(logN)
+  typename SeqType::const_iterator find(KeyType key) const
+  {
+    typename IndexType::const_iterator idxent = index_.find(key);
+    if(idxent == index_.end()) {
+      return seq_.end();
+    } else {
+      return (*idxent).second;
+    }
+  }
+
+  size_t size() const
+  {
+    return index_.size();
+  }
+
+  size_t empty() const
+  {
+    return index_.empty();
+  }
+
+  typename SeqType::iterator begin()
+  {
+    return seq_.begin();
+  }
+
+  typename SeqType::iterator end()
+  {
+    return seq_.end();
+  }
+
+  typename SeqType::const_iterator begin() const
+  {
+    return seq_.begin();
+  }
+
+  typename SeqType::const_iterator end() const
+  {
+    return seq_.end();
+  }
+
+  // Removes all elements from the list.
+  void clear()
+  {
+    index_.clear();
+    seq_.clear();
+  }
+private:
+  SeqType seq_;
+  IndexType index_;
+};
+
+} // namespace aria2
+
+#endif // D_INDEXED_LIST_H

+ 127 - 290
src/RequestGroupMan.cc

@@ -85,12 +85,22 @@
 
 namespace aria2 {
 
+namespace {
+template<typename InputIterator>
+void appendReservedGroup(RequestGroupList& list,
+                         InputIterator first, InputIterator last)
+{
+  for(; first != last; ++first) {
+    list.push_back((*first)->getGID(), *first);
+  }
+}
+} // namespace
+
 RequestGroupMan::RequestGroupMan
 (const std::vector<SharedHandle<RequestGroup> >& requestGroups,
  int maxSimultaneousDownloads,
  const Option* option)
-  : reservedGroups_(requestGroups.begin(), requestGroups.end()),
-    maxSimultaneousDownloads_(maxSimultaneousDownloads),
+  : maxSimultaneousDownloads_(maxSimultaneousDownloads),
     option_(option),
     serverStatMan_(new ServerStatMan()),
     maxOverallDownloadSpeedLimit_
@@ -104,7 +114,8 @@ RequestGroupMan::RequestGroupMan
     maxDownloadResult_(option->getAsInt(PREF_MAX_DOWNLOAD_RESULT)),
     wrDiskCache_(0)
 {
-  addRequestGroupIndex(requestGroups);
+  appendReservedGroup(reservedGroups_,
+                      requestGroups.begin(), requestGroups.end());
 }
 
 RequestGroupMan::~RequestGroupMan()
@@ -123,82 +134,42 @@ bool RequestGroupMan::downloadFinished()
 void RequestGroupMan::addRequestGroup
 (const SharedHandle<RequestGroup>& group)
 {
-  requestGroups_.push_back(group);
+  requestGroups_.push_back(group->getGID(), group);
 }
 
 void RequestGroupMan::addReservedGroup
 (const std::vector<SharedHandle<RequestGroup> >& groups)
 {
   requestQueueCheck();
-  addRequestGroupIndex(groups);
-  reservedGroups_.insert(reservedGroups_.end(), groups.begin(), groups.end());
+  appendReservedGroup(reservedGroups_, groups.begin(), groups.end());
 }
 
 void RequestGroupMan::addReservedGroup
 (const SharedHandle<RequestGroup>& group)
 {
   requestQueueCheck();
-  addRequestGroupIndex(group);
-  reservedGroups_.push_back(group);
+  reservedGroups_.push_back(group->getGID(), group);
 }
 
 void RequestGroupMan::insertReservedGroup
 (size_t pos, const std::vector<SharedHandle<RequestGroup> >& groups)
 {
   requestQueueCheck();
-  addRequestGroupIndex(groups);
-  reservedGroups_.insert
-    (reservedGroups_.begin()+std::min(reservedGroups_.size(), pos),
-     groups.begin(), groups.end());
+  pos = std::min(reservedGroups_.size(), pos);
+  RequestGroupList::SeqType::iterator dest =  reservedGroups_.begin();
+  std::advance(dest, pos);
+  for(std::vector<SharedHandle<RequestGroup> >::const_iterator i =
+        groups.begin(), eoi = groups.end(); i != eoi; ++i, ++dest) {
+    dest = reservedGroups_.insert(dest, (*i)->getGID(), *i);
+  }
 }
 
 void RequestGroupMan::insertReservedGroup
 (size_t pos, const SharedHandle<RequestGroup>& group)
 {
   requestQueueCheck();
-  addRequestGroupIndex(group);
-  reservedGroups_.insert
-    (reservedGroups_.begin()+std::min(reservedGroups_.size(), pos), group);
-}
-
-void RequestGroupMan::addRequestGroupIndex
-(const SharedHandle<RequestGroup>& group)
-{
-  assert(groupIndex_.count(group->getGID()) == 0);
-  groupIndex_[group->getGID()] = group;
-}
-
-void RequestGroupMan::addRequestGroupIndex
-(const std::vector<SharedHandle<RequestGroup> >& groups)
-{
-  for(std::vector<SharedHandle<RequestGroup> >::const_iterator i =
-        groups.begin(); i != groups.end(); ++i) {
-    addRequestGroupIndex(*i);
-  }
-}
-
-namespace {
-void removeRequestGroupIndex
-(std::map<a2_gid_t, SharedHandle<RequestGroup> >& groupIndex,
- const SharedHandle<RequestGroup>& group)
-{
-  assert(groupIndex.count(group->getGID()) == 1);
-  groupIndex.erase(group->getGID());
-}
-} // namespace
-
-void RequestGroupMan::addDownloadResultIndex
-(const SharedHandle<DownloadResult>& dr)
-{
-  assert(drIndex_.count(dr->gid->getNumericId()) == 0);
-  drIndex_[dr->gid->getNumericId()] = dr;
-}
-
-void RequestGroupMan::removeDownloadResultIndex
-(const SharedHandle<DownloadResult>& dr)
-{
-  assert(drIndex_.count(dr->gid->getNumericId()) == 1);
-  drIndex_.erase(dr->gid->getNumericId());
+  pos = std::min(reservedGroups_.size(), pos);
+  reservedGroups_.insert(pos, group->getGID(), group);
 }
 
 size_t RequestGroupMan::countRequestGroup() const
@@ -206,123 +177,30 @@ size_t RequestGroupMan::countRequestGroup() const
   return requestGroups_.size();
 }
 
-SharedHandle<RequestGroup> RequestGroupMan::getRequestGroup(size_t index) const
-{
-  if(index < requestGroups_.size()) {
-    return requestGroups_[index];
-  } else {
-    return SharedHandle<RequestGroup>();
-  }
-}
-
-namespace {
-template<typename Iterator>
-Iterator findByGID(Iterator first, Iterator last, a2_gid_t gid)
-{
-  for(; first != last; ++first) {
-    if((*first)->getGID() == gid) {
-      return first;
-    }
-  }
-  return first;
-}
-} // namespace
-
-SharedHandle<RequestGroup>
-RequestGroupMan::findRequestGroup(a2_gid_t gid) const
-{
-  SharedHandle<RequestGroup> res = findGroup(gid);
-  if(res) {
-    if(res->getState() == RequestGroup::STATE_ACTIVE) {
-      return res;
-    } else {
-      return SharedHandle<RequestGroup>();
-    }
-  } else {
-    return res;
-  }
-}
-
-SharedHandle<RequestGroup>
-RequestGroupMan::findReservedGroup(a2_gid_t gid) const
-{
-  SharedHandle<RequestGroup> res = findGroup(gid);
-  if(res) {
-    if(res->getState() == RequestGroup::STATE_WAITING) {
-      return res;
-    } else {
-      return SharedHandle<RequestGroup>();
-    }
-  } else {
-    return res;
-  }
-}
-
 SharedHandle<RequestGroup> RequestGroupMan::findGroup(a2_gid_t gid) const
 {
-  std::map<a2_gid_t, SharedHandle<RequestGroup> >::const_iterator i =
-    groupIndex_.find(gid);
-  if(i != groupIndex_.end()) {
-    return (*i).second;
-  } else {
-    return SharedHandle<RequestGroup>();
+  SharedHandle<RequestGroup> rg = requestGroups_.get(gid);
+  if(!rg) {
+    rg = reservedGroups_.get(gid);
   }
+  return rg;
 }
 
 size_t RequestGroupMan::changeReservedGroupPosition
-(a2_gid_t gid, int pos, HOW how)
+(a2_gid_t gid, int pos, A2_HOW how)
 {
-  std::deque<SharedHandle<RequestGroup> >::iterator i =
-    findByGID(reservedGroups_.begin(), reservedGroups_.end(), gid);
-  if(i == reservedGroups_.end()) {
+  ssize_t dest = reservedGroups_.move(gid, pos, how);
+  if(dest == -1) {
     throw DL_ABORT_EX(fmt("GID#%s not found in the waiting queue.",
                           GroupId::toHex(gid).c_str()));
-  }
-  SharedHandle<RequestGroup> rg = *i;
-  const size_t maxPos = reservedGroups_.size()-1;
-  if(how == POS_SET) {
-    if(pos < 0) {
-      pos = 0;
-    } else if(pos > 0) {
-      pos = std::min(maxPos, (size_t)pos);
-    }
-  } else if(how == POS_CUR) {
-    size_t abspos = std::distance(reservedGroups_.begin(), i);
-    if(pos < 0) {
-      int dist = -std::distance(reservedGroups_.begin(), i);
-      pos = abspos+std::max(pos, dist);
-    } else if(pos > 0) {
-      int dist = std::distance(i, reservedGroups_.end())-1;
-      pos = abspos+std::min(pos, dist);
-    } else {
-      pos = abspos;
-    }
-  } else if(how == POS_END) {
-    if(pos >= 0) {
-      pos = maxPos;
-    } else {
-      pos = maxPos-std::min(maxPos, (size_t)-pos);
-    }
-  }
-  if(std::distance(reservedGroups_.begin(), i) < pos) {
-    std::rotate(i, i+1, reservedGroups_.begin()+pos+1);
   } else {
-    std::rotate(reservedGroups_.begin()+pos, i, i+1);
+    return dest;
   }
-  return pos;
 }
 
 bool RequestGroupMan::removeReservedGroup(a2_gid_t gid)
 {
-  std::deque<SharedHandle<RequestGroup> >::iterator i =
-    findByGID(reservedGroups_.begin(), reservedGroups_.end(), gid);
-  if(i == reservedGroups_.end()) {
-    return false;
-  } else {
-    removeRequestGroupIndex(groupIndex_, *i);
-    reservedGroups_.erase(i);
-    return true;
-  }
+  return reservedGroups_.erase(gid);
 }
 
 namespace {
@@ -368,9 +246,7 @@ namespace {
 class ProcessStoppedRequestGroup {
 private:
   DownloadEngine* e_;
-  std::deque<SharedHandle<DownloadResult> >& downloadResults_;
-  std::deque<SharedHandle<RequestGroup> >& reservedGroups_;
-  std::map<a2_gid_t, SharedHandle<RequestGroup> >& groupIndex_;
+  RequestGroupList& reservedGroups_;
 
   void saveSignature(const SharedHandle<RequestGroup>& group)
   {
@@ -390,17 +266,14 @@ private:
 public:
   ProcessStoppedRequestGroup
   (DownloadEngine* e,
-   std::deque<SharedHandle<DownloadResult> >& downloadResults,
-   std::deque<SharedHandle<RequestGroup> >& reservedGroups,
-   std::map<a2_gid_t, SharedHandle<RequestGroup> >& groupIndex)
+   RequestGroupList& reservedGroups)
     : e_(e),
-      downloadResults_(downloadResults),
-      reservedGroups_(reservedGroups),
-      groupIndex_(groupIndex)
+      reservedGroups_(reservedGroups)
   {}
 
-  void operator()(const SharedHandle<RequestGroup>& group)
+  void operator()(const RequestGroupList::SeqType::value_type& val)
   {
+    const SharedHandle<RequestGroup>& group = val.second;
     if(group->getNumCommand() == 0) {
       const SharedHandle<DownloadContext>& dctx = group->getDownloadContext();
       // DownloadContext::resetDownloadStopTime() is only called when
@@ -474,7 +347,7 @@ public:
       }
       if(group->isPauseRequested()) {
         group->setState(RequestGroup::STATE_WAITING);
-        reservedGroups_.push_front(group);
+        reservedGroups_.push_front(group->getGID(), group);
         group->releaseRuntimeResource(e_);
         group->setForceHaltRequested(false);
         util::executeHookByOptName(group, e_->getOption(),
@@ -487,7 +360,6 @@ public:
         e_->getRequestGroupMan()->addDownloadResult(dr);
         executeStopHook(group, e_->getOption(), dr->result);
         group->releaseRuntimeResource(e_);
-        removeRequestGroupIndex(groupIndex_, group);
       }
     }
   }
@@ -502,8 +374,9 @@ public:
   CollectServerStat(RequestGroupMan* requestGroupMan):
     requestGroupMan_(requestGroupMan) {}
 
-  void operator()(const SharedHandle<RequestGroup>& group)
+  void operator()(const RequestGroupList::SeqType::value_type& val)
   {
+    const SharedHandle<RequestGroup>& group = val.second;
     if(group->getNumCommand() == 0) {
       // Collect statistics during download in PeerStats and update/register
       // ServerStatMan
@@ -538,15 +411,6 @@ public:
 };
 } // namespace
 
-namespace {
-class FindStoppedRequestGroup {
-public:
-  bool operator()(const SharedHandle<RequestGroup>& group) {
-    return group->getNumCommand() == 0;
-  }
-};
-} // namespace
-
 void RequestGroupMan::updateServerStat()
 {
   std::for_each(requestGroups_.begin(), requestGroups_.end(),
@@ -560,16 +424,18 @@ void RequestGroupMan::removeStoppedGroup(DownloadEngine* e)
   updateServerStat();
 
   std::for_each(requestGroups_.begin(), requestGroups_.end(),
-                ProcessStoppedRequestGroup
-                (e, downloadResults_, reservedGroups_, groupIndex_));
-  std::deque<SharedHandle<RequestGroup> >::iterator i =
-    std::remove_if(requestGroups_.begin(),
-                   requestGroups_.end(),
-                   FindStoppedRequestGroup());
-  if(i != requestGroups_.end()) {
-    requestGroups_.erase(i, requestGroups_.end());
+                ProcessStoppedRequestGroup(e, reservedGroups_));
+  for(RequestGroupList::SeqType::iterator i = requestGroups_.begin(),
+        eoi = requestGroups_.end(); i != eoi;) {
+    const SharedHandle<RequestGroup>& rg = (*i).second;
+    if(rg->getNumCommand() == 0) {
+      ++i;
+      requestGroups_.erase(rg->getGID());
+      // rg is invalid here.
+    } else {
+      ++i;
+    }
   }
-
   size_t numRemoved = numPrev-requestGroups_.size();
   if(numRemoved > 0) {
     A2_LOG_DEBUG(fmt("%lu RequestGroup(s) deleted.",
@@ -610,34 +476,39 @@ void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e)
   if(static_cast<size_t>(maxSimultaneousDownloads_) <= requestGroups_.size()) {
     return;
   }
-  std::vector<SharedHandle<RequestGroup> > temp;
   int count = 0;
   int num = maxSimultaneousDownloads_-requestGroups_.size();
-  while(count < num && (uriListParser_ || !reservedGroups_.empty())) {
-    if(uriListParser_ && reservedGroups_.empty()) {
+  // In the following loop, the download which is not ready to start
+  // is kept in reservedGroups_. We use iterator to see if we
+  // evaluated them all. So don't use empty() for this. Compare to
+  // reservedGroups_.end() instead.
+  RequestGroupList::SeqType::iterator resitr = reservedGroups_.begin();
+  while(count < num && (uriListParser_ || resitr != reservedGroups_.end())) {
+    if(uriListParser_ && resitr == reservedGroups_.end()) {
       std::vector<SharedHandle<RequestGroup> > groups;
       bool ok = createRequestGroupFromUriListParser(groups, option_,
                                                     uriListParser_.get());
       if(ok) {
-        addRequestGroupIndex(groups);
-        reservedGroups_.insert(reservedGroups_.end(), groups.begin(),
-                               groups.end());
+        appendReservedGroup(reservedGroups_, groups.begin(), groups.end());
+        resitr = reservedGroups_.end();
+        std::advance(resitr, -static_cast<ssize_t>(groups.size()));
       } else {
         uriListParser_.reset();
-        if(reservedGroups_.empty()) {
+        if(resitr == reservedGroups_.end()) {
           break;
         }
       }
     }
-    SharedHandle<RequestGroup> groupToAdd = reservedGroups_.front();
-    reservedGroups_.pop_front();
+    SharedHandle<RequestGroup> groupToAdd = (*resitr).second;
     std::vector<Command*> commands;
     try {
       if((rpc_ && groupToAdd->isPauseRequested()) ||
          !groupToAdd->isDependencyResolved()) {
-        temp.push_back(groupToAdd);
+        ++resitr;
         continue;
       }
+      ++resitr;
+      reservedGroups_.erase(groupToAdd->getGID());
       // Drop pieceStorage here because paused download holds its
       // reference.
       groupToAdd->dropPieceStorage();
@@ -648,7 +519,7 @@ void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e)
         requestQueueCheck();
       }
       groupToAdd->setState(RequestGroup::STATE_ACTIVE);
-      requestGroups_.push_back(groupToAdd);
+      requestGroups_.push_back(groupToAdd->getGID(), groupToAdd);
       ++count;
       e->addCommand(commands);
       commands.clear();
@@ -662,16 +533,13 @@ void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e)
       // We add groupToAdd to e in order to it is processed in
       // removeStoppedGroup().
       groupToAdd->setState(RequestGroup::STATE_ACTIVE);
-      requestGroups_.push_back(groupToAdd);
+      requestGroups_.push_back(groupToAdd->getGID(), groupToAdd);
       requestQueueCheck();
     }
     util::executeHookByOptName(groupToAdd, e->getOption(),
                                PREF_ON_DOWNLOAD_START);
     notifyDownloadEvent(Notifier::ON_DOWNLOAD_START, groupToAdd);
   }
-  if(!temp.empty()) {
-    reservedGroups_.insert(reservedGroups_.begin(), temp.begin(), temp.end());
-  }
   if(count > 0) {
     e->setNoWait(true);
     e->setRefreshInterval(0);
@@ -681,14 +549,15 @@ void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e)
 
 void RequestGroupMan::save()
 {
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator itr =
-        requestGroups_.begin(), eoi = requestGroups_.end(); itr != eoi; ++itr) {
-    if((*itr)->allDownloadFinished() &&
-       !(*itr)->getDownloadContext()->isChecksumVerificationNeeded()) {
-      (*itr)->removeControlFile();
+  for(RequestGroupList::SeqType::iterator itr = requestGroups_.begin(),
+        eoi = requestGroups_.end(); itr != eoi; ++itr) {
+    const SharedHandle<RequestGroup>& rg = (*itr).second;
+    if(rg->allDownloadFinished() &&
+       !rg->getDownloadContext()->isChecksumVerificationNeeded()) {
+      rg->removeControlFile();
     } else {
       try {
-        (*itr)->saveControlFile();
+        rg->saveControlFile();
       } catch(RecoverableException& e) {
         A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
       }
@@ -698,9 +567,9 @@ void RequestGroupMan::save()
 
 void RequestGroupMan::closeFile()
 {
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator itr =
-        requestGroups_.begin(), eoi = requestGroups_.end(); itr != eoi; ++itr) {
-    (*itr)->closeFile();
+  for(RequestGroupList::SeqType::iterator itr = requestGroups_.begin(),
+        eoi = requestGroups_.end(); itr != eoi; ++itr) {
+    (*itr).second->closeFile();
   }
 }
 
@@ -711,21 +580,22 @@ RequestGroupMan::DownloadStat RequestGroupMan::getDownloadStat() const
   int inprogress = 0;
   int removed = 0;
   error_code::Value lastError = removedLastErrorResult_;
-  for(std::deque<SharedHandle<DownloadResult> >::const_iterator itr =
-        downloadResults_.begin(), eoi = downloadResults_.end();
-      itr != eoi; ++itr) {
-    if((*itr)->belongsTo != 0) {
+  for(DownloadResultList::SeqType::const_iterator itr =
+        downloadResults_.begin(), eoi = downloadResults_.end(); itr != eoi;
+      ++itr) {
+    const SharedHandle<DownloadResult>& dr = (*itr).second;
+    if(dr->belongsTo != 0) {
       continue;
     }
-    if((*itr)->result == error_code::FINISHED) {
+    if(dr->result == error_code::FINISHED) {
       ++finished;
-    } else if((*itr)->result == error_code::IN_PROGRESS) {
+    } else if(dr->result == error_code::IN_PROGRESS) {
       ++inprogress;
-    } else if((*itr)->result == error_code::REMOVED) {
+    } else if(dr->result == error_code::REMOVED) {
       ++removed;
     } else {
       ++error;
-      lastError = (*itr)->result;
+      lastError = dr->result;
     }
   }
   return DownloadStat(finished, error, inprogress, removed,
@@ -799,30 +669,35 @@ void RequestGroupMan::showDownloadResults(OutputFile& o, bool full) const
   int err = 0;
   int inpr = 0;
   int rm = 0;
-  for(std::deque<SharedHandle<DownloadResult> >::const_iterator itr =
-        downloadResults_.begin(), eoi = downloadResults_.end();
-      itr != eoi; ++itr) {
-    if((*itr)->belongsTo != 0) {
+  for(DownloadResultList::SeqType::const_iterator itr =
+        downloadResults_.begin(), eoi = downloadResults_.end(); itr != eoi;
+      ++itr) {
+    const SharedHandle<DownloadResult>& dr = (*itr).second;
+    if(dr->belongsTo != 0) {
       continue;
     }
     const char* status;
-    if((*itr)->result == error_code::FINISHED) {
+    switch(dr->result) {
+    case error_code::FINISHED:
       status = getStatusStr(A2_STATUS_OK, useColor);
       ++ok;
-    } else if((*itr)->result == error_code::IN_PROGRESS) {
+      break;
+    case error_code::IN_PROGRESS:
       status = getStatusStr(A2_STATUS_INPR, useColor);
       ++inpr;
-    } else if((*itr)->result == error_code::REMOVED) {
+      break;
+    case error_code::REMOVED:
       status = getStatusStr(A2_STATUS_RM, useColor);
       ++rm;
-    } else {
+      break;
+    default:
       status = getStatusStr(A2_STATUS_ERR, useColor);
       ++err;
     }
     if(full) {
-      formatDownloadResultFull(o, status, *itr);
+      formatDownloadResultFull(o, status, dr);
     } else {
-      o.write(formatDownloadResult(status, *itr).c_str());
+      o.write(formatDownloadResult(status, dr).c_str());
       o.write("\n");
     }
   }
@@ -944,11 +819,12 @@ bool RequestGroupMan::isSameFileBeingDownloaded(RequestGroup* requestGroup) cons
     return false;
   }
   std::vector<std::string> files;
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator itr =
-        requestGroups_.begin(), eoi = requestGroups_.end(); itr != eoi; ++itr) {
-    if((*itr).get() != requestGroup) {
+  for(RequestGroupList::SeqType::const_iterator itr = requestGroups_.begin(),
+        eoi = requestGroups_.end(); itr != eoi; ++itr) {
+    const SharedHandle<RequestGroup>& rg = (*itr).second;
+    if(rg.get() != requestGroup) {
       const std::vector<SharedHandle<FileEntry> >& entries =
-        (*itr)->getDownloadContext()->getFileEntries();
+        rg->getDownloadContext()->getFileEntries();
       std::transform(entries.begin(), entries.end(),
                      std::back_inserter(files),
                      mem_fun_sh(&FileEntry::getPath));
@@ -963,17 +839,17 @@ bool RequestGroupMan::isSameFileBeingDownloaded(RequestGroup* requestGroup) cons
 
 void RequestGroupMan::halt()
 {
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator i =
-        requestGroups_.begin(), eoi = requestGroups_.end(); i != eoi; ++i) {
-    (*i)->setHaltRequested(true);
+  for(RequestGroupList::SeqType::const_iterator i = requestGroups_.begin(),
+        eoi = requestGroups_.end(); i != eoi; ++i) {
+    (*i).second->setHaltRequested(true);
   }
 }
 
 void RequestGroupMan::forceHalt()
 {
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator i =
-        requestGroups_.begin(), eoi = requestGroups_.end(); i != eoi; ++i) {
-    (*i)->setForceHaltRequested(true);
+  for(RequestGroupList::SeqType::const_iterator i = requestGroups_.begin(),
+        eoi = requestGroups_.end(); i != eoi; ++i) {
+    (*i).second->setForceHaltRequested(true);
   }
 }
 
@@ -986,71 +862,31 @@ TransferStat RequestGroupMan::calculateStat()
 SharedHandle<DownloadResult>
 RequestGroupMan::findDownloadResult(a2_gid_t gid) const
 {
-  std::map<a2_gid_t, SharedHandle<DownloadResult> >::const_iterator i =
-    drIndex_.find(gid);
-  if(i != drIndex_.end()) {
-    return (*i).second;
-  } else {
-    return SharedHandle<DownloadResult>();
-  }
+  return downloadResults_.get(gid);
 }
 
 bool RequestGroupMan::removeDownloadResult(a2_gid_t gid)
 {
-  for(std::deque<SharedHandle<DownloadResult> >::iterator i =
-        downloadResults_.begin(), eoi = downloadResults_.end(); i != eoi; ++i) {
-    if((*i)->gid->getNumericId() == gid) {
-      downloadResults_.erase(i);
-      removeDownloadResultIndex(*i);
-      return true;
-    }
-  }
-  return false;
+  return downloadResults_.erase(gid);
 }
 
 void RequestGroupMan::addDownloadResult(const SharedHandle<DownloadResult>& dr)
 {
-  if(maxDownloadResult_ == 0) {
-    if(!downloadResults_.empty()) {
-      for(std::deque<SharedHandle<DownloadResult> >::iterator i =
-            downloadResults_.begin(), eoi = downloadResults_.end(); i != eoi;
-          ++i) {
-        if((*i)->belongsTo == 0 && (*i)->result != error_code::FINISHED) {
-          removedLastErrorResult_ = (*i)->result;
-          ++removedErrorResult_;
-        }
-      }
-      drIndex_.clear();
-      downloadResults_.clear();
-    }
+  bool rv = downloadResults_.push_back(dr->gid->getNumericId(), dr);
+  assert(rv);
+  while(downloadResults_.size() > maxDownloadResult_){
+    DownloadResultList::SeqType::iterator i = downloadResults_.begin();
+    const SharedHandle<DownloadResult>& dr = (*i).second;
     if(dr->belongsTo == 0 && dr->result != error_code::FINISHED) {
       removedLastErrorResult_ = dr->result;
       ++removedErrorResult_;
     }
-  } else {
-    int curSize = downloadResults_.size();
-    if(curSize >= maxDownloadResult_) {
-      std::deque<SharedHandle<DownloadResult> >::iterator last =
-        downloadResults_.begin()+curSize-maxDownloadResult_+1;
-      for(std::deque<SharedHandle<DownloadResult> >::iterator i =
-            downloadResults_.begin(); i != last; ++i) {
-        if((*i)->belongsTo == 0 && (*i)->result != error_code::FINISHED) {
-          removedLastErrorResult_ = (*i)->result;
-          ++removedErrorResult_;
-        }
-        removeDownloadResultIndex(*i);
-      }
-      downloadResults_.erase(downloadResults_.begin(), last);
-    }
-    downloadResults_.push_back(dr);
-    addDownloadResultIndex(dr);
   }
 }
 
 void RequestGroupMan::purgeDownloadResult()
 {
   downloadResults_.clear();
-  drIndex_.clear();
 }
 
 SharedHandle<ServerStat>
@@ -1112,10 +948,11 @@ void RequestGroupMan::getUsedHosts
   // speed. We use -download speed so that we can sort them using
   // operator<().
   std::vector<Triplet<size_t, int, std::string> > tempHosts;
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator i =
-        requestGroups_.begin(), eoi = requestGroups_.end(); i != eoi; ++i) {
+  for(RequestGroupList::SeqType::const_iterator i = requestGroups_.begin(),
+        eoi = requestGroups_.end(); i != eoi; ++i) {
+    const SharedHandle<RequestGroup>& rg = (*i).second;
     const FileEntry::InFlightRequestSet& inFlightReqs =
-      (*i)->getDownloadContext()->getFirstFileEntry()->getInFlightRequests();
+      rg->getDownloadContext()->getFirstFileEntry()->getInFlightRequests();
     for(FileEntry::InFlightRequestSet::iterator j =
           inFlightReqs.begin(), eoj = inFlightReqs.end(); j != eoj; ++j) {
       uri_split_result us;

+ 16 - 32
src/RequestGroupMan.h

@@ -47,6 +47,7 @@
 #include "TransferStat.h"
 #include "RequestGroup.h"
 #include "NetStat.h"
+#include "IndexedList.h"
 
 namespace aria2 {
 
@@ -60,15 +61,16 @@ class OutputFile;
 class UriListParser;
 class WrDiskCache;
 
+typedef IndexedList<a2_gid_t,
+                    SharedHandle<RequestGroup> > RequestGroupList;
+typedef IndexedList<a2_gid_t,
+                    SharedHandle<DownloadResult> > DownloadResultList;
+
 class RequestGroupMan {
 private:
-  std::deque<SharedHandle<RequestGroup> > requestGroups_;
-  std::deque<SharedHandle<RequestGroup> > reservedGroups_;
-  // GID => RequestGroup index for faster retrieval.
-  std::map<a2_gid_t, SharedHandle<RequestGroup> > groupIndex_;
-  std::deque<SharedHandle<DownloadResult> > downloadResults_;
-  // GID => DownloadResult index for faster retrieval.
-  std::map<a2_gid_t, SharedHandle<DownloadResult> > drIndex_;
+  RequestGroupList requestGroups_;
+  RequestGroupList reservedGroups_;
+  DownloadResultList downloadResults_;
 
   int maxSimultaneousDownloads_;
 
@@ -94,7 +96,7 @@ private:
   // The last error of removed DownloadResult
   error_code::Value removedLastErrorResult_;
 
-  int maxDownloadResult_;
+  size_t maxDownloadResult_;
 
   // UriListParser for deferred input.
   SharedHandle<UriListParser> uriListParser_;
@@ -116,9 +118,6 @@ private:
   void addRequestGroupIndex(const SharedHandle<RequestGroup>& group);
   void addRequestGroupIndex
   (const std::vector<SharedHandle<RequestGroup> >& groups);
-
-  void addDownloadResultIndex(const SharedHandle<DownloadResult>& dr);
-  void removeDownloadResultIndex(const SharedHandle<DownloadResult>& dr);
 public:
   RequestGroupMan(const std::vector<SharedHandle<RequestGroup> >& requestGroups,
                   int maxSimultaneousDownloads,
@@ -156,36 +155,20 @@ public:
 
   size_t countRequestGroup() const;
 
-  SharedHandle<RequestGroup> getRequestGroup(size_t index) const;
-
-  const std::deque<SharedHandle<RequestGroup> >& getRequestGroups() const
+  const RequestGroupList& getRequestGroups() const
   {
     return requestGroups_;
   }
 
-  // Note: Use only for unit testing. Use findGroup() and test
-  // RequestGroup::getState() instead.
-  SharedHandle<RequestGroup> findRequestGroup(a2_gid_t gid) const;
-
-  const std::deque<SharedHandle<RequestGroup> >& getReservedGroups() const
+  const RequestGroupList& getReservedGroups() const
   {
     return reservedGroups_;
   }
 
-  // Note: Use only for unit testing. Use findGroup() and test
-  // RequestGroup::getState() instead.
-  SharedHandle<RequestGroup> findReservedGroup(a2_gid_t gid) const;
-
   // Returns RequestGroup object whose gid is gid. This method returns
   // RequestGroup either in requestGroups_ or reservedGroups_.
   SharedHandle<RequestGroup> findGroup(a2_gid_t gid) const;
 
-  enum HOW {
-    POS_SET,
-    POS_CUR,
-    POS_END
-  };
-
   // Changes the position of download denoted by gid.  If how is
   // POS_SET, it moves the download to a position relative to the
   // beginning of the queue.  If how is POS_CUR, it moves the download
@@ -195,7 +178,8 @@ public:
   // beyond the end of the queue, it moves the download to the
   // beginning or the end of the queue respectively.  Returns the
   // destination position.
-  size_t changeReservedGroupPosition(a2_gid_t gid, int pos, HOW how);
+  size_t changeReservedGroupPosition(a2_gid_t gid, int pos,
+                                     A2_HOW how);
 
   bool removeReservedGroup(a2_gid_t gid);
 
@@ -246,7 +230,7 @@ public:
 
   DownloadStat getDownloadStat() const;
 
-  const std::deque<SharedHandle<DownloadResult> >& getDownloadResults() const
+  const DownloadResultList& getDownloadResults() const
   {
     return downloadResults_;
   }
@@ -339,7 +323,7 @@ public:
     return serverStatMan_;
   }
 
-  void setMaxDownloadResult(int v)
+  void setMaxDownloadResult(size_t v)
   {
     maxDownloadResult_ = v;
   }

+ 18 - 20
src/RpcMethodImpl.cc

@@ -48,7 +48,6 @@
 #include "RequestGroup.h"
 #include "download_helper.h"
 #include "util.h"
-#include "RequestGroupMan.h"
 #include "fmt.h"
 #include "RpcRequest.h"
 #include "PieceStorage.h"
@@ -486,7 +485,7 @@ void pauseRequestGroups
 (InputIterator first, InputIterator last, bool reserved, bool forcePause)
 {
   for(; first != last; ++first) {
-    pauseRequestGroup(*first, reserved, forcePause);
+    pauseRequestGroup((*first).second, reserved, forcePause);
   }
 }
 } // namespace
@@ -495,10 +494,9 @@ namespace {
 SharedHandle<ValueBase> pauseAllDownloads
 (const RpcRequest& req, DownloadEngine* e, bool forcePause)
 {
-  const std::deque<SharedHandle<RequestGroup> >& groups =
-    e->getRequestGroupMan()->getRequestGroups();
+  const RequestGroupList& groups = e->getRequestGroupMan()->getRequestGroups();
   pauseRequestGroups(groups.begin(), groups.end(), false, forcePause);
-  const std::deque<SharedHandle<RequestGroup> >& reservedGroups =
+  const RequestGroupList& reservedGroups =
     e->getRequestGroupMan()->getReservedGroups();
   pauseRequestGroups(reservedGroups.begin(), reservedGroups.end(),
                      true, forcePause);
@@ -540,11 +538,12 @@ SharedHandle<ValueBase> UnpauseRpcMethod::process
 SharedHandle<ValueBase> UnpauseAllRpcMethod::process
 (const RpcRequest& req, DownloadEngine* e)
 {
-  const std::deque<SharedHandle<RequestGroup> >& groups =
+  const RequestGroupList& groups =
     e->getRequestGroupMan()->getReservedGroups();
-  std::for_each(groups.begin(), groups.end(),
-                std::bind2nd(mem_fun_sh(&RequestGroup::setPauseRequested),
-                             false));
+  for(RequestGroupList::SeqType::const_iterator i = groups.begin(),
+        eoi = groups.end(); i != eoi; ++i) {
+    (*i).second->setPauseRequested(false);
+  }
   e->getRequestGroupMan()->requestQueueCheck();
   return VLB_OK;
 }
@@ -1038,21 +1037,20 @@ SharedHandle<ValueBase> TellActiveRpcMethod::process
   std::vector<std::string> keys;
   toStringList(std::back_inserter(keys), keysParam);
   SharedHandle<List> list = List::g();
-  const std::deque<SharedHandle<RequestGroup> >& groups =
-    e->getRequestGroupMan()->getRequestGroups();
-  for(std::deque<SharedHandle<RequestGroup> >::const_iterator i =
-        groups.begin(), eoi = groups.end(); i != eoi; ++i) {
+  const RequestGroupList& groups = e->getRequestGroupMan()->getRequestGroups();
+  for(RequestGroupList::SeqType::const_iterator i = groups.begin(),
+        eoi = groups.end(); i != eoi; ++i) {
     SharedHandle<Dict> entryDict = Dict::g();
     if(requested_key(keys, KEY_STATUS)) {
       entryDict->put(KEY_STATUS, VLB_ACTIVE);
     }
-    gatherProgress(entryDict, *i, e, keys);
+    gatherProgress(entryDict, (*i).second, e, keys);
     list->append(entryDict);
   }
   return list;
 }
 
-const std::deque<SharedHandle<RequestGroup> >&
+const RequestGroupList&
 TellWaitingRpcMethod::getItems(DownloadEngine* e) const
 {
   return e->getRequestGroupMan()->getReservedGroups();
@@ -1074,7 +1072,7 @@ void TellWaitingRpcMethod::createEntry
   gatherProgress(entryDict, item, e, keys);
 }
 
-const std::deque<SharedHandle<DownloadResult> >&
+const DownloadResultList&
 TellStoppedRpcMethod::getItems(DownloadEngine* e) const
 {
   return e->getRequestGroupMan()->getDownloadResults();
@@ -1328,13 +1326,13 @@ SharedHandle<ValueBase> ChangePositionRpcMethod::process
   a2_gid_t gid = str2Gid(gidParam);
   int pos = posParam->i();
   const std::string& howStr = howParam->s();
-  RequestGroupMan::HOW how;
+  A2_HOW how;
   if(howStr == "POS_SET") {
-    how = RequestGroupMan::POS_SET;
+    how = A2_POS_SET;
   } else if(howStr == "POS_CUR") {
-    how = RequestGroupMan::POS_CUR;
+    how = A2_POS_CUR;
   } else if(howStr == "POS_END") {
-    how = RequestGroupMan::POS_END;
+    how = A2_POS_END;
   } else {
     throw DL_ABORT_EX("Illegal argument.");
   }

+ 12 - 10
src/RpcMethodImpl.h

@@ -46,6 +46,9 @@
 #include "TorrentAttribute.h"
 #include "DlAbortEx.h"
 #include "fmt.h"
+#include "IndexedList.h"
+#include "GroupId.h"
+#include "RequestGroupMan.h"
 
 namespace aria2 {
 
@@ -371,6 +374,8 @@ private:
     return std::make_pair(first, last);
   }
 protected:
+  typedef IndexedList<a2_gid_t, SharedHandle<T> > ItemListType;
+
   virtual SharedHandle<ValueBase> process
   (const RpcRequest& req, DownloadEngine* e)
   {
@@ -382,14 +387,14 @@ protected:
     int64_t num = numParam->i();
     std::vector<std::string> keys;
     toStringList(std::back_inserter(keys), keysParam);
-    const std::deque<SharedHandle<T> >& items = getItems(e);
-    std::pair<typename std::deque<SharedHandle<T> >::const_iterator,
-      typename std::deque<SharedHandle<T> >::const_iterator> range =
+    const ItemListType& items = getItems(e);
+    std::pair<typename ItemListType::SeqType::const_iterator,
+              typename ItemListType::SeqType::const_iterator> range =
       getPaginationRange(offset, num, items.begin(), items.end());
     SharedHandle<List> list = List::g();
     for(; range.first != range.second; ++range.first) {
       SharedHandle<Dict> entryDict = Dict::g();
-      createEntry(entryDict, *range.first, e, keys);
+      createEntry(entryDict, (*range.first).second, e, keys);
       list->append(entryDict);
     }
     if(offset < 0) {
@@ -398,8 +403,7 @@ protected:
     return list;
   }
 
-  virtual const std::deque<SharedHandle<T> >&
-  getItems(DownloadEngine* e) const = 0;
+  virtual const ItemListType& getItems(DownloadEngine* e) const = 0;
 
   virtual void createEntry
   (const SharedHandle<Dict>& entryDict,
@@ -411,8 +415,7 @@ protected:
 class TellWaitingRpcMethod:
     public AbstractPaginationRpcMethod<RequestGroup> {
 protected:
-  virtual const std::deque<SharedHandle<RequestGroup> >&
-  getItems(DownloadEngine* e) const;
+  virtual const RequestGroupList& getItems(DownloadEngine* e) const;
 
   virtual void createEntry
   (const SharedHandle<Dict>& entryDict,
@@ -429,8 +432,7 @@ public:
 class TellStoppedRpcMethod:
     public AbstractPaginationRpcMethod<DownloadResult> {
 protected:
-   virtual const std::deque<SharedHandle<DownloadResult> >&
-   getItems(DownloadEngine* e) const;
+  virtual const DownloadResultList& getItems(DownloadEngine* e) const;
 
   virtual void createEntry
   (const SharedHandle<Dict>& entryDict,

+ 17 - 17
src/SessionSerializer.cc

@@ -184,46 +184,46 @@ bool writeDownloadResult
 bool SessionSerializer::save(BufferedFile& fp) const
 {
   std::set<a2_gid_t> metainfoCache;
-  const std::deque<SharedHandle<DownloadResult> >& results =
-    rgman_->getDownloadResults();
-  for(std::deque<SharedHandle<DownloadResult> >::const_iterator itr =
-        results.begin(), eoi = results.end(); itr != eoi; ++itr) {
-    if((*itr)->result == error_code::FINISHED ||
-       (*itr)->result == error_code::REMOVED) {
-      if((*itr)->option->getAsBool(PREF_FORCE_SAVE)) {
-        if(!writeDownloadResult(fp, metainfoCache, *itr)) {
+  const DownloadResultList& results = rgman_->getDownloadResults();
+  for(DownloadResultList::SeqType::const_iterator itr = results.begin(),
+        eoi = results.end(); itr != eoi; ++itr) {
+    const SharedHandle<DownloadResult>& dr = (*itr).second;
+    if(dr->result == error_code::FINISHED ||
+       dr->result == error_code::REMOVED) {
+      if(dr->option->getAsBool(PREF_FORCE_SAVE)) {
+        if(!writeDownloadResult(fp, metainfoCache, dr)) {
           return false;
         }
       } else {
         continue;
       }
-    } else if((*itr)->result == error_code::IN_PROGRESS) {
+    } else if(dr->result == error_code::IN_PROGRESS) {
       if(saveInProgress_) {
-        if(!writeDownloadResult(fp, metainfoCache, *itr)) {
+        if(!writeDownloadResult(fp, metainfoCache, dr)) {
           return false;
         }
       }
     } else {
       // error download
       if(saveError_) {
-        if(!writeDownloadResult(fp, metainfoCache, *itr)) {
+        if(!writeDownloadResult(fp, metainfoCache, dr)) {
           return false;
         }
       }
     }
   }
   if(saveWaiting_) {
-    const std::deque<SharedHandle<RequestGroup> >& groups =
-      rgman_->getReservedGroups();
-    for(std::deque<SharedHandle<RequestGroup> >::const_iterator itr =
-          groups.begin(), eoi = groups.end(); itr != eoi; ++itr) {
-      SharedHandle<DownloadResult> result = (*itr)->createDownloadResult();
+    const RequestGroupList& groups = rgman_->getReservedGroups();
+    for(RequestGroupList::SeqType::const_iterator itr = groups.begin(),
+          eoi = groups.end(); itr != eoi; ++itr) {
+      const SharedHandle<RequestGroup>& rg = (*itr).second;
+      SharedHandle<DownloadResult> result = rg->createDownloadResult();
       if(!writeDownloadResult(fp, metainfoCache, result)) {
         return false;
       }
       // PREF_PAUSE was removed from option, so save it here looking
       // property separately.
-      if((*itr)->isPauseRequested()) {
+      if(rg->isPauseRequested()) {
         if(fp.printf(" %s=true\n", PREF_PAUSE->k) < 0) {
           return false;
         }

+ 240 - 0
test/IndexedListTest.cc

@@ -0,0 +1,240 @@
+#include "IndexedList.h"
+
+#include <vector>
+#include <deque>
+#include <iostream>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "TestUtil.h"
+#include "array_fun.h"
+#include "TimerA2.h"
+
+namespace aria2 {
+
+class IndexedListTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(IndexedListTest);
+  CPPUNIT_TEST(testPushBack);
+  CPPUNIT_TEST(testPushFront);
+  CPPUNIT_TEST(testErase);
+  CPPUNIT_TEST(testPopFront);
+  CPPUNIT_TEST(testMove);
+  CPPUNIT_TEST(testGet);
+  CPPUNIT_TEST(testInsert);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void setUp()
+  {}
+
+  void testPushBack();
+  void testPushFront();
+  void testErase();
+  void testPopFront();
+  void testMove();
+  void testGet();
+  void testInsert();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( IndexedListTest );
+
+void IndexedListTest::testPushBack()
+{
+  int a[] = {1,2,3,4,5};
+  IndexedList<int, int*> list;
+  for(int i = 0; i < 5; ++i) {
+    CPPUNIT_ASSERT(list.push_back(i, &a[i]));
+  }
+  for(int i = 0; i < 5; ++i) {
+    CPPUNIT_ASSERT_EQUAL(a[i], *list.get(i));
+  }
+  int ai = 0;
+  for(IndexedList<int, int*>::SeqType::iterator i = list.begin();
+      i != list.end(); ++i) {
+    CPPUNIT_ASSERT_EQUAL(a[ai++], *((*i).second));
+  }
+}
+
+void IndexedListTest::testPushFront()
+{
+  int a[] = {1,2,3,4,5};
+  IndexedList<int, int*> list;
+  for(int i = 0; i < 5; ++i) {
+    CPPUNIT_ASSERT(list.push_front(i, &a[i]));
+  }
+  for(int i = 0; i < 5; ++i) {
+    CPPUNIT_ASSERT_EQUAL(a[i], *list.get(i));
+  }
+  int ai = 4;
+  for(IndexedList<int, int*>::SeqType::iterator i = list.begin();
+      i != list.end(); ++i) {
+    CPPUNIT_ASSERT_EQUAL(a[ai--], *((*i).second));
+  }
+}
+
+void IndexedListTest::testErase()
+{
+  int a[] = {1,2,3,4,5};
+  IndexedList<int, int*> list;
+  for(int i = 0; i < 5; ++i) {
+    list.push_back(i, &a[i]);
+  }
+  for(int i = 0; i < 5; ++i) {
+    CPPUNIT_ASSERT(list.erase(i));
+    CPPUNIT_ASSERT_EQUAL((size_t)5-i-1, list.size());
+    for(int j = i+1; j < 5; ++j) {
+      CPPUNIT_ASSERT_EQUAL(a[j], *list.get(j));
+    }
+  }
+}
+
+void IndexedListTest::testPopFront()
+{
+  int a[] = {1,2,3,4,5};
+  IndexedList<int, int*> list;
+  for(int i = 0; i < 5; ++i) {
+    list.push_back(i, &a[i]);
+  }
+  for(int i = 0; i < 5; ++i) {
+    CPPUNIT_ASSERT(list.pop_front());
+    CPPUNIT_ASSERT_EQUAL((size_t)5-i-1, list.size());
+    for(int j = i+1; j < 5; ++j) {
+      CPPUNIT_ASSERT_EQUAL(a[j], *list.get(j));
+    }
+  }
+}
+
+#define LIST_CHECK(a, list)                                             \
+  {                                                                     \
+    int ai = 0;                                                         \
+    for(IndexedList<int, int*>::SeqType::iterator i = list.begin();     \
+        i != list.end(); ++i) {                                         \
+      CPPUNIT_ASSERT_EQUAL(a[ai++], *((*i).second));                    \
+    }                                                                   \
+  }
+
+void IndexedListTest::testMove()
+{
+  int a[] = {0,1,2,3,4};
+  IndexedList<int, int*> list;
+  for(int i = 0; i < 5; ++i) {
+    list.push_back(i, &a[i]);
+  }
+  CPPUNIT_ASSERT_EQUAL((ssize_t)-1, list.move(100, 0, A2_POS_SET));
+  int a0[] = {0,1,2,3,4};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)0, list.move(0, 0, A2_POS_SET));
+  LIST_CHECK(a0, list);
+
+  int a1[] = {0,2,3,4,1};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)4, list.move(1, 4, A2_POS_SET));
+  LIST_CHECK(a1, list);
+
+  int a2[] = {0,3,4,2,1};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)3, list.move(2, 3, A2_POS_SET));
+  LIST_CHECK(a2, list);
+
+  int a3[] = {0,2,3,4,1};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)1, list.move(2, 1, A2_POS_SET));
+  LIST_CHECK(a3, list);
+
+  int a4[] = {1,0,2,3,4};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)0, list.move(1, 0, A2_POS_SET));
+  LIST_CHECK(a4, list);
+
+  int a5[] = {1,0,3,2,4};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)2, list.move(3, 2, A2_POS_SET));
+  LIST_CHECK(a5, list);
+
+  int a6[] = {1,3,2,4,0};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)4, list.move(0, 5, A2_POS_SET));
+  LIST_CHECK(a6, list);
+
+  int a7[] = {3,1,2,4,0};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)1, list.move(1, 1, A2_POS_CUR));
+  LIST_CHECK(a7, list);
+
+  int a8[] = {3,2,4,1,0};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)3, list.move(1, 2, A2_POS_CUR));
+  LIST_CHECK(a8, list);
+
+  int a9[] = {3,2,1,4,0};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)2, list.move(1, -1, A2_POS_CUR));
+  LIST_CHECK(a9, list);
+
+  int a10[] = {1,3,2,4,0};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)0, list.move(1, -1233, A2_POS_CUR));
+  LIST_CHECK(a10, list);
+
+  int a11[] = {3,2,4,0,1};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)4, list.move(1, 8733, A2_POS_CUR));
+  LIST_CHECK(a11, list);
+
+  int a12[] = {3,2,4,0,1};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)3, list.move(0, -1, A2_POS_END));
+  LIST_CHECK(a12, list);
+
+  int a13[] = {3,2,0,4,1};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)2, list.move(0, -2, A2_POS_END));
+  LIST_CHECK(a13, list);
+
+  int a14[] = {0,3,2,4,1};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)0, list.move(0, -8733, A2_POS_END));
+  LIST_CHECK(a14, list);
+
+  int a15[] = {0,2,4,1,3};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)4, list.move(3, 0, A2_POS_END));
+  LIST_CHECK(a15, list);
+
+  int a16[] = {2,4,1,3,0};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)4, list.move(0, 1000, A2_POS_END));
+  LIST_CHECK(a16, list);
+
+  int a17[] = {2,1,4,3,0};
+  CPPUNIT_ASSERT_EQUAL((ssize_t)2, list.move(4, 2, A2_POS_SET));
+  LIST_CHECK(a17, list);
+}
+
+void IndexedListTest::testGet()
+{
+  IndexedList<int, int*> list;
+  int a = 1000;
+  int b = 1;
+  list.push_back(123, &a);
+  list.push_back(1, &b);
+  CPPUNIT_ASSERT_EQUAL((int*)0, list.get(1000));
+  CPPUNIT_ASSERT_EQUAL(&a, list.get(123));
+
+  CPPUNIT_ASSERT(list.begin() == list.find(123));
+  CPPUNIT_ASSERT(list.end() == list.find(124));
+}
+
+void IndexedListTest::testInsert()
+{
+  int a[] = {0,1,2,3,4,5,6,7,8,9};
+  IndexedList<int, int*> list;
+  IndexedList<int, int*>::SeqType::iterator itr;
+  CPPUNIT_ASSERT(list.end() == list.insert(1, 0, &a[5]));
+  itr = list.insert(0, 5, &a[5]);
+  CPPUNIT_ASSERT_EQUAL(5, *(*itr).second);
+  itr = list.insert(1, 3, &a[3]);
+  CPPUNIT_ASSERT_EQUAL(3, *(*itr).second);
+  itr = list.insert(1, 4, &a[4]);
+  CPPUNIT_ASSERT_EQUAL(4, *(*itr).second);
+  itr = list.insert(0, 9, &a[9]);
+  CPPUNIT_ASSERT_EQUAL(9, *(*itr).second);
+  int a1[] = { 9,5,4,3 };
+  LIST_CHECK(a1, list);
+
+  // use iterator to insert
+  itr = list.insert(itr, 2, &a[2]);
+  CPPUNIT_ASSERT_EQUAL(2, *(*itr).second);
+  itr = list.insert(list.end(), 1, &a[1]);
+  CPPUNIT_ASSERT_EQUAL(1, *(*itr).second);
+  int a2[] = { 2,9,5,4,3,1 };
+  LIST_CHECK(a2, list);
+
+  // 2 has been already added.
+  CPPUNIT_ASSERT(list.end() == list.insert(list.end(), 2, &a[2]));
+}
+
+} // namespace aria2

+ 2 - 1
test/Makefile.am

@@ -88,7 +88,8 @@ aria2c_SOURCES = AllTest.cc\
 	SinkStreamFilterTest.cc\
 	WrDiskCacheTest.cc\
 	WrDiskCacheEntryTest.cc\
-	GroupIdTest.cc
+	GroupIdTest.cc\
+	IndexedListTest.cc
 
 if ENABLE_XML_RPC
 aria2c_SOURCES += XmlRpcRequestParserControllerTest.cc

+ 16 - 16
test/RequestGroupManTest.cc

@@ -133,57 +133,57 @@ void RequestGroupManTest::testChangeReservedGroupPosition()
 
   CPPUNIT_ASSERT_EQUAL
     ((size_t)0, rm.changeReservedGroupPosition(gs[0]->getGID(),
-                                               0, RequestGroupMan::POS_SET));
+                                               0, A2_POS_SET));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)1, rm.changeReservedGroupPosition(gs[0]->getGID(),
-                                               1, RequestGroupMan::POS_SET));
+                                               1, A2_POS_SET));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)3, rm.changeReservedGroupPosition(gs[0]->getGID(),
-                                               10,RequestGroupMan::POS_SET));
+                                               10,A2_POS_SET));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)0, rm.changeReservedGroupPosition(gs[0]->getGID(),
-                                               -10, RequestGroupMan::POS_SET));
+                                               -10, A2_POS_SET));
 
   CPPUNIT_ASSERT_EQUAL
     ((size_t)1, rm.changeReservedGroupPosition(gs[1]->getGID(),
-                                               0, RequestGroupMan::POS_CUR));
+                                               0, A2_POS_CUR));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)2, rm.changeReservedGroupPosition(gs[1]->getGID(),
-                                               1, RequestGroupMan::POS_CUR));
+                                               1, A2_POS_CUR));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)1, rm.changeReservedGroupPosition(gs[1]->getGID(),
-                                               -1,RequestGroupMan::POS_CUR));
+                                               -1,A2_POS_CUR));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)0, rm.changeReservedGroupPosition(gs[1]->getGID(),
-                                               -10, RequestGroupMan::POS_CUR));
+                                               -10, A2_POS_CUR));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)1, rm.changeReservedGroupPosition(gs[1]->getGID(),
-                                               1, RequestGroupMan::POS_CUR));
+                                               1, A2_POS_CUR));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)3, rm.changeReservedGroupPosition(gs[1]->getGID(),
-                                               10, RequestGroupMan::POS_CUR));
+                                               10, A2_POS_CUR));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)1, rm.changeReservedGroupPosition(gs[1]->getGID(),
-                                               -2,RequestGroupMan::POS_CUR));
+                                               -2,A2_POS_CUR));
 
   CPPUNIT_ASSERT_EQUAL
     ((size_t)3, rm.changeReservedGroupPosition(gs[3]->getGID(),
-                                               0, RequestGroupMan::POS_END));
+                                               0, A2_POS_END));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)2, rm.changeReservedGroupPosition(gs[3]->getGID(),
-                                               -1,RequestGroupMan::POS_END));
+                                               -1,A2_POS_END));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)0, rm.changeReservedGroupPosition(gs[3]->getGID(),
-                                               -10, RequestGroupMan::POS_END));
+                                               -10, A2_POS_END));
   CPPUNIT_ASSERT_EQUAL
     ((size_t)3, rm.changeReservedGroupPosition(gs[3]->getGID(),
-                                               10, RequestGroupMan::POS_END));
+                                               10, A2_POS_END));
 
   CPPUNIT_ASSERT_EQUAL((size_t)4, rm.getReservedGroups().size());
 
   try {
     rm.changeReservedGroupPosition(GroupId::create()->getNumericId(),
-                                   0, RequestGroupMan::POS_CUR);
+                                   0, A2_POS_CUR);
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(RecoverableException& e) {
     // success

+ 19 - 19
test/RpcMethodTest.cc

@@ -161,11 +161,11 @@ void RpcMethodTest::testAddUri()
   {
     RpcResponse res = m.execute(req, e_.get());
     CPPUNIT_ASSERT_EQUAL(0, res.code);
-    const std::deque<SharedHandle<RequestGroup> > rgs =
+    const RequestGroupList& rgs =
       e_->getRequestGroupMan()->getReservedGroups();
     CPPUNIT_ASSERT_EQUAL((size_t)1, rgs.size());
     CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/"),
-                         rgs.front()->getDownloadContext()->
+                         (*rgs.begin()).second->getDownloadContext()->
                          getFirstFileEntry()->getRemainingUris().front());
   }
   // with options
@@ -179,7 +179,7 @@ void RpcMethodTest::testAddUri()
     CPPUNIT_ASSERT_EQUAL(0, GroupId::toNumericId
                          (gid, downcast<String>(res.param)->s().c_str()));
     CPPUNIT_ASSERT_EQUAL(std::string("/sink"),
-                         e_->getRequestGroupMan()->findReservedGroup(gid)->
+                         findReservedGroup(e_->getRequestGroupMan(), gid)->
                          getOption()->get(PREF_DIR));
   }
 }
@@ -236,7 +236,7 @@ void RpcMethodTest::testAddUri_withPosition()
   m.execute(req2, e_.get());
 
   std::string uri =
-    e_->getRequestGroupMan()->getReservedGroups()[0]->
+    getReservedGroup(e_->getRequestGroupMan(), 0)->
     getDownloadContext()->getFirstFileEntry()->getRemainingUris()[0];
 
   CPPUNIT_ASSERT_EQUAL(std::string("http://uri2"), uri);
@@ -288,7 +288,7 @@ void RpcMethodTest::testAddTorrent()
                          (gid, downcast<String>(res.param)->s().c_str()));
 
     SharedHandle<RequestGroup> group =
-      e_->getRequestGroupMan()->findReservedGroup(gid);
+      findReservedGroup(e_->getRequestGroupMan(), gid);
     CPPUNIT_ASSERT(group);
     CPPUNIT_ASSERT_EQUAL(e_->getOption()->get(PREF_DIR)+"/aria2-0.8.2.tar.bz2",
                          group->getFirstFilePath());
@@ -314,7 +314,7 @@ void RpcMethodTest::testAddTorrent()
                          (gid, downcast<String>(res.param)->s().c_str()));
     CPPUNIT_ASSERT_EQUAL
       (dir+"/aria2-0.8.2.tar.bz2",
-       e_->getRequestGroupMan()->findReservedGroup(gid)->getFirstFilePath());
+       findReservedGroup(e_->getRequestGroupMan(), gid)->getFirstFilePath());
     CPPUNIT_ASSERT
       (File(dir+"/0a3893293e27ac0490424c06de4d09242215f0a6.torrent").exists());
   }
@@ -355,7 +355,7 @@ void RpcMethodTest::testAddTorrent_withPosition()
   m.execute(req2, e_.get());
 
   CPPUNIT_ASSERT_EQUAL((size_t)1,
-                       e_->getRequestGroupMan()->getReservedGroups()[0]->
+                       getReservedGroup(e_->getRequestGroupMan(), 0)->
                        getDownloadContext()->getFileEntries().size());
 }
 
@@ -406,12 +406,12 @@ void RpcMethodTest::testAddMetalink()
 #endif // ENABLE_MESSAGE_DIGEST
 
     SharedHandle<RequestGroup> tar =
-      e_->getRequestGroupMan()->findReservedGroup(gid3);
+      findReservedGroup(e_->getRequestGroupMan(), gid3);
     CPPUNIT_ASSERT(tar);
     CPPUNIT_ASSERT_EQUAL(e_->getOption()->get(PREF_DIR)+"/aria2-5.0.0.tar.bz2",
                          tar->getFirstFilePath());
     SharedHandle<RequestGroup> deb =
-      e_->getRequestGroupMan()->findReservedGroup(gid4);
+      findReservedGroup(e_->getRequestGroupMan(), gid4);
     CPPUNIT_ASSERT(deb);
     CPPUNIT_ASSERT_EQUAL(e_->getOption()->get(PREF_DIR)+"/aria2-5.0.0.deb",
                          deb->getFirstFilePath());
@@ -433,7 +433,7 @@ void RpcMethodTest::testAddMetalink()
       (0, GroupId::toNumericId
        (gid5, downcast<String>(resParams->get(0))->s().c_str()));
     CPPUNIT_ASSERT_EQUAL(dir+"/aria2-5.0.0.tar.bz2",
-                         e_->getRequestGroupMan()->findReservedGroup(gid5)->
+                         findReservedGroup(e_->getRequestGroupMan(), gid5)->
                          getFirstFilePath());
 #ifdef ENABLE_MESSAGE_DIGEST
     CPPUNIT_ASSERT
@@ -478,7 +478,7 @@ void RpcMethodTest::testAddMetalink_withPosition()
   CPPUNIT_ASSERT_EQUAL(0, res2.code);
 
   CPPUNIT_ASSERT_EQUAL(e_->getOption()->get(PREF_DIR)+"/aria2-5.0.0.tar.bz2",
-                       e_->getRequestGroupMan()->getReservedGroups()[0]->
+                       getReservedGroup(e_->getRequestGroupMan(), 0)->
                        getFirstFilePath());
 }
 
@@ -684,9 +684,9 @@ void RpcMethodTest::testTellWaiting()
   CPPUNIT_ASSERT_EQUAL(0, res.code);
   const List* resParams = downcast<List>(res.param);
   CPPUNIT_ASSERT_EQUAL((size_t)2, resParams->size());
-  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(rgman->getReservedGroups()[1]->getGID()),
+  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(getReservedGroup(rgman, 1)->getGID()),
                        getString(downcast<Dict>(resParams->get(0)), "gid"));
-  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(rgman->getReservedGroups()[2]->getGID()),
+  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(getReservedGroup(rgman, 2)->getGID()),
                        getString(downcast<Dict>(resParams->get(1)), "gid"));
   // waiting.size() == offset+num
   req = RpcRequest(TellWaitingRpcMethod::getMethodName(), List::g());
@@ -742,9 +742,9 @@ void RpcMethodTest::testTellWaiting()
   CPPUNIT_ASSERT_EQUAL(0, res.code);
   resParams = downcast<List>(res.param);
   CPPUNIT_ASSERT_EQUAL((size_t)2, resParams->size());
-  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(rgman->getReservedGroups()[3]->getGID()),
+  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(getReservedGroup(rgman, 3)->getGID()),
                        getString(downcast<Dict>(resParams->get(0)), "gid"));
-  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(rgman->getReservedGroups()[2]->getGID()),
+  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(getReservedGroup(rgman, 2)->getGID()),
                        getString(downcast<Dict>(resParams->get(1)), "gid"));
   // negative offset and size < num
   req.params->set(1, Integer::g(100));
@@ -936,7 +936,7 @@ void RpcMethodTest::testChangePosition()
     (SharedHandle<RequestGroup>(new RequestGroup(GroupId::create(),
                                                  util::copy(option_))));
 
-  a2_gid_t gid = e_->getRequestGroupMan()->getReservedGroups()[0]->getGID();
+  a2_gid_t gid = getReservedGroup(e_->getRequestGroupMan(), 0)->getGID();
   ChangePositionRpcMethod m;
   RpcRequest req(ChangePositionRpcMethod::getMethodName(), List::g());
   req.params->append(GroupId::toHex(gid));
@@ -946,7 +946,7 @@ void RpcMethodTest::testChangePosition()
   CPPUNIT_ASSERT_EQUAL(0, res.code);
   CPPUNIT_ASSERT_EQUAL((int64_t)1, downcast<Integer>(res.param)->i());
   CPPUNIT_ASSERT_EQUAL
-    (gid, e_->getRequestGroupMan()->getReservedGroups()[1]->getGID());
+    (gid, getReservedGroup(e_->getRequestGroupMan(), 1)->getGID());
 }
 
 void RpcMethodTest::testChangePosition_fail()
@@ -1211,9 +1211,9 @@ void RpcMethodTest::testSystemMulticall()
   const List* resParams = downcast<List>(res.param);
   CPPUNIT_ASSERT_EQUAL((size_t)7, resParams->size());
   SharedHandle<RequestGroupMan> rgman = e_->getRequestGroupMan();
-  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(rgman->getReservedGroups()[0]->getGID()),
+  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(getReservedGroup(rgman, 0)->getGID()),
                        downcast<String>(downcast<List>(resParams->get(0))->get(0))->s());
-  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(rgman->getReservedGroups()[1]->getGID()),
+  CPPUNIT_ASSERT_EQUAL(GroupId::toHex(getReservedGroup(rgman, 1)->getGID()),
                        downcast<String>(downcast<List>(resParams->get(1))->get(0))->s());
   CPPUNIT_ASSERT_EQUAL((int64_t)1,
                        downcast<Integer>

+ 26 - 0
test/TestUtil.cc

@@ -15,6 +15,8 @@
 #include "DefaultDiskWriter.h"
 #include "fmt.h"
 #include "util.h"
+#include "RequestGroupMan.h"
+#include "RequestGroup.h"
 #ifdef ENABLE_MESSAGE_DIGEST
 # include "message_digest_helper.h"
 #endif // ENABLE_MESSAGE_DIGEST
@@ -98,4 +100,28 @@ WrDiskCacheEntry::DataCell* createDataCell(int64_t goff,
   return cell;
 }
 
+SharedHandle<RequestGroup> findReservedGroup
+(const SharedHandle<RequestGroupMan>& rgman, a2_gid_t gid)
+{
+  SharedHandle<RequestGroup> rg = rgman->findGroup(gid);
+  if(rg) {
+    if(rg->getState() == RequestGroup::STATE_WAITING) {
+      return rg;
+    } else {
+      rg.reset();
+    }
+  }
+  return rg;
+}
+
+SharedHandle<RequestGroup> getReservedGroup
+(const SharedHandle<RequestGroupMan>& rgman, size_t index)
+{
+  assert(rgman->getReservedGroups().size() > index);
+  RequestGroupList::SeqType::const_iterator i =
+    rgman->getReservedGroups().begin();
+  std::advance(i, index);
+  return (*i).second;
+}
+
 } // namespace aria2

+ 9 - 0
test/TestUtil.h

@@ -5,10 +5,13 @@
 #include "SharedHandle.h"
 #include "Cookie.h"
 #include "WrDiskCacheEntry.h"
+#include "GroupId.h"
 
 namespace aria2 {
 
 class MessageDigest;
+class RequestGroupMan;
+class RequestGroup;
 
 void createFile(const std::string& filename, size_t length);
 
@@ -55,4 +58,10 @@ WrDiskCacheEntry::DataCell* createDataCell(int64_t goff,
                                            const char* data,
                                            size_t offset = 0);
 
+SharedHandle<RequestGroup> findReservedGroup
+(const SharedHandle<RequestGroupMan>& rgman, a2_gid_t gid);
+
+SharedHandle<RequestGroup> getReservedGroup
+(const SharedHandle<RequestGroupMan>& rgman, size_t index);
+
 } // namespace aria2