瀏覽代碼

2010-06-18 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

	Introduced ValueBase class, which is a replacement of BDE.  In
	this change ValueBase is used instead of BDE except DHT messages,
	UTMetadata messages and XML-RPC. They'll be replaced in the later
	commits. DownloadContext::_attrs is now ContextAttribute rather
	than BDE.
	* src/ActivePeerConnectionCommand.cc
	* src/AnnounceList.cc
	* src/AnnounceList.h
	* src/BtDependency.cc
	* src/BtRegistry.cc
	* src/BtSetup.cc
	* src/ConsoleStatCalc.cc
	* src/ContextAttribute.h
	* src/DefaultBtAnnounce.cc
	* src/DefaultBtInteractive.cc
	* src/DownloadContext.cc
	* src/DownloadContext.h
	* src/HandshakeExtensionMessage.cc
	* src/InitiateConnectionCommand.cc
	* src/LpdReceiveMessageCommand.cc
	* src/MSEHandshake.cc
	* src/Makefile.am
	* src/Makefile.in
	* src/PeerInteractionCommand.cc
	* src/PeerListProcessor.h
	* src/ProtocolDetector.cc
	* src/RequestGroup.cc
	* src/RequestGroupMan.cc
	* src/TorrentAttribute.h
	* src/TrackerWatcherCommand.cc
	* src/UTMetadataDataExtensionMessage.cc
	* src/UTMetadataPostDownloadHandler.cc
	* src/UTMetadataRequestExtensionMessage.cc
	* src/ValueBase.cc
	* src/ValueBase.h
	* src/XmlRpcMethodImpl.cc
	* src/XmlRpcMethodImpl.h
	* src/bencode2.cc
	* src/bencode2.h
	* src/bittorrent_helper.cc
	* src/bittorrent_helper.h
	* src/download_helper.cc
	* src/magnet.cc
	* src/magnet.h
	* test/AnnounceListTest.cc
	* test/Bencode2Test.cc
	* test/BencodeTest.cc
	* test/BittorrentHelperTest.cc
	* test/BtDependencyTest.cc
	* test/BtRegistryTest.cc
	* test/DefaultBtAnnounceTest.cc
	* test/DefaultBtProgressInfoFileTest.cc
	* test/HandshakeExtensionMessageTest.cc
	* test/MSEHandshakeTest.cc
	* test/MagnetTest.cc
	* test/Makefile.am
	* test/Makefile.in
	* test/RequestGroupManTest.cc
	* test/UTMetadataDataExtensionMessageTest.cc
	* test/UTMetadataPostDownloadHandlerTest.cc
	* test/UTMetadataRequestExtensionMessageTest.cc
	* test/ValueBaseTest.cc
	* test/XmlRpcMethodTest.cc
Tatsuhiro Tsujikawa 15 年之前
父節點
當前提交
8ba97188ce
共有 59 個文件被更改,包括 2303 次插入641 次删除
  1. 66 0
      ChangeLog
  2. 2 2
      src/ActivePeerConnectionCommand.cc
  3. 12 22
      src/AnnounceList.cc
  4. 3 4
      src/AnnounceList.h
  5. 2 2
      src/BtDependency.cc
  6. 3 4
      src/BtRegistry.cc
  7. 6 5
      src/BtSetup.cc
  8. 1 2
      src/ConsoleStatCalc.cc
  9. 44 0
      src/ContextAttribute.h
  10. 29 30
      src/DefaultBtAnnounce.cc
  11. 4 3
      src/DefaultBtInteractive.cc
  12. 17 19
      src/DownloadContext.cc
  13. 6 6
      src/DownloadContext.h
  14. 7 7
      src/HandshakeExtensionMessage.cc
  15. 1 0
      src/InitiateConnectionCommand.cc
  16. 5 8
      src/LpdReceiveMessageCommand.cc
  17. 3 4
      src/MSEHandshake.cc
  18. 5 1
      src/Makefile.am
  19. 20 13
      src/Makefile.in
  20. 4 6
      src/PeerInteractionCommand.cc
  21. 59 0
      src/PeerListProcessor.h
  22. 1 0
      src/ProtocolDetector.cc
  23. 12 18
      src/RequestGroup.cc
  24. 1 0
      src/RequestGroupMan.cc
  25. 66 0
      src/TorrentAttribute.h
  26. 5 5
      src/TrackerWatcherCommand.cc
  27. 2 3
      src/UTMetadataDataExtensionMessage.cc
  28. 4 4
      src/UTMetadataPostDownloadHandler.cc
  29. 8 10
      src/UTMetadataRequestExtensionMessage.cc
  30. 347 0
      src/ValueBase.cc
  31. 288 0
      src/ValueBase.h
  32. 22 23
      src/XmlRpcMethodImpl.cc
  33. 5 3
      src/XmlRpcMethodImpl.h
  34. 263 0
      src/bencode2.cc
  35. 75 0
      src/bencode2.h
  36. 261 202
      src/bittorrent_helper.cc
  37. 16 13
      src/bittorrent_helper.h
  38. 3 2
      src/download_helper.cc
  39. 11 11
      src/magnet.cc
  40. 7 6
      src/magnet.h
  41. 35 19
      test/AnnounceListTest.cc
  42. 206 0
      test/Bencode2Test.cc
  43. 3 0
      test/BencodeTest.cc
  44. 76 101
      test/BittorrentHelperTest.cc
  45. 1 1
      test/BtDependencyTest.cc
  46. 4 4
      test/BtRegistryTest.cc
  47. 39 28
      test/DefaultBtAnnounceTest.cc
  48. 2 3
      test/DefaultBtProgressInfoFileTest.cc
  49. 2 2
      test/HandshakeExtensionMessageTest.cc
  50. 2 3
      test/MSEHandshakeTest.cc
  51. 11 7
      test/MagnetTest.cc
  52. 3 1
      test/Makefile.am
  53. 7 3
      test/Makefile.in
  54. 1 0
      test/RequestGroupManTest.cc
  55. 2 3
      test/UTMetadataDataExtensionMessageTest.cc
  56. 18 16
      test/UTMetadataPostDownloadHandlerTest.cc
  57. 7 6
      test/UTMetadataRequestExtensionMessageTest.cc
  58. 182 0
      test/ValueBaseTest.cc
  59. 6 6
      test/XmlRpcMethodTest.cc

+ 66 - 0
ChangeLog

@@ -1,3 +1,69 @@
+2010-06-18  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Introduced ValueBase class, which is a replacement of BDE.  In
+	this change ValueBase is used instead of BDE except DHT messages,
+	UTMetadata messages and XML-RPC. They'll be replaced in the later
+	commits. DownloadContext::_attrs is now ContextAttribute rather
+	than BDE.
+	* src/ActivePeerConnectionCommand.cc
+	* src/AnnounceList.cc
+	* src/AnnounceList.h
+	* src/BtDependency.cc
+	* src/BtRegistry.cc
+	* src/BtSetup.cc
+	* src/ConsoleStatCalc.cc
+	* src/ContextAttribute.h
+	* src/DefaultBtAnnounce.cc
+	* src/DefaultBtInteractive.cc
+	* src/DownloadContext.cc
+	* src/DownloadContext.h
+	* src/HandshakeExtensionMessage.cc
+	* src/InitiateConnectionCommand.cc
+	* src/LpdReceiveMessageCommand.cc
+	* src/MSEHandshake.cc
+	* src/Makefile.am
+	* src/Makefile.in
+	* src/PeerInteractionCommand.cc
+	* src/PeerListProcessor.h
+	* src/ProtocolDetector.cc
+	* src/RequestGroup.cc
+	* src/RequestGroupMan.cc
+	* src/TorrentAttribute.h
+	* src/TrackerWatcherCommand.cc
+	* src/UTMetadataDataExtensionMessage.cc
+	* src/UTMetadataPostDownloadHandler.cc
+	* src/UTMetadataRequestExtensionMessage.cc
+	* src/ValueBase.cc
+	* src/ValueBase.h
+	* src/XmlRpcMethodImpl.cc
+	* src/XmlRpcMethodImpl.h
+	* src/bencode2.cc
+	* src/bencode2.h
+	* src/bittorrent_helper.cc
+	* src/bittorrent_helper.h
+	* src/download_helper.cc
+	* src/magnet.cc
+	* src/magnet.h
+	* test/AnnounceListTest.cc
+	* test/Bencode2Test.cc
+	* test/BencodeTest.cc
+	* test/BittorrentHelperTest.cc
+	* test/BtDependencyTest.cc
+	* test/BtRegistryTest.cc
+	* test/DefaultBtAnnounceTest.cc
+	* test/DefaultBtProgressInfoFileTest.cc
+	* test/HandshakeExtensionMessageTest.cc
+	* test/MSEHandshakeTest.cc
+	* test/MagnetTest.cc
+	* test/Makefile.am
+	* test/Makefile.in
+	* test/RequestGroupManTest.cc
+	* test/UTMetadataDataExtensionMessageTest.cc
+	* test/UTMetadataPostDownloadHandlerTest.cc
+	* test/UTMetadataRequestExtensionMessageTest.cc
+	* test/ValueBaseTest.cc
+	* test/XmlRpcMethodTest.cc
+
 2010-06-15  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	Fixed assertion error if updateTransferStatFor is called with peer

+ 2 - 2
src/ActivePeerConnectionCommand.cc

@@ -89,8 +89,8 @@ bool ActivePeerConnectionCommand::execute() {
       _requestGroup->getMaxDownloadSpeedLimit();
     const unsigned int maxUploadLimit = _requestGroup->getMaxUploadSpeedLimit();
     unsigned int thresholdSpeed;
-    if(_requestGroup->getDownloadContext()->
-       getAttribute(bittorrent::BITTORRENT).containsKey(bittorrent::METADATA)) {
+    if(!bittorrent::getTorrentAttrs
+       (_requestGroup->getDownloadContext())->metadata.empty()) {
       thresholdSpeed =
         _requestGroup->getOption()->getAsInt(PREF_BT_REQUEST_PEER_SPEED_LIMIT);
     } else {

+ 12 - 22
src/AnnounceList.cc

@@ -49,7 +49,8 @@ const std::string AnnounceList::STOPPED("stopped");
 
 const std::string AnnounceList::COMPLETED("completed");
 
-AnnounceList::AnnounceList(const BDE& announceList):
+AnnounceList::AnnounceList
+(const std::vector<std::vector<std::string> >& announceList):
   _currentTrackerInitialized(false) {
   reconfigure(announceList);
 }
@@ -60,30 +61,19 @@ AnnounceList::AnnounceList
   resetIterator();
 }
 
-void AnnounceList::reconfigure(const BDE& announceList)
+void AnnounceList::reconfigure
+(const std::vector<std::vector<std::string> >& announceList)
 {
-  if(announceList.isList()) {
-    for(BDE::List::const_iterator itr = announceList.listBegin(),
-          eoi = announceList.listEnd(); itr != eoi; ++itr) {
-      const BDE& elemList = *itr;
-      if(!elemList.isList()) {
-        continue;
-      }
-      std::deque<std::string> urls;
-      for(BDE::List::const_iterator elemItr = elemList.listBegin(),
-            eoi2 = elemList.listEnd(); elemItr != eoi2; ++elemItr) {
-        const BDE& data = *elemItr;
-        if(data.isString()) {
-          urls.push_back(data.s());
-        }
-      }
-      if(!urls.empty()) {
-        SharedHandle<AnnounceTier> tier(new AnnounceTier(urls));
-        _tiers.push_back(tier);
-      }
+  for(std::vector<std::vector<std::string> >::const_iterator itr =
+        announceList.begin(), eoi = announceList.end(); itr != eoi; ++itr) {
+    if((*itr).empty()) {
+      continue;
     }
-    resetIterator();
+    std::deque<std::string> urls((*itr).begin(), (*itr).end());
+    SharedHandle<AnnounceTier> tier(new AnnounceTier(urls));
+    _tiers.push_back(tier);
   }
+  resetIterator();
 }
 
 void AnnounceList::reconfigure(const std::string& url) {

+ 3 - 4
src/AnnounceList.h

@@ -38,11 +38,10 @@
 #include "common.h"
 #include "SharedHandle.h"
 #include "AnnounceTier.h"
+#include "ValueBase.h"
 
 namespace aria2 {
 
-class BDE;
-
 class AnnounceList {
 public:
 private:
@@ -56,10 +55,10 @@ private:
   (const std::deque<SharedHandle<AnnounceTier> >::iterator& itr);
 public:
   AnnounceList():_currentTrackerInitialized(false) {}
-  AnnounceList(const BDE& announceList);
+  AnnounceList(const std::vector<std::vector<std::string> >& announceList);
   AnnounceList(const std::deque<SharedHandle<AnnounceTier> >& tiers);
 
-  void reconfigure(const BDE& announceList);
+  void reconfigure(const std::vector<std::vector<std::string> >& announceList);
   void reconfigure(const std::string& url);
 
   size_t countTier() const {

+ 2 - 2
src/BtDependency.cc

@@ -85,8 +85,8 @@ bool BtDependency::resolve()
       diskAdaptor->openExistingFile();
       std::string content = util::toString(diskAdaptor);
       if(dependee->getDownloadContext()->hasAttribute(bittorrent::BITTORRENT)) {
-        const BDE& attrs =
-          dependee->getDownloadContext()->getAttribute(bittorrent::BITTORRENT);
+        SharedHandle<TorrentAttribute> attrs =
+          bittorrent::getTorrentAttrs(dependee->getDownloadContext());
         bittorrent::loadFromMemory
           (bittorrent::metadata2Torrent(content, attrs), context, "default");
       } else {

+ 3 - 4
src/BtRegistry.cc

@@ -56,13 +56,12 @@ BtRegistry::getDownloadContext(const std::string& infoHash) const
   SharedHandle<DownloadContext> dctx;
   for(std::map<gid_t, BtObject>::const_iterator i = _pool.begin(),
         eoi = _pool.end(); i != eoi; ++i) {
-    const BDE& attrs =
-      (*i).second._downloadContext->getAttribute(bittorrent::BITTORRENT);
-    if(attrs[bittorrent::INFO_HASH].s() == infoHash) {
+    if(bittorrent::getTorrentAttrs((*i).second._downloadContext)->infoHash ==
+       infoHash) {
       dctx = (*i).second._downloadContext;
       break;
     }
-  }
+  }      
   return dctx;
 }
 

+ 6 - 5
src/BtSetup.cc

@@ -73,6 +73,7 @@
 #include "FileAllocationEntry.h"
 #include "CheckIntegrityEntry.h"
 #include "ServerStatMan.h"
+#include "DlAbortEx.h"
 
 namespace aria2 {
 
@@ -86,9 +87,9 @@ void BtSetup::setup(std::vector<Command*>& commands,
   if(!requestGroup->getDownloadContext()->hasAttribute(bittorrent::BITTORRENT)){
     return;
   }
-  const BDE& torrentAttrs =
-    requestGroup->getDownloadContext()->getAttribute(bittorrent::BITTORRENT);
-  bool metadataGetMode = !torrentAttrs.containsKey(bittorrent::METADATA);
+  SharedHandle<TorrentAttribute> torrentAttrs =
+    bittorrent::getTorrentAttrs(requestGroup->getDownloadContext());
+  bool metadataGetMode = torrentAttrs->metadata.empty();
   BtObject btObject = e->getBtRegistry()->get(requestGroup->getGID());
   SharedHandle<PieceStorage> pieceStorage = btObject._pieceStorage;
   SharedHandle<PeerStorage> peerStorage = btObject._peerStorage;
@@ -125,7 +126,7 @@ void BtSetup::setup(std::vector<Command*>& commands,
     commands.push_back(c);
   }
 
-  if((metadataGetMode || torrentAttrs[bittorrent::PRIVATE].i() == 0) &&
+  if((metadataGetMode || !torrentAttrs->privateTorrent) &&
      DHTSetup::initialized()) {
     DHTGetPeersCommand* command =
       new DHTGetPeersCommand(e->newCUID(), requestGroup, e);
@@ -179,7 +180,7 @@ void BtSetup::setup(std::vector<Command*>& commands,
     btRuntime->setListenPort(listenCommand->getPort());
   }
   if(option->getAsBool(PREF_BT_ENABLE_LPD) &&
-     (metadataGetMode || torrentAttrs[bittorrent::PRIVATE].i() == 0)) {
+     (metadataGetMode || !torrentAttrs->privateTorrent)) {
     if(LpdReceiveMessageCommand::getNumInstance() == 0) {
       _logger->info("Initializing LpdMessageReceiver.");
       SharedHandle<LpdMessageReceiver> receiver

+ 1 - 2
src/ConsoleStatCalc.cc

@@ -89,8 +89,7 @@ static void printProgress
 
 #ifdef ENABLE_BITTORRENT
   if(rg->getDownloadContext()->hasAttribute(bittorrent::BITTORRENT) &&
-     rg->getDownloadContext()->getAttribute(bittorrent::BITTORRENT)
-     .containsKey(bittorrent::METADATA) &&
+     !bittorrent::getTorrentAttrs(rg->getDownloadContext())->metadata.empty() &&
      rg->downloadFinished()) {
     o << "SEEDING" << "(" << "ratio:";
     if(rg->getCompletedLength() > 0) {

+ 44 - 0
src/ContextAttribute.h

@@ -0,0 +1,44 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_CONTEXT_ATTRIBUTE_H
+#define D_CONTEXT_ATTRIBUTE_H
+
+#include "common.h"
+
+struct ContextAttribute {
+  virtual ~ContextAttribute() {}
+};
+
+#endif // D_CONTEXT_ATTRIBUTE_H

+ 29 - 30
src/DefaultBtAnnounce.cc

@@ -50,7 +50,7 @@
 #include "StringFormat.h"
 #include "A2STR.h"
 #include "Request.h"
-#include "bencode.h"
+#include "bencode2.h"
 #include "bittorrent_helper.h"
 #include "wallclock.h"
 
@@ -67,9 +67,7 @@ DefaultBtAnnounce::DefaultBtAnnounce
   _userDefinedInterval(0),
   _complete(0),
   _incomplete(0),
-  _announceList
-  (downloadContext->getAttribute
-   (bittorrent::BITTORRENT)[bittorrent::ANNOUNCE_LIST]),
+  _announceList(bittorrent::getTorrentAttrs(downloadContext)->announceList),
   _option(option),
   _logger(LogFactory::getInstance()),
   _randomizer(SimpleRandomizer::getInstance())
@@ -219,37 +217,38 @@ DefaultBtAnnounce::processAnnounceResponse(const unsigned char* trackerResponse,
   if(_logger->debug()) {
     _logger->debug("Now processing tracker response.");
   }
-  const BDE dict =
-    bencode::decode(trackerResponse, trackerResponseLength);
-  if(!dict.isDict()) {
+  SharedHandle<ValueBase> decodedValue =
+    bencode2::decode(trackerResponse, trackerResponseLength);
+  const Dict* dict = asDict(decodedValue);
+  if(!dict) {
     throw DL_ABORT_EX(MSG_NULL_TRACKER_RESPONSE);
   }
-  const BDE& failure = dict[BtAnnounce::FAILURE_REASON];
-  if(failure.isString()) {
+  const String* failure = asString(dict->get(BtAnnounce::FAILURE_REASON));
+  if(failure) {
     throw DL_ABORT_EX
-      (StringFormat(EX_TRACKER_FAILURE, failure.s().c_str()).str());
+      (StringFormat(EX_TRACKER_FAILURE, failure->s().c_str()).str());
   }
-  const BDE& warn = dict[BtAnnounce::WARNING_MESSAGE];
-  if(warn.isString()) {
-    _logger->warn(MSG_TRACKER_WARNING_MESSAGE, warn.s().c_str());
+  const String* warn = asString(dict->get(BtAnnounce::WARNING_MESSAGE));
+  if(warn) {
+    _logger->warn(MSG_TRACKER_WARNING_MESSAGE, warn->s().c_str());
   }
-  const BDE& tid = dict[BtAnnounce::TRACKER_ID];
-  if(tid.isString()) {
-    _trackerId = tid.s();
+  const String* tid = asString(dict->get(BtAnnounce::TRACKER_ID));
+  if(tid) {
+    _trackerId = tid->s();
     if(_logger->debug()) {
       _logger->debug("Tracker ID:%s", _trackerId.c_str());
     }
   }
-  const BDE& ival = dict[BtAnnounce::INTERVAL];
-  if(ival.isInteger() && ival.i() > 0) {
-    _interval = ival.i();
+  const Integer* ival = asInteger(dict->get(BtAnnounce::INTERVAL));
+  if(ival && ival->i() > 0) {
+    _interval = ival->i();
     if(_logger->debug()) {
       _logger->debug("Interval:%d", _interval);
     }
   }
-  const BDE& mival = dict[BtAnnounce::MIN_INTERVAL];
-  if(mival.isInteger() && mival.i() > 0) {
-    _minInterval = mival.i();
+  const Integer* mival = asInteger(dict->get(BtAnnounce::MIN_INTERVAL));
+  if(mival && mival->i() > 0) {
+    _minInterval = mival->i();
     if(_logger->debug()) {
       _logger->debug("Min interval:%d", _minInterval);
     }
@@ -258,22 +257,22 @@ DefaultBtAnnounce::processAnnounceResponse(const unsigned char* trackerResponse,
     // Use interval as a minInterval if minInterval is not supplied.
     _minInterval = _interval;
   }
-  const BDE& comp = dict[BtAnnounce::COMPLETE];
-  if(comp.isInteger()) {
-    _complete = comp.i();
+  const Integer* comp = asInteger(dict->get(BtAnnounce::COMPLETE));
+  if(comp) {
+    _complete = comp->i();
     if(_logger->debug()) {
       _logger->debug("Complete:%d", _complete);
     }
   }
-  const BDE& incomp = dict[BtAnnounce::INCOMPLETE];
-  if(incomp.isInteger()) {
-    _incomplete = incomp.i();
+  const Integer* incomp = asInteger(dict->get(BtAnnounce::INCOMPLETE));
+  if(incomp) {
+    _incomplete = incomp->i();
     if(_logger->debug()) {
       _logger->debug("Incomplete:%d", _incomplete);
     }
   }
-  const BDE& peerData = dict[BtAnnounce::PEERS];
-  if(peerData.isNone()) {
+  const SharedHandle<ValueBase>& peerData = dict->get(BtAnnounce::PEERS);
+  if(peerData.isNull()) {
     _logger->info(MSG_NO_PEER_LIST_RECEIVED);
   } else {
     if(!_btRuntime->isHalt() && _btRuntime->lessThanMinPeers()) {

+ 4 - 3
src/DefaultBtInteractive.cc

@@ -202,9 +202,10 @@ void DefaultBtInteractive::addHandshakeExtendedMessageToQueue()
   m->setClientVersion(CLIENT_ARIA2);
   m->setTCPPort(_btRuntime->getListenPort());
   m->setExtensions(_extensionMessageRegistry->getExtensions());
-  const BDE& attrs = _downloadContext->getAttribute(bittorrent::BITTORRENT);
-  if(attrs.containsKey(bittorrent::METADATA)) {
-    m->setMetadataSize(attrs[bittorrent::METADATA_SIZE].i());
+  SharedHandle<TorrentAttribute> attrs =
+    bittorrent::getTorrentAttrs(_downloadContext);
+  if(!attrs->metadata.empty()) {
+    m->setMetadataSize(attrs->metadataSize);
   }
   SharedHandle<BtMessage> msg = _messageFactory->createBtExtendedMessage(m);
   _dispatcher->addMessageToQueue(msg);

+ 17 - 19
src/DownloadContext.cc

@@ -40,6 +40,7 @@
 #include "StringFormat.h"
 #include "util.h"
 #include "wallclock.h"
+#include "DlAbortEx.h"
 
 namespace aria2 {
 
@@ -138,36 +139,33 @@ void DownloadContext::setFileFilter(IntSequence seq)
   }
 }
 
-void DownloadContext::ensureAttrs()
+void DownloadContext::setAttribute
+(const std::string& key, const SharedHandle<ContextAttribute>& value)
 {
-  if(_attrs.isNone()) {
-    _attrs = BDE::dict();
+  std::map<std::string, SharedHandle<ContextAttribute> >::value_type p =
+    std::make_pair(key, value);
+  std::pair<std::map<std::string, SharedHandle<ContextAttribute> >::iterator,
+            bool> r = _attrs.insert(p);
+  if(!r.second) {
+    (*r.first).second = value;
   }
 }
 
-void DownloadContext::setAttribute(const std::string& key, const BDE& value)
+const SharedHandle<ContextAttribute>& DownloadContext::getAttribute
+(const std::string& key)
 {
-  ensureAttrs();
-  _attrs[key] = value;
-}
-
-BDE& DownloadContext::getAttribute(const std::string& key)
-{
-  ensureAttrs();
-  if(_attrs.containsKey(key)) {
-    return _attrs[key];
-  } else {
+  std::map<std::string, SharedHandle<ContextAttribute> >::const_iterator itr =
+    _attrs.find(key);
+  if(itr == _attrs.end()) {
     throw DL_ABORT_EX(StringFormat("No attribute named %s", key.c_str()).str());
+  } else {
+    return (*itr).second;
   }
 }
 
 bool DownloadContext::hasAttribute(const std::string& key) const
 {
-  if(_attrs.isNone()) {
-    return false;
-  } else {
-    return _attrs.containsKey(key);
-  }
+  return _attrs.count(key) == 1;
 }
 
 void DownloadContext::releaseRuntimeResource()

+ 6 - 6
src/DownloadContext.h

@@ -45,9 +45,10 @@
 #include "Signature.h"
 #include "TimerA2.h"
 #include "A2STR.h"
-#include "BDE.h"
+#include "ValueBase.h"
 #include "IntSequence.h"
 #include "FileEntry.h"
+#include "TorrentAttribute.h"
 
 namespace aria2 {
 
@@ -76,15 +77,13 @@ private:
 
   RequestGroup* _ownerRequestGroup;
   
-  BDE _attrs;
+  std::map<std::string, SharedHandle<ContextAttribute> > _attrs;
 
   Timer _downloadStartTime;
 
   Timer _downloadStopTime;
 
   SharedHandle<Signature> _signature;
-
-  void ensureAttrs();
 public:
   DownloadContext();
 
@@ -224,9 +223,10 @@ public:
   // this function.
   void setFilePathWithIndex(size_t index, const std::string& path);
 
-  void setAttribute(const std::string& key, const BDE& value);
+  void setAttribute
+  (const std::string& key, const SharedHandle<ContextAttribute>& value);
 
-  BDE& getAttribute(const std::string& key);
+  const SharedHandle<ContextAttribute>& getAttribute(const std::string& key);
 
   bool hasAttribute(const std::string& key) const;
 

+ 7 - 7
src/HandshakeExtensionMessage.cc

@@ -110,21 +110,21 @@ void HandshakeExtensionMessage::doReceivedAction()
     const std::map<std::string, uint8_t>::value_type& vt = *itr;
     _peer->setExtension(vt.first, vt.second);
   }
-  BDE& attrs = _dctx->getAttribute(bittorrent::BITTORRENT);
-  if(!attrs.containsKey(bittorrent::METADATA) &&
-     !_peer->getExtensionMessageID("ut_metadata")) {
+  SharedHandle<TorrentAttribute> attrs =
+    bittorrent::getTorrentAttrs(_dctx);
+  if(attrs->metadata.empty() && !_peer->getExtensionMessageID("ut_metadata")) {
     // TODO In metadataGetMode, if peer don't support metadata
     // transfer, should we drop connection? There is a possibility
     // that peer can still tell us peers using PEX.
     throw DL_ABORT_EX("Peer doesn't support ut_metadata extension. Goodbye.");
   }
   if(_metadataSize > 0) {
-    if(attrs.containsKey(bittorrent::METADATA_SIZE)) {
-      if(_metadataSize != (size_t)attrs[bittorrent::METADATA_SIZE].i()) {
+    if(attrs->metadataSize) {
+      if(_metadataSize != attrs->metadataSize) {
         throw DL_ABORT_EX("Wrong metadata_size. Which one is correct!?");
       }
     } else {
-      attrs[bittorrent::METADATA_SIZE] = _metadataSize;
+      attrs->metadataSize = _metadataSize;
       _dctx->getFirstFileEntry()->setLength(_metadataSize);
       _dctx->markTotalLengthIsKnown();
       _dctx->getOwnerRequestGroup()->initPieceStorage();
@@ -133,7 +133,7 @@ void HandshakeExtensionMessage::doReceivedAction()
         _dctx->getOwnerRequestGroup()->getPieceStorage();
       pieceStorage->setEndGamePieceNum(0);
     }
-  } else if(!attrs.containsKey(bittorrent::METADATA)) {
+  } else if(attrs->metadata.empty()) {
     throw DL_ABORT_EX("Peer didn't provide metadata_size."
                       " It seems that it doesn't have whole metadata.");
   }

+ 1 - 0
src/InitiateConnectionCommand.cc

@@ -52,6 +52,7 @@
 #include "ServerStatMan.h"
 #include "FileAllocationEntry.h"
 #include "CheckIntegrityEntry.h"
+#include "RecoverableException.h"
 
 namespace aria2 {
 

+ 5 - 8
src/LpdReceiveMessageCommand.cc

@@ -100,15 +100,12 @@ bool LpdReceiveMessageCommand::execute()
       }
       continue;
     }
-    const BDE& torrentAttrs = dctx->getAttribute(bittorrent::BITTORRENT);
-    if(torrentAttrs.containsKey(bittorrent::PRIVATE)) {
-      if(torrentAttrs[bittorrent::PRIVATE].i() == 1) {
-        if(getLogger()->debug()) {
-          getLogger()->debug
-            ("Ignore LPD message because the torrent is private.");
-        }
-        continue;
+    if(bittorrent::getTorrentAttrs(dctx)->privateTorrent) {
+      if(getLogger()->debug()) {
+        getLogger()->debug
+          ("Ignore LPD message because the torrent is private.");
       }
+      continue;
     }
     RequestGroup* group = dctx->getOwnerRequestGroup();
     assert(group);

+ 3 - 4
src/MSEHandshake.cc

@@ -476,14 +476,13 @@ bool MSEHandshake::receiveReceiverHashAndPadCLength
         downloadContexts.begin(), eoi = downloadContexts.end();
       i != eoi; ++i) {
     unsigned char md[20];
-    const BDE& torrentAttrs = (*i)->getAttribute(bittorrent::BITTORRENT);
-    createReq23Hash(md, torrentAttrs[bittorrent::INFO_HASH].uc());
+    const unsigned char* infohash = bittorrent::getInfoHash(*i);
+    createReq23Hash(md, infohash);
     if(memcmp(md, rbufptr, sizeof(md)) == 0) {
       if(_logger->debug()) {
         _logger->debug("CUID#%s - info hash found: %s",
                        util::itos(_cuid).c_str(),
-                       util::toHex
-                       (torrentAttrs[bittorrent::INFO_HASH].s()).c_str());
+                       util::toHex(infohash, INFO_HASH_LENGTH).c_str());
       }
       downloadContext = *i;
       break;

+ 5 - 1
src/Makefile.am

@@ -202,7 +202,11 @@ SRCS =  Socket.h\
 	MetadataInfo.cc MetadataInfo.h\
 	SessionSerializer.cc SessionSerializer.h\
 	Event.h\
-	timespec.h
+	timespec.h\
+	ValueBase.cc ValueBase.h\
+	bencode2.cc bencode2.h\
+	ContextAttribute.h\
+	TorrentAttribute.h
 
 if ENABLE_XML_RPC
 SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\

+ 20 - 13
src/Makefile.in

@@ -437,7 +437,9 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	CreateRequestCommand.h DownloadResultCode.h wallclock.h \
 	download_helper.cc download_helper.h MetadataInfo.cc \
 	MetadataInfo.h SessionSerializer.cc SessionSerializer.h \
-	Event.h timespec.h XmlRpcRequestParserController.cc \
+	Event.h timespec.h ValueBase.cc ValueBase.h bencode2.cc \
+	bencode2.h ContextAttribute.h TorrentAttribute.h \
+	XmlRpcRequestParserController.cc \
 	XmlRpcRequestParserController.h \
 	XmlRpcRequestParserStateMachine.cc \
 	XmlRpcRequestParserStateMachine.h XmlRpcRequestParserState.h \
@@ -873,7 +875,8 @@ am__objects_32 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
 	LongestSequencePieceSelector.$(OBJEXT) bitfield.$(OBJEXT) \
 	BDE.$(OBJEXT) CreateRequestCommand.$(OBJEXT) \
 	download_helper.$(OBJEXT) MetadataInfo.$(OBJEXT) \
-	SessionSerializer.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
+	SessionSerializer.$(OBJEXT) ValueBase.$(OBJEXT) \
+	bencode2.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
 	$(am__objects_3) $(am__objects_4) $(am__objects_5) \
 	$(am__objects_6) $(am__objects_7) $(am__objects_8) \
 	$(am__objects_9) $(am__objects_10) $(am__objects_11) \
@@ -1213,17 +1216,19 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \
 	CreateRequestCommand.h DownloadResultCode.h wallclock.h \
 	download_helper.cc download_helper.h MetadataInfo.cc \
 	MetadataInfo.h SessionSerializer.cc SessionSerializer.h \
-	Event.h timespec.h $(am__append_1) $(am__append_2) \
-	$(am__append_3) $(am__append_4) $(am__append_5) \
-	$(am__append_6) $(am__append_7) $(am__append_8) \
-	$(am__append_9) $(am__append_10) $(am__append_11) \
-	$(am__append_12) $(am__append_13) $(am__append_14) \
-	$(am__append_15) $(am__append_16) $(am__append_17) \
-	$(am__append_18) $(am__append_19) $(am__append_20) \
-	$(am__append_21) $(am__append_22) $(am__append_23) \
-	$(am__append_24) $(am__append_25) $(am__append_26) \
-	$(am__append_27) $(am__append_28) $(am__append_29) \
-	$(am__append_30) $(am__append_31)
+	Event.h timespec.h ValueBase.cc ValueBase.h bencode2.cc \
+	bencode2.h ContextAttribute.h TorrentAttribute.h \
+	$(am__append_1) $(am__append_2) $(am__append_3) \
+	$(am__append_4) $(am__append_5) $(am__append_6) \
+	$(am__append_7) $(am__append_8) $(am__append_9) \
+	$(am__append_10) $(am__append_11) $(am__append_12) \
+	$(am__append_13) $(am__append_14) $(am__append_15) \
+	$(am__append_16) $(am__append_17) $(am__append_18) \
+	$(am__append_19) $(am__append_20) $(am__append_21) \
+	$(am__append_22) $(am__append_23) $(am__append_24) \
+	$(am__append_25) $(am__append_26) $(am__append_27) \
+	$(am__append_28) $(am__append_29) $(am__append_30) \
+	$(am__append_31)
 noinst_LIBRARIES = libaria2c.a
 libaria2c_a_SOURCES = $(SRCS)
 aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\
@@ -1612,6 +1617,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTPexExtensionMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UnknownLengthPieceStorage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UriListParser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ValueBase.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XML2SAXMetalinkProcessor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Xml2XmlRpcRequestProcessor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcElements.Po@am__quote@
@@ -1626,6 +1632,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asctime_r.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bencode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bencode2.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitfield.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bittorrent_helper.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clock_gettime_mingw.Po@am__quote@

+ 4 - 6
src/PeerInteractionCommand.cc

@@ -102,11 +102,9 @@ PeerInteractionCommand::PeerInteractionCommand
     setWriteCheckSocket(getSocket());
     setTimeout(getOption()->getAsInt(PREF_PEER_CONNECTION_TIMEOUT));
   }
-
-  const BDE& torrentAttrs =
-    _requestGroup->getDownloadContext()->getAttribute(bittorrent::BITTORRENT);
-
-  bool metadataGetMode = !torrentAttrs.containsKey(bittorrent::METADATA);
+  SharedHandle<TorrentAttribute> torrentAttrs =
+    bittorrent::getTorrentAttrs(_requestGroup->getDownloadContext());
+  bool metadataGetMode = torrentAttrs->metadata.empty();
 
   SharedHandle<ExtensionMessageRegistry> exMsgRegistry
     (new ExtensionMessageRegistry());
@@ -189,7 +187,7 @@ PeerInteractionCommand::PeerInteractionCommand
     (getOption()->getAsInt(PREF_BT_KEEP_ALIVE_INTERVAL));
   btInteractive->setRequestGroupMan(getDownloadEngine()->getRequestGroupMan());
   btInteractive->setBtMessageFactory(factory);
-  if((metadataGetMode || torrentAttrs[bittorrent::PRIVATE].i() == 0) &&
+  if((metadataGetMode || !torrentAttrs->privateTorrent) &&
      !getPeer()->isLocalPeer()) {
     if(getOption()->getAsBool(PREF_ENABLE_PEER_EXCHANGE)) {
       btInteractive->setUTPexEnabled(true);

+ 59 - 0
src/PeerListProcessor.h

@@ -42,11 +42,70 @@
 #include "a2netcompat.h"
 #include "bencode.h"
 #include "Peer.h"
+#include "ValueBase.h"
 
 namespace aria2 {
 
 class PeerListProcessor {
+private:
+  template<typename OutputIterator>
+  class PeerListValueBaseVisitor:public ValueBaseVisitor {
+    OutputIterator dest_;
+  public:
+    PeerListValueBaseVisitor(OutputIterator dest):dest_(dest) {}
+
+    virtual void visit(const String& peerData)
+    {
+      size_t length = peerData.s().size();
+      if(length%6 == 0) {
+        const char* base = peerData.s().data();
+        for(size_t i = 0; i < length; i += 6) {
+          struct in_addr in;
+          memcpy(&in.s_addr, base+i, sizeof(uint32_t));
+          std::string ipaddr = inet_ntoa(in);
+          uint16_t port_nworder;
+          memcpy(&port_nworder, base+i+4, sizeof(uint16_t));
+          uint16_t port = ntohs(port_nworder);
+          *dest_ = SharedHandle<Peer>(new Peer(ipaddr, port));
+          ++dest_;
+        }
+      }
+    }
+
+    virtual void visit(const Integer& v) {}
+
+    virtual void visit(const List& peerData)
+    {
+      for(List::ValueType::const_iterator itr = peerData.begin(),
+            eoi = peerData.end(); itr != eoi; ++itr) {
+        const Dict* peerDict = asDict(*itr);
+        if(!peerDict) {
+          continue;
+        }
+        static const std::string IP = "ip";
+        static const std::string PORT = "port";
+        const String* ip = asString(peerDict->get(IP));
+        const Integer* port = asInteger(peerDict->get(PORT));
+        if(!ip || !port || !(0 < port->i() && port->i() < 65536)) {
+          continue;
+        }
+        *dest_ = SharedHandle<Peer>(new Peer(ip->s(), port->i()));
+        ++dest_;
+      }
+    }
+
+    virtual void visit(const Dict& v) {}
+  };
 public:
+  template<typename OutputIterator>
+  void extractPeer(const SharedHandle<ValueBase>& peerData, OutputIterator dest)
+  {
+    if(!peerData.isNull()) {
+      PeerListValueBaseVisitor<OutputIterator> visitor(dest);
+      peerData->accept(visitor);
+    }
+  }
+
   template<typename OutputIterator>
   void extractPeer(const BDE& peerData, OutputIterator dest)
   {

+ 1 - 0
src/ProtocolDetector.cc

@@ -41,6 +41,7 @@
 #include "Request.h"
 #include "File.h"
 #include "util.h"
+#include "RecoverableException.h"
 #ifdef ENABLE_BITTORRENT
 # include "bittorrent_helper.h"
 #endif // ENABLE_BITTORRENT

+ 12 - 18
src/RequestGroup.cc

@@ -202,16 +202,15 @@ void RequestGroup::createInitialCommand
 #ifdef ENABLE_BITTORRENT
   {
     if(_downloadContext->hasAttribute(bittorrent::BITTORRENT)) {
-      const BDE& torrentAttrs =
-        _downloadContext->getAttribute(bittorrent::BITTORRENT);
-      bool metadataGetMode = !torrentAttrs.containsKey(bittorrent::METADATA);
+      SharedHandle<TorrentAttribute> torrentAttrs =
+        bittorrent::getTorrentAttrs(_downloadContext);
+      bool metadataGetMode = torrentAttrs->metadata.empty();
       if(_option->getAsBool(PREF_DRY_RUN)) {
         throw DOWNLOAD_FAILURE_EXCEPTION
           ("Cancel BitTorrent download in dry-run context.");
       }
       SharedHandle<BtRegistry> btRegistry = e->getBtRegistry();
-      if(!btRegistry->getDownloadContext
-         (torrentAttrs[bittorrent::INFO_HASH].s()).isNull()) {
+      if(!btRegistry->getDownloadContext(torrentAttrs->infoHash).isNull()) {
         // TODO If metadataGetMode == false and each FileEntry has
         // URI, then go without BT.
         throw DOWNLOAD_FAILURE_EXCEPTION
@@ -337,20 +336,14 @@ void RequestGroup::createInitialCommand
       }
       _progressInfoFile = progressInfoFile;
 
-      if(torrentAttrs[bittorrent::PRIVATE].i() == 0 &&
-         _option->getAsBool(PREF_ENABLE_DHT)) {
+      if(!torrentAttrs->privateTorrent && _option->getAsBool(PREF_ENABLE_DHT)) {
         std::vector<Command*> dhtCommands;
         DHTSetup().setup(dhtCommands, e);
         e->addCommand(dhtCommands);
-        if(!torrentAttrs[bittorrent::NODES].empty() && DHTSetup::initialized()) {
-          std::vector<std::pair<std::string, uint16_t> > entryPoints;
-          const BDE& nodes = torrentAttrs[bittorrent::NODES];
-          for(BDE::List::const_iterator i = nodes.listBegin(),
-                eoi = nodes.listEnd(); i != eoi; ++i) {
-            std::pair<std::string, uint16_t> addr
-              ((*i)[bittorrent::HOSTNAME].s(), (*i)[bittorrent::PORT].i());
-            entryPoints.push_back(addr);
-          }
+        const std::vector<std::pair<std::string, uint16_t> >& nodes =
+          torrentAttrs->nodes;
+        if(!nodes.empty() && DHTSetup::initialized()) {
+          std::vector<std::pair<std::string, uint16_t> > entryPoints(nodes);
           DHTEntryPointNameResolveCommand* command =
             new DHTEntryPointNameResolveCommand(e->newCUID(), e, entryPoints);
           command->setTaskQueue(DHTRegistry::getData().taskQueue);
@@ -1094,8 +1087,9 @@ void RequestGroup::reportDownloadFinished()
     TransferStat stat = calculateStat();
     double shareRatio =
       ((stat.getAllTimeUploadLength()*10)/getCompletedLength())/10.0;
-    const BDE& attrs = _downloadContext->getAttribute(bittorrent::BITTORRENT);
-    if(attrs.containsKey(bittorrent::METADATA)) {
+    SharedHandle<TorrentAttribute> attrs =
+      bittorrent::getTorrentAttrs(_downloadContext);
+    if(!attrs->metadata.empty()) {
       _logger->notice(MSG_SHARE_RATIO_REPORT,
                       shareRatio,
                       util::abbrevSize(stat.getAllTimeUploadLength()).c_str(),

+ 1 - 0
src/RequestGroupMan.cc

@@ -70,6 +70,7 @@
 #include "FileAllocationEntry.h"
 #include "CheckIntegrityEntry.h"
 #include "Segment.h"
+#include "DlAbortEx.h"
 
 namespace aria2 {
 

+ 66 - 0
src/TorrentAttribute.h

@@ -0,0 +1,66 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_TORRENT_ATTRIBUTE_H
+#define D_TORRENT_ATTRIBUTE_H
+
+#include "ContextAttribute.h"
+
+#include <string>
+#include <vector>
+
+#include "a2time.h"
+
+struct TorrentAttribute:public ContextAttribute {
+  std::string name;
+  std::string mode;
+  std::vector<std::vector<std::string> > announceList;
+  std::vector<std::pair<std::string, uint16_t> > nodes;
+  // raw hash value 20 bytes.
+  std::string infoHash;
+  std::string metadata;
+  size_t metadataSize;
+  bool privateTorrent;
+  time_t creationDate;
+  std::string comment;
+  std::string createdBy;
+  std::vector<std::string> urlList;
+
+  TorrentAttribute():metadataSize(0),
+                     privateTorrent(false),
+                     creationDate(0) {}
+};
+
+#endif // D_TORRENT_ATTRIBUTE_H
+

+ 5 - 5
src/TrackerWatcherCommand.cc

@@ -200,15 +200,15 @@ SharedHandle<RequestGroup> TrackerWatcherCommand::createAnnounce() {
 static bool backupTrackerIsAvailable
 (const SharedHandle<DownloadContext>& context)
 {
-  const BDE& announceList =
-    context->getAttribute(bittorrent::BITTORRENT)[bittorrent::ANNOUNCE_LIST];
-  if(announceList.size() >= 2) {
+  SharedHandle<TorrentAttribute> torrentAttrs =
+    bittorrent::getTorrentAttrs(context);
+  if(torrentAttrs->announceList.size() >= 2) {
     return true;
   }
-  if(announceList.empty()) {
+  if(torrentAttrs->announceList.empty()) {
     return false;
   }
-  if(announceList[0].size() >= 2) {
+  if(torrentAttrs->announceList[0].size() >= 2) {
     return true;
   } else {
     return false;

+ 2 - 3
src/UTMetadataDataExtensionMessage.cc

@@ -85,9 +85,8 @@ void UTMetadataDataExtensionMessage::doReceivedAction()
       MessageDigestHelper::digest(infoHash, INFO_HASH_LENGTH,
                                   MessageDigestContext::SHA1,
                                   metadata.data(), metadata.size());
-      const BDE& attrs = _dctx->getAttribute(bittorrent::BITTORRENT);
-      if(std::string(&infoHash[0], &infoHash[INFO_HASH_LENGTH]) == 
-         attrs[bittorrent::INFO_HASH].s()){
+      if(memcmp(infoHash, bittorrent::getInfoHash(_dctx),
+                INFO_HASH_LENGTH) == 0) {
         _logger->info("Got ut_metadata");
       } else {
         _logger->info("Got wrong ut_metadata");

+ 4 - 4
src/UTMetadataPostDownloadHandler.cc

@@ -59,8 +59,8 @@ bool UTMetadataPostDownloadHandler::Criteria::match
   const SharedHandle<DownloadContext>& dctx =
     requestGroup->getDownloadContext();
   if(dctx->hasAttribute(bittorrent::BITTORRENT)) {
-    const BDE& attrs = dctx->getAttribute(bittorrent::BITTORRENT);
-    if(!attrs.containsKey(bittorrent::METADATA)) {
+    SharedHandle<TorrentAttribute> attrs = bittorrent::getTorrentAttrs(dctx);
+    if(attrs->metadata.empty()) {
       return true;
     }
   }
@@ -77,7 +77,7 @@ void UTMetadataPostDownloadHandler::getNextRequestGroups
 (std::vector<SharedHandle<RequestGroup> >& groups, RequestGroup* requestGroup)
 {
   const SharedHandle<DownloadContext>& dctx =requestGroup->getDownloadContext();
-  const BDE& attrs = dctx->getAttribute(bittorrent::BITTORRENT);
+  SharedHandle<TorrentAttribute> attrs = bittorrent::getTorrentAttrs(dctx);
   std::string metadata =
     util::toString(requestGroup->getPieceStorage()->getDiskAdaptor());
   std::string torrent = bittorrent::metadata2Torrent(metadata, attrs);
@@ -85,7 +85,7 @@ void UTMetadataPostDownloadHandler::getNextRequestGroups
   if(requestGroup->getOption()->getAsBool(PREF_BT_SAVE_METADATA)) {
     std::string filename =
       util::applyDir(requestGroup->getOption()->get(PREF_DIR),
-                     util::toHex(attrs[bittorrent::INFO_HASH].s())+".torrent");
+                     util::toHex(attrs->infoHash)+".torrent");
     if(util::saveAs(filename, torrent)) {
       _logger->notice(MSG_METADATA_SAVED, filename.c_str());
     } else {

+ 8 - 10
src/UTMetadataRequestExtensionMessage.cc

@@ -70,27 +70,25 @@ std::string UTMetadataRequestExtensionMessage::toString() const
 
 void UTMetadataRequestExtensionMessage::doReceivedAction()
 {
-  const BDE& attrs = _dctx->getAttribute(bittorrent::BITTORRENT);
+  SharedHandle<TorrentAttribute> attrs = bittorrent::getTorrentAttrs(_dctx);
   uint8_t id = _peer->getExtensionMessageID("ut_metadata");
-  if(!attrs.containsKey(bittorrent::METADATA)) {
+  if(attrs->metadata.empty()) {
     SharedHandle<UTMetadataRejectExtensionMessage> m
       (new UTMetadataRejectExtensionMessage(id));
     m->setIndex(getIndex());
     SharedHandle<BtMessage> msg = _messageFactory->createBtExtendedMessage(m);
     _dispatcher->addMessageToQueue(msg);
-  }else if(getIndex()*METADATA_PIECE_SIZE <
-           (size_t)attrs[bittorrent::METADATA_SIZE].i()){
+  }else if(getIndex()*METADATA_PIECE_SIZE < attrs->metadataSize) {
     SharedHandle<UTMetadataDataExtensionMessage> m
       (new UTMetadataDataExtensionMessage(id));
     m->setIndex(getIndex());
-    m->setTotalSize(attrs[bittorrent::METADATA_SIZE].i());
-    const BDE& metadata = attrs[bittorrent::METADATA];
+    m->setTotalSize(attrs->metadataSize);
     std::string::const_iterator begin =
-      metadata.s().begin()+getIndex()*METADATA_PIECE_SIZE;
+      attrs->metadata.begin()+getIndex()*METADATA_PIECE_SIZE;
     std::string::const_iterator end =
-      (getIndex()+1)*METADATA_PIECE_SIZE <= metadata.s().size()?
-      metadata.s().begin()+(getIndex()+1)*METADATA_PIECE_SIZE:
-      metadata.s().end();
+      (getIndex()+1)*METADATA_PIECE_SIZE <= attrs->metadata.size()?
+      attrs->metadata.begin()+(getIndex()+1)*METADATA_PIECE_SIZE:
+      attrs->metadata.end();
     m->setData(std::string(begin, end));
     SharedHandle<BtMessage> msg = _messageFactory->createBtExtendedMessage(m);
     _dispatcher->addMessageToQueue(msg);

+ 347 - 0
src/ValueBase.cc

@@ -0,0 +1,347 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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 "ValueBase.h"
+
+namespace aria2 {
+
+const SharedHandle<ValueBase> ValueBase::none;
+
+String::String(const ValueType& string):str_(string) {}
+
+String::String(const char* cstring):str_(cstring) {}
+
+String::String(const char* data, size_t length):str_(&data[0], &data[length]) {}
+
+String::String(const unsigned char* data, size_t length):
+  str_(&data[0], &data[length]) {}
+
+String::String() {}
+
+const String::ValueType& String::s() const
+{
+  return str_;
+}
+
+const unsigned char* String::uc() const
+{
+  return reinterpret_cast<const unsigned char*>(str_.data());
+}
+
+SharedHandle<String> String::g(const ValueType& string)
+{
+  return SharedHandle<String>(new String(string));
+}
+
+void String::accept(ValueBaseVisitor& v) const
+{
+  v.visit(*this);
+}
+
+Integer::Integer(ValueType integer):integer_(integer) {}
+
+Integer::Integer():integer_(0) {}
+
+Integer::ValueType Integer::i() const
+{
+  return integer_;
+}
+
+SharedHandle<Integer> Integer::g(ValueType integer)
+{
+  return SharedHandle<Integer>(new Integer(integer));
+}
+
+void Integer::accept(ValueBaseVisitor& v) const
+{
+  v.visit(*this);
+}
+
+List::List() {}
+
+const SharedHandle<ValueBase>& List::get(size_t index) const
+{
+  return list_[index];
+}
+
+void List::append(const SharedHandle<ValueBase>& v)
+{
+  list_.push_back(v);
+}
+
+List& List::operator<<(const SharedHandle<ValueBase>& v)
+{
+  list_.push_back(v);
+  return *this;
+}
+
+const SharedHandle<ValueBase>& List::operator[](size_t index) const
+{
+  return list_[index];
+}
+
+List::ValueType::iterator List::begin()
+{
+  return list_.begin();
+}
+
+List::ValueType::iterator List::end()
+{
+  return list_.end();
+}
+
+List::ValueType::const_iterator List::begin() const
+{
+  return list_.begin();
+}
+
+List::ValueType::const_iterator List::end() const
+{
+  return list_.end();
+}
+
+size_t List::size() const
+{
+  return list_.size();
+}
+
+bool List::empty() const
+{
+  return list_.empty();
+}
+
+SharedHandle<List> List::g()
+{
+  return SharedHandle<List>(new List());
+}
+
+void List::accept(ValueBaseVisitor& v) const
+{
+  v.visit(*this);
+}
+
+Dict::Dict() {}
+
+void Dict::put(const std::string& key, const SharedHandle<ValueBase>& vlb)
+{
+  ValueType::value_type p = std::make_pair(key, vlb);
+  std::pair<ValueType::iterator, bool> r = dict_.insert(p);
+  if(!r.second) {
+    (*r.first).second = vlb;
+  }
+}
+
+void Dict::put(const std::string& key, const String::ValueType& string)
+{
+  put(key, String::g(string));
+}
+
+const SharedHandle<ValueBase>& Dict::get(const std::string& key) const
+{
+  ValueType::const_iterator itr = dict_.find(key);
+  if(itr == dict_.end()) {
+    return ValueBase::none;
+  } else {
+    return (*itr).second;
+  }
+}
+
+SharedHandle<ValueBase>& Dict::operator[](const std::string& key)
+{
+  return dict_[key];
+}
+
+const SharedHandle<ValueBase>& Dict::operator[](const std::string& key) const
+{
+  return get(key);
+}
+
+bool Dict::containsKey(const std::string& key) const
+{
+  return dict_.count(key) == 1;
+}
+
+void Dict::removeKey(const std::string& key)
+{
+  dict_.erase(key);
+}
+
+Dict::ValueType::iterator Dict::begin()
+{
+  return dict_.begin();
+}
+
+Dict::ValueType::iterator Dict::end()
+{
+  return dict_.end();
+}
+
+Dict::ValueType::const_iterator Dict::begin() const
+{
+  return dict_.begin();
+}
+
+Dict::ValueType::const_iterator Dict::end() const
+{
+  return dict_.end();
+}
+
+size_t Dict::size() const
+{
+  return dict_.size();
+}
+
+bool Dict::empty() const
+{
+  return dict_.empty();
+}
+
+SharedHandle<Dict> Dict::g()
+{
+  return SharedHandle<Dict>(new Dict());
+}
+void Dict::accept(ValueBaseVisitor& v) const
+{
+  v.visit(*this);
+}
+
+const String* asString(const ValueBase* v)
+{
+  if(v) {
+    return downcast<String, Integer, List, Dict>(v);
+  } else {
+    return 0;
+  }
+}
+
+String* asString(ValueBase* v)
+{
+  if(v) {
+    return const_cast<String*>(downcast<String, Integer, List, Dict>(v));
+  } else {
+    return 0;
+  }
+}
+
+String* asString(const SharedHandle<ValueBase>& v)
+{
+  if(v.get()) {
+    return const_cast<String*>(downcast<String, Integer, List, Dict>(v));
+  } else {
+    return 0;
+  }
+}
+
+const Integer* asInteger(const ValueBase* v)
+{
+  if(v) {
+    return downcast<Integer, String, List, Dict>(v);
+  } else {
+    return 0;
+  }
+}
+
+Integer* asInteger(ValueBase* v)
+{
+  if(v) {
+    return const_cast<Integer*>(downcast<Integer, String, List, Dict>(v));
+  } else {
+    return 0;
+  }
+}
+
+Integer* asInteger(const SharedHandle<ValueBase>& v)
+{
+  if(v.get()) {
+    return const_cast<Integer*>(downcast<Integer, String, List, Dict>(v));
+  } else {
+    return 0;
+  }
+}
+
+const List* asList(const ValueBase* v)
+{
+  if(v) {
+    return downcast<List, String, Integer, Dict>(v);
+  } else {
+    return 0;
+  }
+}
+
+List* asList(ValueBase* v)
+{
+  if(v) {
+    return const_cast<List*>(downcast<List, String, Integer, Dict>(v));
+  } else {
+    return 0;
+  }
+}
+
+List* asList(const SharedHandle<ValueBase>& v)
+{
+  if(v.get()) {
+    return const_cast<List*>(downcast<List, String, Integer, Dict>(v));
+  } else {
+    return 0;
+  }
+}
+
+const Dict* asDict(const ValueBase* v)
+{
+  if(v) {
+    return downcast<Dict, String, Integer, List>(v);
+  } else {
+    return 0;
+  }
+}
+
+Dict* asDict(ValueBase* v)
+{
+  if(v) {
+    return const_cast<Dict*>(downcast<Dict, String, Integer, List>(v));
+  } else {
+    return 0;
+  }
+}
+
+Dict* asDict(const SharedHandle<ValueBase>& v)
+{
+  if(v.get()) {
+    return const_cast<Dict*>(downcast<Dict, String, Integer, List>(v));
+  } else {
+    return 0;
+  }
+}
+
+} // namespace aria2

+ 288 - 0
src/ValueBase.h

@@ -0,0 +1,288 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_VALUE_BASE_H
+#define D_VALUE_BASE_H
+
+#include "common.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class ValueBaseVisitor;
+
+class ValueBase {
+public:
+  virtual ~ValueBase() {}
+
+  virtual void accept(ValueBaseVisitor& visitor) const = 0;
+
+  static const SharedHandle<ValueBase> none;
+};
+
+class String;
+class Integer;
+class List;
+class Dict;
+
+class ValueBaseVisitor {
+public:
+  virtual ~ValueBaseVisitor() {}
+
+  virtual void visit(const String& string) = 0;
+
+  virtual void visit(const Integer& integer) = 0;
+
+  virtual void visit(const List& list) = 0;
+
+  virtual void visit(const Dict& dict) = 0;
+};
+
+class String:public ValueBase {
+public:
+  typedef std::string ValueType;
+
+  String(const ValueType& string);
+
+  explicit String(const char* cstring);
+
+  String(const char* data, size_t length);
+
+  String(const unsigned char* data, size_t length);
+
+  String();
+  
+  const ValueType& s() const;
+
+  // Returns std::string.data() casted to unsigned char*.
+  // Use s().size() to get length.
+  const unsigned char* uc() const;
+
+  static SharedHandle<String> g(const ValueType& string);
+
+  virtual void accept(ValueBaseVisitor& visitor) const;
+private:
+  ValueType str_;
+};
+
+class Integer:public ValueBase {
+public:
+  typedef int64_t ValueType;
+
+  Integer(ValueType integer);
+
+  Integer();
+
+  // Returns Integer.
+  ValueType i() const;
+
+  static SharedHandle<Integer> g(ValueType integer);
+
+  virtual void accept(ValueBaseVisitor& visitor) const;
+private:
+  ValueType integer_;
+};
+
+class List:public ValueBase {
+public:
+  typedef std::vector<SharedHandle<ValueBase> > ValueType;
+
+  List();
+
+  // Appends given v to list.
+  void append(const SharedHandle<ValueBase>& v);
+
+  // Alias for append()
+  List& operator<<(const SharedHandle<ValueBase>& v);
+
+  // Returns the object at given index.
+  const SharedHandle<ValueBase>& get(size_t index) const;
+
+  // Returns the const reference of the object at the given index.
+  const SharedHandle<ValueBase>& operator[](size_t index) const;
+
+  // Returns a read/write iterator that points to the first object in
+  // list.
+  ValueType::iterator begin();
+
+  // Returns a read/write iterator that points to the one past the
+  // last object in list.
+  ValueType::iterator end();
+
+  // Returns a read/write read-only iterator that points to the first
+  // object in list.
+  ValueType::const_iterator begin() const;
+
+  // Returns a read/write read-only iterator that points to the one
+  // past the last object in list.
+  ValueType::const_iterator end() const;
+
+  // Returns size of list.
+  size_t size() const;
+
+  // Returns true if size of list is 0.
+  bool empty() const;
+
+  static SharedHandle<List> g();
+
+  virtual void accept(ValueBaseVisitor& visitor) const;
+private:
+  ValueType list_;
+};
+
+class Dict:public ValueBase {
+public:
+  typedef std::map<std::string, SharedHandle<ValueBase> > ValueType;
+
+  Dict();
+
+  void put(const std::string& key, const SharedHandle<ValueBase>& vlb);
+
+  // Putting string is so common that we provide shortcut function.
+  void put(const std::string& key, const String::ValueType& string);
+
+  const SharedHandle<ValueBase>& get(const std::string& key) const;
+
+  // Returns the reference to object associated with given key.  If
+  // the key is not found, new pair with that key is created using
+  // default values, which is then returned. In other words, this is
+  // the same behavior of std::map's operator[].
+  SharedHandle<ValueBase>& operator[](const std::string& key);
+
+  // Returns the const reference to ojbect associated with given key.
+  // If the key is not found, ValueBase::none is returned.
+  const SharedHandle<ValueBase>& operator[](const std::string& key) const;
+
+  // Returns true if the given key is found in dict.
+  bool containsKey(const std::string& key) const;
+
+  // Removes specified key from dict.
+  void removeKey(const std::string& key);
+
+  // Returns a read/write iterator that points to the first pair in
+  // the dict.
+  ValueType::iterator begin();
+
+  // Returns a read/write read-only iterator that points to one past
+  // the last pair in the dict.
+  ValueType::iterator end();
+
+  // Returns a read/write read-only iterator that points to the first
+  // pair in the dict.
+  ValueType::const_iterator begin() const;
+
+  // Returns a read/write read-only iterator that points to one past
+  // the last pair in the dict.
+  ValueType::const_iterator end() const;
+
+  // Returns size of Dict.
+  size_t size() const;
+
+  // Returns true if size of Dict is 0.
+  bool empty() const;
+
+  static SharedHandle<Dict> g();
+
+  virtual void accept(ValueBaseVisitor& visitor) const;
+private:
+  ValueType dict_;
+};
+
+template<typename T, typename T1, typename T2, typename T3>
+class DowncastValueBaseVisitor:public ValueBaseVisitor {
+private:
+  const T* result_;
+public:
+  DowncastValueBaseVisitor():result_(0) {}
+
+  virtual void visit(const T& t)
+  {
+    result_ = &t;
+  }
+
+  virtual void visit(const T1& t1) {}
+  virtual void visit(const T2& t2) {}
+  virtual void visit(const T3& t3) {}
+
+  const T* getResult() const
+  {
+    return result_;
+  }
+
+  void setResult(const T* r)
+  {
+    result_ = r;
+  }
+};
+
+template<typename T, typename T1, typename T2, typename T3, typename VPtr>
+const T* downcast(const VPtr& v)
+{
+  DowncastValueBaseVisitor<T, T1, T2, T3> visitor;
+  v->accept(visitor);
+  return visitor.getResult();
+}
+
+const String* asString(const ValueBase* v);
+
+String* asString(ValueBase* v);
+
+String* asString(const SharedHandle<ValueBase>& v);
+
+const Integer* asInteger(const ValueBase* v);
+
+Integer* asInteger(ValueBase* v);
+
+Integer* asInteger(const SharedHandle<ValueBase>& v);
+
+const List* asList(const ValueBase* v);
+
+List* asList(ValueBase* v);
+
+List* asList(const SharedHandle<ValueBase>& v);
+
+const Dict* asDict(const ValueBase* v);
+
+Dict* asDict(ValueBase* v);
+
+Dict* asDict(const SharedHandle<ValueBase>& v);
+
+} // namespace aria2
+
+#endif // D_VALUE_BASE_H

+ 22 - 23
src/XmlRpcMethodImpl.cc

@@ -563,44 +563,43 @@ void gatherProgressCommon
 }
 
 #ifdef ENABLE_BITTORRENT
-void gatherBitTorrentMetadata(BDE& btDict, const BDE& torrentAttrs)
+void gatherBitTorrentMetadata
+(BDE& btDict, const SharedHandle<TorrentAttribute>& torrentAttrs)
 {
-  if(torrentAttrs.containsKey(bittorrent::COMMENT)) {
-    btDict[KEY_COMMENT] = torrentAttrs[bittorrent::COMMENT];
+  if(!torrentAttrs->comment.empty()) {
+    btDict[KEY_COMMENT] = torrentAttrs->comment;
   }
-  if(torrentAttrs.containsKey(bittorrent::CREATION_DATE)) {
-    btDict[KEY_CREATION_DATE] = torrentAttrs[bittorrent::CREATION_DATE];
+  if(torrentAttrs->creationDate) {
+    btDict[KEY_CREATION_DATE] = torrentAttrs->creationDate;
   }
-  if(torrentAttrs.containsKey(bittorrent::MODE)) {
-    btDict[KEY_MODE] = torrentAttrs[bittorrent::MODE];
+  if(!torrentAttrs->mode.empty()) {
+    btDict[KEY_MODE] = torrentAttrs->mode;
   }
-  // Copy announceList to avoid modification on entyDict to be
-  // affected original announceList.
-  // TODO Would it be good to add copy() method in BDE?
-  const BDE& announceList = torrentAttrs[bittorrent::ANNOUNCE_LIST];
   BDE destAnnounceList = BDE::list();
-  for(BDE::List::const_iterator l = announceList.listBegin(),
-        eoi = announceList.listEnd(); l != eoi; ++l) {
+  for(std::vector<std::vector<std::string> >::const_iterator l =
+        torrentAttrs->announceList.begin(),
+        eoi = torrentAttrs->announceList.end(); l != eoi; ++l) {
     BDE destAnnounceTier = BDE::list();
-    for(BDE::List::const_iterator t = (*l).listBegin(),
-          eoi2 = (*l).listEnd(); t != eoi2; ++t) {
-      destAnnounceTier <<  (*t);
+    for(std::vector<std::string>::const_iterator t = (*l).begin(),
+          eoi2 = (*l).end(); t != eoi2; ++t) {
+      destAnnounceTier <<  *t;
     }
     destAnnounceList << destAnnounceTier;
   }
   btDict[KEY_ANNOUNCE_LIST] = destAnnounceList;
-  if(torrentAttrs.containsKey(bittorrent::METADATA)) {
+  if(!torrentAttrs->metadata.empty()) {
     BDE infoDict = BDE::dict();
-    infoDict[KEY_NAME] = torrentAttrs[bittorrent::NAME];
+    infoDict[KEY_NAME] = torrentAttrs->name;
     btDict[KEY_INFO] = infoDict;
   }
 }
 
 static void gatherProgressBitTorrent
-(BDE& entryDict, const BDE& torrentAttrs, const BtObject& btObject)
+(BDE& entryDict,
+ const SharedHandle<TorrentAttribute>& torrentAttrs,
+ const BtObject& btObject)
 {
-  const std::string& infoHash = torrentAttrs[bittorrent::INFO_HASH].s();
-  entryDict[KEY_INFO_HASH] = util::toHex(infoHash);
+  entryDict[KEY_INFO_HASH] = util::toHex(torrentAttrs->infoHash);
   BDE btDict = BDE::dict();
   gatherBitTorrentMetadata(btDict, torrentAttrs);
   entryDict[KEY_BITTORRENT] = btDict;
@@ -651,8 +650,8 @@ static void gatherProgress
   gatherProgressCommon(entryDict, group);
 #ifdef ENABLE_BITTORRENT
   if(group->getDownloadContext()->hasAttribute(bittorrent::BITTORRENT)) {
-    const BDE& torrentAttrs =
-      group->getDownloadContext()->getAttribute(bittorrent::BITTORRENT);
+    SharedHandle<TorrentAttribute> torrentAttrs =
+      bittorrent::getTorrentAttrs(group->getDownloadContext());
     BtObject btObject = e->getBtRegistry()->get(group->getGID());
     gatherProgressBitTorrent(entryDict, torrentAttrs, btObject);
   }

+ 5 - 3
src/XmlRpcMethodImpl.h

@@ -43,6 +43,8 @@
 
 #include "BDE.h"
 #include "XmlRpcRequest.h"
+#include "ValueBase.h"
+#include "TorrentAttribute.h"
 
 namespace aria2 {
 
@@ -500,9 +502,9 @@ void gatherProgressCommon
 (BDE& entryDict, const SharedHandle<RequestGroup>& group);
 
 #ifdef ENABLE_BITTORRENT
-// Helper function to store BitTorrent metadata from torrentAttrs in
-// btDict. btDict must be an BDE::Dict.
-void gatherBitTorrentMetadata(BDE& btDict, const BDE& torrentAttrs);
+// Helper function to store BitTorrent metadata from torrentAttrs.
+void gatherBitTorrentMetadata
+(BDE& btDict, const SharedHandle<TorrentAttribute>& torrentAttrs);
 #endif // ENABLE_BITTORRENT
 
 } // namespace xmlrpc

+ 263 - 0
src/bencode2.cc

@@ -0,0 +1,263 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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 "bencode2.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "StringFormat.h"
+#include "DlAbortEx.h"
+
+namespace aria2 {
+
+namespace bencode2 {
+
+static SharedHandle<ValueBase> decodeiter(std::istream& ss, size_t depth);
+
+static void checkdelim(std::istream& ss, const char delim = ':')
+{
+  char d;
+  if(!(ss.get(d) && d == delim)) {
+    throw DL_ABORT_EX
+      (StringFormat("Bencode decoding failed: Delimiter '%c' not found.",
+                    delim).str());
+  }
+}
+
+static std::string decoderawstring(std::istream& ss)
+{
+  int length;
+  ss >> length;
+  if(!ss || length < 0) {
+    throw DL_ABORT_EX("Bencode decoding failed:"
+                      " A positive integer expected but none found.");
+  }
+  // TODO check length, it must be less than or equal to INT_MAX
+  checkdelim(ss);
+  char* buf = new char[length];
+  ss.read(buf, length);
+  std::string str(&buf[0], &buf[length]);
+  delete [] buf;
+  if(ss.gcount() != static_cast<int>(length)) {
+    throw DL_ABORT_EX
+      (StringFormat("Bencode decoding failed:"
+                    " Expected %lu bytes of data, but only %d read.",
+                    static_cast<unsigned long>(length), ss.gcount()).str());
+  }
+  return str;
+}
+
+static SharedHandle<ValueBase> decodestring(std::istream& ss)
+{
+  return String::g(decoderawstring(ss));
+}
+
+static SharedHandle<ValueBase> decodeinteger(std::istream& ss)
+{
+  Integer::ValueType iv;
+  ss >> iv;
+  if(!ss) {
+    throw DL_ABORT_EX("Bencode decoding failed:"
+                      " Integer expected but none found");
+  }
+  checkdelim(ss, 'e');
+  return Integer::g(iv);
+}
+
+static SharedHandle<ValueBase> decodedict(std::istream& ss, size_t depth)
+{
+  SharedHandle<Dict> dict = Dict::g();
+  char c;
+  while(ss.get(c)) {
+    if(c == 'e') {
+      return dict;
+    } else {
+      ss.unget();
+      std::string key = decoderawstring(ss);
+      dict->put(key, decodeiter(ss, depth));
+    }
+  }
+  throw DL_ABORT_EX("Bencode decoding failed:"
+                    " Unexpected EOF in dict context. 'e' expected.");
+}
+
+static SharedHandle<ValueBase> decodelist(std::istream& ss, size_t depth)
+{
+  SharedHandle<List> list = List::g();
+  char c;
+  while(ss.get(c)) {
+    if(c == 'e') {
+      return list;
+    } else {
+      ss.unget();
+      list->append(decodeiter(ss, depth));
+    }
+  }
+  throw DL_ABORT_EX("Bencode decoding failed:"
+                    " Unexpected EOF in list context. 'e' expected.");
+}
+
+static void checkDepth(size_t depth)
+{
+  if(depth >= MAX_STRUCTURE_DEPTH) {
+    throw DL_ABORT_EX("Bencode decoding failed: Structure is too deep.");
+  }
+}
+
+static SharedHandle<ValueBase> decodeiter(std::istream& ss, size_t depth)
+{
+  checkDepth(depth);
+  char c;
+  if(!ss.get(c)) {
+    throw DL_ABORT_EX("Bencode decoding failed:"
+                      " Unexpected EOF in term context."
+                      " 'd', 'l', 'i' or digit is expected.");
+  }
+  if(c == 'd') {
+    return decodedict(ss, depth+1);
+  } else if(c == 'l') {
+    return decodelist(ss, depth+1);
+  } else if(c == 'i') {
+    return decodeinteger(ss);
+  } else {
+    ss.unget();
+    return decodestring(ss);
+  }
+}
+
+SharedHandle<ValueBase> decode(std::istream& in)
+{
+  return decodeiter(in, 0);
+}
+
+SharedHandle<ValueBase> decode(const std::string& s)
+{
+  size_t end;
+  return decode(s, end);
+}
+
+SharedHandle<ValueBase> decode(const std::string& s, size_t& end)
+{
+  if(s.empty()) {
+    return SharedHandle<ValueBase>();
+  }
+  std::istringstream ss(s);
+
+  SharedHandle<ValueBase> vlb = decodeiter(ss, 0);
+  end = ss.tellg();
+  return vlb;
+}
+
+SharedHandle<ValueBase> decode(const unsigned char* data, size_t length)
+{
+  return decode(std::string(&data[0], &data[length]));
+}
+
+SharedHandle<ValueBase> decode(const unsigned char* data, size_t length, size_t& end)
+{
+  return decode(std::string(&data[0], &data[length]), end);
+}
+
+SharedHandle<ValueBase> decodeFromFile(const std::string& filename)
+{
+  std::ifstream f(filename.c_str(), std::ios::binary);
+  if(f) {
+    return decode(f);
+  } else {
+    throw DL_ABORT_EX
+      (StringFormat("Bencode decoding failed:"
+                    " Cannot open file '%s'.", filename.c_str()).str());
+  }
+}
+
+std::string encode(const ValueBase* vlb)
+{
+  class BencodeValueBaseVisitor:public ValueBaseVisitor {
+  private:
+    std::ostringstream out_;
+  public:
+    virtual void visit(const String& string)
+    {
+      const std::string& s = string.s();
+      out_ << s.size() << ":";
+      out_.write(s.data(), s.size());
+    }
+
+    virtual void visit(const Integer& integer)
+    {
+      out_ << "i" << integer.i() << "e";
+    }
+
+    virtual void visit(const List& list)
+    {
+      out_ << "l";
+      for(List::ValueType::const_iterator i = list.begin(), eoi = list.end();
+          i != eoi; ++i){
+        (*i)->accept(*this);
+      }
+      out_ << "e";
+    }
+
+    virtual void visit(const Dict& dict)
+    {
+      out_ << "d";
+      for(Dict::ValueType::const_iterator i = dict.begin(), eoi = dict.end();
+          i != eoi; ++i){
+        const std::string& key = (*i).first;
+        out_ << key.size() << ":";
+        out_.write(key.data(), key.size());
+        (*i).second->accept(*this);
+      }
+      out_ << "e";
+    }
+
+    std::string getResult() const
+    {
+      return out_.str();
+    }
+  };
+  BencodeValueBaseVisitor visitor;
+  vlb->accept(visitor);
+  return visitor.getResult();
+}
+
+std::string encode(const SharedHandle<ValueBase>& vlb)
+{
+  return encode(vlb.get());
+}
+
+} // namespace bencode2
+
+} // namespace aria2

+ 75 - 0
src/bencode2.h

@@ -0,0 +1,75 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_BENCODE2_H_
+#define _D_BENCODE2_H_
+
+#include "common.h"
+
+#include <string>
+#include <iosfwd>
+
+#include "ValueBase.h"
+
+namespace aria2 {
+
+namespace bencode2 {
+
+const size_t MAX_STRUCTURE_DEPTH = 100;
+
+SharedHandle<ValueBase> decode(std::istream& in);
+
+// Decode the data in s.
+SharedHandle<ValueBase> decode(const std::string& s);
+
+// Decode the data in s. After decode is done successfully, return the
+// bencoded string length in end.
+SharedHandle<ValueBase> decode(const std::string& s, size_t& end);
+
+SharedHandle<ValueBase> decode(const unsigned char* data, size_t length);
+
+SharedHandle<ValueBase> decode
+(const unsigned char* data, size_t length, size_t& end);
+
+SharedHandle<ValueBase> decodeFromFile(const std::string& filename);
+
+std::string encode(const ValueBase* vlb);
+
+std::string encode(const SharedHandle<ValueBase>& vlb);
+
+} // namespace bencode2
+
+} // namespace aria2
+
+#endif // _D_BENCODE2_H_

+ 261 - 202
src/bittorrent_helper.cc

@@ -54,6 +54,9 @@
 #include "bitfield.h"
 #include "base32.h"
 #include "magnet.h"
+#include "ValueBase.h"
+#include "bencode2.h"
+#include "TorrentAttribute.h"
 
 namespace aria2 {
 
@@ -147,23 +150,44 @@ static void extractPieceHash(const SharedHandle<DownloadContext>& ctx,
 }
 
 static void extractUrlList
-(BDE& torrent, std::vector<std::string>& uris, const BDE& bde)
+(const SharedHandle<TorrentAttribute>& torrent, std::vector<std::string>& uris,
+ const ValueBase* v)
 {
-  if(bde.isList()) {
-    for(BDE::List::const_iterator itr = bde.listBegin(), eoi = bde.listEnd();
-        itr != eoi; ++itr) {
-      if((*itr).isString()) {
-        uris.push_back((*itr).s());
+  class UrlListVisitor:public ValueBaseVisitor {
+  private:
+    std::vector<std::string>& uris_;
+    const SharedHandle<TorrentAttribute>& torrent_;
+  public:
+    UrlListVisitor
+    (std::vector<std::string>& uris,
+     const SharedHandle<TorrentAttribute>& torrent):
+      uris_(uris), torrent_(torrent) {}
+
+    virtual void visit(const String& v)
+    {
+      uris_.push_back(v.s());
+      torrent_->urlList.push_back(v.s());
+    }
+
+    virtual void visit(const Integer& v) {}
+
+    virtual void visit(const List& v)
+    {
+      for(List::ValueType::const_iterator itr = v.begin(), eoi = v.end();
+          itr != eoi; ++itr) {
+        const String* uri = asString(*itr);
+        if(uri) {
+          uris_.push_back(uri->s());
+          torrent_->urlList.push_back(uri->s());
+        }
       }
     }
-    torrent[URL_LIST] = bde;
-  } else if(bde.isString()) {
-    uris.push_back(bde.s());
-    BDE urlList = BDE::list();
-    urlList << bde;
-    torrent[URL_LIST] = urlList;
-  } else {
-    torrent[URL_LIST] = BDE::list();
+    virtual void visit(const Dict& v) {}
+  };
+
+  if(v) {
+    UrlListVisitor visitor(uris, torrent);
+    v->accept(visitor);
   }
 }
 
@@ -184,8 +208,8 @@ static OutputIterator createUri
 
 static void extractFileEntries
 (const SharedHandle<DownloadContext>& ctx,
- BDE& torrent,
- const BDE& infoDict,
+ const SharedHandle<TorrentAttribute>& torrent,
+ const Dict* infoDict,
  const std::string& defaultName,
  const std::string& overrideName,
  const std::vector<std::string>& urlList)
@@ -193,62 +217,71 @@ static void extractFileEntries
   std::string name;
   if(overrideName.empty()) {
     std::string nameKey;
-    if(infoDict.containsKey(C_NAME_UTF8)) {
+    if(infoDict->containsKey(C_NAME_UTF8)) {
       nameKey = C_NAME_UTF8;
     } else {
       nameKey = C_NAME;
     }
-    const BDE& nameData = infoDict[nameKey];
-    if(nameData.isString()) {
-      if(util::detectDirTraversal(nameData.s())) {
+    const String* nameData = asString(infoDict->get(nameKey));
+    if(nameData) {
+      if(util::detectDirTraversal(nameData->s())) {
         throw DL_ABORT_EX
-          (StringFormat(MSG_DIR_TRAVERSAL_DETECTED,nameData.s().c_str()).str());
+          (StringFormat
+           (MSG_DIR_TRAVERSAL_DETECTED,nameData->s().c_str()).str());
       }
-      name = nameData.s();
+      name = nameData->s();
     } else {
       name = strconcat(File(defaultName).getBasename(), ".file");
     }
   } else {
     name = overrideName;
   }
-  torrent[NAME] = name;
-
-  const BDE& filesList = infoDict[C_FILES];
+  torrent->name = name;
   std::vector<SharedHandle<FileEntry> > fileEntries;
-  if(filesList.isList()) {
-    fileEntries.reserve(filesList.size());
+  const List* filesList = asList(infoDict->get(C_FILES));
+  if(filesList) {
+    fileEntries.reserve(filesList->size());
     uint64_t length = 0;
     off_t offset = 0;
     // multi-file mode
-    torrent[MODE] = MULTI;
-    for(BDE::List::const_iterator itr = filesList.listBegin(),
-          eoi = filesList.listEnd(); itr != eoi; ++itr) {
-      const BDE& fileDict = *itr;
-      if(!fileDict.isDict()) {
+    torrent->mode = MULTI;
+    for(List::ValueType::const_iterator itr = filesList->begin(),
+          eoi = filesList->end(); itr != eoi; ++itr) {
+      const Dict* fileDict = asDict(*itr);
+      if(!fileDict) {
         continue;
       }
-      const BDE& fileLengthData = fileDict[C_LENGTH];
-      if(!fileLengthData.isInteger()) {
+      const Integer* fileLengthData = asInteger(fileDict->get(C_LENGTH));
+      if(!fileLengthData) {
         throw DL_ABORT_EX(StringFormat(MSG_MISSING_BT_INFO,
                                        C_LENGTH.c_str()).str());
       }
-      length += fileLengthData.i();
+      length += fileLengthData->i();
 
       std::string pathKey;
-      if(fileDict.containsKey(C_PATH_UTF8)) {
+      if(fileDict->containsKey(C_PATH_UTF8)) {
         pathKey = C_PATH_UTF8;
       } else {
         pathKey = C_PATH;
       }
-      const BDE& pathList = fileDict[pathKey];
-      if(!pathList.isList() || pathList.empty()) {
+      const List* pathList = asList(fileDict->get(pathKey));
+      if(!pathList || pathList->empty()) {
         throw DL_ABORT_EX("Path is empty.");
       }
       
-      std::vector<std::string> pathelem(pathList.size()+1);
+      std::vector<std::string> pathelem(pathList->size()+1);
       pathelem[0] = name;
-      std::transform(pathList.listBegin(), pathList.listEnd(),
-                     pathelem.begin()+1, std::mem_fun_ref(&BDE::s));
+      std::vector<std::string>::iterator pathelemOutItr = pathelem.begin();
+      ++pathelemOutItr;
+      for(List::ValueType::const_iterator itr = pathList->begin(),
+            eoi = pathList->end(); itr != eoi; ++itr) {
+        const String* elem = asString(*itr);
+        if(elem) {
+          (*pathelemOutItr++) = elem->s();
+        } else {
+          throw DL_ABORT_EX("Path element is not string.");
+        }
+      }
       std::string path = strjoin(pathelem.begin(), pathelem.end(), '/');
       if(util::detectDirTraversal(path)) {
         throw DL_ABORT_EX
@@ -262,7 +295,7 @@ static void extractFileEntries
       createUri(urlList.begin(), urlList.end(),std::back_inserter(uris),pePath);
       SharedHandle<FileEntry> fileEntry
         (new FileEntry(util::applyDir(ctx->getDir(), util::escapePath(path)),
-                       fileLengthData.i(),
+                       fileLengthData->i(),
                        offset, uris));
       fileEntry->setOriginalName(path);
       fileEntries.push_back(fileEntry);
@@ -270,13 +303,13 @@ static void extractFileEntries
     }
   } else {
     // single-file mode;
-    torrent[MODE] = SINGLE;
-    const BDE& lengthData = infoDict[C_LENGTH];
-    if(!lengthData.isInteger()) {
+    torrent->mode = SINGLE;
+    const Integer* lengthData = asInteger(infoDict->get(C_LENGTH));
+    if(!lengthData) {
       throw DL_ABORT_EX(StringFormat(MSG_MISSING_BT_INFO,
                                      C_LENGTH.c_str()).str());      
     }
-    uint64_t totalLength = lengthData.i();
+    uint64_t totalLength = lengthData->i();
 
     // For each uri in urlList, if it ends with '/', then
     // concatenate name to it. Specification just says so.
@@ -298,98 +331,103 @@ static void extractFileEntries
     fileEntries.push_back(fileEntry);
   }
   ctx->setFileEntries(fileEntries.begin(), fileEntries.end());
-  if(torrent[MODE].s() == MULTI) {
+  if(torrent->mode == MULTI) {
     ctx->setBasePath(util::applyDir(ctx->getDir(), name));
   }
 }
 
-static void extractAnnounce(BDE& torrent, const BDE& rootDict)
+static void extractAnnounce
+(const SharedHandle<TorrentAttribute>& torrent, const Dict* rootDict)
 {
-  const BDE& announceList = rootDict[C_ANNOUNCE_LIST];
-  if(announceList.isList()) {
-    torrent[ANNOUNCE_LIST] = announceList;
-    BDE& tiers = torrent[ANNOUNCE_LIST];
-    for(BDE::List::iterator tieriter = tiers.listBegin(),
-          eoi = tiers.listEnd(); tieriter != eoi; ++tieriter) {
-      for(BDE::List::iterator uriiter = (*tieriter).listBegin(),
-            eoi2 = (*tieriter).listEnd(); uriiter != eoi2; ++uriiter) {
-        if((*uriiter).isString()) {
-          *uriiter = util::trim((*uriiter).s());
+  const List* announceList = asList(rootDict->get(C_ANNOUNCE_LIST));
+  if(announceList) {
+    for(List::ValueType::const_iterator tierIter = announceList->begin(),
+          eoi = announceList->end(); tierIter != eoi; ++tierIter) {
+      const List* tier = asList(*tierIter);
+      if(!tier) {
+        continue;
+      }
+      std::vector<std::string> ntier;
+      for(List::ValueType::const_iterator uriIter = tier->begin(),
+            eoi2 = tier->end(); uriIter != eoi2; ++uriIter) {
+        const String* uri = asString(*uriIter);
+        if(uri) {
+          ntier.push_back(util::trim(uri->s()));
         }
       }
+      if(!ntier.empty()) {
+        torrent->announceList.push_back(ntier);
+      }
     }
   } else {
-    const BDE& announce = rootDict[C_ANNOUNCE];
-    BDE announceList = BDE::list();
-    if(announce.isString()) {
-      announceList << BDE::list();
-      announceList[0] << util::trim(announce.s());
+    const String* announce = asString(rootDict->get(C_ANNOUNCE));
+    if(announce) {
+      std::vector<std::string> tier;
+      tier.push_back(util::trim(announce->s()));
+      torrent->announceList.push_back(tier);
     }
-    torrent[ANNOUNCE_LIST] = announceList;
   }
 }
 
-static void extractNodes(BDE& torrent, const BDE& nodesList)
+static void extractNodes
+(const SharedHandle<TorrentAttribute>& torrent, const ValueBase* nodesListSrc)
 {
-  BDE nodes = BDE::list();
-  if(nodesList.isList()) {
-    for(BDE::List::const_iterator i = nodesList.listBegin(),
-          eoi = nodesList.listEnd(); i != eoi; ++i) {
-      const BDE& addrPairList = (*i);
-      if(!addrPairList.isList() || addrPairList.size() != 2) {
+  const List* nodesList = asList(nodesListSrc);
+  if(nodesList) {
+    for(List::ValueType::const_iterator i = nodesList->begin(),
+          eoi = nodesList->end(); i != eoi; ++i) {
+      const List* addrPairList = asList(*i);
+      if(!addrPairList || addrPairList->size() != 2) {
         continue;
       }
-      const BDE& hostname = addrPairList[0];
-      if(!hostname.isString()) {
+      const String* hostname = asString(addrPairList->get(0));
+      if(!hostname) {
         continue;
       }
-      if(util::trim(hostname.s()).empty()) {
+      if(util::trim(hostname->s()).empty()) {
         continue;
       }
-      const BDE& port = addrPairList[1];
-      if(!port.isInteger() || !(0 < port.i() && port.i() < 65536)) {
+      const Integer* port = asInteger(addrPairList->get(1));
+      if(!port || !(0 < port->i() && port->i() < 65536)) {
         continue;
       }
-      BDE node = BDE::dict();
-      node[HOSTNAME] = hostname;
-      node[PORT] = port;
-      nodes << node;
+      torrent->nodes.push_back(std::make_pair(hostname->s(), port->i()));
     }
   }
-  torrent[NODES] = nodes;
 }
 
 static void processRootDictionary
 (const SharedHandle<DownloadContext>& ctx,
- const BDE& rootDict,
+ const SharedHandle<ValueBase>& root,
  const std::string& defaultName,
  const std::string& overrideName,
  const std::vector<std::string>& uris)
 {
-  if(!rootDict.isDict()) {
+  const Dict* rootDict = asDict(root);
+  if(!rootDict) {
     throw DL_ABORT_EX("torrent file does not contain a root dictionary.");
   }
-  const BDE& infoDict = rootDict[C_INFO];
-  if(!infoDict.isDict()) {
+  const Dict* infoDict = asDict(rootDict->get(C_INFO));
+  if(!infoDict) {
     throw DL_ABORT_EX(StringFormat(MSG_MISSING_BT_INFO,
                                    C_INFO.c_str()).str());
   }
-  BDE torrent = BDE::dict();
+  SharedHandle<TorrentAttribute> torrent(new TorrentAttribute());
 
   // retrieve infoHash
-  std::string encodedInfoDict = bencode::encode(infoDict);
+  std::string encodedInfoDict = bencode2::encode(infoDict);
   unsigned char infoHash[INFO_HASH_LENGTH];
   MessageDigestHelper::digest(infoHash, INFO_HASH_LENGTH,
                               MessageDigestContext::SHA1,
                               encodedInfoDict.data(),
                               encodedInfoDict.size());
-  torrent[INFO_HASH] = std::string(&infoHash[0], &infoHash[INFO_HASH_LENGTH]);
-  torrent[METADATA] = encodedInfoDict;
-  torrent[METADATA_SIZE] = encodedInfoDict.size();
+  torrent->infoHash = std::string(&infoHash[0], &infoHash[INFO_HASH_LENGTH]);
+  torrent->metadata = encodedInfoDict;
+  torrent->metadataSize = encodedInfoDict.size();
 
   // calculate the number of pieces
-  const BDE& piecesData = infoDict[C_PIECES];
-  if(!piecesData.isString()) {
+  const String* piecesData = asString(infoDict->get(C_PIECES));
+  if(!piecesData) {
     throw DL_ABORT_EX(StringFormat(MSG_MISSING_BT_INFO,
                                    C_PIECES.c_str()).str());
   }
@@ -397,65 +435,68 @@ static void processRootDictionary
   //   if(piecesData.s().empty()) {
   //     throw DL_ABORT_EX("The length of piece hash is 0.");
   //   }
-  size_t numPieces = piecesData.s().size()/PIECE_HASH_LENGTH;
+  size_t numPieces = piecesData->s().size()/PIECE_HASH_LENGTH;
   // Commented out to download 0 length torrent.
   //   if(numPieces == 0) {
   //     throw DL_ABORT_EX("The number of pieces is 0.");
   //   }
   // retrieve piece length
-  const BDE& pieceLengthData = infoDict[C_PIECE_LENGTH];
-  if(!pieceLengthData.isInteger()) {
+  const Integer* pieceLengthData = asInteger(infoDict->get(C_PIECE_LENGTH));
+  if(!pieceLengthData) {
     throw DL_ABORT_EX(StringFormat(MSG_MISSING_BT_INFO,
                                    C_PIECE_LENGTH.c_str()).str());
   }
-  size_t pieceLength = pieceLengthData.i();
+  size_t pieceLength = pieceLengthData->i();
   ctx->setPieceLength(pieceLength);
   // retrieve piece hashes
-  extractPieceHash(ctx, piecesData.s(), PIECE_HASH_LENGTH, numPieces);
+  extractPieceHash(ctx, piecesData->s(), PIECE_HASH_LENGTH, numPieces);
   // private flag
-  const BDE& privateData = infoDict[C_PRIVATE];
+  const Integer* privateData = asInteger(infoDict->get(C_PRIVATE));
   int privatefg = 0;
-  if(privateData.isInteger()) {
-    if(privateData.i() == 1) {
+  if(privateData) {
+    if(privateData->i() == 1) {
       privatefg = 1;
     }
   }
-  torrent[PRIVATE] = BDE((int64_t)privatefg);
+  if(privatefg) {
+    torrent->privateTorrent = true;
+  }
   // retrieve uri-list.
   // This implemantation obeys HTTP-Seeding specification:
   // see http://www.getright.com/seedtorrent.html
   std::vector<std::string> urlList;
-  extractUrlList(torrent, urlList, rootDict[C_URL_LIST]);
+  extractUrlList(torrent, urlList, rootDict->get(C_URL_LIST).get());
   urlList.insert(urlList.end(), uris.begin(), uris.end());
   std::sort(urlList.begin(), urlList.end());
   urlList.erase(std::unique(urlList.begin(), urlList.end()), urlList.end());
 
   // retrieve file entries
-  extractFileEntries(ctx, torrent, infoDict, defaultName, overrideName, urlList);
+  extractFileEntries
+    (ctx, torrent, infoDict, defaultName, overrideName, urlList);
   if((ctx->getTotalLength()+pieceLength-1)/pieceLength != numPieces) {
     throw DL_ABORT_EX("Too few/many piece hash.");
   }
   // retrieve announce
   extractAnnounce(torrent, rootDict);
   // retrieve nodes
-  extractNodes(torrent, rootDict[C_NODES]);
+  extractNodes(torrent, rootDict->get(C_NODES).get());
 
-  const BDE& creationDate = rootDict[C_CREATION_DATE];
-  if(creationDate.isInteger()) {
-    torrent[CREATION_DATE] = creationDate;
+  const Integer* creationDate = asInteger(rootDict->get(C_CREATION_DATE));
+  if(creationDate) {
+    torrent->creationDate = creationDate->i();
   }
-  const BDE& commentUtf8 = rootDict[C_COMMENT_UTF8];
-  if(commentUtf8.isString()) {
-    torrent[COMMENT] = commentUtf8;
+  const String* commentUtf8 = asString(rootDict->get(C_COMMENT_UTF8));
+  if(commentUtf8) {
+    torrent->comment = commentUtf8->s();
   } else {
-    const BDE& comment = rootDict[C_COMMENT];
-    if(comment.isString()) {
-      torrent[COMMENT] = comment;
+    const String* comment = asString(rootDict->get(C_COMMENT));
+    if(comment) {
+      torrent->comment = comment->s();
     }
   }
-  const BDE& createdBy = rootDict[C_CREATED_BY];
-  if(createdBy.isString()) {
-    torrent[CREATED_BY] = createdBy;
+  const String* createdBy = asString(rootDict->get(C_CREATED_BY));
+  if(createdBy) {
+    torrent->createdBy = createdBy->s();
   }
 
   ctx->setAttribute(BITTORRENT, torrent);
@@ -466,7 +507,7 @@ void load(const std::string& torrentFile,
           const std::string& overrideName)
 {
   processRootDictionary(ctx,
-                        bencode::decodeFromFile(torrentFile),
+                        bencode2::decodeFromFile(torrentFile),
                         torrentFile,
                         overrideName,
                         std::vector<std::string>());
@@ -478,7 +519,7 @@ void load(const std::string& torrentFile,
           const std::string& overrideName)
 {
   processRootDictionary(ctx,
-                        bencode::decodeFromFile(torrentFile),
+                        bencode2::decodeFromFile(torrentFile),
                         torrentFile,
                         overrideName,
                         uris);
@@ -491,7 +532,7 @@ void loadFromMemory(const unsigned char* content,
                     const std::string& overrideName)
 {
   processRootDictionary(ctx,
-                        bencode::decode(content, length),
+                        bencode2::decode(content, length),
                         defaultName,
                         overrideName,
                         std::vector<std::string>());
@@ -505,7 +546,7 @@ void loadFromMemory(const unsigned char* content,
                     const std::string& overrideName)
 {
   processRootDictionary(ctx,
-                        bencode::decode(content, length),
+                        bencode2::decode(content, length),
                         defaultName,
                         overrideName,
                         uris);
@@ -518,7 +559,7 @@ void loadFromMemory(const std::string& context,
 {
   processRootDictionary
     (ctx,
-     bencode::decode(context),
+     bencode2::decode(context),
      defaultName, overrideName,
      std::vector<std::string>());
 }
@@ -531,73 +572,82 @@ void loadFromMemory(const std::string& context,
 {
   processRootDictionary
     (ctx,
-     bencode::decode(context),
+     bencode2::decode(context),
      defaultName, overrideName,
      uris);
 }
 
+SharedHandle<TorrentAttribute> getTorrentAttrs
+(const SharedHandle<DownloadContext>& dctx)
+{
+  return static_pointer_cast<TorrentAttribute>(dctx->getAttribute(BITTORRENT));
+}
+
 const unsigned char*
-getInfoHash(const SharedHandle<DownloadContext>& downloadContext)
+getInfoHash(const SharedHandle<DownloadContext>& dctx)
 {
-  return downloadContext->getAttribute(BITTORRENT)[INFO_HASH].uc();
+  return reinterpret_cast<const unsigned char*>
+    (getTorrentAttrs(dctx)->infoHash.data());
 }
 
 std::string
-getInfoHashString(const SharedHandle<DownloadContext>& downloadContext)
+getInfoHashString(const SharedHandle<DownloadContext>& dctx)
 {
-  return util::toHex(downloadContext->getAttribute(BITTORRENT)[INFO_HASH].s());
+  return util::toHex(getTorrentAttrs(dctx)->infoHash);
 }
 
 void print(std::ostream& o, const SharedHandle<DownloadContext>& dctx)
 {
-  const BDE& torrentAttrs = dctx->getAttribute(BITTORRENT);
+  SharedHandle<TorrentAttribute> torrentAttrs = getTorrentAttrs(dctx);
   o << "*** BitTorrent File Information ***" << "\n";
-  if(torrentAttrs.containsKey(COMMENT)) {
-    o << "Comment: " << torrentAttrs[COMMENT].s() << "\n";
+  if(!torrentAttrs->comment.empty()) {
+    o << "Comment: " << torrentAttrs->comment << "\n";
   }
-  if(torrentAttrs.containsKey(CREATION_DATE)) {
+  if(torrentAttrs->creationDate) {
     struct tm* staticNowtmPtr;
     char buf[26];
-    time_t t = torrentAttrs[CREATION_DATE].i();
+    time_t t = torrentAttrs->creationDate;
     if((staticNowtmPtr = localtime(&t)) != 0 &&
        asctime_r(staticNowtmPtr, buf) != 0) {
       // buf includes "\n"
       o << "Creation Date: " << buf;
     }
   }
-  if(torrentAttrs.containsKey(CREATED_BY)) {
-    o << "Created By: " << torrentAttrs[CREATED_BY].s() << "\n";
+  if(!torrentAttrs->createdBy.empty()) {
+    o << "Created By: " << torrentAttrs->createdBy << "\n";
   }
-  o << "Mode: " << torrentAttrs[MODE].s() << "\n";
+  o << "Mode: " << torrentAttrs->mode << "\n";
   o << "Announce:" << "\n";
-  const BDE& announceList = torrentAttrs[ANNOUNCE_LIST];
-  for(BDE::List::const_iterator tieritr = announceList.listBegin(),
-        eoi = announceList.listEnd(); tieritr != eoi; ++tieritr) {
-    if(!(*tieritr).isList()) {
-      continue;
-    }
-    for(BDE::List::const_iterator i = (*tieritr).listBegin(),
-          eoi2 = (*tieritr).listEnd(); i != eoi2; ++i) {
-      o << " " << (*i).s();
+  for(std::vector<std::vector<std::string> >::const_iterator tierIter =
+        torrentAttrs->announceList.begin(),
+        eoi = torrentAttrs->announceList.end(); tierIter != eoi; ++tierIter) {
+    for(std::vector<std::string>::const_iterator i = (*tierIter).begin(),
+          eoi2 = (*tierIter).end(); i != eoi2; ++i) {
+      o << " " << *i;
     }
     o << "\n";
   }
-  o << "Info Hash: " << util::toHex(torrentAttrs[INFO_HASH].s()) << "\n";
-  o << "Piece Length: " << util::abbrevSize(dctx->getPieceLength()) << "B\n";
-  o << "The Number of Pieces: " << dctx->getNumPieces() << "\n";
-  o << "Total Length: " << util::abbrevSize(dctx->getTotalLength()) << "B ("
+  o << "Info Hash: "
+    << util::toHex(torrentAttrs->infoHash) << "\n"
+    << "Piece Length: "
+    << util::abbrevSize(dctx->getPieceLength()) << "B\n"
+    << "The Number of Pieces: "
+    << dctx->getNumPieces() << "\n"
+    << "Total Length: "
+    << util::abbrevSize(dctx->getTotalLength()) << "B ("
     << util::uitos(dctx->getTotalLength(), true) << ")\n";
-  if(!torrentAttrs[URL_LIST].empty()) {
-    const BDE& urlList = torrentAttrs[URL_LIST];
+  if(!torrentAttrs->urlList.empty()) {
     o << "URL List: " << "\n";
-    for(BDE::List::const_iterator i = urlList.listBegin(),
-          eoi = urlList.listEnd(); i != eoi; ++i) {
-      o << " " << (*i).s() << "\n";
+    for(std::vector<std::string>::const_iterator i =
+          torrentAttrs->urlList.begin(),
+          eoi = torrentAttrs->urlList.end(); i != eoi; ++i) {
+      o << " " << *i << "\n";
     }
   }
-  o << "Name: " << torrentAttrs[NAME].s() << "\n";
-  o << "Magnet URI: " << torrent2Magnet(torrentAttrs) << "\n";
-  util::toStream(dctx->getFileEntries().begin(), dctx->getFileEntries().end(), o);
+  o << "Name: " << torrentAttrs->name << "\n"
+    << "Magnet URI: " << torrent2Magnet(torrentAttrs) << "\n";
+  util::toStream
+    (dctx->getFileEntries().begin(), dctx->getFileEntries().end(), o);
 }
 
 void computeFastSet
@@ -857,22 +907,23 @@ void assertID
   }
 }
 
-BDE parseMagnet(const std::string& magnet)
+SharedHandle<TorrentAttribute> parseMagnet(const std::string& magnet)
 {
-  BDE result;
-  BDE r = magnet::parse(magnet);
-  if(r.isNone()) {
+  SharedHandle<Dict> r = magnet::parse(magnet);
+  if(r.isNull()) {
     throw DL_ABORT_EX("Bad BitTorrent Magnet URI.");
   }
-  if(!r.containsKey("xt")) {
+  const List* xts = asList(r->get("xt"));
+  if(!xts) {
     throw DL_ABORT_EX("Missing xt parameter in Magnet URI.");
   }
+  SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
   std::string infoHash;
-  const BDE& xts = r["xt"];
-  for(BDE::List::const_iterator xtiter = xts.listBegin(),
-        eoi = xts.listEnd(); xtiter != eoi && infoHash.empty(); ++xtiter) {
-    if(util::startsWith((*xtiter).s(), "urn:btih:")) {
-      std::string xtarg = (*xtiter).s().substr(9);
+  for(List::ValueType::const_iterator xtiter = xts->begin(),
+        eoi = xts->end(); xtiter != eoi && infoHash.empty(); ++xtiter) {
+    const String* xt = asString(*xtiter);
+    if(util::startsWith(xt->s(), "urn:btih:")) {
+      std::string xtarg = xt->s().substr(9);
       size_t size = xtarg.size();
       if(size == 32) {
         std::string rawhash = base32::decode(xtarg);
@@ -891,74 +942,82 @@ BDE parseMagnet(const std::string& magnet)
     throw DL_ABORT_EX("Bad BitTorrent Magnet URI. "
                       "No valid BitTorrent Info Hash found.");
   }
-  BDE announceList = BDE::list();
-  if(r.containsKey("tr")) {
-    const BDE& uris = r["tr"];
-    for(BDE::List::const_iterator i = uris.listBegin(), eoi = uris.listEnd();
+  const List* trs = asList(r->get("tr"));
+  if(trs) {
+    for(List::ValueType::const_iterator i = trs->begin(), eoi = trs->end();
         i != eoi; ++i) {
-      BDE tier = BDE::list();
-      tier << *i;
-      announceList << tier;
+      std::vector<std::string> tier;
+      tier.push_back(asString(*i)->s());
+      attrs->announceList.push_back(tier);
     }
   }
   std::string name = "[METADATA]";
-  if(r.containsKey("dn") && r["dn"].size()) {
-    name += r["dn"][0].s();
+  const List* dns = asList(r->get("dn"));
+  if(dns && !dns->empty()) {
+    const String* dn = asString(dns->get(0));
+    name += dn->s();
   } else {
     name += util::toHex(infoHash);
   }
-  BDE attrs = BDE::dict();
-  attrs[INFO_HASH] = infoHash;
-  attrs[NAME] = name;
-  attrs[ANNOUNCE_LIST] = announceList;
-  result = attrs;
-  return result;
+  attrs->infoHash = infoHash;
+  attrs->name = name;
+  return attrs;
 }
 
 void loadMagnet
 (const std::string& magnet, const SharedHandle<DownloadContext>& dctx)
 {
-  BDE attrs = parseMagnet(magnet);
+  SharedHandle<TorrentAttribute> attrs = parseMagnet(magnet);
   dctx->setAttribute(BITTORRENT, attrs);
 }
 
-std::string metadata2Torrent(const std::string& metadata, const BDE& attrs)
+std::string metadata2Torrent
+(const std::string& metadata, const SharedHandle<TorrentAttribute>& attrs)
 {
   std::string torrent = "d";
-  if(attrs.containsKey(bittorrent::ANNOUNCE_LIST)) {
-    const BDE& announceList = attrs[bittorrent::ANNOUNCE_LIST];
-    if(announceList.size() > 0) {
-      torrent += "13:announce-list";
-      torrent += bencode::encode(announceList);
+
+  List announceList;
+  for(std::vector<std::vector<std::string> >::const_iterator tierIter =
+        attrs->announceList.begin(),
+        eoi = attrs->announceList.end(); tierIter != eoi; ++tierIter) {
+    SharedHandle<List> tier = List::g();
+    for(std::vector<std::string>::const_iterator uriIter = (*tierIter).begin(),
+          eoi2 = (*tierIter).end(); uriIter != eoi2; ++uriIter) {
+      tier->append(String::g(*uriIter));
+    }
+    if(!tier->empty()) {
+      announceList.append(tier);
     }
   }
+  if(!announceList.empty()) {
+    torrent += "13:announce-list";
+    torrent += bencode2::encode(&announceList);
+  }
   torrent +=
     strconcat("4:info", metadata, "e");
   return torrent;
 }
 
-std::string torrent2Magnet(const BDE& attrs)
+std::string torrent2Magnet(const SharedHandle<TorrentAttribute>& attrs)
 {
   std::string uri = "magnet:?";
-  if(attrs.containsKey(INFO_HASH)) {
+  if(!attrs->infoHash.empty()) {
     uri += "xt=urn:btih:";
-    uri += util::toUpper(util::toHex(attrs[INFO_HASH].s()));
+    uri += util::toUpper(util::toHex(attrs->infoHash));
   } else {
     return A2STR::NIL;
   }
-  if(attrs.containsKey(NAME)) {
+  if(!attrs->name.empty()) {
     uri += "&dn=";
-    uri += util::percentEncode(attrs[NAME].s());
-  }
-  if(attrs.containsKey(ANNOUNCE_LIST)) {
-    const BDE& tiers = attrs[ANNOUNCE_LIST];
-    for(BDE::List::const_iterator tieriter = tiers.listBegin(),
-          eoi = tiers.listEnd(); tieriter != eoi; ++tieriter) {
-      for(BDE::List::const_iterator uriiter = (*tieriter).listBegin(),
-            eoi2 = (*tieriter).listEnd(); uriiter != eoi2; ++uriiter) {
-        uri += "&tr=";
-        uri += util::percentEncode((*uriiter).s());
-      }
+    uri += util::percentEncode(attrs->name);
+  }
+  for(std::vector<std::vector<std::string> >::const_iterator tierIter =
+        attrs->announceList.begin(),
+        eoi = attrs->announceList.end(); tierIter != eoi; ++tierIter) {
+    for(std::vector<std::string>::const_iterator uriIter = (*tierIter).begin(),
+          eoi2 = (*tierIter).end(); uriIter != eoi2; ++uriIter) {
+      uri += "&tr=";
+      uri += util::percentEncode(*uriIter);
     }
   }
   return uri;

+ 16 - 13
src/bittorrent_helper.h

@@ -43,8 +43,8 @@
 
 #include "SharedHandle.h"
 #include "AnnounceTier.h"
-#include "BDE.h"
 #include "util.h"
+#include "TorrentAttribute.h"
 
 namespace aria2 {
 
@@ -118,15 +118,16 @@ void loadFromMemory(const std::string& context,
                     const std::string& defaultName,
                     const std::string& overrideName = "");
 
-// Parses BitTorrent Magnet URI and returns BDE::dict() which includes
-// infoHash, name and announceList. If parsing operation failed, an
-// RecoverableException will be thrown.  infoHash and name are string
-// and announceList is a list of list of announce URI.
+// Parses BitTorrent Magnet URI and returns
+// SharedHandle<TorrentAttribute> which includes infoHash, name and
+// announceList. If parsing operation failed, an RecoverableException
+// will be thrown.  infoHash and name are string and announceList is a
+// list of list of announce URI.
 //
 // magnet:?xt=urn:btih:<info-hash>&dn=<name>&tr=<tracker-url>
 // <info-hash> comes in 2 flavors: 40bytes hexadecimal ascii string,
 // or 32bytes Base32 encoded string.
-BDE parseMagnet(const std::string& magnet);
+SharedHandle<TorrentAttribute> parseMagnet(const std::string& magnet);
 
 // Parses BitTorrent Magnet URI and set them in ctx as a
 // bittorrent::BITTORRENT attibute. If parsing operation failed, an
@@ -161,6 +162,9 @@ void computeFastSet
 // Writes the detailed information about torrent loaded in dctx.
 void print(std::ostream& o, const SharedHandle<DownloadContext>& dctx);
 
+SharedHandle<TorrentAttribute> getTorrentAttrs
+(const SharedHandle<DownloadContext>& dctx);
+
 // Returns the value associated with INFO_HASH key in BITTORRENT
 // attribute.
 const unsigned char*
@@ -227,14 +231,13 @@ void assertPayloadLengthEqual
 void assertID
 (uint8_t expected, const unsigned char* data, const std::string& msgName);
 
-// Converts attrs into torrent data. attrs must be a BDE::dict.  This
-// function does not guarantee the returned string is valid torrent
-// data.
-std::string metadata2Torrent(const std::string& metadata, const BDE& attrs);
+// Converts attrs into torrent data. This function does not guarantee
+// the returned string is valid torrent data.
+std::string metadata2Torrent
+(const std::string& metadata, const SharedHandle<TorrentAttribute>& attrs);
 
-// Constructs BitTorrent Magnet URI using attrs. attrs must be a
-// BDE::dict.
-std::string torrent2Magnet(const BDE& attrs);
+// Constructs BitTorrent Magnet URI using attrs.
+std::string torrent2Magnet(const SharedHandle<TorrentAttribute>& attrs);
 
 } // namespace bittorrent
 

+ 3 - 2
src/download_helper.cc

@@ -276,8 +276,9 @@ createBtMagnetRequestGroup(const std::string& magnetLink,
   rg->setFileAllocationEnabled(false);
   rg->setPreLocalFileCheckEnabled(false);
   bittorrent::loadMagnet(magnetLink, dctx);
-  dctx->getFirstFileEntry()->setPath
-    (dctx->getAttribute(bittorrent::BITTORRENT)[bittorrent::NAME].s());
+  SharedHandle<TorrentAttribute> torrentAttrs =
+    bittorrent::getTorrentAttrs(dctx);
+  dctx->getFirstFileEntry()->setPath(torrentAttrs->name);
   rg->setDownloadContext(dctx);
   rg->clearPostDownloadHandler();
   rg->addPostDownloadHandler

+ 11 - 11
src/magnet.cc

@@ -39,31 +39,31 @@ namespace aria2 {
 
 namespace magnet {
 
-BDE parse(const std::string& magnet)
+SharedHandle<Dict> parse(const std::string& magnet)
 {
-  BDE result;
+  SharedHandle<Dict> dict;
   if(!util::startsWith(magnet, "magnet:?")) {
-    return result;
+    return dict;
   }
+  dict.reset(new Dict());
   std::vector<std::string> queries;
   util::split(std::string(magnet.begin()+8, magnet.end()),
               std::back_inserter(queries), "&");
-  BDE dict = BDE::dict();
   for(std::vector<std::string>::const_iterator i = queries.begin(),
         eoi = queries.end(); i != eoi; ++i) {
     std::pair<std::string, std::string> kv;
     util::split(kv, *i, '=');
     std::string value = util::percentDecode(kv.second);
-    if(dict.containsKey(kv.first)) {
-      dict[kv.first] << value;
+    List* l = asList(dict->get(kv.first));
+    if(l) {
+      l->append(String::g(value));
     } else {
-      BDE list = BDE::list();
-      list << value;
-      dict[kv.first] = list;
+      SharedHandle<List> l = List::g();
+      l->append(String::g(value));
+      dict->put(kv.first, l);
     }
   }
-  result = dict;
-  return result;
+  return dict;
 }
 
 } // namespace magnet

+ 7 - 6
src/magnet.h

@@ -36,17 +36,18 @@
 #define _D_MAGNET_H_
 
 #include "common.h"
-#include "BDE.h"
+#include "ValueBase.h"
 
 namespace aria2 {
 
 namespace magnet {
 
-// Parses Magnet URI magnet and stores parameters in BDE::dict().
-// Because same parameter name can appear more than once, the value
-// associated with a key is BDE::list(). A parameter value is stored
-// in a list. If parsing operation failed, BDE::none is returned.
-BDE parse(const std::string& magnet);
+// Parses Magnet URI magnet and stores parameters in
+// SharedHandle<Dict>.  Because same parameter name can appear more
+// than once, the value associated with a key is SharedHandle<List>. A
+// parameter value is stored in a list. If parsing operation failed,
+// SharedHandle<Dict>() is returned.
+SharedHandle<Dict> parse(const std::string& magnet);
 
 } // namespace magnet
 

+ 35 - 19
test/AnnounceListTest.cc

@@ -3,7 +3,7 @@
 #include <cppunit/extensions/HelperMacros.h>
 
 #include "Exception.h"
-#include "bencode.h"
+#include "bencode2.h"
 
 namespace aria2 {
 
@@ -42,13 +42,31 @@ public:
 
 CPPUNIT_TEST_SUITE_REGISTRATION( AnnounceListTest );
 
+static std::vector<std::vector<std::string> > toVector
+(const List* announceList)
+{
+  std::vector<std::vector<std::string> > dest;
+  for(List::ValueType::const_iterator tierIter = announceList->begin(),
+        eoi = announceList->end(); tierIter != eoi; ++tierIter) {
+    std::vector<std::string> ntier;
+    const List* tier = asList(*tierIter);
+    for(List::ValueType::const_iterator uriIter = tier->begin(),
+          eoi2 = tier->end(); uriIter != eoi2; ++uriIter) {
+      const String* uri = asString(*uriIter);
+      ntier.push_back(uri->s());
+    }
+    dest.push_back(ntier);
+  }
+  return dest;
+}
+
 void AnnounceListTest::testSingleElementList() {
   std::string peersString = "ll8:tracker1el8:tracker2el8:tracker3ee";
-  const BDE announcesList = bencode::decode(peersString);
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
 
   // ANNOUNCE_LIST
   // [ [ tracker1 ], [ tracker2 ], [ tracker3 ] ]
-  AnnounceList announceList(announcesList);
+  AnnounceList announceList(toVector(asList(announcesList)));
   
   CPPUNIT_ASSERT(!announceList.allTiersFailed());
   std::string url =  announceList.getAnnounce();
@@ -90,11 +108,11 @@ void AnnounceListTest::testSingleElementList() {
 
 void AnnounceListTest::testMultiElementList() {
   std::string peersString = "ll8:tracker18:tracker28:tracker3ee";
-  const BDE announcesList = bencode::decode(peersString);
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
 
   // ANNOUNCE_LIST
   // [ [ tracker1, tracker2, tracker3 ] ]
-  AnnounceList announceList(announcesList);
+  AnnounceList announceList(toVector(asList(announcesList)));
   
   CPPUNIT_ASSERT(!announceList.allTiersFailed());
   std::string url = announceList.getAnnounce();
@@ -123,11 +141,11 @@ void AnnounceListTest::testMultiElementList() {
 
 void AnnounceListTest::testSingleAndMulti() {
   std::string peersString = "ll8:tracker18:tracker2el8:tracker3ee";
-  const BDE announcesList = bencode::decode(peersString);
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
 
   // ANNOUNCE_LIST
   // [ [ tracker1, tracker2 ], [ tracker3 ] ]
-  AnnounceList announceList(announcesList);
+  AnnounceList announceList(toVector(asList(announcesList)));
 
   std::string url = announceList.getAnnounce();
   CPPUNIT_ASSERT_EQUAL(std::string("tracker1"), url);
@@ -149,20 +167,18 @@ void AnnounceListTest::testSingleAndMulti() {
 
 void AnnounceListTest::testNoGroup() {
   std::string peersString = "llee";
-  const BDE announcesList = bencode::decode(peersString);
-
-  AnnounceList announceList(announcesList);
-
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
+  AnnounceList announceList(toVector(asList(announcesList)));
   CPPUNIT_ASSERT(announceList.countTier() == 0);
 }
 
 void AnnounceListTest::testNextEventIfAfterStarted() {
   std::string peersString = "ll8:tracker1ee";
-  const BDE announcesList = bencode::decode(peersString);
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
 
   // ANNOUNCE_LIST
   // [ [ tracker1 ] ]
-  AnnounceList announceList(announcesList);
+  AnnounceList announceList(toVector(asList(announcesList)));
   announceList.setEvent(AnnounceTier::STOPPED);
   announceList.announceFailure();
   announceList.resetTier();
@@ -178,11 +194,11 @@ void AnnounceListTest::testNextEventIfAfterStarted() {
 
 void AnnounceListTest::testEvent() {
   std::string peersString = "ll8:tracker1el8:tracker2el8:tracker3ee";
-  const BDE announcesList = bencode::decode(peersString);
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
 
   // ANNOUNCE_LIST
   // [ [ tracker1 ], [ tracker2 ], [ tracker3 ] ]
-  AnnounceList announceList(announcesList);
+  AnnounceList announceList(toVector(asList(announcesList)));
 
   announceList.setEvent(AnnounceTier::STOPPED);
   announceList.announceSuccess();
@@ -202,11 +218,11 @@ void AnnounceListTest::testEvent() {
 
 void AnnounceListTest::testCountStoppedAllowedTier() {
   std::string peersString = "ll8:tracker1el8:tracker2el8:tracker3ee";
-  const BDE announcesList = bencode::decode(peersString);
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
 
   // ANNOUNCE_LIST
   // [ [ tracker1 ], [ tracker2 ], [ tracker3 ] ]
-  AnnounceList announceList(announcesList);
+  AnnounceList announceList(toVector(asList(announcesList)));
 
   CPPUNIT_ASSERT_EQUAL((size_t)0, announceList.countStoppedAllowedTier());
   announceList.setEvent(AnnounceTier::STARTED);
@@ -229,11 +245,11 @@ void AnnounceListTest::testCountStoppedAllowedTier() {
 
 void AnnounceListTest::testCountCompletedAllowedTier() {
   std::string peersString = "ll8:tracker1el8:tracker2el8:tracker3ee";
-  const BDE announcesList = bencode::decode(peersString);
+  SharedHandle<ValueBase> announcesList = bencode2::decode(peersString);
 
   // ANNOUNCE_LIST
   // [ [ tracker1 ], [ tracker2 ], [ tracker3 ] ]
-  AnnounceList announceList(announcesList);
+  AnnounceList announceList(toVector(asList(announcesList)));
 
   CPPUNIT_ASSERT_EQUAL((size_t)0, announceList.countCompletedAllowedTier());
   announceList.setEvent(AnnounceTier::STARTED);

+ 206 - 0
test/Bencode2Test.cc

@@ -0,0 +1,206 @@
+#include "bencode2.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "RecoverableException.h"
+
+namespace aria2 {
+
+class Bencode2Test:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(Bencode2Test);
+  CPPUNIT_TEST(testDecode);
+  CPPUNIT_TEST(testDecode_overflow);
+  CPPUNIT_TEST(testEncode);
+  CPPUNIT_TEST_SUITE_END();
+private:
+
+public:
+  void testDecode();
+  void testDecode_overflow();
+  void testEncode();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( Bencode2Test );
+
+void Bencode2Test::testDecode()
+{
+  {
+    // string, integer and list in dict
+    SharedHandle<ValueBase> r =
+      bencode2::decode("d4:name5:aria24:sizei12345678900e5:filesl3:bin3:docee");
+    const Dict* dict = asDict(r);
+    CPPUNIT_ASSERT(dict);
+    CPPUNIT_ASSERT_EQUAL(std::string("aria2"),
+                         asString(dict->get("name"))->s());
+    CPPUNIT_ASSERT_EQUAL(static_cast<Integer::ValueType>(12345678900LL),
+                         asInteger(dict->get("size"))->i());
+    const List* list = asList(dict->get("files"));
+    CPPUNIT_ASSERT(list);
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), list->size());
+    CPPUNIT_ASSERT_EQUAL(std::string("bin"),
+                         asString(list->get(0))->s());
+    CPPUNIT_ASSERT_EQUAL(std::string("doc"),
+                         asString(list->get(1))->s());
+  }
+  {
+    // dict in list
+    SharedHandle<ValueBase> r = bencode2::decode("ld1:ki123eee");
+    const List* list = asList(r);
+    CPPUNIT_ASSERT(list);
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), list->size());
+    const Dict* dict = asDict(list->get(0));
+    CPPUNIT_ASSERT(dict);
+    CPPUNIT_ASSERT_EQUAL(static_cast<Integer::ValueType>(123),
+                         asInteger(dict->get("k"))->i());
+  }
+  {
+    // empty key is allowed
+    SharedHandle<ValueBase> s = bencode2::decode("d0:1:ve");
+  }
+  {
+    // empty string
+    SharedHandle<ValueBase> s = bencode2::decode("0:");
+    CPPUNIT_ASSERT_EQUAL(std::string(""), asString(s)->s());
+  }
+  {
+    // empty dict
+    SharedHandle<ValueBase> d = bencode2::decode("de");
+    CPPUNIT_ASSERT(asDict(d)->empty());
+  }
+  {
+    // empty list
+    SharedHandle<ValueBase> l = bencode2::decode("le");
+    CPPUNIT_ASSERT(asList(l)->empty());
+  }
+  {
+    // integer, without ending 'e'
+    try {
+      bencode2::decode("i3");
+      CPPUNIT_FAIL("exception must be thrown.");
+    } catch(RecoverableException& e) {
+      CPPUNIT_ASSERT_EQUAL(std::string("Bencode decoding failed:"
+                                       " Delimiter 'e' not found."),
+                           std::string(e.what()));
+    }    
+  }
+  {
+    // dict, without ending 'e'
+    try {
+      bencode2::decode("d");
+      CPPUNIT_FAIL("exception must be thrown.");
+    } catch(RecoverableException& e) {
+      CPPUNIT_ASSERT_EQUAL(std::string("Bencode decoding failed:"
+                                       " Unexpected EOF in dict context."
+                                       " 'e' expected."),
+                           std::string(e.what()));
+    }          
+  }
+  {
+    // list, without ending 'e'
+    try {
+      bencode2::decode("l");
+      CPPUNIT_FAIL("exception must be thrown.");
+    } catch(RecoverableException& e) {
+      CPPUNIT_ASSERT_EQUAL(std::string("Bencode decoding failed:"
+                                       " Unexpected EOF in list context."
+                                       " 'e' expected."),
+                           std::string(e.what()));
+    }          
+  }
+  {
+    // string, less than the specified length.
+    try {
+      bencode2::decode("3:ab");
+      CPPUNIT_FAIL("exception must be thrown.");
+    } catch(RecoverableException& e) {
+      CPPUNIT_ASSERT_EQUAL(std::string("Bencode decoding failed:"
+                                       " Expected 3 bytes of data,"
+                                       " but only 2 read."),
+                           std::string(e.what()));
+    }
+  }
+  {
+    // string, but length is invalid
+    try {
+      bencode2::decode("x:abc");
+      CPPUNIT_FAIL("exception must be thrown.");
+    } catch(RecoverableException& e) {
+      CPPUNIT_ASSERT_EQUAL(std::string("Bencode decoding failed:"
+                                       " A positive integer expected"
+                                       " but none found."),
+                           std::string(e.what()));
+    }
+  }
+  {
+    // string with minus length
+    try {
+      bencode2::decode("-1:a");
+      CPPUNIT_FAIL("exception must be thrown.");
+    } catch(RecoverableException& e) {
+      CPPUNIT_ASSERT_EQUAL(std::string("Bencode decoding failed:"
+                                       " A positive integer expected"
+                                       " but none found."),
+                           std::string(e.what()));
+    }
+  }
+  {
+    // empty encoded data
+    CPPUNIT_ASSERT(bencode2::decode("").isNull());
+  }
+  {
+    // ignore trailing garbage at the end of the input.
+    SharedHandle<ValueBase> s = bencode2::decode("5:aria2trail");
+    CPPUNIT_ASSERT_EQUAL(std::string("aria2"), asString(s)->s());
+  }
+  {
+    // Get trailing garbage position
+    size_t end;
+    SharedHandle<ValueBase> s = bencode2::decode("5:aria2trail", end);
+    CPPUNIT_ASSERT_EQUAL(std::string("aria2"), asString(s)->s());
+    CPPUNIT_ASSERT_EQUAL((size_t)7, end);
+  }
+}
+
+void Bencode2Test::testDecode_overflow()
+{
+  std::string s;
+  size_t depth = bencode2::MAX_STRUCTURE_DEPTH+1;
+  for(size_t i = 0; i < depth; ++i) {
+    s += "l";
+  }
+  for(size_t i = 0; i < depth; ++i) {
+    s += "e";
+  }
+  try {
+    bencode2::decode(s);
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(RecoverableException& e) {
+    // success
+  }
+}
+
+void Bencode2Test::testEncode()
+{
+  {
+    Dict dict;
+    dict["name"] = String::g("aria2");
+    dict["loc"] = Integer::g(80000);
+    SharedHandle<List> files = List::g();
+    files->append(String::g("aria2c"));
+    dict["files"] = files;
+    SharedHandle<Dict> attrs = Dict::g();
+    attrs->put("license", String::g("GPL"));
+    dict["attrs"] = attrs;
+
+    CPPUNIT_ASSERT_EQUAL(std::string("d"
+                                     "5:attrsd7:license3:GPLe"
+                                     "5:filesl6:aria2ce"
+                                     "3:loci80000e"
+                                     "4:name5:aria2"
+                                     "e"),
+                         bencode2::encode(&dict));
+  }
+}
+
+} // namespace aria2

+ 3 - 0
test/BencodeTest.cc

@@ -1,8 +1,11 @@
 #include "bencode.h"
 
+#include <cstdlib>
+
 #include <cppunit/extensions/HelperMacros.h>
 
 #include "RecoverableException.h"
+#include "TimeA2.h"
 
 namespace aria2 {
 

+ 76 - 101
test/BittorrentHelperTest.cc

@@ -14,7 +14,7 @@
 #include "array_fun.h"
 #include "messageDigest.h"
 #include "a2netcompat.h"
-#include "bencode.h"
+#include "bencode2.h"
 #include "TestUtil.h"
 #include "base32.h"
 
@@ -111,26 +111,6 @@ public:
 
 CPPUNIT_TEST_SUITE_REGISTRATION(BittorrentHelperTest);
 
-static const BDE& getAnnounceList(const SharedHandle<DownloadContext>& dctx)
-{
-  return dctx->getAttribute(BITTORRENT)[ANNOUNCE_LIST];
-}
-
-static const std::string& getMode(const SharedHandle<DownloadContext>& dctx)
-{
-  return dctx->getAttribute(BITTORRENT)[MODE].s();
-}
-
-static const std::string& getName(const SharedHandle<DownloadContext>& dctx)
-{
-  return dctx->getAttribute(BITTORRENT)[NAME].s();
-}
-
-static const BDE& getNodes(const SharedHandle<DownloadContext>& dctx)
-{
-  return dctx->getAttribute(BITTORRENT)[NODES];
-}
-
 void BittorrentHelperTest::testGetInfoHash() {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("test.torrent", dctx);
@@ -210,21 +190,21 @@ void BittorrentHelperTest::testGetFileModeMulti() {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("test.torrent", dctx);
 
-  CPPUNIT_ASSERT_EQUAL(MULTI, getMode(dctx));
+  CPPUNIT_ASSERT_EQUAL(MULTI, getTorrentAttrs(dctx)->mode);
 }
 
 void BittorrentHelperTest::testGetFileModeSingle() {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("single.torrent", dctx);
 
-  CPPUNIT_ASSERT_EQUAL(SINGLE, getMode(dctx));
+  CPPUNIT_ASSERT_EQUAL(SINGLE, getTorrentAttrs(dctx)->mode);
 }
 
 void BittorrentHelperTest::testGetNameMulti() {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("test.torrent", dctx);
 
-  CPPUNIT_ASSERT_EQUAL(std::string("aria2-test"), getName(dctx));
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2-test"), getTorrentAttrs(dctx)->name);
 }
 
 void BittorrentHelperTest::testGetNameSingle() {
@@ -233,7 +213,8 @@ void BittorrentHelperTest::testGetNameSingle() {
 
   CPPUNIT_ASSERT_EQUAL(std::string("./aria2-0.8.2.tar.bz2"),
                        dctx->getBasePath());
-  CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.8.2.tar.bz2"), getName(dctx));
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.8.2.tar.bz2"),
+                       getTorrentAttrs(dctx)->name);
 }
 
 void BittorrentHelperTest::testOverrideName()
@@ -242,46 +223,41 @@ void BittorrentHelperTest::testOverrideName()
   load("test.torrent", dctx, "aria2-override.name");
   CPPUNIT_ASSERT_EQUAL(std::string("./aria2-override.name"),
                        dctx->getBasePath());
-  CPPUNIT_ASSERT_EQUAL(std::string("aria2-override.name"), getName(dctx));
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2-override.name"),
+                       getTorrentAttrs(dctx)->name);
 }
 
 
 void BittorrentHelperTest::testGetAnnounceTier() {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("single.torrent", dctx);
-
-  const BDE& announceList = getAnnounceList(dctx);
-  
+  SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
   // There is 1 tier.
-  CPPUNIT_ASSERT_EQUAL((size_t)1, announceList.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->announceList.size());
 
-  const BDE& tier = announceList[0]; 
-  CPPUNIT_ASSERT_EQUAL((size_t)1, tier.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->announceList[0].size());
   CPPUNIT_ASSERT_EQUAL(std::string("http://aria.rednoah.com/announce.php"),
-                       tier[0].s());
-
+                       attrs->announceList[0][0]);
 }
 
 void BittorrentHelperTest::testGetAnnounceTierAnnounceList() {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("test.torrent", dctx);
-
-  const BDE& announceList = getAnnounceList(dctx);
-  
+  SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
   // There are 3 tiers.
-  CPPUNIT_ASSERT_EQUAL((size_t)3, announceList.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)3, attrs->announceList.size());
 
-  const BDE& tier1 = announceList[0];
-  CPPUNIT_ASSERT_EQUAL((size_t)1, tier1.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker1"), tier1[0].s());
+  CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->announceList[0].size());
+  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker1"),
+                       attrs->announceList[0][0]);
 
-  const BDE& tier2 = announceList[1];
-  CPPUNIT_ASSERT_EQUAL((size_t)1, tier2.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker2"), tier2[0].s());
+  CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->announceList[1].size());
+  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker2"),
+                       attrs->announceList[1][0]);
 
-  const BDE& tier3 = announceList[2];
-  CPPUNIT_ASSERT_EQUAL((size_t)1, tier3.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker3"), tier3[0].s());
+  CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->announceList[2].size());
+  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker3"),
+                       attrs->announceList[2][0]);
 }
 
 void BittorrentHelperTest::testGetPieceLength() {
@@ -440,7 +416,8 @@ void BittorrentHelperTest::testLoadFromMemory_overrideName()
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   loadFromMemory(memory, dctx, "default", "aria2-override.name");
 
-  CPPUNIT_ASSERT_EQUAL(std::string("aria2-override.name"), getName(dctx));
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2-override.name"),
+                       getTorrentAttrs(dctx)->name);
 }
 
 void BittorrentHelperTest::testLoadFromMemory_multiFileDirTraversal()
@@ -486,12 +463,12 @@ void BittorrentHelperTest::testGetNodes()
     SharedHandle<DownloadContext> dctx(new DownloadContext());
     loadFromMemory(memory, dctx, "default");
 
-    const BDE& nodes = getNodes(dctx);
-    CPPUNIT_ASSERT_EQUAL((size_t)2, nodes.size());
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), nodes[0][HOSTNAME].s());
-    CPPUNIT_ASSERT_EQUAL((uint16_t)6881, (uint16_t)nodes[0][PORT].i());
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), nodes[1][HOSTNAME].s());
-    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, (uint16_t)nodes[1][PORT].i());
+    SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
+    CPPUNIT_ASSERT_EQUAL((size_t)2, attrs->nodes.size());
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), attrs->nodes[0].first);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6881, attrs->nodes[0].second);
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), attrs->nodes[1].first);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, attrs->nodes[1].second);
   }
   {
     // empty hostname
@@ -506,10 +483,10 @@ void BittorrentHelperTest::testGetNodes()
     SharedHandle<DownloadContext> dctx(new DownloadContext());
     loadFromMemory(memory, dctx, "default");
 
-    const BDE& nodes = getNodes(dctx);
-    CPPUNIT_ASSERT_EQUAL((size_t)1, nodes.size());
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), nodes[0][HOSTNAME].s());
-    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, (uint16_t)nodes[0][PORT].i());
+    SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
+    CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->nodes.size());
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), attrs->nodes[0].first);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, attrs->nodes[0].second);
   }
   {
     // bad port 
@@ -524,10 +501,10 @@ void BittorrentHelperTest::testGetNodes()
     SharedHandle<DownloadContext> dctx(new DownloadContext());
     loadFromMemory(memory, dctx, "default");
 
-    const BDE& nodes = getNodes(dctx);
-    CPPUNIT_ASSERT_EQUAL((size_t)1, nodes.size());
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), nodes[0][HOSTNAME].s());
-    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, (uint16_t)nodes[0][PORT].i());
+    SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
+    CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->nodes.size());
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), attrs->nodes[0].first);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, attrs->nodes[0].second);
   }
   {
     // port missing
@@ -542,10 +519,10 @@ void BittorrentHelperTest::testGetNodes()
     SharedHandle<DownloadContext> dctx(new DownloadContext());
     loadFromMemory(memory, dctx, "default");
 
-    const BDE& nodes = getNodes(dctx);
-    CPPUNIT_ASSERT_EQUAL((size_t)1, nodes.size());
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), nodes[0][HOSTNAME].s());
-    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, (uint16_t)nodes[0][PORT].i());
+    SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
+    CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->nodes.size());
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), attrs->nodes[0].first);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, attrs->nodes[0].second);
   }
   {
     // nodes is not a list
@@ -559,7 +536,8 @@ void BittorrentHelperTest::testGetNodes()
     SharedHandle<DownloadContext> dctx(new DownloadContext());
     loadFromMemory(memory, dctx, "default");
 
-    CPPUNIT_ASSERT_EQUAL((size_t)0, getNodes(dctx).size());
+    SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
+    CPPUNIT_ASSERT_EQUAL((size_t)0, attrs->nodes.size());
   }
   {
     // the element of node is not Data
@@ -574,10 +552,10 @@ void BittorrentHelperTest::testGetNodes()
     SharedHandle<DownloadContext> dctx(new DownloadContext());
     loadFromMemory(memory, dctx, "default");
 
-    const BDE& nodes = getNodes(dctx);
-    CPPUNIT_ASSERT_EQUAL((size_t)1, nodes.size());
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), nodes[0][HOSTNAME].s());
-    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, (uint16_t)nodes[0][PORT].i());
+    SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
+    CPPUNIT_ASSERT_EQUAL((size_t)1, attrs->nodes.size());
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.2"), attrs->nodes[0].first);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6882, attrs->nodes[0].second);
   }
 }
 
@@ -643,11 +621,12 @@ void BittorrentHelperTest::testUTF8Torrent()
 {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("utf8.torrent", dctx);
-  CPPUNIT_ASSERT_EQUAL(std::string("name in utf-8"), getName(dctx));
+  CPPUNIT_ASSERT_EQUAL(std::string("name in utf-8"),
+                       getTorrentAttrs(dctx)->name);
   CPPUNIT_ASSERT_EQUAL(std::string("./name in utf-8/path in utf-8"),
                        dctx->getFirstFileEntry()->getPath());
   CPPUNIT_ASSERT_EQUAL(std::string("This is utf8 comment."),
-                       dctx->getAttribute(BITTORRENT)[COMMENT].s());
+                       getTorrentAttrs(dctx)->comment);
 }
 
 void BittorrentHelperTest::testEtc()
@@ -655,11 +634,11 @@ void BittorrentHelperTest::testEtc()
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("test.torrent", dctx);
   CPPUNIT_ASSERT_EQUAL(std::string("REDNOAH.COM RULES"),
-                       dctx->getAttribute(BITTORRENT)[COMMENT].s());
+                       getTorrentAttrs(dctx)->comment);
   CPPUNIT_ASSERT_EQUAL(std::string("aria2"),
-                       dctx->getAttribute(BITTORRENT)[CREATED_BY].s());
+                       getTorrentAttrs(dctx)->createdBy);
   CPPUNIT_ASSERT_EQUAL((int64_t)1123456789,
-                       dctx->getAttribute(BITTORRENT)[CREATION_DATE].i());
+                       getTorrentAttrs(dctx)->creationDate);
 }
 
 void BittorrentHelperTest::testCreatecompact()
@@ -698,13 +677,12 @@ void BittorrentHelperTest::testMetadata() {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("test.torrent", dctx);
   std::string torrentData = readFile("test.torrent");
-  BDE tr = bencode::decode(torrentData);
-  BDE infoDic = tr["info"];
-  std::string metadata = bencode::encode(infoDic);
-  const BDE& attrs = dctx->getAttribute(bittorrent::BITTORRENT);
-  CPPUNIT_ASSERT(metadata == attrs[bittorrent::METADATA].s());
-  CPPUNIT_ASSERT_EQUAL(metadata.size(),
-                       (size_t)attrs[bittorrent::METADATA_SIZE].i());
+  SharedHandle<ValueBase> tr = bencode2::decode(torrentData);
+  SharedHandle<ValueBase> infoDic = asDict(tr)->get("info");
+  std::string metadata = bencode2::encode(infoDic);
+  SharedHandle<TorrentAttribute> attrs = getTorrentAttrs(dctx);
+  CPPUNIT_ASSERT(metadata == attrs->metadata);
+  CPPUNIT_ASSERT_EQUAL(metadata.size(), attrs->metadataSize);
 }
 
 void BittorrentHelperTest::testParseMagnet()
@@ -712,29 +690,28 @@ void BittorrentHelperTest::testParseMagnet()
   std::string magnet =
     "magnet:?xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c&dn=aria2"
     "&tr=http://tracker1&tr=http://tracker2";
-  BDE attrs = bittorrent::parseMagnet(magnet);
+  SharedHandle<TorrentAttribute> attrs = bittorrent::parseMagnet(magnet);
   CPPUNIT_ASSERT_EQUAL(std::string("248d0a1cd08284299de78d5c1ed359bb46717d8c"),
-                       util::toHex(attrs[bittorrent::INFO_HASH].s()));
-  CPPUNIT_ASSERT_EQUAL(std::string("[METADATA]aria2"),
-                       attrs[bittorrent::NAME].s());
-  CPPUNIT_ASSERT_EQUAL((size_t)2, attrs[bittorrent::ANNOUNCE_LIST].size());
+                       util::toHex(attrs->infoHash));
+  CPPUNIT_ASSERT_EQUAL(std::string("[METADATA]aria2"), attrs->name);
+  CPPUNIT_ASSERT_EQUAL((size_t)2, attrs->announceList.size());
   CPPUNIT_ASSERT_EQUAL(std::string("http://tracker1"),
-                       attrs[bittorrent::ANNOUNCE_LIST][0][0].s());
+                       attrs->announceList[0][0]);
   CPPUNIT_ASSERT_EQUAL(std::string("http://tracker2"),
-                       attrs[bittorrent::ANNOUNCE_LIST][1][0].s());
+                       attrs->announceList[1][0]);
 
   magnet = "magnet:?xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c";
   attrs = bittorrent::parseMagnet(magnet);
   CPPUNIT_ASSERT_EQUAL
     (std::string("[METADATA]248d0a1cd08284299de78d5c1ed359bb46717d8c"),
-     attrs[bittorrent::NAME].s());
-  CPPUNIT_ASSERT(attrs[bittorrent::ANNOUNCE_LIST].size() == 0);
+     attrs->name);
+  CPPUNIT_ASSERT(attrs->announceList.empty());
 
   magnet = "magnet:?xt=urn:sha1:7899bdb90a026c746f3cbc10839dd9b2a2a3e985&"
     "xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c";
   attrs = bittorrent::parseMagnet(magnet);
   CPPUNIT_ASSERT_EQUAL(std::string("248d0a1cd08284299de78d5c1ed359bb46717d8c"),
-                       util::toHex(attrs[bittorrent::INFO_HASH].s()));
+                       util::toHex(attrs->infoHash));
 }
 
 void BittorrentHelperTest::testParseMagnet_base32()
@@ -742,22 +719,20 @@ void BittorrentHelperTest::testParseMagnet_base32()
   std::string infoHash = "248d0a1cd08284299de78d5c1ed359bb46717d8c";
   std::string base32InfoHash = base32::encode(util::fromHex(infoHash));
   std::string magnet = "magnet:?xt=urn:btih:"+base32InfoHash+"&dn=aria2";
-  BDE attrs = bittorrent::parseMagnet(magnet);
+  SharedHandle<TorrentAttribute> attrs = bittorrent::parseMagnet(magnet);
   CPPUNIT_ASSERT_EQUAL
     (std::string("248d0a1cd08284299de78d5c1ed359bb46717d8c"),
-     util::toHex(attrs[bittorrent::INFO_HASH].s()));
+     util::toHex(attrs->infoHash));
 }
 
 void BittorrentHelperTest::testMetadata2Torrent()
 {
   std::string metadata = "METADATA";
-  BDE attrs = BDE::dict();
-  BDE announceList = BDE::list();
-  attrs[ANNOUNCE_LIST] = announceList;
+  SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
   CPPUNIT_ASSERT_EQUAL
     (std::string("d4:infoMETADATAe"), metadata2Torrent(metadata, attrs));
-  announceList << BDE::list();
-  announceList[0] << std::string("http://localhost/announce");
+  attrs->announceList.push_back(std::vector<std::string>());
+  attrs->announceList[0].push_back("http://localhost/announce");
   CPPUNIT_ASSERT_EQUAL
     (std::string("d"
                  "13:announce-list"
@@ -778,7 +753,7 @@ void BittorrentHelperTest::testTorrent2Magnet()
                  "&tr=http%3A%2F%2Ftracker1"
                  "&tr=http%3A%2F%2Ftracker2"
                  "&tr=http%3A%2F%2Ftracker3"),
-     torrent2Magnet(dctx->getAttribute(BITTORRENT)));
+     torrent2Magnet(getTorrentAttrs(dctx)));
 }
 
 } // namespace bittorrent

+ 1 - 1
test/BtDependencyTest.cc

@@ -179,7 +179,7 @@ void BtDependencyTest::testResolve_metadata()
   pieceStorage->setDiskAdaptor(diskAdaptor);
   pieceStorage->setDownloadFinished(true);
   dependee->setPieceStorage(pieceStorage);
-  BDE attrs = BDE::dict();
+  SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
   dependee->getDownloadContext()->setAttribute(bittorrent::BITTORRENT, attrs);
 
   BtDependency dep(dependant, dependee);

+ 4 - 4
test/BtRegistryTest.cc

@@ -63,10 +63,10 @@ void BtRegistryTest::testGetDownloadContext_infoHash()
 {
   BtRegistry btRegistry;
   addTwoDownloadContext(btRegistry);
-  BDE attrs1 = BDE::dict();
-  attrs1[bittorrent::INFO_HASH] = std::string("hash1");
-  BDE attrs2 = BDE::dict();
-  attrs2[bittorrent::INFO_HASH] = std::string("hash2");
+  SharedHandle<TorrentAttribute> attrs1(new TorrentAttribute());
+  attrs1->infoHash = "hash1";
+  SharedHandle<TorrentAttribute> attrs2(new TorrentAttribute());
+  attrs2->infoHash = "hash2";
   btRegistry.getDownloadContext(1)->setAttribute
     (bittorrent::BITTORRENT, attrs1);
   btRegistry.getDownloadContext(2)->setAttribute

+ 39 - 28
test/DefaultBtAnnounceTest.cc

@@ -56,9 +56,8 @@ public:
     std::string peerId = "-aria2-ultrafastdltl";
 
     _dctx.reset(new DownloadContext(pieceLength, totalLength));
-    BDE torrentAttrs = BDE::dict();
-    torrentAttrs[bittorrent::INFO_HASH] =
-      std::string(vbegin(infoHash), vend(infoHash));
+    SharedHandle<TorrentAttribute> torrentAttrs(new TorrentAttribute());
+    torrentAttrs->infoHash = std::string(vbegin(infoHash), vend(infoHash));
     _dctx->setAttribute(bittorrent::BITTORRENT, torrentAttrs);
     bittorrent::setStaticPeerId(peerId);
 
@@ -97,34 +96,46 @@ public:
 CPPUNIT_TEST_SUITE_REGISTRATION(DefaultBtAnnounceTest);
 
 template<typename InputIterator>
-static BDE createAnnounceTier(InputIterator first, InputIterator last)
+static SharedHandle<List> createAnnounceTier
+(InputIterator first, InputIterator last)
 {
-  BDE announceTier = BDE::list();
+  SharedHandle<List> announceTier = List::g();
   for(; first != last; ++first) {
-    announceTier << BDE(*first);
+    announceTier->append(String::g(*first));
   }
   return announceTier;
 }
 
-static BDE createAnnounceTier(const std::string& uri)
+static SharedHandle<List> createAnnounceTier(const std::string& uri)
 {
-  BDE announceTier = BDE::list();
-  announceTier << uri;
+  SharedHandle<List> announceTier = List::g();
+  announceTier->append(String::g(uri));
   return announceTier;
 }
 
 static void setAnnounceList(const SharedHandle<DownloadContext>& dctx,
-                            const BDE& announceList)
+                            const SharedHandle<List>& announceList)
 {
-  dctx->getAttribute(bittorrent::BITTORRENT)[bittorrent::ANNOUNCE_LIST] =
-    announceList;
+  std::vector<std::vector<std::string> > dest;
+  for(List::ValueType::const_iterator tierIter = announceList->begin(),
+        eoi = announceList->end(); tierIter != eoi; ++tierIter) {
+    std::vector<std::string> ntier;
+    const List* tier = asList(*tierIter);
+    for(List::ValueType::const_iterator uriIter = tier->begin(),
+          eoi2 = tier->end(); uriIter != eoi2; ++uriIter) {
+      const String* uri = asString(*uriIter);
+      ntier.push_back(uri->s());
+    }
+    dest.push_back(ntier);
+  }
+  bittorrent::getTorrentAttrs(dctx)->announceList.swap(dest);
 }
 
 void DefaultBtAnnounceTest::testNoMoreAnnounce()
 {
-  BDE announceList = BDE::list();
-  announceList << createAnnounceTier("http://localhost/announce");
-  announceList << createAnnounceTier("http://backup/announce");
+  SharedHandle<List> announceList = List::g();
+  announceList->append(createAnnounceTier("http://localhost/announce"));
+  announceList->append(createAnnounceTier("http://backup/announce"));
 
   setAnnounceList(_dctx, announceList);
 
@@ -172,8 +183,8 @@ void DefaultBtAnnounceTest::testNoMoreAnnounce()
 void DefaultBtAnnounceTest::testGetAnnounceUrl()
 {
 
-  BDE announceList = BDE::list();
-  announceList << createAnnounceTier("http://localhost/announce");  
+  SharedHandle<List> announceList = List::g();
+  announceList->append(createAnnounceTier("http://localhost/announce"));
   setAnnounceList(_dctx, announceList);
 
   DefaultBtAnnounce btAnnounce(_dctx, _option);
@@ -203,8 +214,8 @@ void DefaultBtAnnounceTest::testGetAnnounceUrl()
 
 void DefaultBtAnnounceTest::testGetAnnounceUrl_withQuery()
 {
-  BDE announceList = BDE::list();
-  announceList << createAnnounceTier("http://localhost/announce?k=v");
+  SharedHandle<List> announceList = List::g();
+  announceList->append(createAnnounceTier("http://localhost/announce?k=v"));
   setAnnounceList(_dctx, announceList);
 
   DefaultBtAnnounce btAnnounce(_dctx, _option);
@@ -225,8 +236,8 @@ void DefaultBtAnnounceTest::testGetAnnounceUrl_withQuery()
 
 void DefaultBtAnnounceTest::testGetAnnounceUrl_externalIP()
 {
-  BDE announceList = BDE::list();
-  announceList << createAnnounceTier("http://localhost/announce");
+  SharedHandle<List> announceList = List::g();
+  announceList->append(createAnnounceTier("http://localhost/announce"));
   setAnnounceList(_dctx, announceList);
 
   _option->put(PREF_BT_EXTERNAL_IP, "192.168.1.1");
@@ -248,9 +259,9 @@ void DefaultBtAnnounceTest::testGetAnnounceUrl_externalIP()
 
 void DefaultBtAnnounceTest::testIsAllAnnounceFailed()
 {
-  BDE announceList = BDE::list();
-  announceList << createAnnounceTier("http://localhost/announce");
-  announceList << createAnnounceTier("http://backup/announce");
+  SharedHandle<List> announceList = List::g();
+  announceList->append(createAnnounceTier("http://localhost/announce"));
+  announceList->append(createAnnounceTier("http://backup/announce"));
   setAnnounceList(_dctx, announceList);
 
   DefaultBtAnnounce btAnnounce(_dctx, _option);
@@ -281,8 +292,8 @@ void DefaultBtAnnounceTest::testURLOrderInStoppedEvent()
   const char* urls[] = { "http://localhost1/announce",
                          "http://localhost2/announce" };
 
-  BDE announceList = BDE::list();
-  announceList << createAnnounceTier(vbegin(urls), vend(urls));
+  SharedHandle<List> announceList = List::g();
+  announceList->append(createAnnounceTier(vbegin(urls), vend(urls)));
   setAnnounceList(_dctx, announceList);
 
   DefaultBtAnnounce btAnnounce(_dctx, _option);
@@ -311,8 +322,8 @@ void DefaultBtAnnounceTest::testURLOrderInCompletedEvent()
   const char* urls[] = { "http://localhost1/announce",
                          "http://localhost2/announce" };
 
-  BDE announceList = BDE::list();
-  announceList << createAnnounceTier(vbegin(urls), vend(urls));
+  SharedHandle<List> announceList = List::g();
+  announceList->append(createAnnounceTier(vbegin(urls), vend(urls)));
   setAnnounceList(_dctx, announceList);
 
   DefaultBtAnnounce btAnnounce(_dctx, _option);

+ 2 - 3
test/DefaultBtProgressInfoFileTest.cc

@@ -70,9 +70,8 @@ public:
     };
   
     _dctx.reset(new DownloadContext());
-    BDE torrentAttrs = BDE::dict();
-    torrentAttrs[bittorrent::INFO_HASH] =
-      std::string(vbegin(infoHash), vend(infoHash));
+    SharedHandle<TorrentAttribute> torrentAttrs(new TorrentAttribute());
+    torrentAttrs->infoHash = std::string(vbegin(infoHash), vend(infoHash));
     _dctx->setAttribute(bittorrent::BITTORRENT, torrentAttrs);
     _dctx->setDir(_option->get(PREF_DIR));
     const SharedHandle<FileEntry> fileEntries[] = {

+ 2 - 2
test/HandshakeExtensionMessageTest.cc

@@ -96,7 +96,7 @@ void HandshakeExtensionMessageTest::testDoReceivedAction()
   RequestGroup rg(op);
   rg.setDownloadContext(dctx);
 
-  BDE attrs = BDE::dict();
+  SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
   dctx->setAttribute(bittorrent::BITTORRENT, attrs);
   dctx->markTotalLengthIsUnknown();
 
@@ -117,7 +117,7 @@ void HandshakeExtensionMessageTest::testDoReceivedAction()
   CPPUNIT_ASSERT_EQUAL((uint16_t)6889, peer->getPort());
   CPPUNIT_ASSERT_EQUAL((uint8_t)1, peer->getExtensionMessageID("ut_pex"));
   CPPUNIT_ASSERT_EQUAL((uint8_t)2, peer->getExtensionMessageID("a2_dht"));
-  CPPUNIT_ASSERT_EQUAL((int64_t)1024, attrs[bittorrent::METADATA_SIZE].i());
+  CPPUNIT_ASSERT_EQUAL((size_t)1024, attrs->metadataSize);
   CPPUNIT_ASSERT_EQUAL((uint64_t)1024, dctx->getTotalLength());
   CPPUNIT_ASSERT(dctx->knowsTotalLength());
 }

+ 2 - 3
test/MSEHandshakeTest.cc

@@ -33,9 +33,8 @@ public:
     _dctx.reset(new DownloadContext());
     unsigned char infoHash[20];
     memset(infoHash, 0, sizeof(infoHash));
-    BDE torrentAttrs = BDE::dict();
-    torrentAttrs[bittorrent::INFO_HASH] =
-      std::string(vbegin(infoHash), vend(infoHash));
+    SharedHandle<TorrentAttribute> torrentAttrs(new TorrentAttribute());
+    torrentAttrs->infoHash = std::string(vbegin(infoHash), vend(infoHash));
     _dctx->setAttribute(bittorrent::BITTORRENT, torrentAttrs);
   }
 

+ 11 - 7
test/MagnetTest.cc

@@ -20,19 +20,23 @@ public:
 
 CPPUNIT_TEST_SUITE_REGISTRATION(MagnetTest);
 
+static const std::string& nthStr(const SharedHandle<ValueBase>& v, size_t index)
+{
+  return asString(asList(v)->get(index))->s();
+}
+
 void MagnetTest::testParse()
 {
-  BDE r = parse
+  SharedHandle<Dict> r = parse
     ("magnet:?xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c&dn=aria2"
      "&tr=http%3A%2F%2Ftracker1&tr=http://tracker2");
   CPPUNIT_ASSERT_EQUAL
     (std::string("urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c"),
-     r["xt"][0].s());
-  CPPUNIT_ASSERT_EQUAL(std::string("aria2"), r["dn"][0].s());
-  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker1"), r["tr"][0].s());
-  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker2"), r["tr"][1].s());
-
-  CPPUNIT_ASSERT(parse("http://localhost").isNone());
+     nthStr(r->get("xt"), 0));
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2"), nthStr(r->get("dn"), 0));
+  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker1"), nthStr(r->get("tr"), 0));
+  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker2"), nthStr(r->get("tr"), 1));
+  CPPUNIT_ASSERT(parse("http://localhost").isNull());
 }
 
 } // namespace magnet

+ 3 - 1
test/Makefile.am

@@ -72,7 +72,9 @@ aria2c_SOURCES = AllTest.cc\
 	bitfieldTest.cc\
 	BDETest.cc\
 	DownloadContextTest.cc\
-	SessionSerializerTest.cc
+	SessionSerializerTest.cc\
+	ValueBaseTest.cc\
+	Bencode2Test.cc
 
 if ENABLE_XML_RPC
 aria2c_SOURCES += XmlRpcRequestParserControllerTest.cc\

+ 7 - 3
test/Makefile.in

@@ -214,6 +214,7 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 	InOrderPieceSelector.h LongestSequencePieceSelectorTest.cc \
 	a2algoTest.cc bitfieldTest.cc BDETest.cc \
 	DownloadContextTest.cc SessionSerializerTest.cc \
+	ValueBaseTest.cc Bencode2Test.cc \
 	XmlRpcRequestParserControllerTest.cc \
 	XmlRpcRequestProcessorTest.cc XmlRpcMethodTest.cc \
 	FallocFileAllocationIteratorTest.cc GZipDecoderTest.cc \
@@ -408,6 +409,7 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) TestUtil.$(OBJEXT) \
 	LongestSequencePieceSelectorTest.$(OBJEXT) \
 	a2algoTest.$(OBJEXT) bitfieldTest.$(OBJEXT) BDETest.$(OBJEXT) \
 	DownloadContextTest.$(OBJEXT) SessionSerializerTest.$(OBJEXT) \
+	ValueBaseTest.$(OBJEXT) Bencode2Test.$(OBJEXT) \
 	$(am__objects_1) $(am__objects_2) $(am__objects_3) \
 	$(am__objects_4) $(am__objects_5) $(am__objects_6) \
 	$(am__objects_7)
@@ -644,9 +646,9 @@ aria2c_SOURCES = AllTest.cc TestUtil.cc TestUtil.h SocketCoreTest.cc \
 	InOrderPieceSelector.h LongestSequencePieceSelectorTest.cc \
 	a2algoTest.cc bitfieldTest.cc BDETest.cc \
 	DownloadContextTest.cc SessionSerializerTest.cc \
-	$(am__append_1) $(am__append_2) $(am__append_3) \
-	$(am__append_4) $(am__append_5) $(am__append_6) \
-	$(am__append_7)
+	ValueBaseTest.cc Bencode2Test.cc $(am__append_1) \
+	$(am__append_2) $(am__append_3) $(am__append_4) \
+	$(am__append_5) $(am__append_6) $(am__append_7)
 
 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
 #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
@@ -752,6 +754,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BNodeTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Base32Test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Base64Test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Bencode2Test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BencodeTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitfieldManTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BittorrentHelperTest.Po@am__quote@
@@ -899,6 +902,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTPexExtensionMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UriListParserTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UtilTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ValueBaseTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XORCloserTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcMethodTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcRequestParserControllerTest.Po@am__quote@

+ 1 - 0
test/RequestGroupManTest.cc

@@ -14,6 +14,7 @@
 #include "ServerStat.h"
 #include "File.h"
 #include "array_fun.h"
+#include "RecoverableException.h"
 
 namespace aria2 {
 

+ 2 - 3
test/UTMetadataDataExtensionMessageTest.cc

@@ -73,7 +73,7 @@ void UTMetadataDataExtensionMessageTest::testDoReceivedAction()
   SharedHandle<UTMetadataRequestTracker> tracker
     (new UTMetadataRequestTracker());
   SharedHandle<DownloadContext> dctx(new DownloadContext());
-  BDE attrs = BDE::dict();
+  SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
 
   std::string piece0 = std::string(METADATA_PIECE_SIZE, '0');
   std::string piece1 = std::string(METADATA_PIECE_SIZE, '1');
@@ -83,8 +83,7 @@ void UTMetadataDataExtensionMessageTest::testDoReceivedAction()
   MessageDigestHelper::digest(infoHash, INFO_HASH_LENGTH,
                               MessageDigestContext::SHA1,
                               metadata.data(), metadata.size());
-  attrs[bittorrent::INFO_HASH] = std::string(&infoHash[0], &infoHash[20]);
-
+  attrs->infoHash = std::string(&infoHash[0], &infoHash[20]);
   dctx->setAttribute(bittorrent::BITTORRENT, attrs);
 
   UTMetadataDataExtensionMessage m(1);

+ 18 - 16
test/UTMetadataPostDownloadHandlerTest.cc

@@ -51,13 +51,13 @@ void UTMetadataPostDownloadHandlerTest::testCanHandle()
 
   CPPUNIT_ASSERT(!handler.canHandle(_requestGroup.get()));
 
-  BDE attrs = BDE::dict();
+  SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
   _dctx->setAttribute(bittorrent::BITTORRENT, attrs);
 
   CPPUNIT_ASSERT(handler.canHandle(_requestGroup.get()));
 
-  // Only checks existence of METADATA key
-  attrs[bittorrent::METADATA] = A2STR::NIL;
+  // Only checks whether metadata is empty or not
+  attrs->metadata = "metadata";
 
   CPPUNIT_ASSERT(!handler.canHandle(_requestGroup.get()));
 }
@@ -76,12 +76,13 @@ void UTMetadataPostDownloadHandlerTest::testGetNextRequestGroups()
     (infoHash, sizeof(infoHash), MessageDigestContext::SHA1,
      reinterpret_cast<const unsigned char*>(metadata.data()), metadata.size());
   _dctx->getFirstFileEntry()->setLength(metadata.size());
-  BDE attrs = BDE::dict();
-  attrs[bittorrent::INFO_HASH] = std::string(&infoHash[0], &infoHash[20]);
-  BDE announceList = BDE::list();
-  announceList << BDE::list();
-  announceList[0] << std::string("http://tracker");
-  attrs[bittorrent::ANNOUNCE_LIST] = announceList;
+  SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
+  attrs->infoHash = std::string(&infoHash[0], &infoHash[20]);
+  std::vector<std::vector<std::string> > announceList;
+  std::vector<std::string> announceTier;
+  announceTier.push_back("http://tracker");
+  announceList.push_back(announceTier);
+  attrs->announceList = announceList;
   _dctx->setAttribute(bittorrent::BITTORRENT, attrs);
   _requestGroup->setDiskWriterFactory
     (SharedHandle<DiskWriterFactory>(new ByteArrayDiskWriterFactory()));
@@ -97,13 +98,14 @@ void UTMetadataPostDownloadHandlerTest::testGetNextRequestGroups()
   CPPUNIT_ASSERT_EQUAL((size_t)1, results.size());
   SharedHandle<RequestGroup> newRg = results.front();
   SharedHandle<DownloadContext> newDctx = newRg->getDownloadContext();
-  const BDE& newAttrs = newDctx->getAttribute(bittorrent::BITTORRENT);
-  CPPUNIT_ASSERT_EQUAL(util::toHex(attrs[bittorrent::INFO_HASH].s()),
-                       util::toHex(newAttrs[bittorrent::INFO_HASH].s()));
-  CPPUNIT_ASSERT(newAttrs.containsKey(bittorrent::ANNOUNCE_LIST));
-  CPPUNIT_ASSERT_EQUAL((size_t)1, newAttrs[bittorrent::ANNOUNCE_LIST].size());
-  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker"),
-                       newAttrs[bittorrent::ANNOUNCE_LIST][0][0].s());
+  SharedHandle<TorrentAttribute> newAttrs =
+    bittorrent::getTorrentAttrs(newDctx);
+  CPPUNIT_ASSERT_EQUAL(bittorrent::getInfoHashString(_dctx),
+                       bittorrent::getInfoHashString(newDctx));
+  const std::vector<std::vector<std::string> >& newAnnounceList =
+    newAttrs->announceList;
+  CPPUNIT_ASSERT_EQUAL((size_t)1, newAnnounceList.size());
+  CPPUNIT_ASSERT_EQUAL(std::string("http://tracker"), newAnnounceList[0][0]);
   CPPUNIT_ASSERT_EQUAL(_option->get("Hello"),
                        newRg->getOption()->get("Hello"));
   CPPUNIT_ASSERT

+ 7 - 6
test/UTMetadataRequestExtensionMessageTest.cc

@@ -15,6 +15,7 @@
 #include "UTMetadataDataExtensionMessage.h"
 #include "PieceStorage.h"
 #include "extension_message_test_helper.h"
+#include "DlAbortEx.h"
 
 namespace aria2 {
 
@@ -39,7 +40,7 @@ public:
     _messageFactory.reset(new WrapExtBtMessageFactory());
     _dispatcher.reset(new MockBtMessageDispatcher());
     _dctx.reset(new DownloadContext());
-    BDE attrs = BDE::dict();
+    SharedHandle<TorrentAttribute> attrs(new TorrentAttribute());
     _dctx->setAttribute(bittorrent::BITTORRENT, attrs);
     _peer.reset(new Peer("host", 6880));
     _peer->allocateSessionResource(0, 0);
@@ -124,11 +125,11 @@ void UTMetadataRequestExtensionMessageTest::testDoReceivedAction_data()
   msg.setBtMessageDispatcher(_dispatcher);
 
   size_t metadataSize = METADATA_PIECE_SIZE*2;
-  BDE& attrs = _dctx->getAttribute(bittorrent::BITTORRENT);
+  SharedHandle<TorrentAttribute> attrs = bittorrent::getTorrentAttrs(_dctx);
   std::string first(METADATA_PIECE_SIZE, '0');
   std::string second(METADATA_PIECE_SIZE, '1');
-  attrs[bittorrent::METADATA] = first+second;
-  attrs[bittorrent::METADATA_SIZE] = metadataSize;
+  attrs->metadata = first+second;
+  attrs->metadataSize = metadataSize;
 
   msg.doReceivedAction();
 
@@ -147,8 +148,8 @@ void UTMetadataRequestExtensionMessageTest::testDoReceivedAction_data()
 
   metadataSize += 100;
   std::string third(100, '2');
-  attrs[bittorrent::METADATA] = attrs[bittorrent::METADATA].s()+third;
-  attrs[bittorrent::METADATA_SIZE] = metadataSize;
+  attrs->metadata = first+second+third;
+  attrs->metadataSize = metadataSize;
 
   msg.doReceivedAction();
 

+ 182 - 0
test/ValueBaseTest.cc

@@ -0,0 +1,182 @@
+#include "ValueBase.h"
+
+#include <cstring>
+#include <iostream>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Exception.h"
+#include "util.h"
+
+namespace aria2 {
+
+class ValueBaseTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(ValueBaseTest);
+  CPPUNIT_TEST(testString);
+  CPPUNIT_TEST(testDict);
+  CPPUNIT_TEST(testDictIter);
+  CPPUNIT_TEST(testList);
+  CPPUNIT_TEST(testListIter);
+  CPPUNIT_TEST(testDowncast);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void setUp() {}
+
+  void tearDown() {}
+
+  void testString();
+  void testDict();
+  void testDictIter();
+  void testList();
+  void testListIter();
+  void testDowncast();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ValueBaseTest);
+
+void ValueBaseTest::testString()
+{
+  String s(std::string("aria2"));
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2"), s.s());
+
+  unsigned char dataWithNull[] = { 0xf0, '\0', 0x0f };
+  String sWithNull(dataWithNull, sizeof(dataWithNull));
+  CPPUNIT_ASSERT(memcmp(dataWithNull, sWithNull.s().c_str(),
+                        sizeof(dataWithNull)) == 0);
+
+  String zero("");
+  CPPUNIT_ASSERT_EQUAL(std::string(""), zero.s());
+
+  String z;
+  CPPUNIT_ASSERT_EQUAL(std::string(""), z.s());
+
+  const unsigned char uc[] = { 0x08, 0x19, 0x2a, 0x3b };
+  String data(uc, sizeof(uc));
+  CPPUNIT_ASSERT_EQUAL(util::toHex(uc, sizeof(uc)),
+                       util::toHex(data.uc(), data.s().size()));
+}
+
+void ValueBaseTest::testDowncast()
+{
+  Integer integer(100);
+  const Integer* x = asInteger(&integer);
+  CPPUNIT_ASSERT(x);
+  CPPUNIT_ASSERT_EQUAL(static_cast<Integer::ValueType>(100), x->i());
+  SharedHandle<Integer> si(new Integer(101));
+  const Integer* x2 = asInteger(si);
+  CPPUNIT_ASSERT_EQUAL(static_cast<Integer::ValueType>(101), x2->i());
+
+  String str("foo");
+  const String* x3 = asString(&str);
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("foo"), x3->s());
+
+  List list;
+  const List* x4 = asList(&list);
+  CPPUNIT_ASSERT(x4);
+
+  Dict dict;
+  const Dict* x5 = asDict(&dict);
+  CPPUNIT_ASSERT(x5);
+}
+
+void ValueBaseTest::testDict()
+{
+  Dict dict;
+  CPPUNIT_ASSERT(dict.empty());
+
+  dict["ki"] = Integer::g(7);
+  dict["ks"] = String::g("abc");
+
+  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), dict.size());
+  CPPUNIT_ASSERT(dict.containsKey("ki"));
+  CPPUNIT_ASSERT_EQUAL(static_cast<Integer::ValueType>(7),
+                       asInteger(dict["ki"])->i());
+  CPPUNIT_ASSERT(dict.containsKey("ks"));
+  CPPUNIT_ASSERT_EQUAL(std::string("abc"),
+                       asString(dict["ks"])->s());
+
+  CPPUNIT_ASSERT(dict["kn"].isNull()); // This adds kn key with default value.
+  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), dict.size());
+  CPPUNIT_ASSERT(dict.containsKey("kn"));
+
+  const Dict& ref = dict;
+  ref["kn2"]; // This doesn't add kn2 key.
+  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), ref.size());
+  CPPUNIT_ASSERT(!ref.containsKey("kn2"));
+
+  dict.removeKey("kn");
+  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), dict.size());
+  CPPUNIT_ASSERT(!dict.containsKey("kn"));
+}
+
+void ValueBaseTest::testDictIter()
+{
+  Dict dict;
+  dict["alpha2"] = String::g("alpha2");
+  dict["charlie"] = String::g("charlie");
+  dict["bravo"] = String::g("bravo");
+  dict["alpha"] = String::g("alpha");
+
+  Dict::ValueType::iterator i = dict.begin();
+  CPPUNIT_ASSERT_EQUAL(std::string("alpha"), (*i++).first); 
+  CPPUNIT_ASSERT_EQUAL(std::string("alpha2"), (*i++).first);
+  CPPUNIT_ASSERT_EQUAL(std::string("bravo"), (*i++).first);
+  CPPUNIT_ASSERT_EQUAL(std::string("charlie"), (*i++).first);
+  CPPUNIT_ASSERT(dict.end() == i);
+
+  const Dict& ref = dict;
+  Dict::ValueType::const_iterator ci = ref.begin();
+  CPPUNIT_ASSERT_EQUAL(std::string("alpha"), (*ci++).first);
+  std::advance(ci, 3);
+  CPPUNIT_ASSERT(ref.end() == ci);
+}
+
+void ValueBaseTest::testList()
+{
+  List list;
+  CPPUNIT_ASSERT(list.empty());
+  list << Integer::g(7) << String::g("aria2");
+
+  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), list.size());
+  CPPUNIT_ASSERT_EQUAL(static_cast<Integer::ValueType>(7),
+                       asInteger(list[0])->i());
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("aria2"),
+                       asString(list[1])->s());
+
+  const List& ref = list;
+  CPPUNIT_ASSERT_EQUAL(static_cast<Integer::ValueType>(7),
+                       asInteger(ref[0])->i());
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("aria2"),
+                       asString(ref[1])->s());
+}
+
+void ValueBaseTest::testListIter()
+{
+  List list;
+  list << String::g("alpha2")
+       << String::g("charlie")
+       << String::g("bravo")
+       << String::g("alpha");
+
+  List::ValueType::iterator i = list.begin();
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("alpha2"),
+                       asString(*i++)->s());
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("charlie"),
+                       asString(*i++)->s());
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("bravo"),
+                       asString(*i++)->s());
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("alpha"),
+                       asString(*i++)->s());
+  CPPUNIT_ASSERT(list.end() == i);
+
+  const List& ref = list;
+  List::ValueType::const_iterator ci = ref.begin();
+  CPPUNIT_ASSERT_EQUAL(static_cast<String::ValueType>("alpha2"),
+                       asString(*ci++)->s());
+  std::advance(ci, 3);
+  CPPUNIT_ASSERT(ref.end() == ci);
+}
+
+} // namespace aria2

+ 6 - 6
test/XmlRpcMethodTest.cc

@@ -745,7 +745,7 @@ void XmlRpcMethodTest::testGatherBitTorrentMetadata()
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   bittorrent::load("test.torrent", dctx);
   BDE btDict = BDE::dict();
-  gatherBitTorrentMetadata(btDict, dctx->getAttribute(bittorrent::BITTORRENT));
+  gatherBitTorrentMetadata(btDict, bittorrent::getTorrentAttrs(dctx));
   CPPUNIT_ASSERT_EQUAL(std::string("REDNOAH.COM RULES"), btDict["comment"].s());
   CPPUNIT_ASSERT_EQUAL((int64_t)1123456789, btDict["creationDate"].i());
   CPPUNIT_ASSERT_EQUAL(std::string("multi"), btDict["mode"].s());
@@ -756,11 +756,11 @@ void XmlRpcMethodTest::testGatherBitTorrentMetadata()
   CPPUNIT_ASSERT_EQUAL(std::string("http://tracker2"), announceList[1][0].s());
   CPPUNIT_ASSERT_EQUAL(std::string("http://tracker3"), announceList[2][0].s());
   // Remove some keys
-  BDE modBtAttrs = dctx->getAttribute(bittorrent::BITTORRENT);
-  modBtAttrs.removeKey(bittorrent::COMMENT);
-  modBtAttrs.removeKey(bittorrent::CREATION_DATE);
-  modBtAttrs.removeKey(bittorrent::MODE);
-  modBtAttrs.removeKey(bittorrent::METADATA);
+  SharedHandle<TorrentAttribute> modBtAttrs = bittorrent::getTorrentAttrs(dctx);
+  modBtAttrs->comment.clear();
+  modBtAttrs->creationDate = 0;
+  modBtAttrs->mode.clear();
+  modBtAttrs->metadata.clear();
   btDict = BDE::dict();
   gatherBitTorrentMetadata(btDict, modBtAttrs);
   CPPUNIT_ASSERT(!btDict.containsKey("comment"));