#include "util.h"
#include <stdlib.h>
#include <string.h>
static int hash_equal(hash_table *hash, hash_key *left, hash_key *right);
static unsigned int hash_data(char *value, unsigned int length);
static unsigned int hash_value(hash_key *key);
static int
hash_equal(hash_table *hash, hash_key *left, hash_key *right)
{
if (left->length == right->length) {
if (left == right)
return (1);
if (hash->compare)
return (hash->compare(left, right));
return (memcmp(left->value, right->value, left->length) == 0);
}
return (0);
}
static unsigned int
hash_data(char *value, unsigned int length)
{
char *ptr;
unsigned int i, key;
for (i = key = 0, ptr = value; i < length; i++)
key = (key << (key & 1)) ^ ptr[i];
return (key);
}
static unsigned int
hash_value(hash_key *key)
{
return (hash_data(key->value, key->length));
}
hash_table *
hash_new(unsigned int length, hash_compare compare)
{
hash_table *hash;
hash = calloc(1, sizeof(hash_table));
if (hash) {
hash->entries = calloc(length, sizeof(hash_entry *));
if (hash->entries) {
hash->length = length;
hash->compare = compare;
hash->iter.offset = -1;
}
else {
free(hash);
hash = (hash_table *)0;
}
}
return (hash);
}
hash_entry *
hash_put(hash_table *hash, hash_entry *entry)
{
unsigned int key;
hash_entry *prev, *ptr;
key = hash_value(entry->key) % hash->length;
ptr = hash->entries[key];
for (prev = ptr; ptr; prev = ptr, ptr = ptr->next) {
if (hash_equal(hash, entry->key, ptr->key)) {
if (entry != ptr) {
if (ptr == prev)
hash->entries[key] = entry;
else
prev->next = entry;
entry->next = ptr->next;
}
else
ptr = (hash_entry *)0;
goto hash_put_done;
}
}
if (prev == (hash_entry *)0)
hash->entries[key] = entry;
else
prev->next = entry;
entry->next = (hash_entry *)0;
++hash->count;
hash_put_done:
return (ptr);
}
hash_entry *
hash_get(hash_table *hash, hash_key *name)
{
unsigned int key;
hash_entry *entry;
key = hash_value(name) % hash->length;
for (entry = hash->entries[key]; entry; entry = entry->next) {
if (hash_equal(hash, name, entry->key)) {
return (entry);
}
}
return ((hash_entry *)0);
}
hash_entry *
hash_check(hash_table *hash, char *name, unsigned int length)
{
unsigned int key;
hash_entry *entry;
key = hash_data(name, length) % hash->length;
for (entry = hash->entries[key]; entry; entry = entry->next) {
if (length == entry->key->length &&
memcmp(name, entry->key->value, length) == 0) {
return (entry);
}
}
return ((hash_entry *)0);
}
hash_entry *
hash_rem_no_free(hash_table *hash, hash_entry *entry)
{
unsigned int key;
hash_entry *ptr, *prev;
key = hash_value(entry->key) % hash->length;
for (ptr = prev = hash->entries[key]; ptr; prev = ptr, ptr = ptr->next) {
if (ptr == entry) {
--hash->count;
if (ptr == prev)
hash->entries[key] = ptr->next;
else
prev->next = ptr->next;
break;
}
}
if (ptr && ptr == hash->iter.entry)
hash->iter.entry = ptr->next;
return (ptr);
}
void
hash_rem(hash_table *hash, hash_entry *entry)
{
entry = hash_rem_no_free(hash, entry);
if (entry) {
free(entry->key->value);
free(entry->key);
free(entry);
}
}
void
hash_rehash(hash_table *hash, unsigned int length)
{
unsigned int i, key;
hash_entry *entry, *next, **entries;
entries = (hash_entry **)calloc(length, sizeof(hash_entry *));
if (entries) {
for (i = 0; i < hash->length; i++) {
for (entry = hash->entries[i]; entry; entry = next) {
next = entry->next;
key = hash_value(entry->key) % length;
entry->next = entries[key];
entries[key] = entry;
}
}
free(hash->entries);
hash->entries = entries;
hash->length = length;
}
hash->iter.offset = -1;
}
hash_entry *
hash_iter_first(hash_table *hash)
{
hash->iter.offset = 0;
hash->iter.entry = (hash_entry *)0;
return (hash_iter_next(hash));
}
hash_entry *
hash_iter_next(hash_table *hash)
{
if (hash->iter.offset >= 0) {
if (hash->iter.entry) {
if ((hash->iter.entry = hash->iter.entry->next))
return (hash->iter.entry);
++hash->iter.offset;
}
for (; hash->iter.offset < hash->length; hash->iter.offset++) {
if ((hash->iter.entry = hash->entries[hash->iter.offset]))
return (hash->iter.entry);
}
hash->iter.entry = (hash_entry *)0;
hash->iter.offset = -1;
}
return ((hash_entry *)0);
}
void
hash_clr(hash_table *hash)
{
unsigned int i;
hash_entry *entry, *next;
for (i = 0; i < hash->length; i++) {
entry = hash->entries[i];
if (entry) {
for (next = entry; entry; entry = next) {
next = entry->next;
free(entry->key->value);
free(entry->key);
free(entry);
}
hash->entries[i] = (hash_entry *)0;
}
}
hash->count = 0;
hash->iter.offset = -1;
}
void
hash_del(hash_table *hash)
{
hash_clr(hash);
free(hash->entries);
free(hash);
}