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,16 @@
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "torrent/object.h"
+
+class ObjectStaticMapTest : public CppUnit::TestFixture {
+  CPPUNIT_TEST_SUITE(ObjectStaticMapTest);
+  CPPUNIT_TEST(test_basics);
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+  void setUp() {}
+  void tearDown() {}
+
+  void test_basics();
+};
+
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,68 @@
+#include "config.h"
+
+#include <torrent/object.h>
+#include "torrent/object_static_map.h"
+
+#import "object_static_map_test.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ObjectStaticMapTest);
+
+// Possible bencode keys in a DHT message.
+enum 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,
+};
+
+
+typedef torrent::static_map_type<keys, key_LAST> test_map_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_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[0]" },
+  { key_e_1,        "e[1]" },
+
+  { 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" },
+};
+
+void
+ObjectStaticMapTest::test_basics() {
+  test_map_type test_map;
+
+}
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)
@@ -116,7 +116,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 +148,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[0]" },
+  { key_e_1,        "e[1]" },
+
+  { 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));
-
-  if (end == compact)
+  std::string nodes = m_router->get_closest_nodes(*HashString::cast_from(target));
+  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);
+  //  reply.data_end += reply[key_r_token].as_string().length();
 
-  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");
@@ -298,33 +321,34 @@
   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));
+//    SimpleString nodes = m_router->get_closest_nodes(*info_hash);
 
     if (end == compact)
       throw dht_error(dht_error_generic, "No peers nor nodes");
 
-    reply.insert_key("nodes", std::string(compact, end));
-
+    reply[key_r_nodes] = std::string(compact, end);
   } 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 +375,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 +403,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 +444,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 +517,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 +559,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 +680,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 +696,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 +731,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 +741,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 +760,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,84 @@
+// 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 <torrent/object.h>
+
+namespace torrent {
+
+struct static_map_mapping_type {
+  uint32_t   index;
+  const char key[16];
+};
+  
+struct static_map_entry_type {
+  torrent::Object object;
+};
+
+// template <size_t tmpl_index, const char* tmpl_key_str>
+// struct static_map_key {
+//   static const static_map_mapping_type result = { tmpl_index, tmpl_key_str };
+// };
+
+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;
+};
+
+}
+
+#endif
Index: libtorrent/src/torrent/object_stream.cc
===================================================================
--- libtorrent/src/torrent/object_stream.cc	(revision 1134)
+++ libtorrent/src/torrent/object_stream.cc	(working copy)
@@ -63,6 +63,18 @@
   return !input->fail();
 }
 
+// Object
+// object_get_sstring(const char** buffer) {
+//   /*const*/ char* next;
+//   size_t length = strtoumax(*buffer, &next, 10);
+
+//   if (next == *buffer || *next != ':')
+//     return Object();
+
+//   *buffer = next + 1 + length;
+//   return Object(next + 1, length);
+// }
+
 // Could consider making this non-recursive, but they seldomly are
 // deep enough to make that worth-while.
 void
@@ -197,6 +209,133 @@
   char* pos;
 };
 
+// const char*
+// staticMap_read_bencode_c(const char* buffer, const char* bufferEnd, uint32_t depth, Object* values, const StaticMapKeys& keys, bool discard) {
+//   if (buffer >= bufferEnd)
+//     return bufferEnd;
+
+//   // Undecoded bencode object.
+//   if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE) {
+//     const char* begin = buffer;
+//     buffer = staticMap_read_bencode_c(buffer, bufferEnd, ++depth, values, keys, true);
+//     values[keys.index_begin()] = SimpleString(begin, buffer - begin);
+//     return buffer;
+//   }
+
+//   if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE_LIST && *buffer != 'l')
+//     discard = true;
+
+//   switch (*buffer) {
+//   case 'i': {
+//     char* next;
+//     intmax_t value = strtoimax(++buffer, &next, 10);
+
+//     if (next == buffer || next > bufferEnd || *next != 'e')
+//       break;
+
+//     if (!discard && keys.type() == StaticMapKeys::TYPE_VALUE)
+//       values[keys.index_begin()] = (int64_t)value;
+
+//     return next + 1;
+//   }
+
+//   case 'l': {
+//     ++buffer;
+//     if (++depth >= 1024)
+//       break;
+
+//     // Want undecoded bencode list: find end of list.
+//     if (!discard && keys.type() == StaticMapKeys::TYPE_BENCODE_LIST) {
+//       const char* end = buffer;
+//       while (end < bufferEnd && *end != 'e')
+//         end = staticMap_read_bencode_c(end, bufferEnd, depth, values, keys, true);
+
+//       values[keys.index_begin()] = SimpleString(buffer, end - buffer);
+//       return ++end;
+//     }
+
+//     StaticMapKeys::const_iterator itr = keys.begin();
+//     while (buffer != bufferEnd) {
+//       if (*buffer == 'e')
+//         return ++buffer;
+
+//       discard |= itr == keys.end();
+//       buffer = staticMap_read_bencode_c(buffer, bufferEnd, depth, values, discard ? keys : *itr, discard);
+
+//       if (itr != keys.end())
+//         ++itr;
+//     }
+
+//     break;
+//   }
+
+//   case 'd': {
+//     ++buffer;
+//     if (++depth >= 1024)
+//       break;
+
+//     StaticMapKeys::const_iterator itr = keys.begin();
+//     SimpleString last;
+//     bool discardThis = discard;
+
+//     while (buffer != bufferEnd) {
+//       if (*buffer == 'e')
+//         return ++buffer;
+    
+//       Object keyObj = object_get_sstring(&buffer);
+//       if (!keyObj.is_sstring())
+//         break;
+
+//       SimpleString key = keyObj.as_sstring();
+//       if (key.end() >= bufferEnd)
+//         break;
+
+//       if (key < last) {
+//         itr = keys.begin();
+//         discardThis = discard;
+//       }
+
+//       discardThis |= itr == keys.end();
+//       int cmp = discardThis ? -1 : key.cmp(itr->key());
+//       while (cmp > 0) {
+//         if (++itr == keys.end()) {
+//           cmp = -1;
+//           discardThis = true;
+//           break;
+//         }
+
+//         cmp = key.cmp(itr->key());
+//       }
+
+//       buffer = staticMap_read_bencode_c(buffer, bufferEnd, depth, values, cmp ? keys : *itr, cmp);
+
+//       last = key;
+//     }
+
+//     break;
+//   }
+
+//   default:
+//     if (*buffer < '0' || *buffer > '9')
+//       break;
+
+//     Object strObj = object_get_sstring(&buffer);
+//     if (!strObj.is_sstring())
+//       break;
+
+//     SimpleString str = strObj.as_sstring();
+//     if (str.end() >= bufferEnd)
+//       break;
+
+//     if (!discard && keys.type() == StaticMapKeys::TYPE_VALUE)
+//       values[keys.index_begin()] = str;
+
+//     return str.end();
+//   }
+
+//   throw bencode_error("Invalid bencode data.");
+// }
+
 void
 object_write_bencode_c_string(object_write_data_t* output, const char* srcData, uint32_t srcLength) {
   do {
@@ -344,6 +483,112 @@
   return output.writeFunc(output.data, object_buffer_t(output.buffer.first, output.pos));
 }
 
+// void
+// staticMap_write_bencode_c_values(object_write_data_t* output, const Object* values, const StaticMapKeys& keys) {
+//   if (keys.type() == StaticMapKeys::TYPE_LIST) {
+//     size_t indexEnd = keys.index_begin();
+//     while (indexEnd < keys.index_end() && values[indexEnd].type() != Object::TYPE_NONE)
+//       indexEnd++;
+
+//     // Empty list? Drop it. Sparse lists are not possible so only check first element.
+//     if (indexEnd == keys.index_begin())
+//       return;
+
+//     object_write_bencode_c_char(output, 'l');
+//     StaticMapKeys::const_iterator itr = keys.begin();
+//     size_t index = keys.index_begin();
+//     while (index < indexEnd) {
+//       staticMap_write_bencode_c_values(output, values, *itr);
+//       index = itr->index_end();
+//       if (++itr == keys.end() && index != indexEnd)
+//         throw internal_error("staticMap_write_bencode_c_values reached end of list before end of index list.");
+//     }
+//     object_write_bencode_c_char(output, 'e');
+
+//   } else if (keys.type() == StaticMapKeys::TYPE_DICT) {
+//     // Find next non-empty entry.
+//     size_t next = keys.index_begin();
+//     while (values[next].type() == Object::TYPE_NONE)
+//       if (++next == keys.index_end())
+//         return;
+
+//     object_write_bencode_c_char(output, 'd');
+//     StaticMapKeys::const_iterator itr = keys.begin();
+//     while (next < keys.index_end()) {
+//       while (itr->index_end() <= next)
+//         if (++itr == keys.end())
+//           throw internal_error("staticMap_write_bencode_c_values reached end of keys before end of index list.");
+
+//       object_write_bencode_c_value(output, itr->key().size());
+//       object_write_bencode_c_char(output, ':');
+//       object_write_bencode_c_string(output, itr->key().c_str(), itr->key().size());
+
+//       staticMap_write_bencode_c_values(output, values, *itr);
+
+//       next = itr->index_end();
+//       while (next < keys.index_end() && values[next].type() == Object::TYPE_NONE)
+//         ++next;
+//     }
+//     object_write_bencode_c_char(output, 'e');
+
+//   // Undecoded bencode value.
+//   } else if (keys.type() == StaticMapKeys::TYPE_BENCODE) {
+//     SimpleString value = values[keys.index_begin()].as_sstring();
+//     object_write_bencode_c_string(output, value.c_str(), value.size());
+
+//   } else if (keys.type() == StaticMapKeys::TYPE_BENCODE_LIST) {
+//     SimpleString value = values[keys.index_begin()].as_sstring();
+//     object_write_bencode_c_char(output, 'l');
+//     object_write_bencode_c_string(output, value.c_str(), value.size());
+//     object_write_bencode_c_char(output, 'e');
+
+//   } else if (keys.type() != StaticMapKeys::TYPE_VALUE) {
+//     throw internal_error("staticMap_write_bencode_c_values received key keys with invalid values type.");
+
+//   } else if (values[keys.index_begin()].type() == Object::TYPE_NONE) {
+
+//   } else if (values[keys.index_begin()].type() == Object::TYPE_VALUE) {
+//     object_write_bencode_c_char(output, 'i');
+//     object_write_bencode_c_value(output, values[keys.index_begin()].as_value());
+//     object_write_bencode_c_char(output, 'e');
+
+//   } else if (values[keys.index_begin()].type() == Object::TYPE_SSTRING) {
+//     SimpleString value = values[keys.index_begin()].as_sstring();
+//     object_write_bencode_c_value(output, value.size());
+//     object_write_bencode_c_char(output, ':');
+//     object_write_bencode_c_string(output, value.c_str(), value.size());
+
+//   } else {
+//     throw internal_error("staticMap_write_bencode_c_values received key keys with invalid values type.");
+//   }
+// }
+
+// object_buffer_t
+// staticMap_write_bencode_c_wrap(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* values, const StaticMapKeys& map) {
+//   object_write_data_t output;
+//   output.writeFunc = writeFunc;
+//   output.data      = data;
+//   output.buffer    = buffer;
+//   output.pos       = buffer.first;
+
+//   staticMap_write_bencode_c_values(&output, values, map);
+// #ifdef USE_EXTRA_DEBUG
+//   std::istringstream sstream; 
+//   sstream.imbue(std::locale::classic()); 
+//   sstream.str(std::string(output.buffer.first, output.pos));
+//   Object request; 
+//   sstream >> request; 
+//   if (sstream.fail())
+//     throw internal_error("staticMap_write_bencode_c_wrap failed to create valid bencode format.");
+// #endif
+
+//   // 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));
+// }
+
 object_buffer_t
 object_write_to_buffer(void* data, object_buffer_t buffer) {
   if (buffer.first == buffer.second)
Index: libtorrent/src/torrent/object_stream.h
===================================================================
--- libtorrent/src/torrent/object_stream.h	(revision 1134)
+++ libtorrent/src/torrent/object_stream.h	(working copy)
@@ -43,6 +43,11 @@
 
 namespace torrent {
 
+template<typename tmpl_key_type, size_t tmpl_length>
+class static_map_type;
+struct static_map_mapping_type;
+struct static_map_entry_type;
+
 std::string object_sha1(const Object* object) LIBTORRENT_EXPORT;
 
 // Assumes the stream's locale has been set to POSIX or C.  Max depth
@@ -50,6 +55,23 @@
 // the client.
 void object_read_bencode(std::istream* input, Object* object, uint32_t depth = 0) LIBTORRENT_EXPORT;
 
+// 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* buffer, const char* bufferEnd, static_map_type<tmpl_key_type, tmpl_length>& map) {
+//   return staticMap_read_bencode_c(buffer, bufferEnd, 0, map.values(), map.map(), false);
+  return NULL;
+};
+
+// Internal use only.
+const char*
+staticMap_read_bencode_c(const char* buffer,
+                         const char* bufferEnd,
+                         uint32_t depth, Object* values,
+                         const static_map_mapping_type* keys,
+                         bool discard);
+
 std::istream& operator >> (std::istream& input, Object& object) LIBTORRENT_EXPORT;
 std::ostream& operator << (std::ostream& output, const Object& object) LIBTORRENT_EXPORT;
 
@@ -66,6 +88,22 @@
                                        const Object* object,
                                        uint32_t skip_mask = 0) LIBTORRENT_EXPORT;
 
+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);
+}
+
+// Internal use only.
+object_buffer_t staticMap_write_bencode_c_wrap(object_write_t writeFunc,
+                                               void* data,
+                                               object_buffer_t buffer,
+                                               const static_map_entry_type* values,
+                                               const static_map_mapping_type* keys) LIBTORRENT_EXPORT;
+
 // To char buffer. 'data' is NULL.
 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;
Index: libtorrent/src/torrent/Makefile.am
===================================================================
--- libtorrent/src/torrent/Makefile.am	(revision 1134)
+++ libtorrent/src/torrent/Makefile.am	(working copy)
@@ -26,6 +26,7 @@
 	http.h \
 	object.cc \
 	object.h \
+	object_static_map.h \
 	object_stream.cc \
 	object_stream.h \
 	path.cc \
@@ -68,6 +69,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,53 @@
 #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;
+
+ExtPEXMessage::mapping_type ext_pex_key_names[ExtPEXMessage::size] = {
+  { key_pex_added,    "added" },
+};
+
+ExtHandshakeMessage::mapping_type ext_handshake_key_names[ExtHandshakeMessage::size] = {
+  { 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 +137,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 +160,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(ext_pex_key_names[message_keys[t]].key),
+                       ext_pex_key_names[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 +237,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 +277,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 +304,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>

