1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/include/ulib/container/hash_map.h
2015-01-23 17:24:36 +01:00

876 lines
21 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// hash_map.h
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#ifndef ULIB_HASH_MAP_H
#define ULIB_HASH_MAP_H 1
#include <ulib/container/construct.h>
typedef uint32_t (*uPFpcu) (const char* t, uint32_t tlen);
typedef bool (*bPFprpv)(UStringRep* key, void* elem);
class UCDB;
class UHTTP;
class WeightWord;
class UMimeHeader;
class UFileConfig;
class UNoCatPlugIn;
class UCertificate;
template <class T> class UVector;
template <class T> class UJsonTypeHandler;
class U_NO_EXPORT UHashMapNode {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
const void* elem;
const UStringRep* key;
UHashMapNode* next;
uint32_t hash;
// COSTRUTTORI
UHashMapNode(const UStringRep* _key, const void* _elem, UHashMapNode* _next, uint32_t _hash) : elem(_elem), key(_key), next(_next), hash(_hash)
{
U_TRACE_REGISTER_OBJECT(0, UHashMapNode, "%.*S,%p,%p,%u", U_STRING_TO_TRACE(*_key), _elem, _next, _hash)
((UStringRep*)_key)->hold(); // NB: we increases the reference string...
}
UHashMapNode(UHashMapNode* n, UHashMapNode* _next) : elem(n->elem), key(n->key), next(_next), hash(n->hash)
{
U_TRACE_REGISTER_OBJECT(0, UHashMapNode, "%p,%p", n, _next)
((UStringRep*)key)->hold(); // NB: we increases the reference string...
}
~UHashMapNode()
{
U_TRACE_UNREGISTER_OBJECT(0, UHashMapNode)
((UStringRep*)key)->release(); // NB: we decreases the reference string...
}
UHashMapNode& operator=(const UHashMapNode& n)
{
U_TRACE(0, "UHashMapNode::operator=(%p)", &n)
U_MEMORY_TEST_COPY(n)
elem = n.elem;
key = n.key;
next = n.next;
hash = n.hash;
return *this;
}
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
U_EXPORT const char* dump(bool reset) const;
#endif
};
template <class T> class UHashMap;
template <> class U_EXPORT UHashMap<void*> {
public:
static uPFpcu gperf;
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
// Costruttori e distruttore
UHashMap(const UString& _key, const void* _elem);
UHashMap(uint32_t n = 53, bool _ignore_case = false);
~UHashMap()
{
U_TRACE_UNREGISTER_OBJECT(0, UHashMap<void*>)
if (_capacity > 1) _deallocate();
}
// allocate and deallocate methods
void deallocate()
{
U_TRACE(0, "UHashMap<void*>::deallocate()")
if (_capacity > 1) _deallocate();
_capacity = 0;
}
void allocate(uint32_t n);
// size and capacity
uint32_t size() const
{
U_TRACE(0, "UHashMap<void*>::size()")
U_RETURN(_length);
}
uint32_t capacity() const
{
U_TRACE(0, "UHashMap<void*>::capacity()")
U_RETURN(_capacity);
}
uint32_t space() const
{
U_TRACE(0, "UHashMap<void*>::space()")
U_RETURN(_space);
}
void setSpace(uint32_t s)
{
U_TRACE(0, "UHashMap<void*>::setSpace(%u)", s)
_space = s;
}
bool empty() const
{
U_TRACE(0, "UHashMap<void*>::empty()")
if (_length) U_RETURN(false);
U_RETURN(true);
}
void setIgnoreCase(bool flag)
{
U_TRACE(0, "UHashMap<void*>::setIgnoreCase(%b)", flag)
U_INTERNAL_ASSERT_EQUALS(_length, 0)
ignore_case = flag;
}
bool ignoreCase() const { return ignore_case; }
// ricerche
bool find(const UString& _key)
{
U_TRACE(0, "UHashMap<void*>::find(%.*S)", U_STRING_TO_TRACE(_key))
lookup(_key);
if (node != 0) U_RETURN(true);
U_RETURN(false);
}
bool find(const char* key, uint32_t keylen);
// set/get methods
void* operator[](const char* _key);
void* operator[](const UString& _key) { return at(_key.rep); }
void* operator[](const UStringRep* _key) { return at(_key); }
const void* elem() const { return node->elem; }
const UStringRep* key() const { return node->key; }
template <typename T> T* get(const UString& _key)
{
U_TRACE(0, "UHashMap<void*>::get(%.*S)", U_STRING_TO_TRACE(_key))
return (T*) operator[](_key);
}
// sets a field, overwriting any existing value
void insert(const UString& _key, const void* _elem)
{
U_TRACE(0, "UHashMap<void*>::insert(%.*S,%p)", U_STRING_TO_TRACE(_key), _elem)
lookup(_key);
insertAfterFind(_key, _elem);
}
// dopo avere chiamato find() (non effettuano il lookup)
void insertAfterFind(const UString& _key, const void* _elem) { insertAfterFind(_key.rep, _elem); }
void insertAfterFind(const UStringRep* _key, const void* _elem);
void eraseAfterFind();
void replaceAfterFind(const void* _elem)
{
U_TRACE(0, "UHashMap<void*>::replaceAfterFind(%p)", _elem)
node->elem = _elem;
}
void replaceKey(const UString& key);
void* erase(const char* _key);
void* erase(const UString& _key) { return erase(_key.rep); }
void* erase(const UStringRep* _key);
// make room for a total of n element
void reserve(uint32_t n);
// call function for all entry
bool next();
bool first();
void getKeys(UVector<UString>& vec);
void callForAllEntry(bPFprpv function);
void callForAllEntrySorted(bPFprpv function)
{
U_TRACE(0, "UHashMap<void*>::callForAllEntrySorted(%p)", function)
U_INTERNAL_DUMP("_length = %u", _length)
if (_length < 2)
{
callForAllEntry(function);
return;
}
_callForAllEntrySorted(function);
}
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* dump(bool reset) const;
#endif
// STREAMS
static bool istream_loading;
protected:
UHashMapNode* node;
UHashMapNode** table;
uint32_t _length, _capacity, _space, index, hash;
bool ignore_case;
static UStringRep* pkey;
static UHashMapNode* tmpnode;
void _allocate(uint32_t n)
{
U_TRACE(0, "UHashMap<void*>::_allocate(%u)", n)
U_CHECK_MEMORY
table = (UHashMapNode**) UMemoryPool::_malloc(&n, sizeof(UHashMapNode*), true);
_capacity = n;
}
void _deallocate()
{
U_TRACE(0, "UHashMap<void*>::_deallocate()")
U_CHECK_MEMORY
U_INTERNAL_ASSERT_MAJOR(_capacity, 1)
UMemoryPool::_free(table, _capacity, sizeof(UHashMapNode*));
}
// Find a elem in the array with <key>
void* at(const UStringRep* keyr);
void* at(const char* _key, uint32_t keylen);
void lookup(const UString& keyr) { return lookup(keyr.rep); }
void lookup(const UStringRep* keyr);
void _eraseAfterFind();
void _callForAllEntrySorted(bPFprpv function);
private:
#ifdef U_COMPILER_DELETE_MEMBERS
UHashMap<void*>(const UHashMap<void*>&) = delete;
UHashMap<void*>& operator=(const UHashMap<void*>&) = delete;
#else
UHashMap<void*>(const UHashMap<void*>&) {}
UHashMap<void*>& operator=(const UHashMap<void*>&) { return *this; }
#endif
friend class UCDB;
friend class UHTTP;
friend class UValue;
friend class WeightWord;
friend class UFileConfig;
friend class UCertificate;
friend void ULib_init();
template <class T> friend class UJsonTypeHandler;
};
template <class T> class U_EXPORT UHashMap<T*> : public UHashMap<void*> {
public:
// Costruttori e distruttore
UHashMap(uint32_t n = 53, bool _ignore_case = false) : UHashMap<void*>(n, _ignore_case)
{
U_TRACE_REGISTER_OBJECT(0, UHashMap<T*>, "%u,%b", n, _ignore_case)
}
UHashMap(const UString& _key, const T* _elem) : UHashMap<void*>(_key, _elem)
{
U_TRACE_REGISTER_OBJECT(0, UHashMap<T*>, "%.*S,%p", U_STRING_TO_TRACE(_key), _elem)
}
~UHashMap()
{
U_TRACE_UNREGISTER_OBJECT(0, UHashMap<T*>)
clear();
}
// Inserimento e cancellazione elementi dalla tabella
T* erase(const char* _key) { return (T*) UHashMap<void*>::erase(_key); }
T* erase(const UString& _key) { return (T*) UHashMap<void*>::erase(_key.rep); }
T* erase(const UStringRep* _key) { return (T*) UHashMap<void*>::erase(_key); }
T* elem() const { return (T*) UHashMap<void*>::elem(); }
T* operator[](const char* _key) { return (T*) UHashMap<void*>::operator[](_key); }
T* operator[](const UString& _key) { return (T*) UHashMap<void*>::operator[](_key); }
T* operator[](const UStringRep* _key) { return (T*) UHashMap<void*>::operator[](_key); }
void eraseAfterFind()
{
U_TRACE(0, "UHashMap<T*>::eraseAfterFind()")
U_INTERNAL_ASSERT_POINTER(node)
u_destroy<T>((const T*)node->elem);
UHashMap<void*>::eraseAfterFind();
}
void insertAfterFind(const UStringRep* _key, const T* _elem)
{
U_TRACE(0, "UHashMap<T*>::insertAfterFind(%.*S,%p)", U_STRING_TO_TRACE(*_key), _elem)
u_construct<T>(&_elem, istream_loading);
if (node)
{
u_destroy<T>((const T*)node->elem);
node->elem = _elem;
}
else
{
UHashMap<void*>::insertAfterFind(_key, _elem);
}
}
void insertAfterFind(const UString& _key, const T* _elem) { insertAfterFind(_key.rep, _elem); }
void replaceAfterFind(const T* _elem)
{
U_TRACE(0, "UHashMap<T*>::replaceAfterFind(%p)", _elem)
U_INTERNAL_ASSERT_POINTER(node)
u_construct<T>(&_elem, false);
u_destroy<T>((const T*)node->elem);
UHashMap<void*>::replaceAfterFind(_elem);
}
// sets a field, overwriting any existing value
void insert(const UString& _key, const T* _elem)
{
U_TRACE(0, "UHashMap<T*>::insert(%.*S,%p)", U_STRING_TO_TRACE(_key), _elem)
UHashMap<void*>::lookup(_key);
insertAfterFind(_key, _elem);
}
#ifdef DEBUG
bool check_memory() const // check all element
{
U_TRACE(0, "UHashMap<T*>::check_memory()")
U_CHECK_MEMORY
U_INTERNAL_DUMP("_length = %u", _length)
if (_length)
{
T* _elem;
UHashMapNode* _node;
UHashMapNode* _next;
int sum = 0, max = 0, min = 1024, width;
for (uint32_t _index = 0; _index < _capacity; ++_index)
{
_node = table[_index];
if (_node)
{
++sum;
width = -1;
do {
++width;
_elem = (T*) _node->elem;
U_INTERNAL_DUMP("_elem = %p", _elem)
U_CHECK_MEMORY_OBJECT(_elem)
_next = _node->next;
}
while ((_node = _next));
if (max < width) max = width;
if (min > width) min = width;
}
}
U_INTERNAL_DUMP("collision(min,max) = (%d,%d) - distribution = %f", min, max, (sum ? (double)_length / (double)sum : 0))
}
U_RETURN(true);
}
#endif
// cancellazione tabella
void clear()
{
U_TRACE(0+256, "UHashMap<T*>::clear()")
U_INTERNAL_ASSERT(check_memory())
U_INTERNAL_DUMP("_length = %u", _length)
if (_length)
{
T* _elem;
UHashMapNode* _node;
UHashMapNode* _next;
UHashMapNode** ptr;
UHashMapNode** end;
for (end = (ptr = table) + _capacity; ptr < end; ++ptr)
{
if (*ptr)
{
_node = *ptr;
do {
_next = _node->next;
_elem = (T*)_node->elem;
if (_elem) u_destroy<T>(_elem);
delete _node;
}
while ((_node = _next));
*ptr = 0;
}
}
_length = 0;
}
}
void callWithDeleteForAllEntry(bPFprpv function)
{
U_TRACE(0, "UHashMap<T*>::callWithDeleteForAllEntry(%p)", function)
U_INTERNAL_DUMP("_length = %u", _length)
const T* _elem;
UHashMapNode** ptr;
UHashMapNode** end;
UHashMapNode* _node;
UHashMapNode** pnode;
for (end = (ptr = table) + _capacity; ptr < end; ++ptr)
{
if (*ptr)
{
_node = *(pnode = ptr);
do {
_elem = (const T*)_node->elem;
if (function((UStringRep*)_node->key, (void*)_elem))
{
*pnode = _node->next; // we remove it from the list collisions
u_destroy<T>(_elem);
delete _node;
--_length;
continue;
}
pnode = &(*pnode)->next;
}
while ((_node = *pnode));
}
}
U_INTERNAL_DUMP("_length = %u", _length)
}
void assign(UHashMap<T*>& t)
{
U_TRACE(0, "UHashMap<T*>::assign(%p)", &t)
U_INTERNAL_ASSERT_EQUALS(gperf,0)
U_INTERNAL_ASSERT_DIFFERS(this, &t)
U_INTERNAL_ASSERT_EQUALS(ignore_case, t.ignore_case)
clear();
if (t._length)
{
const T* _elem;
UHashMapNode* _node;
UHashMapNode* _next;
UHashMapNode** ptr1;
UHashMapNode** end1;
allocate(t._capacity);
UHashMapNode** ptr = table;
for (end1 = (ptr1 = t.table) + t._capacity; ptr1 < end1; ++ptr1, ++ptr)
{
if (*ptr1)
{
_node = *ptr1;
U_INTERNAL_ASSERT_EQUALS(*ptr, 0)
do {
*ptr = U_NEW(UHashMapNode(_node, *ptr)); // we place it in the list collisions
_elem = (const T*) (*ptr)->elem;
U_INTERNAL_DUMP("_elem = %p", _elem)
U_ASSERT_EQUALS(_elem, t[_node->key])
u_construct<T>(&_elem, false);
_next = _node->next;
}
while ((_node = _next));
}
}
_length = t._length;
}
U_INTERNAL_DUMP("_length = %u", _length)
U_INTERNAL_ASSERT_EQUALS(_length, t._length)
}
// STREAMS
#ifdef U_STDCPP_ENABLE
friend istream& operator>>(istream& is, UHashMap<T*>& t)
{
U_TRACE(0+256, "UHashMap<T*>::operator>>(%p,%p)", &is, &t)
U_INTERNAL_ASSERT_MAJOR(t._capacity, 1)
int c = EOF;
if (is.good())
{
istream_loading = true; // NB: we need this flag for distinguish this operation in type's ctor...
const T* _elem = U_NEW(T);
streambuf* sb = is.rdbuf();
// NB: we need this way for plugin...
int terminator = EOF;
if (is.peek() == '{' ||
is.peek() == '[')
{
c = sb->sbumpc(); // skip '{' or '['
terminator = (c == '{' ? '}' : ']');
}
do {
do { c = sb->sbumpc(); } while (c != EOF && u__isspace(c)); // skip white-space
// U_INTERNAL_DUMP("c = %C", c)
if (terminator == c) break;
if (terminator == EOF &&
(c == '}' || c == ']'))
{
break;
}
if (c == EOF) break;
if (c == '#')
{
do { c = sb->sbumpc(); } while (c != EOF && c != '\n'); // skip line comment
continue;
}
U_INTERNAL_ASSERT_EQUALS(u__isspace(c),false)
sb->sputbackc(c);
UString key(U_CAPACITY);
key.get(is);
U_INTERNAL_ASSERT(key)
U_INTERNAL_ASSERT(key.isNullTerminated())
do { c = sb->sbumpc(); } while (c != EOF && u__isspace(c)); // skip white-space
// U_INTERNAL_DUMP("c = %C", c)
if (c == EOF) break;
U_INTERNAL_ASSERT_EQUALS(u__isspace(c),false)
sb->sputbackc(c);
is >> *(T*)_elem;
if (is.bad()) is.clear();
else t.insert(key, _elem);
}
while (c != EOF);
u_destroy<T>(_elem);
istream_loading = false;
}
if (c == EOF) is.setstate(ios::eofbit);
// -------------------------------------------------
// NB: we can load an empty table
// -------------------------------------------------
// if (t._length == 0) is.setstate(ios::failbit);
// -------------------------------------------------
return is;
}
friend ostream& operator<<(ostream& _os, const UHashMap<T*>& t)
{
U_TRACE(0+256, "UHashMap<T*>::operator<<(%p,%p)", &_os, &t)
U_INTERNAL_ASSERT(t.check_memory())
U_INTERNAL_DUMP("t._length = %u", t._length)
UHashMapNode* _node;
UHashMapNode* _next;
UHashMapNode** ptr;
UHashMapNode** end;
_os.put('[');
_os.put('\n');
for (end = (ptr = t.table) + t._capacity; ptr < end; ++ptr)
{
if (*ptr)
{
_node = *ptr;
do {
_node->key->write(_os);
_os.put('\t');
_os << *((T*)_node->elem);
_os.put('\n');
_next = _node->next;
}
while ((_node = _next));
}
}
_os.put(']');
return _os;
}
# ifdef DEBUG
const char* dump(bool reset) const { return UHashMap<void*>::dump(reset); }
# endif
#endif
protected:
void clearTmpNode()
{
U_TRACE(0, "UHashMap<T*>::clearTmpNode()")
U_INTERNAL_DUMP("_length = %u _capacity = %u table = %p", _length, _capacity, table)
U_INTERNAL_ASSERT_EQUALS(_length, 1)
U_INTERNAL_ASSERT_EQUALS(_capacity, 1)
U_INTERNAL_ASSERT_EQUALS(node, tmpnode)
U_INTERNAL_ASSERT_EQUALS(table, &tmpnode)
_length = 0;
u_destroy<T>((const T*)node->elem);
((UStringRep*)node->key)->release(); // NB: we decreases the reference string...
}
// find a elem in the array with <key>
T* at(const UStringRep* keyr) { return (T*) UHashMap<void*>::at(keyr); }
T* at(const char* _key, uint32_t keylen) { return (T*) UHashMap<void*>::at(_key, keylen); }
private:
#ifdef U_COMPILER_DELETE_MEMBERS
UHashMap<T*>(const UHashMap<T*>&) = delete;
UHashMap<T*>& operator=(const UHashMap<T*>&) = delete;
#else
UHashMap<T*>(const UHashMap<T*>&) {}
UHashMap<T*>& operator=(const UHashMap<T*>&) { return *this; }
#endif
friend class UHTTP;
friend class UValue;
friend class WeightWord;
friend class UNoCatPlugIn;
};
// specializzazione stringa
template <> class U_EXPORT UHashMap<UString> : public UHashMap<UStringRep*> {
public:
// Costruttori e distruttore
UHashMap(uint32_t n = 53, bool _ignore_case = false) : UHashMap<UStringRep*>(n, _ignore_case)
{
U_TRACE_REGISTER_OBJECT(0, UHashMap<UString>, "%u,%b", n, _ignore_case)
}
~UHashMap()
{
U_TRACE_UNREGISTER_OBJECT(0, UHashMap<UString>)
}
// inserimento e cancellazione elementi dalla tabella
void replaceAfterFind(const UString& str)
{
U_TRACE(0, "UHashMap<T*>::replaceAfterFind(%.*S)", U_STRING_TO_TRACE(str))
UHashMap<UStringRep*>::replaceAfterFind(str.rep);
}
void insert(const UString& _key, const UString& str)
{
U_TRACE(0, "UHashMap<UString>::insert(%.*S,%.*S)", U_STRING_TO_TRACE(_key), U_STRING_TO_TRACE(str))
UHashMap<UStringRep*>::insert(_key, str.rep);
_space += _key.size() + str.size();
}
UString erase(const UString& key);
void insertAfterFind(const UString& key, const UString& str);
void insertAfterFind(const char* key, uint32_t keylen, const UString& str);
// OPERATOR []
UString operator[](const char* _key);
UString operator[](const UString& _key) { return at(_key.rep); }
UString operator[](const UStringRep* _key) { return at(_key); }
// STREAMS
#ifdef U_STDCPP_ENABLE
friend U_EXPORT istream& operator>>(istream& is, UHashMap<UString>& t);
friend U_EXPORT ostream& operator<<(ostream& os, const UHashMap<UString>& t) { return operator<<(os, (const UHashMap<UStringRep*>&)t); }
#endif
void loadFromData(const UString& str) { (void) loadFromData(U_STRING_TO_PARAM(str)); }
protected:
UString at(const UStringRep* keyr);
UString at(const char* _key, uint32_t keylen);
uint32_t loadFromData(const char* start, uint32_t size);
private:
#ifdef U_COMPILER_DELETE_MEMBERS
UHashMap<UString>(const UHashMap<UString>&) = delete;
UHashMap<UString>& operator=(const UHashMap<UString>&) = delete;
#else
UHashMap<UString>(const UHashMap<UString>&) : UHashMap<UStringRep*>() {}
UHashMap<UString>& operator=(const UHashMap<UString>&) { return *this; }
#endif
friend class UHTTP;
friend class UFileConfig;
friend class UMimeHeader;
friend class UNoCatPlugIn;
};
#endif