* hash.cpp - hash table functions
*
* Copyright (C) 2005, 2007 Stefan Jahn <stefan@lkcc.org>
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this package; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* $Id$
*
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hash.h"
using namespace qucs;
callback for any newly created hash table. */
static inline int hash_code (char * key) {
int code = 0;
char * p = key;
while (*p) { code = (code << 1) ^ *p; p++; }
return code;
}
determining two keys being equal. Return zero if both strings are
equal, otherwise non-zero. */
static inline int hash_key_equals (char * key1, char * key2) {
char * p1, * p2;
if (key1 == key2) return 0;
p1 = key1;
p2 = key2;
while (*p1 && *p2) {
if (*p1 != *p2) return -1;
p1++; p2++;
}
if (!*p1 && !*p2) return 0;
return -1;
}
key length of the given key. */
static inline unsigned hash_key_length (char * key) {
unsigned len = 0;
while (*key++) len++;
len++;
return len;
}
template <class type_t>
qucs::hash<type_t>::hash (int size) {
int n;
for (n = size, buckets = 1; n != 1; n >>= 1)
buckets <<= 1;
if (buckets < HASH_MIN_SIZE)
buckets = HASH_MIN_SIZE;
fill = 0;
keys = 0;
code = hash_code;
equals = hash_key_equals;
keylen = hash_key_length;
table = (hashbucket<type_t> **)
calloc (buckets, sizeof (hashbucket<type_t> *));
}
template <class type_t>
qucs::hash<type_t>::~hash () {
for (int n = 0; n < buckets; n++) {
delete table[n];
}
free (table);
}
The hash table is also shrunken to a minimal size. */
template <class type_t>
void qucs::hash<type_t>::clear (void) {
for (int n = 0; n < buckets; n++) {
delete table[n];
}
free (table);
buckets = HASH_MIN_SIZE;
fill = 0;
keys = 0;
table = (hashbucket<type_t> **)
calloc (buckets, sizeof (hashbucket<type_t> *));
}
template <class type_t>
int qucs::hash<type_t>::count (void) {
return keys;
}
expand the hash codes or half (type is HASH_SHRINK) its size and
shrink the hash codes if these would be placed somewhere else. */
template <class type_t>
void qucs::hash<type_t>::rehash (int type) {
int n, e;
hashbucket<type_t> * bucket, * next;
if (type == HASH_EXPAND) {
buckets *= 2;
table = (hashbucket<type_t> **)
realloc (table, sizeof (hashbucket<type_t> *) * buckets);
for (n = buckets / 2; n < buckets; n++) { table[n] = NULL; }
to relocate them. */
for (n = 0; n < buckets / 2; n++){
bucket = table[n];
for (e = 0; bucket && e < bucket->size; e++) {
int loc = HASH_LOCATION (bucket->entry[e]->code);
if (n != loc) {
if ((next = table[loc]) == NULL) {
next = new hashbucket<type_t> ();
table[loc] = next;
}
next->add (bucket->entry[e]);
if (next->size == 1) fill++;
bucket->del (e);
if (bucket->size == 0) fill--;
e--;
}
}
}
}
else if (type == HASH_SHRINK && buckets > HASH_MIN_SIZE) {
buckets /= 2;
for (n = buckets; n < buckets * 2; n++) {
bucket = table[n];
if (bucket && bucket->size) {
for (e = 0; e < bucket->size; e++) {
int loc = HASH_LOCATION (bucket->entry[e]->code);
if ((next = table[loc]) == NULL) {
next = new hashbucket<type_t> ();
}
next->add (bucket->entry[e]);
if (next->size == 1) fill++;
}
delete bucket;
}
fill--;
}
table = (hashbucket<type_t> **)
realloc (table, sizeof (hashbucket<type_t> *) * buckets);
}
}
existing hash. If the hash is 75% filled it gets rehashed (size
will be doubled). When the key already exists then the value just
gets replaced dropping and returning the old value. */
template <class type_t>
type_t * qucs::hash<type_t>::put (char * key, type_t * value) {
int code = this->code (key);
hashbucket<type_t> * bucket = table[HASH_LOCATION (code)];
if (bucket) {
for (int e = 0; e < bucket->size; e++) {
if (bucket->entry[e]->code == code) {
if (equals (bucket->entry[e]->key, key) == 0) {
type_t * old = bucket->entry[e]->value;
bucket->entry[e]->value = value;
return old;
}
}
}
}
else {
bucket = new hashbucket<type_t> ();
table[HASH_LOCATION (code)] = bucket;
}
hashentry<type_t> * entry = new hashentry<type_t> ();
entry->key = (char *) malloc (keylen (key));
memcpy (entry->key, key, keylen (key));
entry->value = value;
entry->code = code;
bucket->add (entry);
keys++;
if (bucket->size == 1) {
fill++;
if (fill > HASH_EXPAND_LIMIT) {
rehash (HASH_EXPAND);
}
}
return NULL;
}
hash table. Return NULL if the key has not been found within the
hash, otherwise the previous value. */
template <class type_t>
type_t * qucs::hash<type_t>::del (char * key) {
type_t * value;
int code = this->code (key);
hashbucket<type_t> * bucket = table[HASH_LOCATION (code)];
if (bucket) {
for (int n = 0; n < bucket->size; n++) {
if (bucket->entry[n]->code == code) {
if (equals (bucket->entry[n]->key, key) == 0) {
value = bucket->entry[n]->value;
bucket->del (n);
if (bucket->size == 0) {
fill--;
if (fill < HASH_SHRINK_LIMIT) {
rehash (HASH_SHRINK);
}
}
keys--;
return value;
}
}
}
}
return NULL;
}
Return NULL if the key has not been found within the hash table. */
template <class type_t>
type_t * qucs::hash<type_t>::get (char * key) {
int code = this->code (key);
hashbucket<type_t> * bucket = table[HASH_LOCATION (code)];
if (bucket) {
for (int n = 0; n < bucket->size; n++) {
if (bucket->entry[n]->code == code)
if (equals (bucket->entry[n]->key, key) == 0)
return bucket->entry[n]->value;
}
}
return NULL;
}
template <class type_t>
hashiterator<type_t>::hashiterator (hash<type_t> & h) {
_hash = &h;
_bucket = 0;
_entry = 0;
toLast ();
toFirst ();
}
template <class type_t>
hashiterator<type_t>::hashiterator () {
_hash = NULL;
_bucket = 0;
_entry = 0;
_first = _last = _current = NULL;
}
template <class type_t>
hashiterator<type_t>::~hashiterator () {
}
template <class type_t>
int hashiterator<type_t>::count (void) {
return _hash->keys;
}
template <class type_t>
char * hashiterator<type_t>::toFirst (void) {
for (int n = 0; n < _hash->buckets; n++) {
hashbucket<type_t> * bucket = _hash->table[n];
if (bucket && bucket->size) {
_bucket = n;
_entry = 0;
_current = _first = bucket->entry[_entry];
return _current->key;
}
}
_current = _first = NULL;
return NULL;
}
template <class type_t>
char * hashiterator<type_t>::toLast (void) {
for (int n = _hash->buckets - 1; n >= 0; n--) {
hashbucket<type_t> * bucket = _hash->table[n];
if (bucket && bucket->size) {
_bucket = n;
_entry = bucket->size - 1;
_current = _last = bucket->entry[_entry];
return _current->key;
}
}
_current = _last = NULL;
return NULL;
}
template <class type_t>
char * hashiterator<type_t>::operator++ (void) {
hashbucket<type_t> * bucket = _hash->table[_bucket];
if (bucket && _entry < bucket->size - 1) {
_entry++;
_current = bucket->entry[_entry];
return _current->key;
}
for (int n = _bucket + 1; n < _hash->buckets; n++) {
bucket = _hash->table[n];
if (bucket && bucket->size) {
_bucket = n;
_entry = 0;
_current = bucket->entry[_entry];
return _current->key;
}
}
_current = NULL;
return NULL;
}
template <class type_t>
char * hashiterator<type_t>::operator-- (void) {
hashbucket<type_t> * bucket = _hash->table[_bucket];
if (bucket && _entry > 0) {
_entry--;
_current = bucket->entry[_entry];
return _current->key;
}
for (int n = _bucket - 1; n >= 0 ; n--) {
bucket = _hash->table[n];
if (bucket && bucket->size) {
_bucket = n;
_entry = bucket->size - 1;
_current = bucket->entry[_entry];
return _current->key;
}
}
_current = NULL;
return NULL;
}
template <class type_t>
char * hashiterator<type_t>::current (void) {
return _current ? _current->key : NULL;
}
template <class type_t>
char * hashiterator<type_t>::currentKey (void) {
return current ();
}
template <class type_t>
type_t * hashiterator<type_t>::currentVal (void) {
return _current ? _current->value : NULL;
}
template <class type_t>
char * hashiterator<type_t>::first (void) {
return _first ? _first->key : NULL;
}
template <class type_t>
char * hashiterator<type_t>::last (void) {
return _last ? _last->key : NULL;
}