Parcourir la source

a) Take account of the DNS class in calculating the hash. The HashKey class has
been created for this.
b) Move the IOAddress object into the correct namespace.


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac356@3389 e5f2f494-b856-4b98-b285-d166d9295462

Stephen Morris il y a 14 ans
Parent
commit
7b5b7df0fb

+ 1 - 0
src/lib/nsas/Makefile.am

@@ -9,6 +9,7 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 lib_LTLIBRARIES = libnsas.la
 libnsas_la_SOURCES  = address_entry.h address_entry.cc
 libnsas_la_SOURCES += hash.cc hash.h
+libnsas_la_SOURCES += hash_key.h
 libnsas_la_SOURCES += hash_table.h
 libnsas_la_SOURCES += lru_list.h
 libnsas_la_SOURCES += nameserver_entry.cc nameserver_entry.h

+ 3 - 3
src/lib/nsas/address_entry.h

@@ -36,12 +36,12 @@ public:
     ///
     /// \param address Address object representing this address
     /// \param rtt Initial round-trip time
-    AddressEntry(const IOAddress& address, uint32_t rtt = 0) :
+    AddressEntry(const asiolink::IOAddress& address, uint32_t rtt = 0) :
         address_(address), rtt_(rtt)
     {}
 
     /// \return Address object
-    IOAddress getAddress() const {
+    asiolink::IOAddress getAddress() const {
         return address_;
     }
 
@@ -83,7 +83,7 @@ public:
     static const uint32_t UNREACHABLE;  ///< RTT indicating unreachable address
 
 private:
-    IOAddress       address_;           ///< Address
+    asiolink::IOAddress address_;       ///< Address
     uint32_t        rtt_;               ///< Round-trip time
 };
 

+ 4 - 0
src/lib/nsas/asiolink.h

@@ -20,6 +20,8 @@
 #include <string>
 #include <sys/socket.h>
 
+namespace asiolink {
+
 /// \brief IO Address Dummy Class
 ///
 /// As part of ther recursor, Evan has written the asiolink.h file, which
@@ -50,4 +52,6 @@ private:
     std::string     address_;       ///< Address represented
 };
 
+}   // namespace asiolink
+
 #endif // __ASIOLINK_H

+ 30 - 13
src/lib/nsas/hash.cc

@@ -54,6 +54,7 @@ or otherwise) arising in any way out of the use of this software, even
 if advised of the possibility of such damage.
 */
 
+#include <cassert>
 #include <stdlib.h>
 #include <algorithm>
 #include <cassert>
@@ -71,17 +72,22 @@ namespace nsas {
 // Constructor.
 
 Hash::Hash(uint32_t tablesize, uint32_t maxkeylen, bool randomise) :
-    tablesize_(tablesize), maxkeylen_(maxkeylen)
+    tablesize_(tablesize), maxkeylen_(min(maxkeylen, (255 - sizeof(uint16_t))))
 {
-    // Check to see that we can cope with the maximum key length.
-    // (This code adapted from BIND-9)
+    // (Code taken from BIND-9)
+    //
+    // Check to see that we can cope with the maximum key length which, due
+    // to the limitations, should not be more than 255 in total.  The actual
+    // number of characters in the name that are considered is reduced to
+    // ensure that the class is taken into account in the hash.  (This accounts
+    // for the "+ sizeof(uint16_t)" in the calculations below.
     //
     // Overflow check.  Since our implementation only does a modulo
     // operation at the last stage of hash calculation, the accumulator
     // must not overflow.
     hash_accum_t overflow_limit =
         1 << (((sizeof(hash_accum_t) - sizeof(hash_random_t))) * 8);
-    if (overflow_limit < (maxkeylen + 1) * 0xff) {
+    if (overflow_limit < (maxkeylen_ + sizeof(uint16_t) + 1) * 0xff) {
         isc_throw(KeyLengthTooLong, "Hash key length too long for Hash class");
     }
 
@@ -101,8 +107,8 @@ Hash::Hash(uint32_t tablesize, uint32_t maxkeylen, bool randomise) :
     srandom(init_value.seed);
 
     // Fill in the random vector.
-    randvec_.reserve(maxkeylen + 1);
-    for (uint32_t i = 0; i < (maxkeylen + 1); ++i) {
+    randvec_.reserve(maxkeylen_ + sizeof(uint16_t) + 1);
+    for (uint32_t i = 0; i < (maxkeylen + sizeof(uint16_t) + 1); ++i) {
         randvec_.push_back(static_cast<hash_random_t>(random() & 0xffff));
     }
     assert(sizeof(hash_random_t) == 2); // So that the "& 0xffff" is valid
@@ -120,10 +126,8 @@ Hash::Hash(uint32_t tablesize, uint32_t maxkeylen, bool randomise) :
 }
 
 
-uint32_t Hash::operator()(const char* key, uint32_t keylen,
-    bool ignorecase) const
+uint32_t Hash::operator()(const HashKey& key, bool ignorecase) const
 {
-
     // Calculation as given in BIND-9.
     hash_accum_t partial_sum = 0;
     uint32_t i = 0;                 // Used after the end of the loop
@@ -131,14 +135,27 @@ uint32_t Hash::operator()(const char* key, uint32_t keylen,
     // Perform the hashing.  If the key length if more than the maximum we set
     // up this hash for, ignore the excess.
     if (ignorecase) {
-        for (i = 0; i < min(keylen, maxkeylen_); ++i) {
-            partial_sum += mapLower(key[i]) * randvec_[i];
+        for (i = 0; i < min(key.keylen, maxkeylen_); ++i) {
+            partial_sum += mapLower(key.key[i]) * randvec_[i];
         }
     } else {
-        for (i = 0; i < min(keylen, maxkeylen_); ++i) {
-            partial_sum += key[i] * randvec_[i];
+        for (i = 0; i < min(key.keylen, maxkeylen_); ++i) {
+            partial_sum += key.key[i] * randvec_[i];
         }
     }
+
+    // Add the hash of the class code
+    union {
+        uint16_t    class_code;                 // Copy of the class code
+        char        bytes[sizeof(uint16_t)];    // Byte equivalent
+    } convert;
+
+    convert.class_code = key.class_code;
+    for (int j = 0; j < sizeof(uint16_t); ++j, ++i) {
+        partial_sum += convert.bytes[j] * randvec_[i];
+    }
+
+    // ... and finish up.
     partial_sum += randvec_[i];
 
     // Determine the hash value

+ 4 - 4
src/lib/nsas/hash.h

@@ -22,6 +22,8 @@
 
 #include "exceptions/exceptions.h"
 
+#include "hash_key.h"
+
 namespace isc {
 namespace nsas {
 
@@ -79,14 +81,12 @@ public:
 
     /// \brief Hash Value
     ///
-    /// \param key String value or array of bytes for which a hash is to be
-    /// calculated.
-    /// \param ketlen Number of characters in the string
+    /// \param key Parameters comprising the key to be hashed.
     /// \param ignorecase true for case to be ignored when calculating the
     /// hash value, false for it to be taken into account.
     ///
     /// \return Hash value, a number between 0 and N-1.
-    virtual uint32_t operator()(const char* key, uint32_t keylen,
+    virtual uint32_t operator()(const HashKey& key, 
         bool ignorecase = true) const;
 
     /// \brief Map Lower Case to Upper Case

+ 76 - 0
src/lib/nsas/hash_key.h

@@ -0,0 +1,76 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __HASH_KEY_H
+#define __HASH_KEY_H
+
+namespace isc {
+namespace nsas {
+
+#include <stdint.h>
+
+/// \brief Hash Key
+///
+/// In the nameserver address store, an object is placed into a hash table
+/// according to its key (name) and class.
+///
+/// The key comprises two elements, a pointer to the start of a char string
+/// holding the data that describes the key and a length.  This has been
+/// chosen over a std::string because:
+///
+/// # The key may not be a string, it may be binary data
+/// # The overhead of creating std::string objects from such data.
+///
+/// "key" is declared as "const char*" - rather than the more semantically
+/// correct "const uint8_t*" - simply because if std::strings are used, then
+/// the c_str function will return a "const char*".
+///
+/// To avoid passing round three elements (key, key length, and class), they
+/// have been combined into this simple struct.
+struct HashKey {
+
+    /// \brief Constructor
+    ///
+    /// Basic constructor to make the hash key.
+    ///
+    /// \param the_key Array of bytes for which key is to be constructed
+    /// \param the_keylen Length of the byte array
+    /// \param the_class_code Class of this entry
+    HashKey(const char* the_key, uint32_t the_keylen, uint16_t the_class_code) :
+        key(the_key), keylen(the_keylen), class_code(the_class_code)
+    {}
+
+    /// \brief String Constructor
+    ///
+    /// Convenience constructor using a std::string.
+    ///
+    /// \param the_key Name to use as the key for the hash
+    /// \param the_class_code Class of this entry
+    HashKey(const std::string& the_key, uint16_t the_class_code) :
+        key(the_key.c_str()), keylen(the_key.size()), class_code(the_class_code)
+    {}
+
+    // As these are public variables, they names do not end with an underscore.
+    const char* key;        ///< Pointer to the start of the key string
+    uint32_t    keylen;     ///< Length of the key string
+    uint16_t    class_code; ///< Class associated with the key
+};
+
+} // namespace nsas
+} // namespace isc
+
+
+#endif // __HASH_KEY_H

+ 22 - 23
src/lib/nsas/hash_table.h

@@ -27,6 +27,7 @@
 #include "config.h"
 
 #include "hash.h"
+#include "hash_key.h"
 
 // Maximum key length if the maximum size of a DNS name
 #define MAX_KEY_LENGTH 255
@@ -90,11 +91,10 @@ public:
     /// object's name is the same.
     ///
     /// \param object Pointer to the object
-    /// \param key Pointer to the name of the object
-    /// \param keylen Length of the key
+    /// \param key Key describing the object
     ///
     /// \return bool true of the name of the object is equal to the name given.
-    virtual bool operator()(T* object, const char* key, uint32_t keylen) = 0;
+    virtual bool operator()(T* object, const HashKey& key) = 0;
 };
 
 
@@ -131,11 +131,11 @@ public:
     ///
     /// Returns a shared_ptr object pointing to the table entry
     ///
-    /// \param key Name of the object.  The hash of this is calculated and
-    /// used to index the table.
+    /// \param key Name of the object (and class).  The hash of this is
+    /// calculated and used to index the table.
     ///
     /// \return Shared pointer to the object.
-    virtual boost::shared_ptr<T> get(const char* key, uint32_t keylen);
+    virtual boost::shared_ptr<T> get(const HashKey& key);
 
     /// \brief Remove Entry
     ///
@@ -143,12 +143,11 @@ public:
     /// destroyed, so if this is the last pointer, the object itself is also
     /// destroyed.
     ///
-    /// \param key Name of the object.  The hash of this is calculated and
-    /// used to index the table.
-    /// \param keylen Length of the key
+    /// \param key Name of the object (and class).  The hash of this is
+    /// calculated and used to index the table.
     ///
     /// \return true if the object was deleted, false if it was not found.
-    virtual bool remove(const char* key, uint32_t keylen);
+    virtual bool remove(const HashKey& key);
 
     /// \brief Add Entry
     ///
@@ -160,13 +159,12 @@ public:
     /// successful, this object will have a shared pointer pointing to it; it
     /// should not be deleted by the caller.
     /// \param key Key to use to calculate the hash.
-    /// \patam keylen Length of "key"
     /// \param replace If true, when an object is added and an object with the
     /// same name already exists, the existing object is replaced.  If false,
     // the addition fails and a status is returned.
     /// \return true if the object was successfully added, false otherwise.
-    virtual bool add(boost::shared_ptr<T>& object, const char* key,
-        uint32_t keylen, bool replace = false);
+    virtual bool add(boost::shared_ptr<T>& object, const HashKey& key,
+        bool replace = false);
 
     /// \brief Returns Size of Hash Table
     ///
@@ -190,10 +188,10 @@ HashTable<T>::HashTable(HashTableCompare<T>* compare, uint32_t size) :
 
 // Lookup an object in the table
 template <typename T>
-boost::shared_ptr<T> HashTable<T>::get(const char* key, uint32_t keylen) {
+boost::shared_ptr<T> HashTable<T>::get(const HashKey& key) {
 
     // Calculate the hash value
-    uint32_t index = hash_(key, keylen);
+    uint32_t index = hash_(key);
 
     // Take out a read lock on this hash slot.  The lock is released when this
     // object goes out of scope.
@@ -202,7 +200,7 @@ boost::shared_ptr<T> HashTable<T>::get(const char* key, uint32_t keylen) {
     // Locate the object.
     typename HashTableSlot<T>::iterator i;
     for (i = table_[index].list_.begin(); i != table_[index].list_.end(); ++i) {
-        if ((*compare_)(i->get(), key, keylen)) {
+        if ((*compare_)(i->get(), key)) {
 
             // Found it, so return the shared pointer object
             return (*i);
@@ -215,10 +213,10 @@ boost::shared_ptr<T> HashTable<T>::get(const char* key, uint32_t keylen) {
 
 // Remove an entry from the hash table
 template <typename T>
-bool HashTable<T>::remove(const char* key, uint32_t keylen) {
+bool HashTable<T>::remove(const HashKey& key) {
 
     // Calculate the hash value
-    uint32_t index = hash_(key, keylen);
+    uint32_t index = hash_(key);
 
     // Access to the elements of this hash slot are accessed under a mutex.
     // The mutex will be released when this object goes out of scope and is
@@ -228,7 +226,7 @@ bool HashTable<T>::remove(const char* key, uint32_t keylen) {
     // Now search this list to see if the element already exists.
     typename HashTableSlot<T>::iterator i;
     for (i = table_[index].list_.begin(); i != table_[index].list_.end(); ++i) {
-        if ((*compare_)(i->get(), key, keylen)) {
+        if ((*compare_)(i->get(), key)) {
 
             // Object found so delete it.
             table_[index].list_.erase(i);
@@ -243,11 +241,12 @@ bool HashTable<T>::remove(const char* key, uint32_t keylen) {
 
 // Add an entry to the hash table
 template <typename T>
-bool HashTable<T>::add(boost::shared_ptr<T>& object, const char* key,
-    uint32_t keylen, bool replace) {
+bool HashTable<T>::add(boost::shared_ptr<T>& object, const HashKey& key,
+    bool replace)
+{
 
     // Calculate the hash value
-    uint32_t index = hash_(key, keylen);
+    uint32_t index = hash_(key);
 
     // Access to the elements of this hash slot are accessed under a mutex.
     scoped_lock<typename HashTableSlot<T>::mutex_type> lock(table_[index].mutex_);
@@ -255,7 +254,7 @@ bool HashTable<T>::add(boost::shared_ptr<T>& object, const char* key,
     // Now search this list to see if the element already exists.
     typename HashTableSlot<T>::iterator i;
     for (i = table_[index].list_.begin(); i != table_[index].list_.end(); ++i) {
-        if ((*compare_)(i->get(), key, keylen)) {
+        if ((*compare_)(i->get(), key)) {
 
             // Object found.  If we are not allowed to replace the element,
             // return an error.  Otherwise erase it from the list and exit the

+ 1 - 0
src/lib/nsas/nameserver_entry.cc

@@ -32,6 +32,7 @@
 #include "address_entry.h"
 #include "nameserver_entry.h"
 
+using namespace asiolink;
 using namespace isc::nsas;
 using namespace isc::dns;
 using namespace std;

+ 7 - 4
src/lib/nsas/nameserver_entry.h

@@ -24,6 +24,7 @@
 #include "address_entry.h"
 #include "asiolink.h"
 #include "exceptions/exceptions.h"
+#include "lru_list.h"
 #include "rrset.h"
 
 namespace isc {
@@ -71,9 +72,11 @@ public:
 /// different TTLs) there is one expiration time for all address records.
 /// When that is reached, all records are declared expired and new fetches
 /// started for the information.
+///
+/// As this object will be stored in the nameserver address store LRU list,
+/// it is derived from the LRU list element class.
 
-
-class NameserverEntry {
+class NameserverEntry : public LruList<NameserverEntry>::Element {
 public:
     /// List of addresses associated with this nameserver
     typedef std::vector<AddressEntry>   AddressVector;
@@ -126,14 +129,14 @@ public:
     ///
     /// \param address Address to update
     /// \param RTT New RTT for the address
-    virtual void setAddressRTT(const IOAddress& address, uint32_t rtt);
+    virtual void setAddressRTT(const asiolink::IOAddress& address, uint32_t rtt);
 
     /// \brief Set Address Unreachable
     ///
     /// Sets the specified address to be unreachable
     ///
     /// \param address Address to update
-    virtual void setAddressUnreachable(const IOAddress& address);
+    virtual void setAddressUnreachable(const asiolink::IOAddress& address);
 
     /// \return Owner Name of RRset
     virtual std::string getName() const {

+ 1 - 0
src/lib/nsas/tests/Makefile.am

@@ -20,6 +20,7 @@ run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += nsas_test_utilities.h 
 run_unittests_SOURCES += address_entry_unittest.cc
 run_unittests_SOURCES += hash_unittest.cc
+run_unittests_SOURCES += hash_key_unittest.cc
 run_unittests_SOURCES += hash_table_unittest.cc
 run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += nameserver_entry_unittest.cc

+ 1 - 0
src/lib/nsas/tests/address_entry_unittest.cc

@@ -32,6 +32,7 @@ static std::string V4B_TEXT("5.6.7.8");
 static std::string V6A_TEXT("2001:dead:beef::0");
 static std::string V6B_TEXT("1984:1985::1986:1987");
 
+using namespace asiolink;
 using namespace std;
 using namespace isc::nsas;
 

+ 56 - 0
src/lib/nsas/tests/hash_key_unittest.cc

@@ -0,0 +1,56 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <boost/lexical_cast.hpp>
+
+#include "hash.h"
+
+using namespace std;
+
+namespace isc {
+namespace nsas {
+
+
+/// \brief Test Fixture Class
+class HashKeyTest : public ::testing::Test {
+};
+
+
+// Test of the constructor
+TEST_F(HashKeyTest, Constructor) {
+    
+    // Basic constructor
+    string  test1("ABCDEF");
+    HashKey key1(test1.c_str(), test1.size(), 1);
+    EXPECT_EQ(key1.key, test1.c_str());
+    EXPECT_EQ(key1.keylen, test1.size());
+    EXPECT_EQ(key1.class_code, 1);
+
+    // String constructor
+    string  test2("uvwxyz");
+    HashKey key2(test2, 2);
+    EXPECT_EQ(key2.key, test2.c_str());
+    EXPECT_EQ(key2.keylen, test2.size());
+    EXPECT_EQ(key2.class_code, 2);
+}
+
+} // namespace nsas
+} // namespace isc

+ 111 - 47
src/lib/nsas/tests/hash_table_unittest.cc

@@ -21,6 +21,7 @@
 #include <iostream>
 
 #include "hash_table.h"
+#include "hash_key.h"
 
 
 namespace isc {
@@ -37,8 +38,10 @@ public:
     /// Constructor
     ///
     /// \param name Name of the object
+    /// \param class_code Class of the object
     /// \param value Integer value
-    DummyObject(const char* name, int value) : name_(name), value_(value)
+    DummyObject(const char* name, uint16_t class_code, int value) :
+        name_(name), classCode_(class_code), value_(value)
     {}
 
     /// \return Name of the object
@@ -51,9 +54,20 @@ public:
         return value_;
     }
 
+    /// \return Class of the object
+    uint16_t getClass() const {
+        return classCode_;
+    }
+
+    /// \return Hash Key of the Object
+    HashKey getKey() {
+        return HashKey(name_.c_str(), name_.size(), classCode_);
+    }
+
 private:
-    string  name_;      ///< Object name
-    int     value_;     ///< Object value
+    string      name_;      ///< Object name
+    uint16_t    classCode_; ///< Object class
+    int         value_;     ///< Object value
 };
 
 /// \brief Comparison Class
@@ -70,34 +84,51 @@ public:
     /// \param object Pointer to the object being tested
     /// \param key Key string
     /// \param keylen Length of the key string
-    bool operator()(DummyObject* object, const char* key, uint32_t keylen) {
+    bool operator()(DummyObject* object, const HashKey& key) {
         // Do a quick check on size first
-        if (keylen != object->getName().size()) {
-            return false;
+        if (key.keylen == object->getName().size()) {
+
+            // Size matches, does the class?
+            if (key.class_code == object->getClass()) {
+
+                // So does the memory?
+                return (memcmp(object->getName().c_str(), key.key,
+                    key.keylen) == 0);
+            }
         }
-        return (memcmp(object->getName().c_str(), key, keylen) == 0);
+
+        // Size or class do not match.
+        return (false);
     }
 };
 
 
 
 /// \brief Text Fixture Class
+///
+/// Many of the tests are based on checking reference counts.  In all tests,
+/// objects named dummyN_ have a reference count of 1 because they are a member
+/// of this class which has been instantiated for the test.  The reference count
+/// is increased as they are added to the hash table for testing and decreased
+/// as they are removed.
 class HashTableTest : public ::testing::Test {
 protected:
 
     // Constructor - initialize the objects
     HashTableTest() :
         table_(new DummyObjectCompare()),
-        dummy1_(new DummyObject("test", 42)),
-        dummy2_(new DummyObject("test", 47)),
-        dummy3_(new DummyObject("Something_Else", 1332))
+        dummy1_(new DummyObject("test", 1, 42)),
+        dummy2_(new DummyObject("test", 1, 47)),
+        dummy3_(new DummyObject("Something_Else", 1, 1332)),
+        dummy4_(new DummyObject("test", 3, 42))
     {}
 
-    // Members
+    // Members.
     HashTable<DummyObject> table_;
     boost::shared_ptr<DummyObject> dummy1_;
     boost::shared_ptr<DummyObject> dummy2_;
     boost::shared_ptr<DummyObject> dummy3_;
+    boost::shared_ptr<DummyObject> dummy4_;
 };
 
 
@@ -117,27 +148,26 @@ TEST_F(HashTableTest, Constructor) {
 // Basic test of addition
 TEST_F(HashTableTest, AddTest) {
 
-    // Using two objects with the same key.
+    // Using two objects with the same name and class,
+    EXPECT_EQ(dummy1_->getName(), dummy2_->getName());
+    EXPECT_EQ(dummy1_->getClass(), dummy2_->getClass());
     EXPECT_EQ(1, dummy1_.use_count());
     EXPECT_EQ(1, dummy2_.use_count());
 
     // Add first one to the hash table_
-    bool result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    bool result = table_.add(dummy1_, dummy1_->getKey());
     EXPECT_TRUE(result);
     EXPECT_EQ(2, dummy1_.use_count());
     EXPECT_EQ(1, dummy2_.use_count());
 
-    // Attempt to add the second object with the same name should fail.
-    result = table_.add(dummy2_, dummy2_.get()->getName().c_str(),
-        dummy2_.get()->getName().size());
+    // Attempt to add the second object with the same name and class fails.
+    result = table_.add(dummy2_, dummy2_->getKey());
     EXPECT_FALSE(result);
     EXPECT_EQ(2, dummy1_.use_count());
     EXPECT_EQ(1, dummy2_.use_count());
 
     // Replacing an entry should work though
-    result = table_.add(dummy2_, dummy2_.get()->getName().c_str(),
-        dummy2_.get()->getName().size(), true);
+    result = table_.add(dummy2_, dummy2_->getKey(), true);
     EXPECT_TRUE(result);
     EXPECT_EQ(1, dummy1_.use_count());
     EXPECT_EQ(2, dummy2_.use_count());
@@ -146,44 +176,40 @@ TEST_F(HashTableTest, AddTest) {
 // Test the remove functionality
 TEST_F(HashTableTest, RemoveTest) {
 
-    // Using two objects with different keys
+    // Using two objects with different names but the same class
+    EXPECT_NE(dummy1_->getName(), dummy3_->getName());
+    EXPECT_EQ(dummy1_->getClass(), dummy3_->getClass());
     EXPECT_EQ(1, dummy1_.use_count());
     EXPECT_EQ(1, dummy3_.use_count());
 
     // Add first one to the hash table_
-    bool result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    bool result = table_.add(dummy1_, dummy1_->getKey());
     EXPECT_TRUE(result);
     EXPECT_EQ(2, dummy1_.use_count());
     EXPECT_EQ(1, dummy3_.use_count());
 
     // Now remove it.
-    result = table_.remove(dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    result = table_.remove(dummy1_->getKey());
     EXPECT_TRUE(result);
     EXPECT_EQ(1, dummy1_.use_count());
     EXPECT_EQ(1, dummy3_.use_count());
 
     // Attempt to remove it again.
-    result = table_.remove(dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    result = table_.remove(dummy1_->getKey());
     EXPECT_FALSE(result);
     EXPECT_EQ(1, dummy1_.use_count());
     EXPECT_EQ(1, dummy3_.use_count());
 
-    // Add both entries to the table_, then remove one (checks that it will
+    // Add both entries to table_, then remove one (checks that it will
     // remove the correct one).
-    result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    result = table_.add(dummy1_, dummy1_->getKey());
     EXPECT_TRUE(result);
-    result = table_.add(dummy3_, dummy3_.get()->getName().c_str(),
-        dummy3_.get()->getName().size());
+    result = table_.add(dummy3_, dummy3_->getKey());
     EXPECT_TRUE(result);
     EXPECT_EQ(2, dummy1_.use_count());
     EXPECT_EQ(2, dummy3_.use_count());
 
-    result = table_.remove(dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    result = table_.remove(dummy1_->getKey());
     EXPECT_TRUE(result);
     EXPECT_EQ(1, dummy1_.use_count());
     EXPECT_EQ(2, dummy3_.use_count());
@@ -192,39 +218,77 @@ TEST_F(HashTableTest, RemoveTest) {
 // Test the "get" functionality
 TEST_F(HashTableTest, GetTest) {
 
-    // Using two objects with different keys
+    // Using two objects with different names but the same class
+    EXPECT_NE(dummy1_->getName(), dummy3_->getName());
+    EXPECT_EQ(dummy1_->getClass(), dummy3_->getClass());
     EXPECT_EQ(1, dummy1_.use_count());
     EXPECT_EQ(1, dummy3_.use_count());
 
     // Add both to the hash table
-    bool result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    bool result = table_.add(dummy1_, dummy1_->getKey());
     EXPECT_TRUE(result);
-    result = table_.add(dummy3_, dummy3_.get()->getName().c_str(),
-        dummy3_.get()->getName().size());
+    result = table_.add(dummy3_, dummy3_->getKey());
     EXPECT_TRUE(result);
 
     // Lookup the first
-    boost::shared_ptr<DummyObject> value =
-        table_.get(dummy1_.get()->getName().c_str(), 
-        dummy1_.get()->getName().size());
+    boost::shared_ptr<DummyObject> value = table_.get(dummy1_->getKey());
     EXPECT_EQ(value.get(), dummy1_.get());
 
     // ... and the second
-    value = table_.get(dummy3_.get()->getName().c_str(), 
-        dummy3_.get()->getName().size());
+    value = table_.get(dummy3_->getKey());
     EXPECT_EQ(value.get(), dummy3_.get());
 
     // Remove the first
-    result = table_.remove(dummy1_.get()->getName().c_str(),
-        dummy1_.get()->getName().size());
+    result = table_.remove(dummy1_->getKey());
     EXPECT_TRUE(result);
 
     // ... and a lookup should return empty
-    value = table_.get(dummy1_.get()->getName().c_str(), 
-        dummy1_.get()->getName().size());
+    value = table_.get(dummy1_->getKey());
     EXPECT_TRUE(value.get() == NULL);
+}
+
+// Test that objects with the same name and different classes are distinct.
+TEST_F(HashTableTest, ClassTest) {
+
+    // Using two objects with the same name and different classes
+    EXPECT_EQ(dummy1_->getName(), dummy4_->getName());
+    EXPECT_NE(dummy1_->getClass(), dummy4_->getClass());
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(1, dummy4_.use_count());
+
+    // Add both to the hash table
+    bool result = table_.add(dummy1_, dummy1_->getKey());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(2, dummy1_.use_count());
+
+    result = table_.add(dummy4_, dummy4_->getKey());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(2, dummy4_.use_count());
+
+    // Lookup the first
+    boost::shared_ptr<DummyObject> value1 = table_.get(dummy1_->getKey());
+    EXPECT_EQ(value1.get(), dummy1_.get());
+    EXPECT_EQ(3, dummy1_.use_count());
+    EXPECT_EQ(2, dummy4_.use_count());
+
+    // ... and the second
+    boost::shared_ptr<DummyObject> value4 = table_.get(dummy4_->getKey());
+    EXPECT_EQ(value4.get(), dummy4_.get());
+    EXPECT_EQ(3, dummy1_.use_count());
+    EXPECT_EQ(3, dummy4_.use_count());
+
+    // ... and check they are different
+    EXPECT_NE(dummy1_.get(), dummy4_.get());
 
+    // Remove the first
+    result = table_.remove(dummy1_->getKey());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(2, dummy1_.use_count());
+    EXPECT_EQ(3, dummy4_.use_count());
+
+    // ... and a lookup should return empty
+    boost::shared_ptr<DummyObject> value1a = table_.get(dummy1_->getKey());
+    EXPECT_TRUE(value1a.get() == NULL);
 }
 
 

+ 58 - 21
src/lib/nsas/tests/hash_unittest.cc

@@ -28,8 +28,27 @@ using namespace std;
 namespace isc {
 namespace nsas {
 
+// Utility function to count the number of unique values in a vector
+static uint32_t CountUnique(const vector<uint32_t>& input) {
 
-/// \brief Text Fixture Class
+    // Duplicate the vector as this will be destroyed in the process.
+    vector<uint32_t>  vcopy(input);
+
+    // Check to see if values are unique.  Do this by sorting the values into
+    // ascending order, removing duplicates, and checking the size again.
+    //
+    // Note that unique only shifts elements around - it does not remove non-
+    // unique values, so it does change the size of the array.  The call to
+    // erase removes the elements left at the end of the array.
+    sort(vcopy.begin(), vcopy.end());
+    vcopy.erase(unique(vcopy.begin(), vcopy.end()), vcopy.end());
+
+    // ... and return the count of elements remaining.
+    return (vcopy.size());
+}
+
+
+/// \brief Test Fixture Class
 class HashTest : public ::testing::Test {
 };
 
@@ -60,22 +79,16 @@ TEST_F(HashTest, Algorithm) {
     // Generate hash values
     for (int i = 0; i < 50; ++i) {
         string name = base + boost::lexical_cast<string>(i);
-        uint32_t hashval = hash(name.c_str(), name.size());
+        uint32_t hashval = hash(HashKey(name.c_str(), name.size(), 0));
         EXPECT_LT(hashval, size);
         values.push_back(hashval);
     }
     uint32_t cursize = values.size();
 
-    // Check to see if they are unique.  Do this by sorting the values into
-    // ascending order, removing duplicates, and checking the size again.
-    //
-    // Note that unique only shifts elements around - it does not remove non-
-    // unique values, so it does change the size of the array.  The call to
-    // erase removes the elements left at the end of the array.
-    sort(values.begin(), values.end());
-    values.erase(unique(values.begin(), values.end()), values.end());
+    // Count the unique values in the array
+    uint32_t newsize = CountUnique(values);
 
-    uint32_t newsize = values.size();
+    // We don't expect many clashes.
     EXPECT_GE(newsize + 2, cursize);
 }
 
@@ -98,27 +111,26 @@ TEST_F(HashTest, CaseMapping) {
 }
 
 // Test the mapping of mixed-case names
-
 TEST_F(HashTest, MixedCase) {
 
     std::string test1 = "example1234.co.uk.";
     std::string test2 = "EXAmple1234.co.uk.";
 
-    Hash hash(1009, 255);
+    Hash hash(1009, 255, false);    // Disable randomisation for testing
 
     // Case not ignored, hashes should be different
-    uint32_t value1 = hash(test1.c_str(), test1.size(), false);
-    uint32_t value2 = hash(test2.c_str(), test2.size(), false);
+    uint32_t value1 = hash(HashKey(test1.c_str(), test1.size(), 0), false);
+    uint32_t value2 = hash(HashKey(test2.c_str(), test2.size(), 0), false);
     EXPECT_NE(value1, value2);
 
     // Case ignored, hashes should be the same
-    uint32_t value3 = hash(test1.c_str(), test1.size(), true);
-    uint32_t value4 = hash(test2.c_str(), test2.size(), true);
+    uint32_t value3 = hash(HashKey(test1.c_str(), test1.size(), 0), true);
+    uint32_t value4 = hash(HashKey(test2.c_str(), test2.size(), 0), true);
     EXPECT_EQ(value3, value4);
 
     // Check the default setting.
-    uint32_t value5 = hash(test1.c_str(), test1.size());
-    uint32_t value6 = hash(test2.c_str(), test2.size());
+    uint32_t value5 = hash(HashKey(test1.c_str(), test1.size(), 0));
+    uint32_t value6 = hash(HashKey(test2.c_str(), test2.size(), 0));
     EXPECT_EQ(value5, value6);
 
     // ... and just for good measure
@@ -126,6 +138,31 @@ TEST_F(HashTest, MixedCase) {
     EXPECT_EQ(value1, value5);
 }
 
+// Test that the same name but different classes get mapped differently.
+TEST_F(HashTest, ClassCodes) {
+
+    std::string test1 = "example1234.co.uk.";
+    Hash hash(1009, 255, false);    // Disable randomisation for testing
+
+    // Just try codes in the range 0 to 9 - more than covers the allocated
+    // codes.
+    vector<uint32_t> values;
+    for (uint32_t i = 0; i < 10; ++i) {
+        values.push_back(hash(HashKey(test1.c_str(), test1.size(), i)));
+    }
+
+    // find the number of unique values in the array.  Although there can
+    // be some clashes, we don't expect too many.
+    uint32_t cursize = values.size();
+
+    // Count the unique values in the array
+    uint32_t newsize = CountUnique(values);
+
+    // We don't expect many clashes.
+    EXPECT_GE(newsize + 2, cursize);
+}
+
+
 // Test that the code performs when the length of the key is excessive
 TEST_F(HashTest, Overlong) {
 
@@ -137,8 +174,8 @@ TEST_F(HashTest, Overlong) {
     Hash hash(1009, string1.size());
 
     // Do two hashes
-    uint32_t value1 = hash(string1.c_str(), string1.size());
-    uint32_t value2 = hash(string2.c_str(), string2.size());
+    uint32_t value1 = hash(HashKey(string1.c_str(), string1.size(), 0));
+    uint32_t value2 = hash(HashKey(string2.c_str(), string2.size(), 0));
     EXPECT_EQ(value1, value2);
 }
 

+ 1 - 1
src/lib/nsas/tests/nameserver_entry_unittest.cc

@@ -33,7 +33,7 @@
 
 #include "nsas_test_utilities.h"
 
-
+using namespace asiolink;
 using namespace std;
 using namespace isc::dns;
 using namespace rdata;

+ 3 - 1
src/lib/nsas/zone_entry.h

@@ -22,6 +22,8 @@
 #include <boost/thread.h>
 #include <boost/shared_ptr.h>
 
+#include "asiolink.h"
+
 class NameserverEntry;
 
 /// \brief Zone Entry
@@ -45,7 +47,7 @@ public:
     /// \brief Lookup Address
     ///
     /// Returns the address with the lowest RTT.
-    IOAddress getAddress() const;
+    asiolink::IOAddress getAddress() const;
 
 public:
     void updateNS