Files
o3de/Gems/EMotionFX/Code/MCore/Source/HashTable.h
T
Steve Pham 38261d0800 Shorten copyright headers by splitting into 2 lines (#2213)
* Updated all copyright headers to split the longer original copyright line into 2 shorter lines

Signed-off-by: Steve Pham <spham@amazon.com>
2021-07-16 15:25:48 -07:00

240 lines
11 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
// include the needed headers
#include "StandardHeaders.h"
#include "Array.h"
#include "FastMath.h"
#include "HashFunctions.h"
namespace MCore
{
/**
* The Hash Table template.
* Hash tables can be used to speedup searching of specific values based on a key.
* The table has an array of table elements, which each can contain multiple hash table entries.
* Each entry is identified by a unique key, which can be of any type, as long as the == operator is specified.
* Next to a unique key, every entry contains a value. The hash table implementation contains methods to add new
* entries and to retrieve the value for a given key. Hashing is used to speedup this search.
* The hash value of a given key is calculated based on a specified hashing function that you pass to the constructor.
* This hash function will return a non-negative (so positive) integer based on the input key.
* Performance tests have shown that you need at least 100 entries to make it faster than linear searches. This however
* also depends on the speed of your hash function and some other factors. But it is a good practise to replace your
* linear searches by a hash table when you have more than 100 items to search through. The more items to search through
* the bigger the advantage of hashing over linear searches will be. Here follows a small table that shows how much faster your
* searches can be compared to linear searches.
*/
template <class Key, class Value>
class HashTable
{
public:
/**
* A hash table entry, which contains a unique key and a value for this key.
*/
class Entry
{
friend class HashTable<Key, Value>;
MCORE_MEMORYOBJECTCATEGORY(HashTable, MCORE_DEFAULT_ALIGNMENT, MCORE_MEMCATEGORY_HASHTABLE)
public:
/**
* The constructor.
* @param key The unique key of this entry.
* @param value The value linked to this key.
*/
MCORE_INLINE Entry(const Key& key, const Value& value)
: mKey(key)
, mValue(value) {}
/**
* Set the value of this entry.
* @param value The value to set for this entry.
*/
MCORE_INLINE void SetValue(const Value& value) { mValue = value; }
/**
* Get the value of this entry.
* @result The value that is linked to the key of this entry.
*/
MCORE_INLINE const Value& GetValue() const { return mValue; }
/**
* Get the key of this entry.
* @result The unique key of this entry.
*/
MCORE_INLINE const Key& GetKey() const { return mKey; }
private:
Key mKey; /**< The unique key. */
Value mValue; /**< The value that is linked to the given key. */
};
/**
* The default constructor.
* This creates an empty hash table. You need to call the Init function before you can use the table.
* @see Init
*/
HashTable();
/**
* The extended constructor, which also initializes the table automatically.
* You do NOT need to call the Init function anymore when you use this constructor.
* @param maxElements The maximum number of table elements. The higher the value, the more gain when dealing with many entries.
* Values between 100 and 1000 are often good numbers, depending on the number of entries you are dealing with.
*/
HashTable(uint32 maxElements);
/**
* Copy constructor.
* @param other The table to create a copy of.
*/
HashTable(const HashTable<Key, Value>& other);
/**
* The destructor.
* This automatically clears all table entries.
* The hash function object that was passed to the extended constructor or the Init function will be
* deleted from memory automatically.
*/
~HashTable();
/**
* Clear the hash table.
* This removes all entries from the table. If you like to use the table again later on you will need to
* call the Init function again.
* This also automatically deletes the hash function object, that you passed to the extended constructor or init function, from memory.
* @see Init
*/
void Clear();
/**
* Locate an entry with a given key.
* When the entry cannot be found, this method will NOT modify the outElementNr and outEntryNr parameters.
* @param key The key to search for.
* @param outElementNr A pointer to an integer in which this method will store the table element number, in case the entry is found.
* @param outEntryNr A pointer to an integer in which this method will store the entry number (index) into the table element array, in case the entry is found.
* @result Returns true when the entry with the given key could be found, otherwise false is returned.
*/
MCORE_INLINE bool FindEntry(const Key& key, uint32* outElementNr, uint32* outEntryNr) const;
/**
* Initialize the hash table.
* @param maxElements The maximum number of table elements. The higher the value, the more gain when dealing with many entries.
* Values between 100 and 1000 are often good numbers, depending on the number of entries you are dealing with.
*/
void Init(uint32 maxElements);
/**
* Add an entry to the hash table.
* It is VERY important that the key is unique and does NOT already exist within this table!
* @param key The unique key of the entry.
* @param value The value that is linked to this key.
*/
void Add(const Key& key, const Value& value);
/**
* Get a value from the table.
* @param inKey The key of the entry which contains the value.
* @param outValue A pointer to an object where this method will write the value of the entry in.
* @result Returns true when the value has been retrieved successfully. False will be returned when no entry with the specified
* key could be located.
*/
MCORE_INLINE bool GetValue(const Key& inKey, Value* outValue) const;
/**
* Set the value that is linked to a given key.
* @param key The unique key of the entry to set the value for.
* @param value The value to link to the specified key.
* @result Returns true when the value has been set successfully, or false when there is no entry with the specified key inside this table.
*/
MCORE_INLINE bool SetValue(const Key& key, const Value& value);
/**
* Check if this hash table contains an entry with a specified key.
* @param key The key of the entry to search for.
* @result Returns true when this hash table contains an entry with the specified key, otherwise false is returned.
*/
MCORE_INLINE bool Contains(const Key& key) const;
/**
* Remove the entry which has the specified key.
* @param key The key of the entry to remove.
* @result Returns true when the entry with the specified key has been removed successfully, otherwise false is returned, which means
* that there is no entry with the specified key.
*/
bool Remove(const Key& key);
/**
* Get the number of table elements.
* @result The number of table elements.
*/
MCORE_INLINE uint32 GetNumTableElements() const;
/**
* Get the number of entries in a given table element.
* @param tableElementNr The table element number to get the number of entries for.
* @result The number of entries for the specified table entry.
*/
MCORE_INLINE uint32 GetNumEntries(uint32 tableElementNr) const;
/**
* Get the total number of entries inside the table.
* @result The total number of stored entries inside the table.
*/
uint32 GetTotalNumEntries() const;
/**
* Calculate the load balance, which is a percentage that represents how many percent of the
* table elements are used. If the returned value equals 50, then it means that 50 percent of the
* table elements are storing entries. The other 50% are then not used.
* When you have added many entries (more than then the number of table elements), and the load balance
* is not 100% or anywhere near it, it means your hash function is very inefficient, because it does not spread
* the entries over the entire hash table, which might mean that there is some nasty clustering going on, which can
* greatly decrease performance.
* @result A floating point value in range of 0..100, which respresents the percentage of table elements that is in use.
*/
float CalcLoadBalance() const;
/**
* Calculate the average number of entries per used table element.
* The more entries per table element, the slower your searches will be.
* The optimal value returned by this function would therefore be 1.
* @result The average number of entries per table element.
*/
float CalcAverageNumEntries() const;
/**
* Get a given entry from the table, when you know its location in the table.
* @param tableElementNr The table element number.
* @param entryNr The entry number inside this table element.
* @result The reference to the entry, with write access.
*/
MCORE_INLINE Entry& GetEntry(uint32 tableElementNr, uint32 entryNr);
/**
* The assignment operator.
* This clones the entire table.
* @param other The table to create a copy of.
* @result The copied version of the specified table.
*/
HashTable<Key, Value>& operator = (const HashTable<Key, Value>& other);
protected:
MCore::Array< MCore::Array<Entry>* > mElements; /**< The table elements, where nullptr means the element is empty. */
uint32 mTotalNumEntries; /**< The cached number of entries in the table. */
};
// include the inline code
#include "HashTable.inl"
} // namespace MCore