Hashed TypeId
authorPeter D. Barnes, Jr. <barnes26@llnl.gov>
Thu, 18 Jul 2013 12:01:05 -0700
changeset 9959 721b5b528cc0
parent 9958 83a573f72a4c
child 9960 e03e5de1edc9
Hashed TypeId Speed LookupByName with std::map, add LookupByHash Hash chaining, alphabetized TypeId test suite
src/core/model/type-id.cc
src/core/model/type-id.h
src/core/test/type-id-test-suite.cc
src/core/wscript
--- a/src/core/model/type-id.cc	Thu Jul 18 12:04:27 2013 -0700
+++ b/src/core/model/type-id.cc	Thu Jul 18 12:01:05 2013 -0700
@@ -17,21 +17,56 @@
  *
  * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
  */
+#include "log.h"  // NS_ASSERT and NS_LOG
+#include "hash.h"
 #include "type-id.h"
 #include "singleton.h"
 #include "trace-source-accessor.h"
-#include "log.h"
+
+#include <map>
 #include <vector>
 #include <sstream>
+#include <iomanip>
 
 /*********************************************************************
  *         Helper code
  *********************************************************************/
 
-NS_LOG_COMPONENT_DEFINE ("TypeID");
+namespace ns3 {
+
+NS_LOG_COMPONENT_DEFINE ("TypeId");
+
+// IidManager needs to be in ns3 namespace for NS_ASSERT and NS_LOG
+// to find g_log
 
-namespace {
-
+/**
+ * \brief TypeId information manager
+ *
+ * Information records are stored in a vector.  Name and hash lookup
+ * are performed by maps to the vector index.
+ *
+ * \internal
+ * <b>Hash Chaining</b>
+ *
+ * We require all types to produce distinct hashes. What if we encounter
+ * two types that produce the same hash value?  As we move to a
+ * federated distribution model (the App store), it becomes increasingly
+ * likely that the core ns3 team *won't* discover this in test builds.
+ * Therefore, we need to handle this case explicitly.
+ *
+ * Note, we expect this to be *extremely* rare.  As of this writing we
+ * have ~400 < 2^9 types, so the probability of getting a collision
+ * when we introduce a new type is ~2^9/2^31 = 2^-22, assuming we
+ * reserve 31 bits for the hash, and one bit for chaining.  Even with
+ * double the number of types the probability of having a collision
+ * is only 2 x 10^-4.  The probability for a three-fold collision is
+ * 1 x 10^-10.
+ *
+ * Therefore, we'll handle one collision explicitly by reserving
+ * the high order bit of the hash value, and assert on higher level
+ * collisions.  The three-fold collision probability should be an
+ * acceptablly small error rate.
+ */
 class IidManager
 {
 public:
@@ -39,13 +74,15 @@
   uint16_t AllocateUid (std::string name);
   void SetParent (uint16_t uid, uint16_t parent);
   void SetGroupName (uint16_t uid, std::string groupName);
-  void AddConstructor (uint16_t uid, ns3::Callback<ns3::ObjectBase *> callback);
+  void AddConstructor (uint16_t uid, Callback<ObjectBase *> callback);
   void HideFromDocumentation (uint16_t uid);
   uint16_t GetUid (std::string name) const;
+  uint16_t GetUid (TypeId::hash_t hash) const;
   std::string GetName (uint16_t uid) const;
+  TypeId::hash_t GetHash (uint16_t uid) const;
   uint16_t GetParent (uint16_t uid) const;
   std::string GetGroupName (uint16_t uid) const;
-  ns3::Callback<ns3::ObjectBase *> GetConstructor (uint16_t uid) const;
+  Callback<ObjectBase *> GetConstructor (uint16_t uid) const;
   bool HasConstructor (uint16_t uid) const;
   uint32_t GetRegisteredN (void) const;
   uint16_t GetRegistered (uint32_t i) const;
@@ -53,41 +90,54 @@
                      std::string name,
                      std::string help, 
                      uint32_t flags,
-                     ns3::Ptr<const ns3::AttributeValue> initialValue,
-                     ns3::Ptr<const ns3::AttributeAccessor> spec,
-                     ns3::Ptr<const ns3::AttributeChecker> checker);
+                     Ptr<const AttributeValue> initialValue,
+                     Ptr<const AttributeAccessor> spec,
+                     Ptr<const AttributeChecker> checker);
   void SetAttributeInitialValue(uint16_t uid,
                                 uint32_t i,
-                                ns3::Ptr<const ns3::AttributeValue> initialValue);
+                                Ptr<const AttributeValue> initialValue);
   uint32_t GetAttributeN (uint16_t uid) const;
-  struct ns3::TypeId::AttributeInformation GetAttribute(uint16_t uid, uint32_t i) const;
+  struct TypeId::AttributeInformation GetAttribute(uint16_t uid, uint32_t i) const;
   void AddTraceSource (uint16_t uid,
                        std::string name, 
                        std::string help,
-                       ns3::Ptr<const ns3::TraceSourceAccessor> accessor);
+                       Ptr<const TraceSourceAccessor> accessor);
   uint32_t GetTraceSourceN (uint16_t uid) const;
-  struct ns3::TypeId::TraceSourceInformation GetTraceSource(uint16_t uid, uint32_t i) const;
+  struct TypeId::TraceSourceInformation GetTraceSource(uint16_t uid, uint32_t i) const;
   bool MustHideFromDocumentation (uint16_t uid) const;
 
 private:
   bool HasTraceSource (uint16_t uid, std::string name);
   bool HasAttribute (uint16_t uid, std::string name);
+  static TypeId::hash_t Hasher (const std::string name);
 
   struct IidInformation {
     std::string name;
+    TypeId::hash_t hash;
     uint16_t parent;
     std::string groupName;
     bool hasConstructor;
-    ns3::Callback<ns3::ObjectBase *> constructor;
+    Callback<ObjectBase *> constructor;
     bool mustHideFromDocumentation;
-    std::vector<struct ns3::TypeId::AttributeInformation> attributes;
-    std::vector<struct ns3::TypeId::TraceSourceInformation> traceSources;
+    std::vector<struct TypeId::AttributeInformation> attributes;
+    std::vector<struct TypeId::TraceSourceInformation> traceSources;
   };
   typedef std::vector<struct IidInformation>::const_iterator Iterator;
 
   struct IidManager::IidInformation *LookupInformation (uint16_t uid) const;
 
   std::vector<struct IidInformation> m_information;
+
+  typedef std::map<std::string, uint16_t> namemap_t;
+  namemap_t m_namemap;
+
+  typedef std::map<TypeId::hash_t, uint16_t> hashmap_t;
+  hashmap_t m_hashmap;
+
+  
+  // To handle the first collision, we reserve the high bit as a
+  // chain flag:
+  enum { HashChainFlag = 0x80000000};
 };
 
 IidManager::IidManager ()
@@ -95,22 +145,70 @@
   NS_LOG_FUNCTION (this);
 }
 
+  //static
+TypeId::hash_t
+IidManager::Hasher (const std::string name)
+{
+  static ns3::Hasher hasher ( Create<Hash::Function::Murmur3> () );
+  return hasher.clear ().GetHash32 (name);
+}
+  
 uint16_t
 IidManager::AllocateUid (std::string name)
 {
   NS_LOG_FUNCTION (this << name);
-  uint16_t j = 1;
-  for (Iterator i = m_information.begin (); i != m_information.end (); i++)
-    {
-      if (i->name == name)
-        {
-          NS_FATAL_ERROR ("Trying to allocate twice the same uid: " << name);
-          return 0;
-        }
-      j++;
-    }
-  struct IidInformation information;
+  // Type names are definitive: equal names are equal types
+  NS_ASSERT_MSG (m_namemap.count (name) == 0,
+                 "Trying to allocate twice the same uid: " << name);
+  
+  TypeId::hash_t hash = Hasher (name) & (~HashChainFlag);
+  if (m_hashmap.count (hash) == 1) {
+    NS_LOG_ERROR ("Hash chaining TypeId for '" << name << "'.  "
+                 << "This is not a bug, but is extremely unlikely.  "
+                 << "Please contact the ns3 developers.");
+    // ns3 developer contacted about this message:
+    // You have four options (in order of difficulty):
+    //   1. Let it ride, and play the odds that a third collision
+    //        never appears.
+    //   2. Change the name of the new (or old) tag, even trivially, to
+    //        remove the collision.
+    //   3. Switch to 64-bit hashes.
+    //   4. Implement 2-bit (or higher) chaining.
+    //
+    //  Oh, by the way, I owe you a beer, since I bet Mathieu that
+    //  this would never happen..  -- Peter Barnes, LLNL
+
+    NS_ASSERT_MSG (m_hashmap.count (hash | HashChainFlag) == 0,
+                   "Triplicate hash detected while chaining TypeId for '"
+                   << name
+                   << "'. Please contact the ns3 developers for assistance.");
+    // ns3 developer contacted about this message:
+    // You have three options: #2-4 above.
+    //
+    // Oh, by the way, I have no idea how this crazy hashing idea got
+    // into ns3.  -- Peter Barnes, LLNL
+    
+    // Alphabetize the two types, so it's deterministic
+    struct IidInformation * hinfo = LookupInformation (GetUid (hash));
+    if (name > hinfo->name)
+      { // new type gets chained
+        NS_LOG_LOGIC ("New TypeId '" << name << "' getting chained.");
+        hash = hash | HashChainFlag;
+      }
+    else
+      { // chain old type
+        NS_LOG_LOGIC ("Old TypeId '" << hinfo->name << "' getting chained.");
+        uint32_t oldUid = GetUid (hinfo->hash);
+        m_hashmap.erase (m_hashmap.find (hinfo->hash));
+        hinfo->hash = hash | HashChainFlag;
+        m_hashmap.insert (std::make_pair (hinfo->hash, oldUid));
+        // leave new hash unchained
+      }
+  }
+
+ struct IidInformation information;
   information.name = name;
+  information.hash = hash;
   information.parent = 0;
   information.groupName = "";
   information.hasConstructor = false;
@@ -118,6 +216,10 @@
   m_information.push_back (information);
   uint32_t uid = m_information.size ();
   NS_ASSERT (uid <= 0xffff);
+
+  // Add to both maps:
+  m_namemap.insert (std::make_pair (name, uid));
+  m_hashmap.insert (std::make_pair (hash, uid));
   return uid;
 }
 
@@ -153,7 +255,7 @@
 }
 
 void 
-IidManager::AddConstructor (uint16_t uid, ns3::Callback<ns3::ObjectBase *> callback)
+IidManager::AddConstructor (uint16_t uid, Callback<ObjectBase *> callback)
 {
   NS_LOG_FUNCTION (this << uid << &callback);
   struct IidInformation *information = LookupInformation (uid);
@@ -169,17 +271,28 @@
 IidManager::GetUid (std::string name) const
 {
   NS_LOG_FUNCTION (this << name);
-  uint32_t j = 1;
-  for (Iterator i = m_information.begin (); i != m_information.end (); i++)
+  namemap_t::const_iterator it = m_namemap.find (name);
+  if (it != m_namemap.end ())
+    {
+      return it->second;
+    }
+  else
     {
-      if (i->name == name)
-        {
-          NS_ASSERT (j <= 0xffff);
-          return j;
-        }
-      j++;
+      return 0;
     }
-  return 0;
+}
+uint16_t 
+IidManager::GetUid (TypeId::hash_t hash) const
+{
+  hashmap_t::const_iterator it = m_hashmap.find (hash);
+  if (it != m_hashmap.end ())
+    {
+    return it->second;
+    }
+  else
+    { // hash not found 
+      return 0;
+    }
 }
 std::string 
 IidManager::GetName (uint16_t uid) const
@@ -188,6 +301,13 @@
   struct IidInformation *information = LookupInformation (uid);
   return information->name;
 }
+TypeId::hash_t
+IidManager::GetHash (uint16_t uid) const
+{
+  NS_LOG_FUNCTION (this << uid);
+  struct IidInformation *information = LookupInformation (uid);
+  return information->hash;
+}
 uint16_t 
 IidManager::GetParent (uint16_t uid) const
 {
@@ -203,7 +323,7 @@
   return information->groupName;
 }
 
-ns3::Callback<ns3::ObjectBase *> 
+Callback<ObjectBase *> 
 IidManager::GetConstructor (uint16_t uid) const
 {
   NS_LOG_FUNCTION (this << uid);
@@ -244,7 +364,7 @@
   struct IidInformation *information  = LookupInformation (uid);
   while (true)
     {
-      for (std::vector<struct ns3::TypeId::AttributeInformation>::const_iterator i = information->attributes.begin ();
+      for (std::vector<struct TypeId::AttributeInformation>::const_iterator i = information->attributes.begin ();
            i != information->attributes.end (); ++i)
         {
           if (i->name == name)
@@ -269,9 +389,9 @@
                           std::string name,
                           std::string help, 
                           uint32_t flags,
-                          ns3::Ptr<const ns3::AttributeValue> initialValue,
-                          ns3::Ptr<const ns3::AttributeAccessor> accessor,
-                          ns3::Ptr<const ns3::AttributeChecker> checker)
+                          Ptr<const AttributeValue> initialValue,
+                          Ptr<const AttributeAccessor> accessor,
+                          Ptr<const AttributeChecker> checker)
 {
   NS_LOG_FUNCTION (this << uid << name << help << flags << initialValue << accessor << checker);
   struct IidInformation *information = LookupInformation (uid);
@@ -280,7 +400,7 @@
       NS_FATAL_ERROR ("Attribute \"" << name << "\" already registered on tid=\"" << 
                       information->name << "\"");
     }
-  struct ns3::TypeId::AttributeInformation info;
+  struct TypeId::AttributeInformation info;
   info.name = name;
   info.help = help;
   info.flags = flags;
@@ -293,7 +413,7 @@
 void 
 IidManager::SetAttributeInitialValue(uint16_t uid,
                                      uint32_t i,
-                                     ns3::Ptr<const ns3::AttributeValue> initialValue)
+                                     Ptr<const AttributeValue> initialValue)
 {
   NS_LOG_FUNCTION (this << uid << i << initialValue);
   struct IidInformation *information = LookupInformation (uid);
@@ -310,7 +430,7 @@
   struct IidInformation *information = LookupInformation (uid);
   return information->attributes.size ();
 }
-struct ns3::TypeId::AttributeInformation 
+struct TypeId::AttributeInformation 
 IidManager::GetAttribute(uint16_t uid, uint32_t i) const
 {
   NS_LOG_FUNCTION (this << uid << i);
@@ -327,7 +447,7 @@
   struct IidInformation *information  = LookupInformation (uid);
   while (true)
     {
-      for (std::vector<struct ns3::TypeId::TraceSourceInformation>::const_iterator i = information->traceSources.begin ();
+      for (std::vector<struct TypeId::TraceSourceInformation>::const_iterator i = information->traceSources.begin ();
            i != information->traceSources.end (); ++i)
         {
           if (i->name == name)
@@ -351,7 +471,7 @@
 IidManager::AddTraceSource (uint16_t uid,
                             std::string name, 
                             std::string help,
-                            ns3::Ptr<const ns3::TraceSourceAccessor> accessor)
+                            Ptr<const TraceSourceAccessor> accessor)
 {
   NS_LOG_FUNCTION (this << uid << name << help << accessor);
   struct IidInformation *information  = LookupInformation (uid);
@@ -360,7 +480,7 @@
       NS_FATAL_ERROR ("Trace source \"" << name << "\" already registered on tid=\"" << 
                       information->name << "\"");
     }
-  struct ns3::TypeId::TraceSourceInformation source;
+  struct TypeId::TraceSourceInformation source;
   source.name = name;
   source.help = help;
   source.accessor = accessor;
@@ -373,7 +493,7 @@
   struct IidInformation *information = LookupInformation (uid);
   return information->traceSources.size ();
 }
-struct ns3::TypeId::TraceSourceInformation 
+struct TypeId::TraceSourceInformation 
 IidManager::GetTraceSource(uint16_t uid, uint32_t i) const
 {
   NS_LOG_FUNCTION (this << uid << i);
@@ -389,7 +509,7 @@
   return information->mustHideFromDocumentation;
 }
 
-} // anonymous namespace
+} // namespace ns3
 
 namespace ns3 {
 
@@ -431,6 +551,25 @@
   *tid = TypeId (uid);
   return true;
 }
+TypeId
+TypeId::LookupByHash (hash_t hash)
+{
+  uint16_t uid = Singleton<IidManager>::Get ()->GetUid (hash);
+  NS_ASSERT_MSG (uid != 0, "Assert in TypeId::LookupByHash: 0x"
+                 << std::hex << hash << std::dec << " not found");
+  return TypeId (uid);
+}
+bool
+TypeId::LookupByHashFailSafe (hash_t hash, TypeId *tid)
+{
+  uint16_t uid = Singleton<IidManager>::Get ()->GetUid (hash);
+  if (uid == 0)
+    {
+      return false;
+    }
+  *tid = TypeId (uid);
+  return true;
+}
 
 uint32_t 
 TypeId::GetRegisteredN (void)
@@ -522,6 +661,13 @@
   return name;
 }
 
+TypeId::hash_t
+TypeId::GetHash (void) const
+{
+  hash_t hash = Singleton<IidManager>::Get ()->GetHash (m_tid);
+  return hash;
+}
+
 bool 
 TypeId::HasConstructor (void) const
 {
--- a/src/core/model/type-id.h	Thu Jul 18 12:04:27 2013 -0700
+++ b/src/core/model/type-id.h	Thu Jul 18 12:01:05 2013 -0700
@@ -25,6 +25,7 @@
 #include "trace-source-accessor.h"
 #include "attribute-helper.h"
 #include "callback.h"
+#include "hash.h"
 #include <string>
 #include <stdint.h>
 
@@ -40,6 +41,10 @@
  *  - the base class of the subclass
  *  - the set of accessible constructors in the subclass
  *  - the set of 'attributes' accessible in the subclass
+ *
+ * \internal
+ *  See the discussion in IidManager about hash chaining of TypeId's.
+ *
  */
 class TypeId
 {
@@ -57,18 +62,23 @@
     std::string name;
     std::string help;
     uint32_t flags;
-    ns3::Ptr<const ns3::AttributeValue> originalInitialValue;
-    ns3::Ptr<const ns3::AttributeValue> initialValue;
-    ns3::Ptr<const ns3::AttributeAccessor> accessor;
-    ns3::Ptr<const ns3::AttributeChecker> checker;
+    Ptr<const AttributeValue> originalInitialValue;
+    Ptr<const AttributeValue> initialValue;
+    Ptr<const AttributeAccessor> accessor;
+    Ptr<const AttributeChecker> checker;
   };
   struct TraceSourceInformation {
     std::string name;
     std::string help;
-    ns3::Ptr<const ns3::TraceSourceAccessor> accessor;
+    Ptr<const TraceSourceAccessor> accessor;
   };
 
   /**
+   * Type of hash values
+   */
+  typedef uint32_t hash_t;
+
+  /**
    * \param name the name of the requested TypeId
    * \returns the unique id associated with the requested
    *          name. 
@@ -84,6 +94,21 @@
    * \returns true if the requested name was found, false otherwise.
    */
   static bool LookupByNameFailSafe (std::string name, TypeId *tid);
+  /**
+   * \param hash the hash to lookup
+   * \returns the unique id associated with the requested hash.
+   *
+   * This method cannot fail: it will crash if the input 
+   * hash does not match an existing TypeId.
+   */
+  static TypeId LookupByHash (hash_t hash);
+  /**
+   * \param hash the hash of the requested TypeId
+   * \param tid a pointer to the TypeId instance where the 
+   *        result of this function should be stored.
+   * \returns true if the requested hash was found, false otherwise.
+   */
+  static bool LookupByHashFailSafe (hash_t hash, TypeId *tid);
 
   /**
    * \returns the number of TypeId instances registered.
@@ -138,6 +163,11 @@
   std::string GetName (void) const;
 
   /**
+   * \returns the hash of this interface.
+   */
+  hash_t GetHash (void) const;
+
+  /**
    * \returns true if this TypeId has a constructor
    */
   bool HasConstructor (void) const;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/test/type-id-test-suite.cc	Thu Jul 18 12:01:05 2013 -0700
@@ -0,0 +1,321 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2012 Lawrence Livermore National Laboratory
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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
+ *
+ * Author: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <ctime>
+
+#include "ns3/type-id.h"
+#include "ns3/test.h"
+#include "ns3/log.h"
+
+using namespace std;
+
+namespace ns3 {
+
+
+const std::string suite("type-id: ");
+  
+//----------------------------
+//
+// Test for uniqueness of all TypeIds
+
+class UniqueTypeIdTestCase : public TestCase
+{
+public:
+  UniqueTypeIdTestCase ();
+  virtual ~UniqueTypeIdTestCase ();
+private:
+  virtual void DoRun (void);
+  enum { HashChainFlag = 0x80000000};
+};
+
+UniqueTypeIdTestCase::UniqueTypeIdTestCase ()
+  : TestCase ("Check uniqueness of all TypeIds")
+{
+}
+
+UniqueTypeIdTestCase::~UniqueTypeIdTestCase ()
+{
+}
+
+void
+UniqueTypeIdTestCase::DoRun (void)
+{
+  cout << suite << endl;
+  cout << suite << GetName () << endl;
+  
+  // Use same custom hasher as TypeId
+  ns3::Hasher hasher = ns3::Hasher ( Create<Hash::Function::Murmur3> () );
+  
+  uint32_t nids = TypeId::GetRegisteredN ();
+
+  cout << suite << "UniqueTypeIdTestCase: nids: " << nids << endl;
+  cout << suite << "TypeId list:" << endl;
+  cout << suite << "TypeId  Chain  hash          Name" << endl;
+  
+  for (uint32_t i = 0; i < nids; ++i)
+    {
+      const TypeId tid = TypeId::GetRegistered (i);
+      cout << suite << "" << std::setw(6) << tid.GetUid ();
+      if (tid.GetHash () & HashChainFlag)
+        {
+          cout << "  chain";
+        }
+      else
+        {
+          cout << "       ";
+        }
+      cout << "  0x"     << std::setfill ('0') << std::hex << std::setw (8)
+           << tid.GetHash () << std::dec << std::setfill (' ')
+           << "    "     << tid.GetName ()
+           << endl;
+
+      NS_TEST_ASSERT_MSG_EQ (tid.GetUid (),
+                             TypeId::LookupByName (tid.GetName ()).GetUid (),
+                             "LookupByName returned different TypeId for "
+                             << tid.GetName ());
+
+      // Mask off HashChainFlag in this test, since tid might have been chained
+      NS_TEST_ASSERT_MSG_EQ ((tid.GetHash () & (~HashChainFlag)),
+                             (hasher.clear ().GetHash32 (tid.GetName ()) & (~HashChainFlag)),
+                             "TypeId .hash and Hash32 (.name) unequal for "
+                             << tid.GetName ());
+
+      NS_TEST_ASSERT_MSG_EQ (tid.GetUid (),
+                             TypeId::LookupByHash (tid.GetHash ()).GetUid (),
+                             "LookupByHash returned different TypeId for "
+                             << tid.GetName ());
+      
+    }
+
+  cout << suite << "<-- end TypeId list -->" << endl;
+}
+
+
+//----------------------------
+//
+// Collision test
+
+class CollisionTestCase : public TestCase
+{
+public:
+  CollisionTestCase ();
+  virtual ~CollisionTestCase ();
+private:
+  virtual void DoRun (void);
+  enum { HashChainFlag = 0x80000000};
+};
+
+CollisionTestCase::CollisionTestCase ()
+  : TestCase ("Check behavour when type names collide")
+{
+}
+
+CollisionTestCase::~CollisionTestCase ()
+{
+}
+
+void
+CollisionTestCase::DoRun (void)
+{
+  cout << suite << endl;
+  cout << suite << GetName () << endl;
+  
+  // Register two types whose hashes collide, in alphabetical order
+  // Murmur3 collision from /usr/share/dict/web2
+  string t1Name = "daemon";
+  string t2Name = "unerring";
+  cout << suite << "creating colliding types "
+       << "'" << t1Name << "', '" << t2Name << "'"
+       << " in alphabetical order:"
+       << endl;
+  TypeId t1 (t1Name.c_str ());
+  TypeId t2 (t2Name.c_str ());
+
+  // Check that they are alphabetical: t1 name < t2 name
+  NS_TEST_ASSERT_MSG_EQ ( (t1.GetHash () & HashChainFlag), 0,
+                         "First and lesser TypeId has HashChainFlag set");
+  cout << suite << "collision: first,lesser  not chained: OK" << endl;
+
+  NS_TEST_ASSERT_MSG_NE ( (t2.GetHash () & HashChainFlag), 0,
+                         "Second and greater TypeId does not have HashChainFlag set");
+  cout << suite << "collision: second,greater    chained: OK" << endl;
+
+
+  // Register colliding types in reverse alphabetical order
+  // Murmur3 collision from /usr/share/dict/web2
+  string t3Name = "trigonon";
+  string t4Name = "seriation";
+  cout << suite << "creating colliding types "
+       << "'" << t3Name << "', '" << t4Name << "'"
+       << " in reverse alphabetical order:"
+       << endl;
+  TypeId t3 (t3Name.c_str ());
+  TypeId t4 (t4Name.c_str ());
+
+  // Check that they are alphabetical: t3 name > t4 name
+  NS_TEST_ASSERT_MSG_NE ( (t3.GetHash () & HashChainFlag), 0,
+                          "First and greater TypeId does not have HashChainFlag set");
+  cout << suite << "collision: first,greater     chained: OK" << endl;
+
+  NS_TEST_ASSERT_MSG_EQ ( (t4.GetHash () & HashChainFlag), 0,
+                          "Second and lesser TypeId has HashChainFlag set");
+  cout << suite << "collision: second,lesser not chained: OK" << endl;
+
+  /** TODO Extra credit:  register three types whose hashes collide
+   *
+   *  None found in /usr/share/dict/web2
+   */
+  
+}
+  
+  
+//----------------------------
+//
+// Performance test
+
+class LookupTimeTestCase : public TestCase
+{
+public:
+  LookupTimeTestCase ();
+  virtual ~LookupTimeTestCase ();
+private:
+  void DoRun (void);
+  void DoSetup (void);
+  void Report (const std::string how, const uint32_t delta) const ;
+
+  enum { REPETITIONS = 100000 };
+};
+
+LookupTimeTestCase::LookupTimeTestCase ()
+  : TestCase ("Measure average lookup time")
+{
+}
+
+LookupTimeTestCase::~LookupTimeTestCase ()
+{
+}
+
+void
+LookupTimeTestCase::DoRun (void)
+{
+  cout << suite << endl;
+  cout << suite << GetName () << endl;
+  
+  uint32_t nids = TypeId::GetRegisteredN ();
+
+  int start = clock ();
+  for (uint32_t j = 0; j < REPETITIONS; ++j)
+    {
+      for (uint32_t i = 0; i < nids; ++i)
+        {
+          const TypeId tid = TypeId::GetRegistered (i);
+          const TypeId sid = TypeId::LookupByName (tid.GetName ());
+        }
+  }
+  int stop = clock ();
+  Report ("name", stop - start);
+
+  start = clock ();
+  for (uint32_t j = 0; j < REPETITIONS; ++j)
+    {
+      for (uint32_t i = 0; i < nids; ++i)
+        {
+          const TypeId tid = TypeId::GetRegistered (i);
+          const TypeId sid = TypeId::LookupByHash (tid.GetHash ());
+        }
+  }
+  stop = clock ();
+  Report ("hash", stop - start);
+  
+}
+
+void
+LookupTimeTestCase::DoSetup (void)
+{
+  uint32_t nids = TypeId::GetRegisteredN ();
+  
+  cout << suite << "Lookup time: reps: " << REPETITIONS
+       << ", num TypeId's: " << nids
+       << endl;
+
+}
+
+void
+LookupTimeTestCase::Report (const std::string how,
+                            const uint32_t    delta) const
+{
+  double nids = TypeId::GetRegisteredN ();
+  double reps = nids * REPETITIONS;
+  
+  double per = 1E6 * double(delta) / (reps * double(CLOCKS_PER_SEC));
+  
+  cout << suite << "Lookup time: by " << how << ": "
+       << "ticks: " << delta
+       << "\tper: "   << per
+       << " microsec/lookup"
+       << endl;					
+}
+
+
+//----------------------------
+//
+// TypeId test suites
+
+class TypeIdTestSuite : public TestSuite
+{
+public:
+  TypeIdTestSuite ();
+};
+  
+TypeIdTestSuite::TypeIdTestSuite ()
+  : TestSuite ("type-id", UNIT)
+{
+  // Turn on logging, so we see the result of collisions
+  LogComponentEnable ("TypeId", ns3::LogLevel(LOG_ERROR|LOG_PREFIX_FUNC));
+  
+  // If the CollisionTestCase is performed before the
+  // UniqueIdTestCase, the artificial collisions added by
+  // CollisionTestCase will show up in the list of TypeIds
+  // as chained.
+  AddTestCase (new UniqueTypeIdTestCase);
+  AddTestCase (new CollisionTestCase);
+}
+
+static TypeIdTestSuite g_TypeIdTestSuite;  
+  
+
+class TypeIdPerformanceSuite : public TestSuite
+{
+public:
+  TypeIdPerformanceSuite ();
+};
+  
+TypeIdPerformanceSuite::TypeIdPerformanceSuite ()
+  : TestSuite ("type-id-perf", PERFORMANCE)
+{
+  AddTestCase (new LookupTimeTestCase);
+}
+
+static TypeIdPerformanceSuite g_TypeIdPerformanceSuite;  
+  
+
+}  // namespace ns3
--- a/src/core/wscript	Thu Jul 18 12:04:27 2013 -0700
+++ b/src/core/wscript	Thu Jul 18 12:01:05 2013 -0700
@@ -178,6 +178,7 @@
         'test/type-traits-test-suite.cc',
         'test/watchdog-test-suite.cc',
         'test/hash-test-suite.cc',
+        'test/type-id-test-suite.cc',
         ]
 
     headers = bld(features='ns3header')