// ============================================================================ // // = LIBRARY // ULib - c++ library // // = FILENAME // hash_map.h // // = AUTHOR // Stefano Casazza // // ============================================================================ #ifndef ULIB_HASH_MAP_H #define ULIB_HASH_MAP_H 1 #include 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 UVector; template 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 UHashMap; template <> class U_EXPORT UHashMap { 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) if (_capacity > 1) _deallocate(); } // allocate and deallocate methods void deallocate() { U_TRACE(0, "UHashMap::deallocate()") if (_capacity > 1) _deallocate(); _capacity = 0; } void allocate(uint32_t n); // size and capacity uint32_t size() const { U_TRACE(0, "UHashMap::size()") U_RETURN(_length); } uint32_t capacity() const { U_TRACE(0, "UHashMap::capacity()") U_RETURN(_capacity); } uint32_t space() const { U_TRACE(0, "UHashMap::space()") U_RETURN(_space); } void setSpace(uint32_t s) { U_TRACE(0, "UHashMap::setSpace(%u)", s) _space = s; } bool empty() const { U_TRACE(0, "UHashMap::empty()") if (_length) U_RETURN(false); U_RETURN(true); } void setIgnoreCase(bool flag) { U_TRACE(0, "UHashMap::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::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 T* get(const UString& _key) { U_TRACE(0, "UHashMap::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::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::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& vec); void callForAllEntry(bPFprpv function); void callForAllEntrySorted(bPFprpv function) { U_TRACE(0, "UHashMap::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::_allocate(%u)", n) U_CHECK_MEMORY table = (UHashMapNode**) UMemoryPool::_malloc(&n, sizeof(UHashMapNode*), true); _capacity = n; } void _deallocate() { U_TRACE(0, "UHashMap::_deallocate()") U_CHECK_MEMORY U_INTERNAL_ASSERT_MAJOR(_capacity, 1) UMemoryPool::_free(table, _capacity, sizeof(UHashMapNode*)); } // Find a elem in the array with 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(const UHashMap&) = delete; UHashMap& operator=(const UHashMap&) = delete; #else UHashMap(const UHashMap&) {} UHashMap& operator=(const UHashMap&) { 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 friend class UJsonTypeHandler; }; template class U_EXPORT UHashMap : public UHashMap { public: // Costruttori e distruttore UHashMap(uint32_t n = 53, bool _ignore_case = false) : UHashMap(n, _ignore_case) { U_TRACE_REGISTER_OBJECT(0, UHashMap, "%u,%b", n, _ignore_case) } UHashMap(const UString& _key, const T* _elem) : UHashMap(_key, _elem) { U_TRACE_REGISTER_OBJECT(0, UHashMap, "%.*S,%p", U_STRING_TO_TRACE(_key), _elem) } ~UHashMap() { U_TRACE_UNREGISTER_OBJECT(0, UHashMap) clear(); } // Inserimento e cancellazione elementi dalla tabella T* erase(const char* _key) { return (T*) UHashMap::erase(_key); } T* erase(const UString& _key) { return (T*) UHashMap::erase(_key.rep); } T* erase(const UStringRep* _key) { return (T*) UHashMap::erase(_key); } T* elem() const { return (T*) UHashMap::elem(); } T* operator[](const char* _key) { return (T*) UHashMap::operator[](_key); } T* operator[](const UString& _key) { return (T*) UHashMap::operator[](_key); } T* operator[](const UStringRep* _key) { return (T*) UHashMap::operator[](_key); } void eraseAfterFind() { U_TRACE(0, "UHashMap::eraseAfterFind()") U_INTERNAL_ASSERT_POINTER(node) u_destroy((const T*)node->elem); UHashMap::eraseAfterFind(); } void insertAfterFind(const UStringRep* _key, const T* _elem) { U_TRACE(0, "UHashMap::insertAfterFind(%.*S,%p)", U_STRING_TO_TRACE(*_key), _elem) u_construct(&_elem, istream_loading); if (node) { u_destroy((const T*)node->elem); node->elem = _elem; } else { UHashMap::insertAfterFind(_key, _elem); } } void insertAfterFind(const UString& _key, const T* _elem) { insertAfterFind(_key.rep, _elem); } void replaceAfterFind(const T* _elem) { U_TRACE(0, "UHashMap::replaceAfterFind(%p)", _elem) U_INTERNAL_ASSERT_POINTER(node) u_construct(&_elem, false); u_destroy((const T*)node->elem); UHashMap::replaceAfterFind(_elem); } // sets a field, overwriting any existing value void insert(const UString& _key, const T* _elem) { U_TRACE(0, "UHashMap::insert(%.*S,%p)", U_STRING_TO_TRACE(_key), _elem) UHashMap::lookup(_key); insertAfterFind(_key, _elem); } #ifdef DEBUG bool check_memory() const // check all element { U_TRACE(0, "UHashMap::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::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(_elem); delete _node; } while ((_node = _next)); *ptr = 0; } } _length = 0; } } void callWithDeleteForAllEntry(bPFprpv function) { U_TRACE(0, "UHashMap::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(_elem); delete _node; --_length; continue; } pnode = &(*pnode)->next; } while ((_node = *pnode)); } } U_INTERNAL_DUMP("_length = %u", _length) } void assign(UHashMap& t) { U_TRACE(0, "UHashMap::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(&_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) { U_TRACE(0+256, "UHashMap::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(_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) { U_TRACE(0+256, "UHashMap::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::dump(reset); } # endif #endif protected: void clearTmpNode() { U_TRACE(0, "UHashMap::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((const T*)node->elem); ((UStringRep*)node->key)->release(); // NB: we decreases the reference string... } // find a elem in the array with T* at(const UStringRep* keyr) { return (T*) UHashMap::at(keyr); } T* at(const char* _key, uint32_t keylen) { return (T*) UHashMap::at(_key, keylen); } private: #ifdef U_COMPILER_DELETE_MEMBERS UHashMap(const UHashMap&) = delete; UHashMap& operator=(const UHashMap&) = delete; #else UHashMap(const UHashMap&) {} UHashMap& operator=(const UHashMap&) { return *this; } #endif friend class UHTTP; friend class UValue; friend class WeightWord; friend class UNoCatPlugIn; }; // specializzazione stringa template <> class U_EXPORT UHashMap : public UHashMap { public: // Costruttori e distruttore UHashMap(uint32_t n = 53, bool _ignore_case = false) : UHashMap(n, _ignore_case) { U_TRACE_REGISTER_OBJECT(0, UHashMap, "%u,%b", n, _ignore_case) } ~UHashMap() { U_TRACE_UNREGISTER_OBJECT(0, UHashMap) } // inserimento e cancellazione elementi dalla tabella void replaceAfterFind(const UString& str) { U_TRACE(0, "UHashMap::replaceAfterFind(%.*S)", U_STRING_TO_TRACE(str)) UHashMap::replaceAfterFind(str.rep); } void insert(const UString& _key, const UString& str) { U_TRACE(0, "UHashMap::insert(%.*S,%.*S)", U_STRING_TO_TRACE(_key), U_STRING_TO_TRACE(str)) UHashMap::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& t); friend U_EXPORT ostream& operator<<(ostream& os, const UHashMap& t) { return operator<<(os, (const UHashMap&)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(const UHashMap&) = delete; UHashMap& operator=(const UHashMap&) = delete; #else UHashMap(const UHashMap&) : UHashMap() {} UHashMap& operator=(const UHashMap&) { return *this; } #endif friend class UHTTP; friend class UFileConfig; friend class UMimeHeader; friend class UNoCatPlugIn; }; #endif