blob: 6eb0ace04f8d880dc5776d6a17d07cce73ddbc51 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Aleksey Ignatenko
*/
#ifndef __HASHTABLE_H__
#define __HASHTABLE_H__
#include <assert.h>
#include "cxxlog.h"
#include <map>
// FIXME: we expect POINTER_SIZE_INT is already defined by some includes...
#ifdef USE_HASH_CLASS
template <class Key, unsigned hashtable_len>
class HashMaker
{
public:
HashMaker() {}
unsigned operator ()(Key key) {
return HashFunc(key);
}
private:
unsigned HashFunc(Key key)
{
return (unsigned) ((((POINTER_SIZE_INT)key)>>2)%hashtable_len);
}
};
#else
template <class Key, unsigned hashtable_len>
static unsigned default_hash_func(Key key)
{
return (unsigned) ((((POINTER_SIZE_INT)key)>>2)%hashtable_len);
}
#endif
static const unsigned DEFAULT_HASH_TABLE_LENGTH = 101;
/*
* Users of this hash table, please,
* NOTE: Elem MUST be ASSIGNABLE or COPY-CONSTRUCTABLE
*/
template <class Key, class Elem, unsigned hashtable_len = DEFAULT_HASH_TABLE_LENGTH>
class HashTable
{
template <class _Key, class _Elem>
class IntHashItem
{
public:
_Key m_key;
_Elem m_data;
IntHashItem* m_prev;
IntHashItem* m_next;
IntHashItem() : m_prev(NULL), m_next(NULL) {}
};
template <class _Key, class _Elem, unsigned hashlink_len>
class IntHashLink
{
public:
IntHashItem<_Key, _Elem> m_entry[hashlink_len];
IntHashLink<_Key, _Elem, hashlink_len>* m_next;
IntHashLink() : m_next(NULL) {
m_entry[0].m_prev = NULL;
m_entry[0].m_next = &(m_entry[1]);
for( unsigned i = 1; i < hashlink_len - 1; i++ )
{
m_entry[i].m_prev = &(m_entry[i-1]);
m_entry[i].m_next = &(m_entry[i+1]);
}
m_entry[hashlink_len - 1].m_prev = &(m_entry[hashlink_len - 2]);
m_entry[hashlink_len - 1].m_next = NULL;
}
};
template <class _Key, class _Elem>
class IntHashEntry
{
public:
IntHashItem<_Key, _Elem>* m_free;
IntHashItem<_Key, _Elem>* m_used;
IntHashLink<_Key, _Elem, 32>* m_link;
IntHashEntry() : m_free(NULL), m_used(NULL), m_link(NULL) {}
};
private:
unsigned m_itemsnumber;
unsigned m_hashtable_len;
IntHashEntry<Key, Elem> m_hashed[hashtable_len];
#ifdef USE_HASH_CLASS
HashMaker<Key, hashtable_len>* m_hashfunc;
#else
typedef unsigned (*HashFunc_t)(Key key);
HashFunc_t m_hashfunc;
#endif
public:
HashTable(
#ifdef USE_HASH_CLASS
HashMaker<Key, hashtable_len>* fnptr = 0
#else
HashFunc_t fnptr = 0
#endif
):m_itemsnumber(0), m_hashtable_len(hashtable_len), m_hashfunc(fnptr) {
if(!m_hashfunc) {
#ifdef USE_HASH_CLASS
m_hashfunc = new HashMaker<Key, hashtable_len>();
#else
m_hashfunc = (HashFunc_t)default_hash_func<Key, hashtable_len>;
#endif
}
}
Elem* Lookup(Key key) {
// hash on key
unsigned h = (*m_hashfunc)(key);
IntHashEntry<Key, Elem>* curEntry = &(m_hashed[h]);
if(!curEntry->m_link) return 0; // this HT entry is empty
for( IntHashItem<Key, Elem>* entry = curEntry->m_used; entry != 0; entry = entry->m_next ) {
if(entry->m_key == key) return &(entry->m_data);
}
return 0;
}
Elem* Insert(Key key, Elem& data, bool unique = true) {
if(unique) {
// check that this hash table does not contain element for this key
Elem* elem = Lookup(key);
if(elem) {
DIE(("Element is inserted second time"));
}
}
unsigned h = (*m_hashfunc)(key);
IntHashEntry<Key, Elem>* curEntry = &(m_hashed[h]);
// setup hash entry if first used
if(curEntry->m_link == 0) {
curEntry->m_link = new IntHashLink<Key, Elem, 32>();
curEntry->m_free = &(curEntry->m_link->m_entry[0]);
}
IntHashItem<Key, Elem>* use = curEntry->m_free;
// only one free element left in this link (which will be used now); allocate more
if( curEntry->m_free->m_next == 0 ) {
IntHashLink<Key, Elem, 32>* newL = new IntHashLink<Key, Elem, 32>();
if( newL == 0 ) return NULL;
newL->m_next = curEntry->m_link;
curEntry->m_free = &(newL->m_entry[0]);
} else {
curEntry->m_free = curEntry->m_free->m_next;
}
use->m_prev = 0;
use->m_next = curEntry->m_used;
if( use->m_next ) use->m_next->m_prev = use;
curEntry->m_used = use;
use->m_key = key;
use->m_data = data;
m_itemsnumber++;
return &(use->m_data);
}
void Remove(Key key) {
// 2005/06/21 psrebriy: need to remove entry with key 0!
// if(key == 0) return;
// hash on string name address
unsigned h = (*m_hashfunc)(key);
IntHashEntry<Key, Elem>* curEntry = &(m_hashed[h]);
for(IntHashItem<Key, Elem>* entry = curEntry->m_used; entry != NULL; entry = entry->m_next) {
if(entry->m_key == key) {
// found; remove
entry->m_key = NULL;
// first, unlink it from used list
if(entry->m_prev != NULL)
entry->m_prev->m_next = entry->m_next;
else // entry is curEntry->m_used; move it on
curEntry->m_used = entry->m_next;
if(entry->m_next) entry->m_next->m_prev = entry->m_prev;
// then, link it to free list
entry->m_prev = NULL;
entry->m_next = curEntry->m_free;
if(curEntry->m_free) curEntry->m_free->m_prev = entry;
curEntry->m_free = entry;
// decrement number of classes registered in this table
m_itemsnumber--;
return;
}
}
// this table entry is empty; thus contains no data at all
DIE(("Trying to remove data which was not inserted before"));
}
unsigned GetItemCount() { return m_itemsnumber; }
// ppervov: temporary - simple iterators
// will be removed after class enumeration will be changed
Elem* get_first_item()
{
for(unsigned i = 0; i < hashtable_len; i++) {
if(m_hashed[i].m_used != NULL) {
IntHashItem<Key, Elem>* first = m_hashed[i].m_used;
if(!first) continue;
return &(first->m_data);
}
}
return NULL;
}
Elem* get_next_item(Key prev_key, Elem& prev)
{
// hash on string name address
unsigned h = (*m_hashfunc)(prev_key), i;
IntHashItem<Key, Elem>* hit;
for( hit = m_hashed[h].m_used; hit != NULL; hit = hit->m_next ) {
if(hit->m_data == prev) {
break;
}
}
assert(hit != NULL);
for(i = h, hit = hit->m_next; i < hashtable_len; i++, hit = m_hashed[i].m_used)
{
if(!hit) continue;
IntHashItem<Key, Elem>* first = hit;
if(!first) continue;
return &(first->m_data);
}
return NULL;
}
};
// MapEx based on std::map and customized to repeat HashTable functionality
template <class Key, class Elem>
class MapEx : public std::map<Key, Elem >
{
public:
inline Elem* Lookup(Key key)
{
typename std::map<Key, Elem >::iterator it = this->find(key);
if (it == this->end())
return NULL;
else
return &(it->second);
}
inline Elem* Insert(Key key, Elem& elem)
{
// typename is used to avoid g++ warning
std::pair<typename std::map<Key, Elem >::iterator, bool> pr =
this->insert(std::pair<Key, Elem >(key, elem));
if (pr.second)
return &(pr.first->second);
else
{
DIE(( "Element was inserted second time in MapEx!"));
return NULL; // not reachable; to satisfy compiler warning
}
}
inline void Remove(Key key)
{
this->erase(key);
}
inline unsigned int GetItemCount()
{
return (unsigned int)(this->size());
}
};
#endif // __HASHTABLE_H__