Index: libtorrent/test/Makefile.am
===================================================================
--- libtorrent/test/Makefile.am	(revision 1134)
+++ libtorrent/test/Makefile.am	(working copy)
@@ -7,6 +7,8 @@
 	torrent/object_test.h \
 	torrent/object_test_utils.cc \
 	torrent/object_test_utils.h \
+	torrent/object_static_map_test.cc \
+	torrent/object_static_map_test.h \
 	torrent/object_stream_test.cc \
 	torrent/object_stream_test.h \
 	main.cc
Index: libtorrent/test/torrent/object_static_map_test.h
===================================================================
--- libtorrent/test/torrent/object_static_map_test.h	(revision 0)
+++ libtorrent/test/torrent/object_static_map_test.h	(revision 0)
@@ -0,0 +1,30 @@
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "torrent/object.h"
+
+class ObjectStaticMapTest : public CppUnit::TestFixture {
+  CPPUNIT_TEST_SUITE(ObjectStaticMapTest);
+  CPPUNIT_TEST(test_basics);
+  CPPUNIT_TEST(test_write);
+  CPPUNIT_TEST(test_read);
+
+  CPPUNIT_TEST(test_read_empty);
+  CPPUNIT_TEST(test_read_single);
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+  void setUp() {}
+  void tearDown() {}
+
+  void test_basics();
+  void test_write();
+
+  void test_read();
+
+  // Proper unit tests:
+  void test_read_empty();
+  void test_read_single();
+
+  void test_write_empty();
+};
+
Index: libtorrent/test/torrent/object_stream_test.cc
===================================================================
--- libtorrent/test/torrent/object_stream_test.cc	(revision 1134)
+++ libtorrent/test/torrent/object_stream_test.cc	(working copy)
@@ -61,3 +61,21 @@
 
   object_write_bencode_c(&object_write_to_invalidate, NULL, buffer, &obj);
 }
+
+static const char* single_level_bencode = "d1:ai1e1:bi2e1:cl1:ai1e1:bi2eee";
+
+void
+ObjectStreamTest::testReadBencodeC() {
+  torrent::Object orderedObj   = create_bencode_c(ordered_bencode);
+  torrent::Object unorderedObj = create_bencode_c(unordered_bencode);
+
+  CPPUNIT_ASSERT(!(orderedObj.flags() & torrent::Object::flag_unordered));
+  CPPUNIT_ASSERT(unorderedObj.flags() & torrent::Object::flag_unordered);
+  CPPUNIT_ASSERT(compare_bencode(orderedObj, ordered_bencode));
+
+  //  torrent::Object single_level = create_bencode_c(single_level_bencode);
+  torrent::Object single_level = create_bencode_c(single_level_bencode);
+
+  CPPUNIT_ASSERT(compare_bencode(single_level, single_level_bencode));
+}
+
Index: libtorrent/test/torrent/object_stream_test.h
===================================================================
--- libtorrent/test/torrent/object_stream_test.h	(revision 1134)
+++ libtorrent/test/torrent/object_stream_test.h	(working copy)
@@ -8,6 +8,7 @@
   CPPUNIT_TEST(testInputNullKey);
   CPPUNIT_TEST(testOutputMask);
   CPPUNIT_TEST(testBuffer);
+  CPPUNIT_TEST(testReadBencodeC);
   CPPUNIT_TEST_SUITE_END();
 
 public:
@@ -18,5 +19,7 @@
   void testInputNullKey();
   void testOutputMask();
   void testBuffer();
+
+  void testReadBencodeC();
 };
 
Index: libtorrent/test/torrent/object_test_utils.cc
===================================================================
--- libtorrent/test/torrent/object_test_utils.cc	(revision 1134)
+++ libtorrent/test/torrent/object_test_utils.cc	(working copy)
@@ -15,7 +15,22 @@
   return obj;
 }
 
+torrent::Object
+create_bencode_c(const char* str) {
+  torrent::Object obj;
+  const char* last = str + strlen(str);
+
+  CPPUNIT_ASSERT(object_read_bencode_c(str, last, &obj) == last);
+  return obj;
+}
+
 bool
+validate_bencode(const char* first, const char* last) {
+  torrent::Object obj;
+  return object_read_bencode_c(first, last, &obj) == last;
+}
+
+bool
 compare_bencode(const torrent::Object& obj, const char* str, uint32_t skip_mask) {
   char buffer[256];
   std::memset(buffer, 0, 256);
Index: libtorrent/test/torrent/object_test_utils.h
===================================================================
--- libtorrent/test/torrent/object_test_utils.h	(revision 1134)
+++ libtorrent/test/torrent/object_test_utils.h	(working copy)
@@ -1,5 +1,8 @@
 #include "torrent/object.h"
 
 torrent::Object create_bencode(const char* str);
+torrent::Object create_bencode_c(const char* str);
 
+bool validate_bencode(const char* first, const char* last);
+
 bool compare_bencode(const torrent::Object& obj, const char* str, uint32_t skip_mask = 0);
Index: libtorrent/test/torrent/object_static_map_test.cc
===================================================================
--- libtorrent/test/torrent/object_static_map_test.cc	(revision 0)
+++ libtorrent/test/torrent/object_static_map_test.cc	(revision 0)
@@ -0,0 +1,201 @@
+#include "config.h"
+
+#include <torrent/object.h>
+#include <torrent/object_stream.h>
+#include "torrent/object_static_map.h"
+
+#import "object_test_utils.h"
+#import "object_static_map_test.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ObjectStaticMapTest);
+
+// Possible bencode keys in a DHT message.
+enum keys {
+  key_d_a,
+  key_d_d_a,
+  key_d_b,
+
+  key_e_0,
+  key_e_1,
+  key_e_2,
+
+  key_s_a,
+  key_s_b,
+  key_v_a,
+  key_v_b,
+
+  key_LAST,
+};
+
+enum keys_2 {
+  key_2_d_x_y,
+  key_2_d_x_z,
+  key_2_l_x_1,
+  key_2_l_x_2,
+  key_2_s_a,
+  key_2_v_a,
+  key_2_LAST
+};
+
+typedef torrent::static_map_type<keys, key_LAST> test_map_type;
+typedef torrent::static_map_type<keys_2, key_2_LAST> test_map_2_type;
+
+// List of all possible keys we need/support in a DHT message.
+// Unsupported keys we receive are dropped (ignored) while decoding.
+// See torrent/static_map.h for how this works.
+template <>
+const test_map_type::key_list_type test_map_type::keys = {
+  { key_d_a,          "d_a::b" },
+  { key_d_d_a,        "d_a::c::a" },
+  { key_d_b,          "d_a::d" },
+
+  { key_e_0,          "e[]" },
+  { key_e_1,          "e[]" },
+  { key_e_2,          "e[]" },
+
+  { key_s_a,          "s_a" },
+  { key_s_b,          "s_b" },
+
+  { key_v_a,          "v_a" },
+  { key_v_b,          "v_b" },
+};
+
+template <>
+const test_map_2_type::key_list_type test_map_2_type::keys = {
+  { key_2_d_x_y,        "d_x::f" },
+  { key_2_d_x_z,        "d_x::g" },
+  { key_2_l_x_1,        "l_x[]" },
+  { key_2_l_x_2,        "l_x[]" },
+  { key_2_s_a,          "s_a" },
+  { key_2_v_a,          "v_a" },
+};
+
+void
+ObjectStaticMapTest::test_basics() {
+  test_map_type test_map;
+
+  test_map[key_v_a] = int64_t(1);
+  test_map[key_v_b] = int64_t(2);
+ 
+  test_map[key_s_a] = std::string("a");
+  test_map[key_s_b] = std::string("b");
+
+  CPPUNIT_ASSERT(test_map[key_v_a].as_value() == 1);
+  CPPUNIT_ASSERT(test_map[key_v_b].as_value() == 2);
+  CPPUNIT_ASSERT(test_map[key_s_a].as_string() == "a");
+  CPPUNIT_ASSERT(test_map[key_s_b].as_string() == "b");
+}
+
+void
+ObjectStaticMapTest::test_write() {
+  test_map_type test_map;
+
+  test_map[key_v_a] = int64_t(1);
+  test_map[key_v_b] = int64_t(2);
+ 
+  test_map[key_s_a] = std::string("a");
+  test_map[key_s_b] = std::string("b");
+
+  test_map[key_d_a] = std::string("xx");
+  test_map[key_d_d_a] = std::string("a");
+  test_map[key_d_b] = std::string("yy");
+
+  test_map[key_e_0] = std::string("f");
+  test_map[key_e_1] = std::string("g");
+  test_map[key_e_2] = std::string("h");
+
+  char buffer[1024];
+
+  torrent::object_buffer_t result = torrent::staticMap_write_bencode_c(torrent::object_write_to_buffer,
+                                                                       NULL,
+                                                                       std::make_pair(buffer, buffer + sizeof(buffer)),
+                                                                       test_map);
+
+  std::cout << "static map write: '" << std::string(buffer, std::distance(buffer, result.first)) << "'" << std::endl;
+
+  CPPUNIT_ASSERT(validate_bencode(buffer, result.first));
+}
+
+static const char* test_read_bencode = "d3:d_xd1:fi3e1:gli4ei5eee3:l_xli1ei2ei3ee3:s_a1:a3:v_ai2ee";
+static const char* test_read_skip_bencode = "d3:d_xd1:fi3e1:gli4ei5eee1:fi1e3:l_xli1ei2ei3ee3:s_a1:a3:v_ai2ee";
+
+// TODO:
+// - Test very long keys, etc.
+
+#define STATIC_MAP_READ_BENCODE_ASSERT(map, input)                           \
+  CPPUNIT_ASSERT(torrent::staticMap_read_bencode(input, input + strlen(input), map) == input + strlen(input));
+
+template <typename map_type>
+bool static_map_read_bencode_exception(map_type map, const char* str) {
+  try {
+    torrent::staticMap_read_bencode(str, str + strlen(str), map) == str + strlen(str);
+    return false;
+  } catch (torrent::bencode_error&) { return true; }
+}
+
+void
+ObjectStaticMapTest::test_read() {
+  test_map_2_type test_map;
+
+  STATIC_MAP_READ_BENCODE_ASSERT(test_map, test_read_bencode);
+  CPPUNIT_ASSERT(test_map[key_2_d_x_y].as_value() == 3);
+  CPPUNIT_ASSERT(test_map[key_2_d_x_z].as_list().size() == 2);
+  CPPUNIT_ASSERT(test_map[key_2_l_x_1].as_value() == 1);
+  CPPUNIT_ASSERT(test_map[key_2_l_x_2].as_value() == 2);
+  CPPUNIT_ASSERT(test_map[key_2_s_a].as_string() == "a");
+  CPPUNIT_ASSERT(test_map[key_2_v_a].as_value() == 2);
+
+  test_map_2_type test_skip_map;
+
+  STATIC_MAP_READ_BENCODE_ASSERT(test_skip_map, test_read_skip_bencode);
+}
+
+//
+// Proper unit tests:
+//
+
+enum keys_empty { key_empty_LAST };
+enum keys_single { key_single_a, key_single_LAST };
+
+typedef torrent::static_map_type<keys_empty, key_empty_LAST> test_empty_type;
+typedef torrent::static_map_type<keys_single, key_single_LAST> test_single_type;
+
+template <>
+const test_empty_type::key_list_type test_empty_type::keys = { };
+template <>
+const test_single_type::key_list_type test_single_type::keys = { { key_single_a, "b" } };
+
+void
+ObjectStaticMapTest::test_read_empty() {
+  test_empty_type map_normal;
+  test_empty_type map_skip;
+  test_empty_type map_incomplete;
+
+  STATIC_MAP_READ_BENCODE_ASSERT(map_normal, "de");
+  STATIC_MAP_READ_BENCODE_ASSERT(map_skip, "d1:ai1ee");
+  
+  CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, ""));
+  CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d"));
+}
+
+void
+ObjectStaticMapTest::test_read_single() {
+  test_single_type map_normal;
+  test_single_type map_skip;
+  test_single_type map_incomplete;
+
+  STATIC_MAP_READ_BENCODE_ASSERT(map_normal, "d1:bi1ee");
+  CPPUNIT_ASSERT(map_normal[key_single_a].as_value() == 1);
+
+  STATIC_MAP_READ_BENCODE_ASSERT(map_skip, "d1:ai0e1:bi1e1:ci2ee");
+  CPPUNIT_ASSERT(map_skip[key_single_a].as_value() == 1);
+
+  CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d"));
+  CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d1:b"));
+  CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d1:bi1"));
+  CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d1:bi1e"));
+}
+
+void
+ObjectStaticMapTest::test_write_empty() {
+}
Index: libtorrent/src/dht/dht_bucket.cc
===================================================================
--- libtorrent/src/dht/dht_bucket.cc	(revision 1134)
+++ libtorrent/src/dht/dht_bucket.cc	(working copy)
@@ -52,6 +52,8 @@
   m_good(0),
   m_bad(0),
 
+  m_fullCacheLength(0),
+
   m_begin(begin),
   m_end(end) {
 
@@ -67,6 +69,8 @@
     m_good++;
   else if (n->is_bad())
     m_bad++;
+
+  m_fullCacheLength = 0;
 }
 
 void
@@ -81,6 +85,8 @@
     m_good--;
   else if (n->is_bad())
     m_bad--;
+
+  m_fullCacheLength = 0;
 }
 
 void
@@ -92,9 +98,11 @@
 // Called every 15 minutes for housekeeping.
 void
 DhtBucket::update() {
-  // For now we only update the counts after some nodes have become bad
-  // due to prolonged inactivity.
   count();
+
+  // In case adjacent buckets whose nodes we borrowed have changed,
+  // we force an update of the cache.
+  m_fullCacheLength = 0;
 }
 
 DhtBucket::iterator
@@ -188,4 +196,23 @@
   return other;
 }
 
+void
+DhtBucket::build_full_cache() {
+  DhtBucketChain chain(this);
+
+  char* pos = m_fullCache;
+  do {
+    for (const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end() && pos < m_fullCache + sizeof(m_fullCache); ++itr) {
+      if (!(*itr)->is_bad()) {
+        pos = (*itr)->store_compact(pos);
+
+        if (pos > m_fullCache + sizeof(m_fullCache))
+          throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end.");
+      }
+    }
+  } while (pos < m_fullCache + sizeof(m_fullCache) && chain.next() != NULL);
+
+  m_fullCacheLength = pos - m_fullCache;
 }
+
+}
Index: libtorrent/src/dht/dht_tracker.h
===================================================================
--- libtorrent/src/dht/dht_tracker.h	(revision 1134)
+++ libtorrent/src/dht/dht_tracker.h	(working copy)
@@ -72,7 +72,19 @@
   void                prune(uint32_t maxAge);
 
 private:
-  typedef std::vector<SocketAddressCompact> PeerList;
+  // We need to store the address as a bencoded string.
+  struct BencodeAddress {
+    char                 header[2];
+    SocketAddressCompact peer;
+ 
+    BencodeAddress(const SocketAddressCompact& p) : peer(p) { header[0] = '6'; header[1] = ':'; }
+ 
+    const char*  bencode() const { return header; }
+ 
+    bool         empty() const   { return !peer.port; }
+  } __attribute__ ((packed));
+ 
+  typedef std::vector<BencodeAddress> PeerList;
 
   PeerList               m_peers;
   std::vector<uint32_t>  m_lastSeen;
Index: libtorrent/src/dht/dht_bucket.h
===================================================================
--- libtorrent/src/dht/dht_bucket.h	(revision 1134)
+++ libtorrent/src/dht/dht_bucket.h	(working copy)
@@ -111,6 +111,10 @@
   DhtBucket*          parent() const                          { return m_parent; }
   DhtBucket*          child() const                           { return m_child; }
 
+  // Return a full bucket's worth of compact node data. If this bucket is not
+  // full, it uses nodes from the child/parent buckets until we have enough.
+  std::string         full_bucket();
+
   // Called by the DhtNode on its bucket to update good/bad node counts.
   void                node_now_good(bool was_bad);
   void                node_now_bad(bool was_good);
@@ -118,6 +122,8 @@
 private:
   void                count();
 
+  void                build_full_cache();
+
   DhtBucket*          m_parent;
   DhtBucket*          m_child;
   
@@ -126,11 +132,15 @@
   unsigned int        m_good;
   unsigned int        m_bad;
 
+  size_t              m_fullCacheLength;
+
   // These are 40 bytes together, so might as well put them last.
   // m_end is const because it is used as key for the DhtRouter routing table
   // map, which would be inconsistent if m_end were changed carelessly.
   HashString          m_begin;
   const HashString    m_end;
+
+  char                m_fullCache[num_nodes * 26];
 };
 
 // Helper class to recursively follow a chain of buckets.  It first recurses
@@ -160,6 +170,13 @@
   m_bad++;
 }
 
+inline std::string
+DhtBucket::full_bucket() {
+  if (!m_fullCacheLength)
+    build_full_cache();
+  return std::string(m_fullCache, m_fullCacheLength);
+}
+
 inline const DhtBucket*
 DhtBucketChain::next() {
   // m_restart is clear when we're done recursing into the children and
Index: libtorrent/src/dht/dht_router.cc
===================================================================
--- libtorrent/src/dht/dht_router.cc	(revision 1134)
+++ libtorrent/src/dht/dht_router.cc	(working copy)
@@ -329,24 +329,6 @@
   delete_node(m_nodes.find(&node->id()));
 }
 
-char*
-DhtRouter::store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd) {
-  DhtBucketChain chain(find_bucket(id)->second);
-
-  do {
-    for (DhtBucket::const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end() && buffer != bufferEnd; ++itr) {
-      if (!(*itr)->is_bad()) {
-        buffer = (*itr)->store_compact(buffer);
-
-        if (buffer > bufferEnd)
-          throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end.");
-      }
-    }
-  } while (buffer != bufferEnd && chain.next() != NULL);
-
-  return buffer;
-}
-
 Object*
 DhtRouter::store_cache(Object* container) const {
   container->insert_key("self_id", str());
@@ -470,7 +452,7 @@
   for (DhtBucketList::const_iterator itr = m_routingTable.begin(); itr != m_routingTable.end(); ++itr) {
     itr->second->update();
 
-    if (!itr->second->is_full() || itr->second->age() > timeout_bucket_bootstrap)
+    if (!itr->second->is_full() || itr->second == bucket() || itr->second->age() > timeout_bucket_bootstrap)
       bootstrap_bucket(itr->second);
   }
 
Index: libtorrent/src/dht/dht_router.h
===================================================================
--- libtorrent/src/dht/dht_router.h	(revision 1134)
+++ libtorrent/src/dht/dht_router.h	(working copy)
@@ -115,8 +115,8 @@
 
   // Store compact node information (26 bytes) for nodes closest to the
   // given ID in the given buffer, return new buffer end.
-  char*               store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd);
-
+  std::string         get_closest_nodes(const HashString& id)  { return find_bucket(id)->second->full_bucket(); }
+ 
   // Store DHT cache in the given container.
   Object*             store_cache(Object* container) const;
 
@@ -147,6 +147,8 @@
   bool                add_node_to_bucket(DhtNode* node);
   void                delete_node(const DhtNodeList::accessor& itr);
 
+  //  void                store_closest_nodes(const HashString& id, DhtBucket* bucket);
+
   DhtBucketList::iterator split_bucket(const DhtBucketList::iterator& itr, DhtNode* node);
 
   void                bootstrap();
Index: libtorrent/src/dht/dht_transaction.cc
===================================================================
--- libtorrent/src/dht/dht_transaction.cc	(revision 1134)
+++ libtorrent/src/dht/dht_transaction.cc	(working copy)
@@ -123,7 +123,7 @@
   // We keep:
   // - the max_contacts=18 closest good or unknown nodes and all nodes closer
   //   than them (to see if further searches find closer ones)
-  // - for announces, also the 8 closest good nodes (i.e. nodes that have
+  // - for announces, also the 3 closest good nodes (i.e. nodes that have
   //   replied) to have at least that many for the actual announce
   // - any node that currently has transactions pending
   //
@@ -136,7 +136,7 @@
   // node is new and unknown otherwise
 
   int needClosest = final ? 0 : max_contacts;
-  int needGood = is_announce() ? DhtBucket::num_nodes : 0;
+  int needGood = is_announce() ? max_announce : 0;
 
   // We're done if we can't find any more nodes to contact.
   m_next = end();
@@ -252,7 +252,7 @@
 }
 
 void 
-DhtAnnounce::receive_peers(const Object& peers) {
+DhtAnnounce::receive_peers(const std::string& peers) {
   m_tracker->receive_peers(peers);
 }
 
@@ -262,9 +262,12 @@
 }
 
 void
-DhtTransactionPacket::build_buffer(const Object& data) {
+DhtTransactionPacket::build_buffer(const DhtMessage& msg) {
   char buffer[1500];  // If the message would exceed an Ethernet frame, something went very wrong.
-  object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), &data);
+  object_buffer_t result = staticMap_write_bencode_c<>(object_write_to_buffer,
+                                                       NULL,
+                                                       std::make_pair(buffer, buffer + sizeof(buffer)),
+                                                       msg.base());
 
   m_length = result.second - buffer;
   m_data = new char[m_length];
@@ -277,7 +280,6 @@
     m_sa(*sa),
     m_timeout(cachedTime.seconds() + timeout),
     m_quickTimeout(cachedTime.seconds() + quick_timeout),
-    m_retry(3),
     m_packet(NULL) {
 
 }
Index: libtorrent/src/dht/dht_server.cc
===================================================================
--- libtorrent/src/dht/dht_server.cc	(revision 1134)
+++ libtorrent/src/dht/dht_server.cc	(working copy)
@@ -38,7 +38,7 @@
 #include "globals.h"
 
 #include <algorithm>
-#include <sstream>
+#include <cstdio>
 
 #include "torrent/exceptions.h"
 #include "torrent/connection_manager.h"
@@ -63,6 +63,32 @@
   "announce_peer",
 };
 
+// List of all possible keys we need/support in a DHT message.
+// Unsupported keys we receive are dropped (ignored) while decoding.
+// See torrent/static_map.h for how this works.
+template <>
+const DhtMessage::base_type::key_list_type DhtMessage::base_type::keys = {
+  { key_a_id,       "a::id" },
+  { key_a_infoHash, "a::info_hash" },
+  { key_a_port,     "a::port", },
+  { key_a_target,   "a::target" },
+  { key_a_token,    "a::token" },
+
+  { key_e_0,        "e[]" },
+  { key_e_1,        "e[]" },
+
+  { key_q,          "q" },
+
+  { key_r_id,       "r::id" },
+  { key_r_nodes,    "r::nodes" },
+  { key_r_token,    "r::token" },
+  { key_r_values,   "r::values" },
+
+  { key_t,          "t" },
+  { key_v,          "v" },
+  { key_y,          "y" }
+};
+
 // Error in DHT protocol, avoids std::string ctor from communication_error
 class dht_error : public network_error {
 public:
@@ -238,54 +264,51 @@
 }
 
 void
-DhtServer::process_query(const Object& transactionId, const HashString& id, const rak::socket_address* sa, Object& request) {
+DhtServer::process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& msg) {
   m_queriesReceived++;
   m_networkUp = true;
 
-  std::string& query = request.get_key_string("q");
+  const std::string& query = msg[key_q].as_string();
 
-  Object& arg = request.get_key("a");
-
   // Construct reply.
-  Object reply = Object::create_map();
+  DhtMessage reply;
 
   if (query == "find_node")
-    create_find_node_response(arg, reply);
+    create_find_node_response(msg, reply);
 
   else if (query == "get_peers")
-    create_get_peers_response(arg, sa, reply);
+    create_get_peers_response(msg, sa, reply);
 
   else if (query == "announce_peer")
-    create_announce_peer_response(arg, sa, reply);
+    create_announce_peer_response(msg, sa, reply);
 
   else if (query != "ping")
     throw dht_error(dht_error_bad_method, "Unknown query type.");
 
   m_router->node_queried(id, sa);
-  create_response(transactionId, sa, reply);
+  create_response(msg, sa, reply);
 }
 
 void
-DhtServer::create_find_node_response(const Object& arg, Object& reply) {
-  const std::string& target = arg.get_key_string("target");
+DhtServer::create_find_node_response(const DhtMessage& req, DhtMessage& reply) {
+  const std::string& target = req[key_a_target].as_string();
 
   if (target.length() < HashString::size_data)
     throw dht_error(dht_error_protocol, "target string too short");
 
-  char compact[sizeof(compact_node_info) * DhtBucket::num_nodes];
-  char* end = m_router->store_closest_nodes(*HashString::cast_from(target), compact, compact + sizeof(compact));
+  std::string nodes = m_router->get_closest_nodes(*HashString::cast_from(target));
 
-  if (end == compact)
+  if (nodes.empty())
     throw dht_error(dht_error_generic, "No nodes");
 
-  reply.insert_key("nodes", std::string(compact, end));
+  reply[key_r_nodes] = nodes;
 }
 
 void
-DhtServer::create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply) {
-  reply.insert_key("token", m_router->make_token(sa));
+DhtServer::create_get_peers_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) {
+  reply[key_r_token] = m_router->make_token(sa);
 
-  const std::string& info_hash_str = arg.get_key_string("info_hash");
+  const std::string& info_hash_str = req[key_a_infoHash].as_string();
 
   if (info_hash_str.length() < HashString::size_data)
     throw dht_error(dht_error_protocol, "info hash too short");
@@ -296,35 +319,34 @@
 
   // If we're not tracking or have no peers, send closest nodes.
   if (!tracker || tracker->empty()) {
-    char compact[sizeof(compact_node_info) * DhtBucket::num_nodes];
-    char* end = m_router->store_closest_nodes(*info_hash, compact, compact + sizeof(compact));
+    std::string nodes = m_router->get_closest_nodes(*info_hash);
 
-    if (end == compact)
+    if (nodes.empty())
       throw dht_error(dht_error_generic, "No peers nor nodes");
 
-    reply.insert_key("nodes", std::string(compact, end));
-
+    reply[key_r_nodes] = nodes;
   } else {
-    reply.insert_key("values", Object::create_list()).as_list().swap(tracker->get_peers().as_list());
+    reply[key_r_values] = tracker->get_peers();
   }
 }
 
 void
-DhtServer::create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply) {
-  const std::string& info_hash = arg.get_key_string("info_hash");
+DhtServer::create_announce_peer_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) {
+  const std::string& info_hash = req[key_a_infoHash].as_string();
 
   if (info_hash.length() < HashString::size_data)
     throw dht_error(dht_error_protocol, "info hash too short");
 
-  if (!m_router->token_valid(arg.get_key_string("token"), sa))
+  if (!m_router->token_valid(req[key_a_token].as_string(), sa))
     throw dht_error(dht_error_protocol, "Token invalid.");
 
   DhtTracker* tracker = m_router->get_tracker(*HashString::cast_from(info_hash), true);
-  tracker->add_peer(sa->sa_inet()->address_n(), arg.get_key_value("port"));
+  tracker->add_peer(sa->sa_inet()->address_n(), req[key_a_port].as_value());
 }
 
 void
-DhtServer::process_response(int transactionId, const HashString& id, const rak::socket_address* sa, Object& request) {
+DhtServer::process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& response) {
+  int transactionId = (unsigned char)response[key_t].as_string()[2];
   transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId));
 
   // Response to a transaction we don't have in our table. At this point it's
@@ -351,11 +373,9 @@
     if ((id != transaction->id() && transaction->id() != m_router->zero_id))
       return;
 
-    const Object& response = request.get_key("r");
-
     switch (transaction->type()) {
       case DhtTransaction::DHT_FIND_NODE:
-        parse_find_node_reply(transaction->as_find_node(), response.get_key_string("nodes"));
+        parse_find_node_reply(transaction->as_find_node(), response[key_r_nodes].as_string());
         break;
 
       case DhtTransaction::DHT_GET_PEERS:
@@ -381,7 +401,8 @@
 }
 
 void
-DhtServer::process_error(int transactionId, const rak::socket_address* sa, Object& request) {
+DhtServer::process_error(const rak::socket_address* sa, const DhtMessage& error) {
+  int transactionId = (unsigned char)error[key_t].as_string()[2];
   transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId));
 
   if (itr == m_transactions.end())
@@ -421,16 +442,20 @@
 }
 
 void
-DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const Object& response) {
+DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const DhtMessage& response) {
   DhtAnnounce* announce = static_cast<DhtAnnounce*>(transaction->as_search()->search());
 
   transaction->complete(true);
 
-  if (response.has_key_list("values"))
-    announce->receive_peers(response.get_key("values"));
+  if (response[key_r_values].is_string())
+    announce->receive_peers(response[key_r_values].as_string());
 
-  if (response.has_key_string("token"))
-    add_transaction(new DhtTransactionAnnouncePeer(transaction->id(), transaction->address(), announce->target(), response.get_key_string("token")), packet_prio_low);
+  if (response[key_r_token].is_string())
+    add_transaction(new DhtTransactionAnnouncePeer(transaction->id(),
+                                                   transaction->address(),
+                                                   announce->target(),
+                                                   response[key_r_token].as_string()),
+                    packet_prio_low);
 
   announce->update_status();
 }
@@ -490,35 +515,37 @@
   if (itr->second->id() == m_router->id())
     throw internal_error("DhtServer::create_query trying to send to itself.");
 
-  Object query = Object::create_map();
+  DhtMessage query;
 
+  // Transaction ID is a bencode string.
+  query[key_t] = std::string(query.data_end, 3);
+  *query.data_end++ = '1';
+  *query.data_end++ = ':';
+  *query.data_end++ = tID;
+
   DhtTransaction* transaction = itr->second;
-  char trans_id = tID;
-  query.insert_key("t", std::string(&trans_id, 1));
-  query.insert_key("y", "q");
-  query.insert_key("q", queries[transaction->type()]);
-  query.insert_key("v", PEER_VERSION);
+  query[key_y] = std::string("q", 1);
+  query[key_q] = std::string(queries[transaction->type()]);
+  query[key_v] = std::string(PEER_VERSION, 4);
+  query[key_a_id] = m_router->str();
 
-  Object& q = query.insert_key("a", Object::create_map());
-  q.insert_key("id", m_router->str());
-
   switch (transaction->type()) {
     case DhtTransaction::DHT_PING:
       // nothing to do
       break;
 
     case DhtTransaction::DHT_FIND_NODE:
-      q.insert_key("target", transaction->as_find_node()->search()->target().str());
+      query[key_a_target] = transaction->as_find_node()->search()->target().str();
       break;
 
     case DhtTransaction::DHT_GET_PEERS:
-      q.insert_key("info_hash", transaction->as_get_peers()->search()->target().str());
+      query[key_a_infoHash] = transaction->as_get_peers()->search()->target().str();
       break;
 
     case DhtTransaction::DHT_ANNOUNCE_PEER:
-      q.insert_key("info_hash", transaction->as_announce_peer()->info_hash().str());
-      q.insert_key("token", transaction->as_announce_peer()->token());
-      q.insert_key("port", manager->connection_manager()->listen_port());
+      query[key_a_infoHash] = transaction->as_announce_peer()->info_hash().str();
+      query[key_a_token] = transaction->as_announce_peer()->token();
+      query[key_a_port] = manager->connection_manager()->listen_port();
       break;
   }
 
@@ -530,32 +557,27 @@
 }
 
 void
-DhtServer::create_response(const Object& transactionId, const rak::socket_address* sa, Object& r) {
-  Object reply = Object::create_map();
-  r.insert_key("id", m_router->str());
+DhtServer::create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) {
+  reply[key_r_id] = m_router->str();
+  reply[key_t] = req[key_t];
+  reply[key_y] = std::string("r", 1);
+  reply[key_v] = std::string(PEER_VERSION, 4);
 
-  reply.insert_key("t", transactionId);
-  reply.insert_key("y", "r");
-  reply.insert_key("r", r);
-  reply.insert_key("v", PEER_VERSION);
-
   add_packet(new DhtTransactionPacket(sa, reply), packet_prio_reply);
 }
 
 void
-DhtServer::create_error(const Object* transactionId, const rak::socket_address* sa, int num, const std::string& msg) {
-  Object error = Object::create_map();
+DhtServer::create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg) {
+  DhtMessage error;
 
-  if (transactionId != NULL)
-    error.insert_key("t", *transactionId);
+  if (req[key_t].is_string())
+    error[key_t] = req[key_t];
 
-  error.insert_key("y", "e");
-  error.insert_key("v", PEER_VERSION);
+  error[key_y] = std::string("e", 1);
+  error[key_v] = std::string(PEER_VERSION, 4);
+  error[key_e_0] = num;
+  error[key_e_1] = std::string(msg);
 
-  Object& e = error.insert_key("e", Object::create_list());
-  e.insert_back(num);
-  e.insert_back(msg);
-
   add_packet(new DhtTransactionPacket(sa, error), packet_prio_reply);
 }
 
@@ -656,15 +678,12 @@
 void
 DhtServer::event_read() {
   uint32_t total = 0;
-  std::istringstream sstream;
 
-  sstream.imbue(std::locale::classic());
-
   while (true) {
     Object request;
     rak::socket_address sa;
     int type = '?';
-    const Object* transactionId = NULL;
+    DhtMessage message;
     const HashString* nodeId = NULL;
 
     try {
@@ -675,32 +694,33 @@
         break;
 
       total += read;
-      sstream.str(std::string(buffer, read));
 
-      sstream >> request;
-
       // If it's not a valid bencode dictionary at all, it's probably not a DHT
       // packet at all, so we don't throw an error to prevent bounce loops.
-      if (sstream.fail() || !request.is_map())
+      try {
+        staticMap_read_bencode(buffer, buffer + read, message);
+      } catch (bencode_error& e) {
         continue;
+      }
 
-      if (!request.has_key("t"))
+      if (!message[key_t].is_string())
         throw dht_error(dht_error_protocol, "No transaction ID");
 
-      transactionId = &request.get_key("t");
-
-      if (!request.has_key_string("y"))
+      if (!message[key_y].is_string())
         throw dht_error(dht_error_protocol, "No message type");
 
-      if (request.get_key_string("y").length() != 1)
+      if (message[key_y].as_string().length() != 1)
         throw dht_error(dht_error_bad_method, "Unsupported message type");
 
-      type = request.get_key_string("y")[0];
+      type = message[key_y].as_string()[0];
 
       // Queries and replies have node ID in different dictionaries.
       if (type == 'r' || type == 'q') {
-        const std::string& nodeIdStr = request.get_key(type == 'q' ? "a" : "r").get_key_string("id");
+        if (!message[type == 'q' ? key_a_id : key_r_id].is_string())
+          throw dht_error(dht_error_protocol, "Invalid `id' value");
 
+        std::string& nodeIdStr = message[type == 'q' ? key_a_id : key_r_id].as_string();
+
         if (nodeIdStr.length() < HashString::size_data)
           throw dht_error(dht_error_protocol, "`id' value too short");
 
@@ -709,7 +729,8 @@
 
       // Sanity check the returned transaction ID.
       if ((type == 'r' || type == 'e') && 
-          (!transactionId->is_string() || transactionId->as_string().length() != 1))
+          (!message[key_t].is_string() || message[key_t].as_string().length() != 3
+           || message[key_t].as_string()[0] != '1' || message[key_t].as_string()[1] != ':'))
         throw dht_error(dht_error_protocol, "Invalid transaction ID type/length.");
 
       // Stupid broken implementations.
@@ -718,15 +739,15 @@
 
       switch (type) {
         case 'q':
-          process_query(*transactionId, *nodeId, &sa, request);
+          process_query(*nodeId, &sa, message);
           break;
 
         case 'r':
-          process_response(((unsigned char*)transactionId->as_string().c_str())[0], *nodeId, &sa, request);
+          process_response(*nodeId, &sa, message);
           break;
 
         case 'e':
-          process_error(((unsigned char*)transactionId->as_string().c_str())[0], &sa, request);
+          process_error(&sa, message);
           break;
 
         default:
@@ -737,16 +758,19 @@
     // so that if it repeatedly sends malformed replies we will drop it instead of propagating it
     // to other nodes.
     } catch (bencode_error& e) {
-      if ((type == 'r' || type == 'e') && nodeId != NULL)
+      if ((type == 'r' || type == 'e') && nodeId != NULL) {
         m_router->node_inactive(*nodeId, &sa);
-      else
-        create_error(transactionId, &sa, dht_error_protocol, std::string("Malformed packet: ") + e.what());
+      } else {
+        snprintf(message.data_end, message.data + message.data_size - message.data_end - 1, "Malformed packet: %s", e.what());
+        message.data[message.data_size - 1] = 0;
+        create_error(message, &sa, dht_error_protocol, message.data_end);
+      }
 
     } catch (dht_error& e) {
       if ((type == 'r' || type == 'e') && nodeId != NULL)
         m_router->node_inactive(*nodeId, &sa);
       else
-        create_error(transactionId, &sa, e.code(), e.what());
+        create_error(message, &sa, e.code(), e.what());
 
     } catch (network_error& e) {
 
Index: libtorrent/src/dht/dht_transaction.h
===================================================================
--- libtorrent/src/dht/dht_transaction.h	(revision 1134)
+++ libtorrent/src/dht/dht_transaction.h	(working copy)
@@ -43,6 +43,7 @@
 
 #include "dht/dht_node.h"
 #include "torrent/hash_string.h"
+#include "torrent/object_static_map.h"
 
 namespace torrent {
 
@@ -93,6 +94,9 @@
   // Number of closest potential contact nodes to keep.
   static const unsigned int max_contacts = 18;
 
+  // Number of closest nodes we actually announce to.
+  static const unsigned int max_announce = 3;
+
   DhtSearch(const HashString& target, const DhtBucket& contacts);
   virtual ~DhtSearch();
 
@@ -178,22 +182,68 @@
   // counts announces instead.
   const_accessor       start_announce();
 
-  void                 receive_peers(const Object& peer_list);
+  void                 receive_peers(const std::string& peer_list);
   void                 update_status();
 
 private:
   TrackerDht*          m_tracker;
 };
 
+// Possible bencode keys in a DHT message.
+enum dht_keys {
+  key_a_id,
+  key_a_infoHash,
+  key_a_port,
+  key_a_target,
+  key_a_token,
+ 
+  key_e_0,
+  key_e_1,
+ 
+  key_q,
+ 
+  key_r_id,
+  key_r_nodes,
+  key_r_token,
+  key_r_values,
+ 
+  key_t,
+  key_v,
+  key_y,
+ 
+  key_LAST,
+};
+ 
+class DhtMessage : public static_map_type<dht_keys, key_LAST> {
+public:
+  typedef static_map_type<dht_keys, key_LAST> base_type;
+  typedef base_type::mapping_type mapping_type;
+ 
+  DhtMessage() : data_end(data) {};
+ 
+  const base_type& base() const { return *static_cast<const base_type*>(this); }
+
+  // Must be big enough to hold one of the possible variable-sized reply data.
+  // Currently either:
+  // - error message (size doesn't really matter, it'll be truncated at worst)
+  // - announce token (8 bytes, needs 20 bytes buffer to build)
+  // Never more than one of the above.
+  // And additionally for queries we send:
+  // - transaction ID (3 bytes)
+  static const size_t data_size = 64;
+  char data[data_size];
+  char* data_end;
+};
+
 // Class holding transaction data to be transmitted.
 class DhtTransactionPacket {
 public:
   // transaction packet
-  DhtTransactionPacket(const rak::socket_address* s, const Object& d, unsigned int id, DhtTransaction* t)
+  DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d, unsigned int id, DhtTransaction* t)
     : m_sa(*s), m_id(id), m_transaction(t) { build_buffer(d); };
 
   // non-transaction packet
-  DhtTransactionPacket(const rak::socket_address* s, const Object& d)
+  DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d)
     : m_sa(*s), m_id(-cachedTime.seconds()), m_transaction(NULL) { build_buffer(d); };
 
   ~DhtTransactionPacket()                               { delete[] m_data; }
@@ -214,7 +264,7 @@
   DhtTransaction*             transaction()             { return m_transaction; }
 
 private:
-  void                        build_buffer(const Object& data);
+  void                        build_buffer(const DhtMessage& data);
 
   rak::socket_address   m_sa;
   char*                 m_data;
@@ -255,9 +305,6 @@
   int                         quick_timeout()      { return m_quickTimeout; }
   bool                        has_quick_timeout()  { return m_hasQuickTimeout; }
 
-  int                         dec_retry()          { return m_retry--; }
-  int                         retry()              { return m_retry; }
-
   DhtTransactionPacket*       packet()             { return m_packet; }
   void                        set_packet(DhtTransactionPacket* p) { m_packet = p; }
 
@@ -282,7 +329,6 @@
   rak::socket_address    m_sa;
   int                    m_timeout;
   int                    m_quickTimeout;
-  int                    m_retry;
   DhtTransactionPacket*  m_packet;
 };
 
Index: libtorrent/src/dht/dht_server.h
===================================================================
--- libtorrent/src/dht/dht_server.h	(revision 1134)
+++ libtorrent/src/dht/dht_server.h	(working copy)
@@ -56,6 +56,7 @@
 class DhtRouter;
 
 class DownloadInfo;
+class DhtMessage;
 class TrackerDht;
 
 // UDP server that handles the DHT node communications.
@@ -134,23 +135,23 @@
 
   void                start_write();
 
-  void                process_query(const Object& transaction, const HashString& id, const rak::socket_address* sa, Object& req);
-  void                process_response(int transaction, const HashString& id, const rak::socket_address* sa, Object& req);
-  void                process_error(int transaction, const rak::socket_address* sa, Object& req);
+  void                process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& req);
+  void                process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& req);
+  void                process_error(const rak::socket_address* sa, const DhtMessage& error);
 
   void                parse_find_node_reply(DhtTransactionSearch* t, const std::string& nodes);
-  void                parse_get_peers_reply(DhtTransactionGetPeers* t, const Object& res);
+  void                parse_get_peers_reply(DhtTransactionGetPeers* t, const DhtMessage& res);
 
   void                find_node_next(DhtTransactionSearch* t);
 
   void                add_packet(DhtTransactionPacket* packet, int priority);
   void                create_query(transaction_itr itr, int tID, const rak::socket_address* sa, int priority);
-  void                create_response(const Object& transactionID, const rak::socket_address* sa, Object& r);
-  void                create_error(const Object* transactionID, const rak::socket_address* sa, int num, const std::string& msg);
+  void                create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply);
+  void                create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg);
 
-  void                create_find_node_response(const Object& arg, Object& reply);
-  void                create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply);
-  void                create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply);
+  void                create_find_node_response(const DhtMessage& arg, DhtMessage& reply);
+  void                create_get_peers_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply);
+  void                create_announce_peer_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply);
 
   int                 add_transaction(DhtTransaction* t, int priority);
 
Index: libtorrent/src/dht/dht_tracker.cc
===================================================================
--- libtorrent/src/dht/dht_tracker.cc	(revision 1134)
+++ libtorrent/src/dht/dht_tracker.cc	(working copy)
@@ -54,8 +54,8 @@
 
   // Check if peer exists. If not, find oldest peer.
   for (unsigned int i = 0; i < size(); i++) {
-    if (m_peers[i].addr == compact.addr) {
-      m_peers[i].port = compact.port;
+    if (m_peers[i].peer.addr == compact.addr) {
+      m_peers[i].peer.port = compact.port;
       m_lastSeen[i] = cachedTime.seconds();
       return;
 
@@ -94,11 +94,7 @@
     last = first + maxPeers;
   }
 
-  Object peers = Object::create_list();
-  for (; first != last; ++first)
-    peers.insert_back(std::string(first->c_str(), sizeof(*first)));
-
-  return peers;
+  return std::string(first->bencode(), last->bencode() - first->bencode());
 }
 
 // Remove old announces.
@@ -107,9 +103,9 @@
   uint32_t minSeen = cachedTime.seconds() - maxAge;
 
   for (unsigned int i = 0; i < m_lastSeen.size(); i++)
-    if (m_lastSeen[i] < minSeen) m_peers[i].port = 0;
+    if (m_lastSeen[i] < minSeen) m_peers[i].peer.port = 0;
 
-  m_peers.erase(std::remove_if(m_peers.begin(), m_peers.end(), rak::on(rak::mem_ref(&SocketAddressCompact::port), std::bind2nd(std::equal_to<uint16_t>(), 0))), m_peers.end());
+  m_peers.erase(std::remove_if(m_peers.begin(), m_peers.end(), std::mem_fun_ref(&BencodeAddress::empty)), m_peers.end());
   m_lastSeen.erase(std::remove_if(m_lastSeen.begin(), m_lastSeen.end(), std::bind2nd(std::less<uint32_t>(), minSeen)), m_lastSeen.end());
 
   if (m_peers.size() != m_lastSeen.size())
Index: libtorrent/src/net/address_list.cc
===================================================================
--- libtorrent/src/net/address_list.cc	(revision 1134)
+++ libtorrent/src/net/address_list.cc	(working copy)
@@ -79,4 +79,20 @@
 	    std::back_inserter(*this));
 }
 
+// TODO: Fix this...
+void
+AddressList::parse_address_bencode(const std::string& s) {
+  if (sizeof(const SocketAddressCompact) != 6)
+    throw internal_error("AddressList::parse_address_bencode(...) bad struct size.");
+
+  // TODO: Fix this.
+//   while (s.length() >= 2 + sizeof(SocketAddressCompact)) {
+//     if (s[0] != '6' || s[1] != ':')
+//       break;
+
+//     insert(end(), *reinterpret_cast<const SocketAddressCompact*>(s.c_str() + 2));
+//     s = std::string(s.c_str() + 2 + sizeof(SocketAddressCompact), s.length() - 2 - sizeof(SocketAddressCompact));
+//   }
 }
+
+}
Index: libtorrent/src/net/address_list.h
===================================================================
--- libtorrent/src/net/address_list.h	(revision 1134)
+++ libtorrent/src/net/address_list.h	(working copy)
@@ -50,6 +50,7 @@
   // Parse normal or compact list of addresses and add to AddressList
   void                        parse_address_normal(const Object::list_type& b);
   void                        parse_address_compact(const std::string& s);
+  void                        parse_address_bencode(const std::string& s);
 
 private:
   static rak::socket_address  parse_address(const Object& b);
Index: libtorrent/src/tracker/tracker_dht.h
===================================================================
--- libtorrent/src/tracker/tracker_dht.h	(revision 1134)
+++ libtorrent/src/tracker/tracker_dht.h	(working copy)
@@ -71,7 +71,7 @@
 
   bool                has_peers() const                { return !m_peers.empty(); }
 
-  void                receive_peers(const Object& peer_list);
+  void                receive_peers(const std::string& peers);
   void                receive_success();
   void                receive_failed(const char* msg);
   void                receive_progress(int replied, int contacted);
Index: libtorrent/src/tracker/tracker_dht.cc
===================================================================
--- libtorrent/src/tracker/tracker_dht.cc	(revision 1134)
+++ libtorrent/src/tracker/tracker_dht.cc	(working copy)
@@ -115,13 +115,11 @@
 }
 
 void
-TrackerDht::receive_peers(const Object& peer_list) {
+TrackerDht::receive_peers(const std::string& peers) {
   if (!is_busy())
     throw internal_error("TrackerDht::receive_peers called while not busy.");
 
-  Object::list_type peers = peer_list.as_list();
-  for (Object::list_type::const_iterator itr = peers.begin(); itr != peers.end(); ++itr)
-    m_peers.parse_address_compact(itr->as_string());
+  m_peers.parse_address_bencode(peers);
 }
 
 void
Index: libtorrent/src/torrent/object_static_map.h
===================================================================
--- libtorrent/src/torrent/object_static_map.h	(revision 0)
+++ libtorrent/src/torrent/object_static_map.h	(revision 0)
@@ -0,0 +1,111 @@
+// libTorrent - BitTorrent library
+// Copyright (C) 2005-2007, Jari Sundell
+//
+// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+//
+// Contact:  Jari Sundell <jaris@ifi.uio.no>
+//
+//           Skomakerveien 33
+//           3185 Skoppum, NORWAY
+
+#ifndef LIBTORRENT_OBJECT_STATIC_MAP_H
+#define LIBTORRENT_OBJECT_STATIC_MAP_H
+
+#include <cstring>
+#include <algorithm>
+#include <torrent/object.h>
+
+namespace torrent {
+
+struct static_map_mapping_type {
+  static const size_t max_key_size = 16;
+
+  bool        is_end() const { return key[0] == '\0'; }
+  static bool is_not_key_char(char c) { return c == '\0' || c == ':' || c == '['; }
+
+  const char* find_key_end(const char* pos) const { return std::find_if(pos, key + max_key_size, &is_not_key_char); }
+
+  uint32_t   index;
+  const char key[max_key_size];
+};
+  
+struct static_map_entry_type {
+  torrent::Object object;
+};
+
+template <typename tmpl_key_type, size_t tmpl_length>
+class static_map_type {
+public:
+  typedef Object          value_type;
+  typedef tmpl_key_type   key_type;
+  typedef static_map_entry_type   entry_type;
+  typedef static_map_mapping_type mapping_type;
+
+  typedef mapping_type    key_list_type[tmpl_length];
+  typedef entry_type      value_list_type[tmpl_length];
+
+  static const size_t size = tmpl_length;
+  static const key_list_type keys;
+
+  entry_type*         values() { return m_values; }
+  const entry_type*   values() const { return m_values; }
+
+  Object&             operator [] (key_type key)        { return m_values[key].object; }
+  const Object&       operator [] (key_type key) const  { return m_values[key].object; }
+
+private:
+  value_list_type m_values;
+};
+
+//
+// Helper functions/classes for parsing keys:
+//
+
+struct static_map_stack_type {
+  void set_key_index(uint32_t start_index, uint32_t end_index, uint32_t delim_size) {
+    key_index = start_index;
+    //    key_size = size; 
+    next_key = end_index + delim_size;
+  }
+
+  void clear() { key_index = 0; /*key_size = 0;*/ next_key = 0; }
+
+  uint32_t key_index;
+  uint32_t key_size;
+  uint32_t next_key;
+};
+
+typedef std::pair<const static_map_mapping_type*, unsigned int> static_map_key_search_result;
+
+const static_map_key_search_result
+find_key_match(const static_map_mapping_type* first, const static_map_mapping_type* last, const char* key) LIBTORRENT_EXPORT;
+
+}
+
+#endif
Index: libtorrent/src/torrent/object_stream.cc
===================================================================
--- libtorrent/src/torrent/object_stream.cc	(revision 1134)
+++ libtorrent/src/torrent/object_stream.cc	(working copy)
@@ -38,12 +38,15 @@
 
 #include <iterator>
 #include <iostream>
+#include <rak/algorithm.h>
 #include <rak/functional.h>
+#include <rak/string_manip.h>
 
 #include "utils/sha1.h"
 
 #include "object.h"
 #include "object_stream.h"
+#include "object_static_map.h"
 
 namespace torrent {
 
@@ -63,6 +66,46 @@
   return !input->fail();
 }
 
+const char*
+object_read_bencode_c_value(const char* first, const char* last, int64_t& value) {
+  if (first == last)
+    return last;
+
+  bool neg = false;
+
+  if (*first == '-') {
+    neg = true;
+    first++;
+  }
+
+  value = 0;
+
+  while (first != last && *first >= '0' && *first <= '9')
+    value = value * 10 + (*first++ - '0');
+
+  if (neg)
+    value = -value;
+
+  return first;
+}
+
+const char*
+object_read_bencode_c_string(const char* first, const char* last, std::string& str) {
+  // Set the most-significant bit so that if there are no numbers in
+  // the input it will fail the length check, while "0" will shift the
+  // bit out.
+  unsigned int length = 0x1 << (std::numeric_limits<unsigned int>::digits - 1);
+
+  while (first != last && *first >= '0' && *first <= '9')
+    length = length * 10 + (*first++ - '0');
+
+  if (length + 1 > (unsigned int)std::distance(first, last) || *first++ != ':')
+    return NULL;
+  
+  str = std::string(first, length);
+  return first + length;
+}
+
 // Could consider making this non-recursive, but they seldomly are
 // deep enough to make that worth-while.
 void
@@ -159,6 +202,98 @@
   object->clear();
 }
 
+const char*
+object_read_bencode_c(const char* first, const char* last, Object* object, uint32_t depth) {
+  if (first == last)
+    return NULL;
+
+  switch (*first) {
+  case 'i':
+    *object = Object::create_value();
+    first = object_read_bencode_c_value(first + 1, last, object->as_value());
+
+    if (first == NULL || first == last || *first++ != 'e')
+      break;
+
+    return first;
+
+  case 'l':
+    if (++depth >= 1024)
+      break;
+
+    first++;
+    *object = Object::create_list();
+
+    while (first != NULL && first != last) {
+      if (*first == 'e')
+	return first + 1;
+
+      Object::list_iterator itr = object->as_list().insert(object->as_list().end(), Object());
+      first = object_read_bencode_c(first, last, &*itr, depth);
+
+      // The unordered flag is inherited also from list elements who
+      // have been marked as unordered, though e.g. unordered strings
+      // in the list itself does not cause this flag to be set.
+      if (itr->flags() & Object::flag_unordered)
+        object->set_internal_flags(Object::flag_unordered);
+    }
+
+    break;
+
+  case 'd': {
+    if (++depth >= 1024)
+      break;
+
+    first++;
+    *object = Object::create_map();
+
+    Object::string_type prev;
+
+    while (first != NULL && first != last) {
+      if (*first == 'e')
+	return first + 1;
+
+      Object::string_type str;
+
+      if ((first = object_read_bencode_c_string(first, last, str)) == NULL)
+	break;
+
+      // We do not set flag_unordered if the first key was zero
+      // length, while multiple zero length keys will trigger the
+      // unordered_flag.
+      if (str <= prev && !object->as_map().empty())
+        object->set_internal_flags(Object::flag_unordered);
+
+      Object* value = &object->as_map()[str];
+      first = object_read_bencode_c(first, last, value, depth);
+
+      if (value->flags() & Object::flag_unordered)
+        object->set_internal_flags(Object::flag_unordered);
+
+      str.swap(prev);
+    }
+
+    break;
+  }
+
+  default:
+    if (*first >= '0' && *first <= '9')
+      return object_read_bencode_c_string(first, last, (*object = Object::create_string()).as_string());
+
+    break;
+  }
+
+  object->clear();
+  return NULL;
+}
+
+// TODO: Optimize this.
+const char*
+object_read_bencode_skip_c(const char* first, const char* last, uint32_t depth) {
+  torrent::Object obj;
+  return object_read_bencode_c(first, last, &obj, depth);
+}
+
 // Would be nice to have a straight stream to hash conversion.
 std::string
 object_sha1(const Object* object) {
@@ -201,7 +336,6 @@
 object_write_bencode_c_string(object_write_data_t* output, const char* srcData, uint32_t srcLength) {
   do {
     uint32_t len = std::min<uint32_t>(srcLength, std::distance(output->pos, output->buffer.second));
-
     std::memcpy(output->pos, srcData, len);
 
     output->pos += len;
@@ -257,24 +391,32 @@
   object_write_bencode_c_string(output, first, 20 - std::distance(buffer, first));
 }
 
+inline void
+object_write_bencode_c_obj_value(object_write_data_t* output, int64_t value) {
+  object_write_bencode_c_char(output, 'i');
+  object_write_bencode_c_value(output, value);
+  object_write_bencode_c_char(output, 'e');
+}
+
+inline void
+object_write_bencode_c_obj_string(object_write_data_t* output, const char* data, uint32_t size) {
+  object_write_bencode_c_value(output, size);
+  object_write_bencode_c_char(output, ':');
+  object_write_bencode_c_string(output, data, size);
+}
+
+inline void
+object_write_bencode_c_obj_string(object_write_data_t* output, const std::string& str) {
+  object_write_bencode_c_obj_string(output, str.c_str(), str.size());
+}
+
 void
 object_write_bencode_c_object(object_write_data_t* output, const Object* object, uint32_t skip_mask) {
   switch (object->type()) {
-  case Object::TYPE_NONE:
-    break;
+  case Object::TYPE_NONE: break;
+  case Object::TYPE_VALUE: object_write_bencode_c_obj_value(output, object->as_value()); break;
+  case Object::TYPE_STRING: object_write_bencode_c_obj_string(output, object->as_string()); break;
 
-  case Object::TYPE_VALUE:
-    object_write_bencode_c_char(output, 'i');
-    object_write_bencode_c_value(output, object->as_value());
-    object_write_bencode_c_char(output, 'e');
-    break;
-
-  case Object::TYPE_STRING:
-    object_write_bencode_c_value(output, object->as_string().size());
-    object_write_bencode_c_char(output, ':');
-    object_write_bencode_c_string(output, object->as_string().c_str(), object->as_string().size());
-    break;
-
   case Object::TYPE_LIST:
     object_write_bencode_c_char(output, 'l');
 
@@ -295,10 +437,7 @@
       if (itr->second.flags() & skip_mask)
         continue;
 
-      object_write_bencode_c_value(output, itr->first.size());
-      object_write_bencode_c_char(output, ':');
-      object_write_bencode_c_string(output, itr->first.c_str(), itr->first.size());
-
+      object_write_bencode_c_obj_string(output, itr->first);
       object_write_bencode_c_object(output, &itr->second, skip_mask);
     }
 
@@ -370,4 +509,230 @@
   return buffer;
 }
 
+//
+// static_map operations:
+//
+
+const char*
+staticMap_read_bencode_c(const char* first,
+                         const char* last,
+                         static_map_entry_type* entry_values,
+                         const static_map_mapping_type* first_key,
+                         const static_map_mapping_type* last_key) {
+  // Temp hack... validate that we got valid bencode data...
+//   {
+//     torrent::Object obj;
+//     if (object_read_bencode_c(first, last, &obj) != last) {
+//       std::string escaped = rak::copy_escape_html(first, last);
+
+//       char buffer[1024];
+//       sprintf(buffer, "Verified wrong, %u, '%u', '%s'.", std::distance(first, last), (unsigned int)*first, escaped.c_str());
+
+//       throw torrent::bencode_error("Invalid bencode data.");
+//     }
+//   }
+
+  if (first == last || *first++ != 'd')
+    throw torrent::bencode_error("Invalid bencode data.");
+  
+  static_map_stack_type stack[8];
+  static_map_stack_type* stack_itr = stack;
+  stack_itr->clear();
+
+  char current_key[static_map_mapping_type::max_key_size + 1] = "";
+
+  while (first != last && first != NULL) {
+    // End a dictionary/list or the whole stream.
+    if (*first == 'e') {
+      first++;
+
+      if (stack_itr == stack)
+        return first;
+
+      stack_itr--;
+    }
+
+    std::string key_str;
+
+    if ((first = object_read_bencode_c_string(first, last, key_str)) == NULL)
+      break;
+
+    // Optimze this buy directly copying into 'current_key'.
+    //
+    // The max length of 'current_key' is one char more than the
+    // mapping key so any bencode which exceeds that will always fail
+    // to find a match.
+    unsigned int key_size = std::min(key_str.size(), static_map_mapping_type::max_key_size - stack_itr->next_key);
+
+    memcpy(current_key + stack_itr->next_key, key_str.c_str(), key_size);
+    current_key[stack_itr->next_key + key_size] = '\0';
+
+    // Locate the right key. Optimize this by remembering previous
+    // position...
+    static_map_key_search_result key_search = find_key_match(first_key, last_key, current_key);    
+
+    // We're not interest in this object, skip it.
+    if (key_search.second == 0) { first = object_read_bencode_skip_c(first, last); continue; }
+
+    // Check if we're interested in any dictionaries/lists entries
+    // under this key.
+    //
+    // Note that 'find_key_match' only returns 'key_search.second !=
+    // 0' for keys where the next characters are '\0', '::' or '[]'.
+    switch (key_search.first->key[key_search.second]) {
+    case '\0':
+      first = object_read_bencode_c(first, last, &entry_values[key_search.first->index].object);
+      first_key = key_search.first + 1;
+      break;
+
+    case ':':
+      if (first == last)
+        break;
+
+      // The bencode object isn't a list. This should either skip it
+      // or produce an error.
+      if (*first++ != 'd')
+        throw torrent::bencode_error("Invalid bencode data.");
+
+      stack_itr++;
+      stack_itr->set_key_index((stack_itr - 1)->next_key, key_search.second, 2);
+
+      current_key[key_search.second] = ':';
+      current_key[key_search.second + 1] = ':';
+      break;
+
+    case '[':
+    {
+      if (first == last)
+        break;
+
+      // The bencode object isn't a list. This should either skip it
+      // or produce an error.
+      if (*first++ != 'l')
+        throw torrent::bencode_error("Invalid bencode data.");
+
+      first_key = key_search.first;
+
+      while (first != last) {
+        if (*first == 'e') {
+          first++;
+          break;
+        }
+
+        if ((first = object_read_bencode_c(first, last, &entry_values[first_key->index].object)) == NULL)
+          break;
+
+        if (++first_key == last_key || strcmp(first_key->key, (first_key - 1)->key) != 0) {
+          while (first != last) {
+            if (*first == 'e') {
+              first++;
+              break;
+            }
+
+            first = object_read_bencode_skip_c(first, last);
+          }
+
+          break;
+        }
+      }
+
+      break;
+    }
+    default:
+      throw internal_error("staticMap_read_bencode_c: key_search.first->key[base] returned invalid character.");
+    };
+  }
+  
+  throw torrent::bencode_error("Invalid bencode data.");
 }
+
+void
+staticMap_write_bencode_c_values(object_write_data_t* output,
+                                 const static_map_entry_type* entry_values,
+                                 const static_map_mapping_type* first_key,
+                                 const static_map_mapping_type* last_key) {
+  static_map_stack_type stack[8];
+  static_map_stack_type* stack_itr = stack;
+  stack_itr->clear();
+
+  object_write_bencode_c_char(output, 'd');
+
+  const char* prev_key = NULL;
+
+  while (first_key != last_key) {
+    // Compare the keys to see if they are part of the same
+    // dictionaries/lists.
+    unsigned int base_size = rak::count_base(first_key->key, first_key->key + stack_itr->next_key,
+                                             prev_key, prev_key + stack_itr->next_key);
+
+    while (base_size < stack_itr->next_key) {
+      object_write_bencode_c_char(output, 'e');
+      stack_itr--;
+    }
+
+    const char* key_begin = first_key->key + stack_itr->next_key;
+
+    do {
+      const char* key_end = first_key->find_key_end(key_begin);
+
+      object_write_bencode_c_obj_string(output, key_begin, std::distance(key_begin, key_end));
+    
+      // Check if '::' or '[' were found...
+      if (*key_end == ':' && *(key_end + 1) == ':') {
+        (++stack_itr)->set_key_index(std::distance(first_key->key, key_begin), std::distance(first_key->key, key_end), 2);
+
+        key_begin = key_end + 2;
+        object_write_bencode_c_char(output, 'd');
+        continue;
+      }
+
+      // Handle "foo[]..." entries. We iterate once for each "foo[]"
+      // found in the key list.
+      if (*key_end == '[' && *(key_end + 1) == ']') {
+        (++stack_itr)->set_key_index(std::distance(first_key->key, key_begin), std::distance(first_key->key, key_end), 2);
+
+        key_begin = key_end + 2;
+        object_write_bencode_c_char(output, 'l');
+        continue;
+      }
+
+      // We have a leaf object.
+      if (*key_end != '\0')
+        throw internal_error("static_map_type key is invalid.");
+
+      object_write_bencode_c_object(output, &entry_values[first_key->index].object, 0);
+
+      break;
+    } while (true);
+
+    prev_key = (first_key++)->key;
+  }
+
+  do {
+    object_write_bencode_c_char(output, 'e');
+  } while (stack_itr-- != stack);
+}
+
+object_buffer_t
+staticMap_write_bencode_c_wrap(object_write_t writeFunc,
+                               void* data,
+                               object_buffer_t buffer,
+                               const static_map_entry_type* entry_values,
+                               const static_map_mapping_type* first_key,
+                               const static_map_mapping_type* last_key) {
+  object_write_data_t output;
+  output.writeFunc = writeFunc;
+  output.data      = data;
+  output.buffer    = buffer;
+  output.pos       = buffer.first;
+
+  staticMap_write_bencode_c_values(&output, entry_values, first_key, last_key);
+
+  // Don't flush the buffer.
+  if (output.pos == output.buffer.first)
+    return output.buffer;
+
+  return output.writeFunc(output.data, object_buffer_t(output.buffer.first, output.pos));
+}
+
+}
Index: libtorrent/src/torrent/object_stream.h
===================================================================
--- libtorrent/src/torrent/object_stream.h	(revision 1134)
+++ libtorrent/src/torrent/object_stream.h	(working copy)
@@ -48,7 +48,9 @@
 // Assumes the stream's locale has been set to POSIX or C.  Max depth
 // is 1024, this ensures files consisting of only 'l' don't segfault
 // the client.
-void object_read_bencode(std::istream* input, Object* object, uint32_t depth = 0) LIBTORRENT_EXPORT;
+void        object_read_bencode(std::istream* input, Object* object, uint32_t depth = 0) LIBTORRENT_EXPORT;
+const char* object_read_bencode_c(const char* first, const char* last, Object* object, uint32_t depth = 0) LIBTORRENT_EXPORT;
+const char* object_read_bencode_skip_c(const char* first, const char* last, uint32_t depth = 0) LIBTORRENT_EXPORT;
 
 std::istream& operator >> (std::istream& input, Object& object) LIBTORRENT_EXPORT;
 std::ostream& operator << (std::ostream& output, const Object& object) LIBTORRENT_EXPORT;
@@ -70,6 +72,47 @@
 object_buffer_t object_write_to_buffer(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT;
 object_buffer_t object_write_to_sha1(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT;
 object_buffer_t object_write_to_stream(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT;
+
+//
+// static_map operations:
+//
+
+template<typename tmpl_key_type, size_t tmpl_length>
+class static_map_type;
+struct static_map_mapping_type;
+struct static_map_entry_type;
+
+// Convert buffer to static key map. Inlined because we don't want
+// a separate wrapper function for each template argument.
+template<typename tmpl_key_type, size_t tmpl_length>
+inline const char*
+staticMap_read_bencode(const char* first, const char* last,
+                       static_map_type<tmpl_key_type, tmpl_length>& object) {
+  return staticMap_read_bencode_c(first, last, object.values(), object.keys, object.keys + object.size);
+};
+
+template <typename tmpl_key_type, size_t tmpl_length>
+inline object_buffer_t
+staticMap_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer,
+                          const static_map_type<tmpl_key_type, tmpl_length>& object) {
+  return staticMap_write_bencode_c_wrap(writeFunc, data, buffer, object.values(), object.keys, object.keys + object.size);
 }
 
+const char*
+staticMap_read_bencode_c(const char* first,
+                         const char* last,
+                         static_map_entry_type* entry_values,
+                         const static_map_mapping_type* first_key,
+                         const static_map_mapping_type* last_key) LIBTORRENT_EXPORT;
+
+object_buffer_t
+staticMap_write_bencode_c_wrap(object_write_t writeFunc,
+                               void* data,
+                               object_buffer_t buffer,
+                               const static_map_entry_type* entry_values,
+                               const static_map_mapping_type* first_key,
+                               const static_map_mapping_type* last_key) LIBTORRENT_EXPORT;
+
+}
+
 #endif
Index: libtorrent/src/torrent/object_static_map.cc
===================================================================
--- libtorrent/src/torrent/object_static_map.cc	(revision 0)
+++ libtorrent/src/torrent/object_static_map.cc	(revision 0)
@@ -0,0 +1,72 @@
+// libTorrent - BitTorrent library
+// Copyright (C) 2005-2007, Jari Sundell
+//
+// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+//
+// Contact:  Jari Sundell <jaris@ifi.uio.no>
+//
+//           Skomakerveien 33
+//           3185 Skoppum, NORWAY
+
+#include "config.h"
+
+#include <rak/algorithm.h>
+
+#include "object_static_map.h"
+
+namespace torrent {
+
+const static_map_key_search_result
+find_key_match(const static_map_mapping_type* first, const static_map_mapping_type* last, const char* key) {
+  unsigned int key_length = strlen(key);
+  const static_map_mapping_type* itr = first;
+
+  while (itr != last) {
+    //int result = strcmp(key, itr->key);
+    unsigned int base = rak::count_base(key, key + key_length, itr->key, itr->key + itr->max_key_size);
+
+    if (key[base] != '\0') {
+      // Return not found here if we know the entry won't come after
+      // this.
+      itr++;
+      continue;
+    }
+
+    if (itr->key[base] == '\0' ||
+        (itr->key[base] == ':' && itr->key[base + 1] == ':') ||
+        (itr->key[base] == '[' && itr->key[base + 1] == ']'))
+      return static_map_key_search_result(itr, base);
+
+    break;
+  }
+
+  return static_map_key_search_result(first, 0);
+}
+
+}
Index: libtorrent/src/torrent/Makefile.am
===================================================================
--- libtorrent/src/torrent/Makefile.am	(revision 1134)
+++ libtorrent/src/torrent/Makefile.am	(working copy)
@@ -26,6 +26,8 @@
 	http.h \
 	object.cc \
 	object.h \
+	object_static_map.cc \
+	object_static_map.h \
 	object_stream.cc \
 	object_stream.h \
 	path.cc \
@@ -68,6 +70,7 @@
 	hash_string.h \
 	http.h \
 	object.h \
+	object_static_map.h \
 	object_stream.h \
 	path.h \
 	poll.h \
Index: libtorrent/src/protocol/extensions.cc
===================================================================
--- libtorrent/src/protocol/extensions.cc	(revision 1134)
+++ libtorrent/src/protocol/extensions.cc	(working copy)
@@ -37,7 +37,7 @@
 #include "config.h"
 
 #include <limits>
-#include <sstream>
+#include <stdarg.h>
 
 #include <cstdio>
 
@@ -46,21 +46,55 @@
 #include "protocol/peer_connection_base.h"
 #include "torrent/connection_manager.h"
 #include "torrent/object.h"
+#include "torrent/object_static_map.h"
 #include "torrent/object_stream.h"
 #include "torrent/peer/connection_list.h"
 #include "torrent/peer/peer_info.h"
-#include "tracker/tracker_http.h"
 #include "manager.h"
 
 #include "extensions.h"
 
 namespace torrent {
 
-const char* ProtocolExtension::message_keys[] = {
-  "HANDSHAKE",
-  "ut_pex",
+enum ext_pex_keys {
+  key_pex_added,
+  key_pex_LAST
 };
 
+enum ext_handshake_keys {
+  key_e,
+  key_m_utPex,
+  key_p,
+  key_reqq,
+  key_v,
+  key_handshake_LAST
+};
+
+typedef static_map_type<ext_pex_keys, key_pex_LAST> ExtPEXMessage;
+typedef static_map_type<ext_handshake_keys, key_handshake_LAST> ExtHandshakeMessage;
+
+template <>
+const ExtPEXMessage::key_list_type ExtPEXMessage::keys = {
+  { key_pex_added,    "added" },
+};
+
+template <>
+const ExtHandshakeMessage::key_list_type ExtHandshakeMessage::keys = {
+  { key_e,            "e" },
+  { key_m_utPex,      "m::ut_pex" },
+  { key_p,            "p" },
+  { key_reqq,         "reqq" },
+  { key_v,            "v" },
+};
+
+// It's a meta entry since index 0 is reserved for the handshake but
+// won't actually be used by setting it to key_handshake_LAST it can
+// be caught if it is used accidentally
+ext_handshake_keys message_keys[ProtocolExtension::FIRST_INVALID] = {
+  key_handshake_LAST,     // Handshake, not actually used.
+  key_m_utPex,
+};
+
 void
 ProtocolExtension::cleanup() {
 //   if (is_default())
@@ -105,23 +139,21 @@
 
 DataBuffer
 ProtocolExtension::generate_handshake_message() {
-  Object map = Object::create_map();
-  Object message = Object::create_map();
+  ExtHandshakeMessage message;
 
-  map.insert_key(message_keys[UT_PEX], is_local_enabled(UT_PEX) ? 1 : 0);
-
   // Add "e" key if encryption is enabled, set it to 1 if we require
   // encryption for incoming connections, or 0 otherwise.
   if ((manager->connection_manager()->encryption_options() & ConnectionManager::encryption_allow_incoming) != 0)
-    message.insert_key("e", (manager->connection_manager()->encryption_options() & ConnectionManager::encryption_require) != 0);
+    message[key_e] = (manager->connection_manager()->encryption_options() & ConnectionManager::encryption_require) != 0;
 
-  message.insert_key("m", map);
-  message.insert_key("p", manager->connection_manager()->listen_port());
-  message.insert_key("v", "libTorrent " VERSION);
-  message.insert_key("reqq", 2048);  // maximum request queue size
+  message[key_p] = manager->connection_manager()->listen_port();
+  message[key_v] = std::string("libTorrent " VERSION);
+  message[key_reqq] = 2048;  // maximum request queue size
 
+  message[key_m_utPex] = is_local_enabled(UT_PEX) ? UT_PEX : 0;
+
   char buffer[1024];
-  object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), &message);
+  object_buffer_t result = staticMap_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), message);
 
   int length = result.second - buffer;
   char* copy = new char[length];
@@ -130,21 +162,33 @@
   return DataBuffer(copy, copy + length);
 }
 
-DataBuffer
-ProtocolExtension::generate_toggle_message(ProtocolExtension::MessageType t, bool on) {
-  // TODO: Check if we're accepting this message type?
+inline DataBuffer
+ProtocolExtension::build_bencode(size_t maxLength, const char* format, ...) {
+  char* b = new char[maxLength];
 
-  // Manually create bencoded map { "m" => { message_keys[t] => on ? t : 0 } }
-  char* b = new char[32];
-  unsigned int length = snprintf(b, 32, "d1:md%zu:%si%deee", strlen(message_keys[t]), message_keys[t], on ? t : 0);
+  va_list args;
+  va_start(args, format);
+  unsigned int length = vsnprintf(b, maxLength, format, args);
+  va_end(args);
 
-  if (length > 32)
-    throw internal_error("ProtocolExtension::toggle_message wrote past buffer.");
+  if (length > maxLength)
+    throw internal_error("ProtocolExtension::build_bencode wrote past buffer.");
 
   return DataBuffer(b, b + length);
 }
 
 DataBuffer
+ProtocolExtension::generate_toggle_message(MessageType t, bool on) {
+  // TODO: Check if we're accepting this message type?
+
+  // Manually create bencoded map { "m" => { message_keys[t] => on ? t : 0 } }
+  return build_bencode(32, "d1:md%zu:%si%deee",
+                       strlen(ExtPEXMessage::keys[message_keys[t]].key),
+                       ExtPEXMessage::keys[message_keys[t]].key,
+                       on ? t : 0);
+}
+
+DataBuffer
 ProtocolExtension::generate_ut_pex_message(const PEXList& added, const PEXList& removed) {
   if (added.empty() && removed.empty())
     return DataBuffer();
@@ -195,37 +239,30 @@
 
 void
 ProtocolExtension::read_done() {
-  if (m_readType == SKIP_EXTENSION) {
-    delete [] m_read;
-    m_read = NULL;
-    return;
-  }
+  try {
+    switch(m_readType) {
+    case SKIP_EXTENSION:
+      break;
 
-  std::stringstream s(std::string(m_read, m_readPos));
-  s.imbue(std::locale::classic());
+    case HANDSHAKE:
+      parse_handshake();
+      break;
 
-  delete [] m_read;
-  m_read = NULL;
+    case UT_PEX:
+      parse_ut_pex();
+      break;
 
-  Object message;
-  s >> message;
+    default:
+      throw internal_error("ProtocolExtension::read_done called with invalid extension type.");
+    }
 
-  if (s.fail() || !message.is_map())
-    throw communication_error("Invalid extension message.");
+  } catch (bencode_error& e) {
+    // Ignore malformed messages.
+  }
 
-  switch(m_readType) {
-  case HANDSHAKE:
-    parse_handshake(message);
-    break;
+  delete [] m_read;
+  m_read = NULL;
 
-  case UT_PEX:
-    parse_ut_pex(message);
-    break;
-
-  default:
-    throw internal_error("ProtocolExtension::down_extension_finished called with invalid extension type.");
-  }
-
   m_readType = FIRST_INVALID;
   m_flags |= flag_received_ext;
 }
@@ -242,24 +279,22 @@
 }
 
 void
-ProtocolExtension::parse_handshake(const Object& message) {
-  if (message.has_key_map("m")) {
-    const Object& idMap = message.get_key("m");
+ProtocolExtension::parse_handshake() {
+  ExtHandshakeMessage message;
+  staticMap_read_bencode(m_read, m_readPos, message);
 
-    for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) {
-      if (!idMap.has_key_value(message_keys[t]))
-        continue;
+  for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) {
+    if (!message[message_keys[t]].is_value())
+      continue;
 
-      uint8_t id = idMap.get_key_value(message_keys[t]);
+    uint8_t id = message[message_keys[t]].as_value();
 
-      set_remote_supported(t);
+    set_remote_supported(t);
 
-      if (id != m_idMap[t - 1]) {
-        peer_toggle_remote(t, id != 0);
+    if (id != m_idMap[t - 1]) {
+      peer_toggle_remote(t, id != 0);
 
-        m_idMap[t - 1] = id;
-      }
-
+      m_idMap[t - 1] = id;
     }
   }
 
@@ -271,29 +306,32 @@
         unset_local_enabled(t);
   }
 
-  if (message.has_key_value("p")) {
-    uint16_t port = message.get_key_value("p");
+  if (message[key_p].is_value()) {
+    uint16_t port = message[key_p].as_value();
 
     if (port > 0)
       m_peerInfo->set_listen_port(port);
   }
 
-  if (message.has_key_value("reqq"))
-    m_maxQueueLength = message.get_key_value("reqq");
+  if (message[key_reqq].is_value())
+    m_maxQueueLength = message[key_reqq].as_value();
 
   m_flags &= ~flag_initial_handshake;
 }
 
 void
-ProtocolExtension::parse_ut_pex(const Object& message) {
+ProtocolExtension::parse_ut_pex() {
   // Ignore message if we're still in the handshake (no connection
   // yet), or no peers are present.
 
+  ExtPEXMessage message;
+  staticMap_read_bencode(m_read, m_readPos, message);
+
   // TODO: Check if pex is enabled?
-  if (!message.has_key_string("added"))
+  if (!message[key_pex_added].is_string())
     return;
 
-  const std::string& peers = message.get_key_string("added");
+  const std::string& peers = message[key_pex_added].as_string();
   if (peers.empty())
     return;
 
Index: libtorrent/src/protocol/extensions.h
===================================================================
--- libtorrent/src/protocol/extensions.h	(revision 1134)
+++ libtorrent/src/protocol/extensions.h	(working copy)
@@ -46,6 +46,13 @@
 #include "download/download_info.h"
 #include "net/data_buffer.h"
 
+// Not really important, so no need to make this a configure check.
+#ifdef __GNUC__
+#define ATTRIBUTE_PRINTF(num) __attribute__ ((format (printf, num, num+1)))
+#else
+#define ATTRIBUTE_PRINTF(num)
+#endif
+
 namespace torrent {
 
 class ProtocolExtension {
@@ -71,8 +78,6 @@
   static const int    flag_local_enabled_base    = 1<<8;
   static const int    flag_remote_supported_base = 1<<16;
 
-  static const char*  message_keys[FIRST_INVALID];
-
   // Number of extensions we support, not counting handshake.
   static const int    extension_count = FIRST_INVALID - HANDSHAKE - 1;
 
@@ -128,9 +133,11 @@
   void                reset()                          { std::memset(&m_idMap, 0, sizeof(m_idMap)); }
 
 private:
-  void                parse_handshake(const Object& message);
-  void                parse_ut_pex(const Object& message);
+  void                parse_handshake();
+  void                parse_ut_pex();
 
+  static DataBuffer   build_bencode(size_t maxLength, const char* format, ...) ATTRIBUTE_PRINTF(2);
+
   void                peer_toggle_remote(int type, bool active);
 
   // Map of IDs peer uses for each extension message type, excluding
Index: libtorrent/src/download/download_constructor.cc
===================================================================
--- libtorrent/src/download/download_constructor.cc	(revision 1134)
+++ libtorrent/src/download/download_constructor.cc	(working copy)
@@ -36,6 +36,7 @@
 
 #include "config.h"
 
+#include <cstdio>
 #include <cstring>
 #include <string.h>
 #include <rak/functional.h>

