etk/etk/Hash.hpp

278 lines
8.3 KiB
C++

/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#include <etk/types.hpp>
#pragma once
namespace etk {
/**
* @brief Internal data of the [class[etk::hash]] class, it contain
* the name and the value of the hash vector.
* @internal
*/
template<class MY_TYPE> class HashData {
public:
etk::String m_key; //!< name of the current hash
MY_TYPE m_value; //!< data of the current Hash
/**
* @brief Constructor of the data for hash table.
* @param[in] _key name of the hash table.
* @param[in] _val Value of the hash element.
*/
HashData(const etk::String& _key, const MY_TYPE& _val) :
m_key(_key),
m_value(_val) {
// nothing to do ...
}
};
/**
* @brief Hash table template is a simple classical hash interface.
* A hash table is a equivalent of the dictionary in python, this is a
* simple interface between a name and a value:
* - "name" : 19
* - "name 2" : 99
*
* @note The name is unique and the value is what you want
*
* @todo check if something else exist in the generic library. (not the std::map and the std::unordered_map
*
* @note The index are all time available since they are created. The order is the the one created
*
* A simple example of use:
* @code{.cpp}
* // Create a integer hash table
* Hash<int> myValue;
* // add some element (note add and set is the same function)
* myValue.add("example", 98837);
* myValue.add("plop", 88);
* // Display an element:
* printf("my value is : %d", myValue["example"]);
* // Change value of an element:
* myValue.set("example", 99);
* // Remove an element:
* myValue.remove("plop");
* //Clean all the table:
* myValue.clear();
* @endcode
*/
template<class MY_TYPE> class Hash {
private:
std::vector<HashData<MY_TYPE>* > m_data; //!< Data of the hash ==> the Hash table is composed of pointer, this permit to have high speed when resize the vector ...
public:
/**
* @brief Constructor of the Hash table.
* @param[in] _count Number of basic element in the table.
*/
Hash(int32_t _count = 0) :
m_data(_count) {
// nothing to do
}
/**
* @brief Destructor of the Hash table (clear all element in the table)
*/
~Hash() {
clear();
}
/**
* @brief Remove all entry in the Hash table.
* @note It does not delete pointer if your value is a pointer type...
*/
void clear() {
for (auto &it : m_data) {
if (it != nullptr) {
delete(it);
it=nullptr;
}
}
m_data.clear();
}
/**
* @brief Get a current element ID in the Hash table
* @param[in] _key Name of the hash requested
* @return Id of the element in the table or -1 of it does not existed
*/
int64_t getId(const etk::String& _key) const {
for (size_t iii=0; iii<m_data.size(); iii++) {
if (m_data[iii] != nullptr) {
//TK_INFO("Compare key : '" << m_data[iii]->m_key << "' with '" << _key << "'" );
if (m_data[iii]->m_key == _key) {
return iii;
}
}
}
//TK_ERROR(" ==> not fund key '" << _key << "'" );
return -1;
}
/**
* @brief Check if an element exist or not
* @param[in] _name Name of the hash requested
* @return true if the element exist
*/
bool exist(const etk::String& _name) const {
int64_t elementId = getId(_name);
//TK_INFO(" Exist ? '" << _name << "' id=" << elementId );
if (elementId<0) {
//TK_INFO(" ==> return false" );
return false;
}
//TK_INFO(" ==> return true" );
return true;
}
/**
* @brief Get a current element in the hash table, with his name.
* @param[in] _key Name of the hash requested
* @return Reference on the Element
*/
MY_TYPE& get(const etk::String& _key) const {
static MY_TYPE g_error;
int64_t elementId = getId(_key);
if (elementId<0) {
//TK_ERROR("try to access at an inexistent hash element : " << _key);
return g_error;
}
return m_data[elementId]->m_value;
}
/**
* @brief Get an copy Element an a special position
* @param[in] _key Name of the hash requested
* @return An reference on the copy of selected element
*/
MY_TYPE& operator[] (const etk::String& _key) {
return get(_key);
}
/**
* @brief Get an copy Element an a special position
* @param[in] _key Name of the hash requested
* @return An reference on the copy of selected element
*/
const MY_TYPE& operator[] (const etk::String& _key) const {
return get(_key);
}
/**
* @brief Add an element OR set an element value
* @note add and set is the same function.
* @param[in] _key Name of the value to set in the hash table.
* @param[in] _value Value to set in the hash table.
*/
void add(const etk::String& _key, const MY_TYPE& _value) {
int64_t elementId = getId(_key);
if (elementId <0) {
HashData<MY_TYPE>* tmp = new HashData<MY_TYPE>(_key, _value);
if (tmp == nullptr) {
//TK_ERROR("allocation error in Hash table : '" << _key << "'");
return;
}
m_data.push_back(tmp);
return;
}
m_data[elementId]->m_value = _value;
}
/**
* @brief Set an element value
* @note add and set is the same function.
* @param[in] _key Name of the value to set in the hash table.
* @param[in] _value Value to set in the hash table.
*/
void set(const etk::String& _key, const MY_TYPE& _value) {
add(_key, _value);
}
/**
* @brief Remove an element in the hash table.
* @param[in] _key Name of the element to remove.
*/
void remove(const etk::String& _key) {
int64_t elementId = getId(_key);
if (elementId <0) {
//nothing to do ==> not existed
return;
}
delete(m_data[elementId]);
m_data[elementId] = nullptr;
m_data.erase(m_data.begin()+elementId);
}
/**
* @brief Get the number of element in the hash table
* @return number of elements
*/
size_t size() const {
return m_data.size();
}
/**
* @brief get an element with his id.
* @param[in] _pos Position on the element in the hash table.
* @return requested element at this position.
* @note this is a dangerous use of the hash table. Maybe you will use a simple vector.
*/
MY_TYPE& operator[] (size_t _pos) {
return getValue(_pos);
}
/**
* @brief get an element with his id.
* @param[in] _pos Position on the element in the hash table.
* @return requested element at this position.
* @note this is a dangerous use of the hash table. Maybe you will use a simple vector.
*/
const MY_TYPE& operator[] (size_t _pos) const {
return getValue(_pos);
}
/**
* @brief Get the name of the element at a specific position.
* @param[in] _pos Position of the element in the hash table.
* @return name of the element (key).
*/
const etk::String& getKey(size_t _pos) const {
// NOTE :Do not change log level, this generate error only in debug mode
#if DEBUG_LEVEL > 2
if(_pos>m_data.size()){
//TK_CRITICAL("Access to an inexistent data in hash : " << _pos << "/ " << m_data.size());
}
#endif
return m_data[_pos]->m_key;
}
/**
* @brief Get all the element name (keys).
* @return a vector of all name (key).
*/
std::vector<etk::String> getKeys() const {
std::vector<etk::String> keys;
for (auto &it : m_data) {
if (it != nullptr) {
keys.push_back(it->m_key);
}
}
return keys;
}
/**
* @brief Get a value of the hash table at a specific position.
* @param[in] _pos of the element in the hash table.
* @return Value available at this position.
*/
const MY_TYPE& getValue(size_t _pos) const {
// NOTE :Do not change log level, this generate error only in debug mode
#if DEBUG_LEVEL > 2
if(_pos>m_data.size()){
//TK_CRITICAL("Access to an inexistent data in hash : " << _pos << "/ " << m_data.size());
}
#endif
return m_data[_pos]->m_value;
}
/**
* @copydoc getValue (size_t)
*/
MY_TYPE& getValue(size_t _pos) {
// NOTE :Do not change log level, this generate error only in debug mode
#if DEBUG_LEVEL > 2
if(_pos>m_data.size()){
//TK_CRITICAL("Access to an inexistent data in hash : " << _pos << "/ " << m_data.size());
}
#endif
return m_data[_pos]->m_value;
}
};
}