// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-// ==++==
-//
-
-//
-
-//
-// ==--==
-
-
#ifndef _SIMPLERHASHTABLE_H_
#define _SIMPLERHASHTABLE_H_
-// #include "utilcode.h" // for string hash functions
-// #include "clrtypes.h"
-// #include "check.h"
-// #include "iterator.h"
#include "iallocator.h"
// SimplerHashTable implements a mapping from a Key type to a Value type,
//
// The "Behavior" argument provides the following static members:
//
-// static const bool s_NoThrow true if GetKey, Equals, and hash are NOTHROW functions.
-// Affects the THROWS clauses of several SHash functions.
-// (Note that the Null- and Deleted-related functions below
-// are not affected by this and must always be NOTHROW.)
-//
// s_growth_factor_numerator
// s_growth_factor_denominator Factor to grow allocation (numerator/denominator).
// Typically inherited from default traits (3/2)
// probably preferable to call Reallocate on initialization rather
// than override his from the default traits. (7)
//
+// NoMemory() Called when the has table is unable to grow due to potential
+// overflow or the lack of a sufficiently large prime.
-
+void DECLSPEC_NORETURN ThrowOutOfMemory();
class DefaultSimplerHashBehavior
{
- public:
+public:
static const unsigned s_growth_factor_numerator = 3;
static const unsigned s_growth_factor_denominator = 2;
static const unsigned s_minimum_allocation = 7;
- static const bool s_NoThrow = true;
+ inline static void DECLSPEC_NORETURN NoMemory()
+ {
+ ::ThrowOutOfMemory();
+ }
};
-
-// stores info about primes, including the magic number and shift amount needed
+// Stores info about primes, including the magic number and shift amount needed
// to implement a divide without using the divide instruction
-
class PrimeInfo
{
public:
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
class SimplerHashTable
{
- public:
-
+public:
// Forward declaration.
class KeyIterator;
SimplerHashTable(IAllocator* alloc);
~SimplerHashTable();
- // We wish to provide overloaded operator new/deletes that take an
- // "IAllocator", so that an shash may be dynamically allocated
- // using a non-standard allocator. If we do so, and in some
- // contexts still want to use the default operator new, C++ also requires us
- // to declare overloads for the standard new and delete operators.
-
- void * operator new(size_t sz)
- {
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
- return ::operator new(sz);
- }
-
- void * operator new(size_t sz, const NoThrow& tothrow)
- {
- CONTRACTL
- {
- if (nothrow.x== tothrow.x)NOTHROW; else THROWS;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
- return ::operator new(sz, tothrow);
- }
-
- void operator delete(void* p)
- {
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
- ::operator delete(p);
- }
-
- void operator delete[](void* p)
- {
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
- ::operator delete[](p);
- }
-
// operators new/delete when an IAllocator is to be used.
void * operator new(size_t sz, IAllocator * alloc);
void * operator new[](size_t sz, IAllocator * alloc);
Value *LookupPointer(Key k) const;
- unsigned GetIndexForKey(Key k) const;
-
// Causes the table to map "key" to "val". Returns "true" if
// "key" had already been mapped by the table, "false" otherwise.
bool Set(Key k, Value val);
// Returns "true" iff it had been mapped.
bool Remove(Key k);
- // Remove the mapping to which the current iterator "points"
- // (i.e., will yield).
- void Remove(KeyIterator& i);
-
// Remove all mappings in the table.
void RemoveAll();
// Return the number of elements currently stored in the table
unsigned GetCount() const;
- private:
-
+private:
// Forward declaration of the linked-list node class.
struct Node;
+ unsigned GetIndexForKey(Key k) const;
+
// If the table has a mapping for "k", return the node containing
// that mapping, else "NULL".
Node* FindNode(Key k) const;
// the hash table.
void CheckGrowth();
- public:
-
+public:
// Reallocates a hash table to a specific size. The size must be big enough
// to hold all elements in the table appropriately.
//
// }
// iter.Get() will yield (a reference to) the
// current key. It will assert the equivalent of "iter != end."
- //
-
class KeyIterator
{
+ private:
friend class SimplerHashTable;
// The method implementations have to be here for portability.
unsigned m_tableSize;
unsigned m_index;
- public:
-
+ public:
KeyIterator(const SimplerHashTable *hash, BOOL begin)
: m_table(hash->m_table),
m_node(NULL),
m_tableSize(hash->m_tableSizeInfo.prime),
m_index(begin ? 0 : m_tableSize)
{
- LIMITED_METHOD_CONTRACT;
if (begin && hash->m_tableCount > 0)
{
assert(m_table != NULL);
const Key& Get() const
{
- LIMITED_METHOD_CONTRACT;
-
assert(m_node != NULL);
return m_node->m_key;
const Value& GetValue() const
{
- LIMITED_METHOD_CONTRACT;
-
assert(m_node != NULL);
return m_node->m_val;
void SetValue(const Value & value) const
{
- LIMITED_METHOD_CONTRACT;
-
assert(m_node != NULL);
m_node->m_val = value;
void Next()
{
- LIMITED_METHOD_CONTRACT;
-
if (m_node != NULL)
{
m_node = m_node->m_next;
bool Equal(const KeyIterator &i) const
{
- LIMITED_METHOD_CONTRACT;
return i.m_node == m_node;
}
- CHECK DoCheck() const
- {
- CHECK_OK;
- }
-
void operator++() {
Next();
}
+
void operator++(int) {
Next();
}
}
private:
-
// Find the next prime number >= the given value.
-
static PrimeInfo NextPrime(unsigned number);
// Instance members
-
IAllocator* m_alloc; // IAllocator to use in this
// table.
// The node type.
}
};
-
-
template<typename T> // Must be coercable to "unsigned" with no loss of information.
struct SmallPrimitiveKeyFuncs: public KeyFuncsDefEquals<T>
{
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-// ==++==
-//
-
-//
-
-//
-// ==--==
-
#ifndef _SIMPLERHASHTABLE_INL_
#define _SIMPLERHASHTABLE_INL_
-// Many SimplerHashTable functions do not throw on their own, but may propagate an exception
-// from Hash, Equals, or GetKey.
-#define NOTHROW_UNLESS_BEHAVIOR_THROWS if (Behavior::s_NoThrow) NOTHROW; else THROWS
-
-void DECLSPEC_NORETURN ThrowOutOfMemory();
-
// To implement magic-number divide with a 32-bit magic number,
// multiply by the magic number, take the top 64 bits, and shift that
// by the amount given in the table.
m_tableCount(0),
m_tableMax(0)
{
- LIMITED_METHOD_CONTRACT;
-
assert(m_alloc != nullptr);
#ifndef __GNUC__ // these crash GCC
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
SimplerHashTable<Key,KeyFuncs,Value,Behavior>::~SimplerHashTable()
{
- LIMITED_METHOD_CONTRACT;
-
RemoveAll();
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void * SimplerHashTable<Key,KeyFuncs,Value,Behavior>::operator new(size_t sz, IAllocator * alloc)
{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
return alloc->Alloc(sz);
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void * SimplerHashTable<Key,KeyFuncs,Value,Behavior>::operator new[](size_t sz, IAllocator * alloc)
{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
return alloc->Alloc(sz);
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void SimplerHashTable<Key,KeyFuncs,Value,Behavior>::operator delete(void * p, IAllocator * alloc)
{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
alloc->Free(p);
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void SimplerHashTable<Key,KeyFuncs,Value,Behavior>::operator delete[](void * p, IAllocator * alloc)
{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
alloc->Free(p);
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
unsigned SimplerHashTable<Key,KeyFuncs,Value,Behavior>::GetCount() const
{
- LIMITED_METHOD_CONTRACT;
-
return m_tableCount;
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
bool SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Lookup(Key key, Value* pVal) const
{
- CONTRACTL
- {
- NOTHROW_UNLESS_BEHAVIOR_THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
Node* pN = FindNode(key);
if (pN != NULL)
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
Value *SimplerHashTable<Key,KeyFuncs,Value,Behavior>::LookupPointer(Key key) const
{
- CONTRACTL
- {
- NOTHROW_UNLESS_BEHAVIOR_THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
Node* pN = FindNode(key);
if (pN != NULL)
typename SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Node*
SimplerHashTable<Key,KeyFuncs,Value,Behavior>::FindNode(Key k) const
{
- CONTRACT(Node*)
- {
- NOTHROW_UNLESS_BEHAVIOR_THROWS;
- GC_NOTRIGGER;
- POSTCONDITION(RETVAL == NULL || KeyFuncs::Equals(k, RETVAL->m_key));
- }
- CONTRACT_END;
-
if (m_tableSizeInfo.prime == 0)
- RETURN NULL;
+ return NULL;
unsigned index = GetIndexForKey(k);
Node* pN = m_table[index];
- if (pN == NULL) RETURN NULL;
+ if (pN == NULL)
+ return NULL;
+
// Otherwise...
while (pN != NULL && !KeyFuncs::Equals(k, pN->m_key))
pN = pN->m_next;
+
+ assert(pN == NULL || KeyFuncs::Equals(k, pN->m_key));
+
// If pN != NULL, it's the node for the key, else the key isn't mapped.
- RETURN pN;
+ return pN;
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
unsigned SimplerHashTable<Key,KeyFuncs,Value,Behavior>::GetIndexForKey(Key k) const
{
- CONTRACTL
- {
- NOTHROW_UNLESS_BEHAVIOR_THROWS;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
unsigned hash = KeyFuncs::GetHashCode(k);
unsigned index = magicNumberRem(hash, m_tableSizeInfo);
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
bool SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Set(Key k, Value v)
{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
CheckGrowth();
assert(m_tableSizeInfo.prime != 0);
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
bool SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Remove(Key k)
{
- CONTRACTL
- {
- NOTHROW_UNLESS_BEHAVIOR_THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
unsigned index = GetIndexForKey(k);
Node* pN = m_table[index];
}
}
-// TBD
-template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
-void SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Remove(KeyIterator& i)
-{
-#if 0
- CONTRACT_VOID
- {
- NOTHROW;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- PRECONDITION(s_supports_remove);
- PRECONDITION(!(IsNull(*i)));
- PRECONDITION(!(IsDeleted(*i)));
- }
- CONTRACT_END;
-
- RemoveElement(m_table, m_tableSize, (element_t*)&(*i));
-#endif
- return;
-}
-
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void SimplerHashTable<Key,KeyFuncs,Value,Behavior>::RemoveAll()
{
- CONTRACT_VOID
- {
- NOTHROW;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACT_END;
-
for (unsigned i = 0; i < m_tableSizeInfo.prime; i++)
{
for (Node* pN = m_table[i]; pN != NULL; )
m_tableCount = 0;
m_tableMax = 0;
- RETURN;
+ return;
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
typename SimplerHashTable<Key,KeyFuncs,Value,Behavior>::KeyIterator SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Begin() const
{
- LIMITED_METHOD_CONTRACT;
-
KeyIterator i(this, TRUE);
return i;
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
typename SimplerHashTable<Key,KeyFuncs,Value,Behavior>::KeyIterator SimplerHashTable<Key,KeyFuncs,Value,Behavior>::End() const
{
- LIMITED_METHOD_CONTRACT;
-
return KeyIterator(this, FALSE);
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void SimplerHashTable<Key,KeyFuncs,Value,Behavior>::CheckGrowth()
{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
if (m_tableCount == m_tableMax)
{
Grow();
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Grow()
{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
unsigned newSize = (unsigned) (m_tableCount
* Behavior::s_growth_factor_numerator / Behavior::s_growth_factor_denominator
* Behavior::s_density_factor_denominator / Behavior::s_density_factor_numerator);
// handle potential overflow
if (newSize < m_tableCount)
- ThrowOutOfMemory();
+ Behavior::NoMemory();
Reallocate(newSize);
}
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
void SimplerHashTable<Key,KeyFuncs,Value,Behavior>::Reallocate(unsigned newTableSize)
{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- PRECONDITION(newTableSize >=
- (GetCount() * Behavior::s_density_factor_denominator / Behavior::s_density_factor_numerator));
- }
- CONTRACTL_END;
+ assert(newTableSize >= (GetCount() * Behavior::s_density_factor_denominator / Behavior::s_density_factor_numerator));
// Allocation size must be a prime number. This is necessary so that hashes uniformly
// distribute to all indices, and so that chaining will visit all indices in the hash table.
template <typename Key, typename KeyFuncs, typename Value, typename Behavior>
PrimeInfo SimplerHashTable<Key,KeyFuncs,Value,Behavior>::NextPrime(unsigned number)
{
- CONTRACT(PrimeInfo)
- {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACT_END;
-
for (int i = 0; i < (int) (sizeof(primeInfo) / sizeof(primeInfo[0])); i++) {
if (primeInfo[i].prime >= number)
- RETURN primeInfo[i];
+ return primeInfo[i];
}
// overflow
- ThrowOutOfMemory();
+ Behavior::NoMemory();
}
#endif // _SIMPLERHASHTABLE_INL_