mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 12:28:22 +01:00
Autoconf provides the necessary stuff to detect if inline keyword is supported, and to detect special syntaxes, so let's use this and remove the multiple local definitions, this makes code simpler.
433 lines
8.3 KiB
C
433 lines
8.3 KiB
C
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "WUtil.h"
|
|
|
|
#define INITIAL_CAPACITY 23
|
|
|
|
|
|
typedef struct HashItem {
|
|
const void *key;
|
|
const void *data;
|
|
|
|
struct HashItem *next; /* collided item list */
|
|
} HashItem;
|
|
|
|
typedef struct W_HashTable {
|
|
WMHashTableCallbacks callbacks;
|
|
|
|
unsigned itemCount;
|
|
unsigned size; /* table size */
|
|
|
|
HashItem **table;
|
|
} HashTable;
|
|
|
|
#define HASH(table, key) (((table)->callbacks.hash ? \
|
|
(*(table)->callbacks.hash)(key) : hashPtr(key)) % (table)->size)
|
|
|
|
#define DUPKEY(table, key) ((table)->callbacks.retainKey ? \
|
|
(*(table)->callbacks.retainKey)(key) : (key))
|
|
|
|
#define RELKEY(table, key) if ((table)->callbacks.releaseKey) \
|
|
(*(table)->callbacks.releaseKey)(key)
|
|
|
|
static inline unsigned hashString(const char *key)
|
|
{
|
|
unsigned ret = 0;
|
|
unsigned ctr = 0;
|
|
|
|
while (*key) {
|
|
ret ^= *(char *)key++ << ctr;
|
|
ctr = (ctr + 1) % sizeof(char *);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline unsigned hashPtr(const void *key)
|
|
{
|
|
return ((size_t) key / sizeof(char *));
|
|
}
|
|
|
|
static void rellocateItem(WMHashTable * table, HashItem * item)
|
|
{
|
|
unsigned h;
|
|
|
|
h = HASH(table, item->key);
|
|
|
|
item->next = table->table[h];
|
|
table->table[h] = item;
|
|
}
|
|
|
|
static void rebuildTable(WMHashTable * table)
|
|
{
|
|
HashItem *next;
|
|
HashItem **oldArray;
|
|
int i;
|
|
int oldSize;
|
|
int newSize;
|
|
|
|
oldArray = table->table;
|
|
oldSize = table->size;
|
|
|
|
newSize = table->size * 2;
|
|
|
|
table->table = wmalloc(sizeof(char *) * newSize);
|
|
table->size = newSize;
|
|
|
|
for (i = 0; i < oldSize; i++) {
|
|
while (oldArray[i] != NULL) {
|
|
next = oldArray[i]->next;
|
|
rellocateItem(table, oldArray[i]);
|
|
oldArray[i] = next;
|
|
}
|
|
}
|
|
wfree(oldArray);
|
|
}
|
|
|
|
WMHashTable *WMCreateHashTable(WMHashTableCallbacks callbacks)
|
|
{
|
|
HashTable *table;
|
|
|
|
table = wmalloc(sizeof(HashTable));
|
|
|
|
table->callbacks = callbacks;
|
|
|
|
table->size = INITIAL_CAPACITY;
|
|
|
|
table->table = wmalloc(sizeof(HashItem *) * table->size);
|
|
|
|
return table;
|
|
}
|
|
|
|
void WMResetHashTable(WMHashTable * table)
|
|
{
|
|
HashItem *item, *tmp;
|
|
int i;
|
|
|
|
for (i = 0; i < table->size; i++) {
|
|
item = table->table[i];
|
|
while (item) {
|
|
tmp = item->next;
|
|
RELKEY(table, item->key);
|
|
wfree(item);
|
|
item = tmp;
|
|
}
|
|
}
|
|
|
|
table->itemCount = 0;
|
|
|
|
if (table->size > INITIAL_CAPACITY) {
|
|
wfree(table->table);
|
|
table->size = INITIAL_CAPACITY;
|
|
table->table = wmalloc(sizeof(HashItem *) * table->size);
|
|
} else {
|
|
memset(table->table, 0, sizeof(HashItem *) * table->size);
|
|
}
|
|
}
|
|
|
|
void WMFreeHashTable(WMHashTable * table)
|
|
{
|
|
HashItem *item, *tmp;
|
|
int i;
|
|
|
|
for (i = 0; i < table->size; i++) {
|
|
item = table->table[i];
|
|
while (item) {
|
|
tmp = item->next;
|
|
RELKEY(table, item->key);
|
|
wfree(item);
|
|
item = tmp;
|
|
}
|
|
}
|
|
wfree(table->table);
|
|
wfree(table);
|
|
}
|
|
|
|
unsigned WMCountHashTable(WMHashTable * table)
|
|
{
|
|
return table->itemCount;
|
|
}
|
|
|
|
void *WMHashGet(WMHashTable * table, const void *key)
|
|
{
|
|
unsigned h;
|
|
HashItem *item;
|
|
|
|
h = HASH(table, key);
|
|
item = table->table[h];
|
|
|
|
if (table->callbacks.keyIsEqual) {
|
|
while (item) {
|
|
if ((*table->callbacks.keyIsEqual) (key, item->key)) {
|
|
break;
|
|
}
|
|
item = item->next;
|
|
}
|
|
} else {
|
|
while (item) {
|
|
if (key == item->key) {
|
|
break;
|
|
}
|
|
item = item->next;
|
|
}
|
|
}
|
|
if (item)
|
|
return (void *)item->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
Bool WMHashGetItemAndKey(WMHashTable * table, const void *key, void **retItem, void **retKey)
|
|
{
|
|
unsigned h;
|
|
HashItem *item;
|
|
|
|
h = HASH(table, key);
|
|
item = table->table[h];
|
|
|
|
if (table->callbacks.keyIsEqual) {
|
|
while (item) {
|
|
if ((*table->callbacks.keyIsEqual) (key, item->key)) {
|
|
break;
|
|
}
|
|
item = item->next;
|
|
}
|
|
} else {
|
|
while (item) {
|
|
if (key == item->key) {
|
|
break;
|
|
}
|
|
item = item->next;
|
|
}
|
|
}
|
|
if (item) {
|
|
if (retKey)
|
|
*retKey = (void *)item->key;
|
|
if (retItem)
|
|
*retItem = (void *)item->data;
|
|
return True;
|
|
} else {
|
|
return False;
|
|
}
|
|
}
|
|
|
|
void *WMHashInsert(WMHashTable * table, const void *key, const void *data)
|
|
{
|
|
unsigned h;
|
|
HashItem *item;
|
|
int replacing = 0;
|
|
|
|
h = HASH(table, key);
|
|
/* look for the entry */
|
|
item = table->table[h];
|
|
if (table->callbacks.keyIsEqual) {
|
|
while (item) {
|
|
if ((*table->callbacks.keyIsEqual) (key, item->key)) {
|
|
replacing = 1;
|
|
break;
|
|
}
|
|
item = item->next;
|
|
}
|
|
} else {
|
|
while (item) {
|
|
if (key == item->key) {
|
|
replacing = 1;
|
|
break;
|
|
}
|
|
item = item->next;
|
|
}
|
|
}
|
|
|
|
if (replacing) {
|
|
const void *old;
|
|
|
|
old = item->data;
|
|
item->data = data;
|
|
RELKEY(table, item->key);
|
|
item->key = DUPKEY(table, key);
|
|
|
|
return (void *)old;
|
|
} else {
|
|
HashItem *nitem;
|
|
|
|
nitem = wmalloc(sizeof(HashItem));
|
|
nitem->key = DUPKEY(table, key);
|
|
nitem->data = data;
|
|
nitem->next = table->table[h];
|
|
table->table[h] = nitem;
|
|
|
|
table->itemCount++;
|
|
}
|
|
|
|
/* OPTIMIZE: put this in an idle handler. */
|
|
if (table->itemCount > table->size) {
|
|
#ifdef DEBUG0
|
|
printf("rebuilding hash table...\n");
|
|
#endif
|
|
rebuildTable(table);
|
|
#ifdef DEBUG0
|
|
printf("finished rebuild.\n");
|
|
#endif
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static HashItem *deleteFromList(HashTable * table, HashItem * item, const void *key)
|
|
{
|
|
HashItem *next;
|
|
|
|
if (item == NULL)
|
|
return NULL;
|
|
|
|
if ((table->callbacks.keyIsEqual && (*table->callbacks.keyIsEqual) (key, item->key))
|
|
|| (!table->callbacks.keyIsEqual && key == item->key)) {
|
|
|
|
next = item->next;
|
|
RELKEY(table, item->key);
|
|
wfree(item);
|
|
|
|
table->itemCount--;
|
|
|
|
return next;
|
|
}
|
|
|
|
item->next = deleteFromList(table, item->next, key);
|
|
|
|
return item;
|
|
}
|
|
|
|
void WMHashRemove(WMHashTable * table, const void *key)
|
|
{
|
|
unsigned h;
|
|
|
|
h = HASH(table, key);
|
|
|
|
table->table[h] = deleteFromList(table, table->table[h], key);
|
|
}
|
|
|
|
WMHashEnumerator WMEnumerateHashTable(WMHashTable * table)
|
|
{
|
|
WMHashEnumerator enumerator;
|
|
|
|
enumerator.table = table;
|
|
enumerator.index = 0;
|
|
enumerator.nextItem = table->table[0];
|
|
|
|
return enumerator;
|
|
}
|
|
|
|
void *WMNextHashEnumeratorItem(WMHashEnumerator * enumerator)
|
|
{
|
|
const void *data = NULL;
|
|
|
|
/* this assumes the table doesn't change between
|
|
* WMEnumerateHashTable() and WMNextHashEnumeratorItem() calls */
|
|
|
|
if (enumerator->nextItem == NULL) {
|
|
HashTable *table = enumerator->table;
|
|
while (++enumerator->index < table->size) {
|
|
if (table->table[enumerator->index] != NULL) {
|
|
enumerator->nextItem = table->table[enumerator->index];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enumerator->nextItem) {
|
|
data = ((HashItem *) enumerator->nextItem)->data;
|
|
enumerator->nextItem = ((HashItem *) enumerator->nextItem)->next;
|
|
}
|
|
|
|
return (void *)data;
|
|
}
|
|
|
|
void *WMNextHashEnumeratorKey(WMHashEnumerator * enumerator)
|
|
{
|
|
const void *key = NULL;
|
|
|
|
/* this assumes the table doesn't change between
|
|
* WMEnumerateHashTable() and WMNextHashEnumeratorKey() calls */
|
|
|
|
if (enumerator->nextItem == NULL) {
|
|
HashTable *table = enumerator->table;
|
|
while (++enumerator->index < table->size) {
|
|
if (table->table[enumerator->index] != NULL) {
|
|
enumerator->nextItem = table->table[enumerator->index];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enumerator->nextItem) {
|
|
key = ((HashItem *) enumerator->nextItem)->key;
|
|
enumerator->nextItem = ((HashItem *) enumerator->nextItem)->next;
|
|
}
|
|
|
|
return (void *)key;
|
|
}
|
|
|
|
Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator * enumerator, void **item, void **key)
|
|
{
|
|
/* this assumes the table doesn't change between
|
|
* WMEnumerateHashTable() and WMNextHashEnumeratorItemAndKey() calls */
|
|
|
|
if (enumerator->nextItem == NULL) {
|
|
HashTable *table = enumerator->table;
|
|
while (++enumerator->index < table->size) {
|
|
if (table->table[enumerator->index] != NULL) {
|
|
enumerator->nextItem = table->table[enumerator->index];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enumerator->nextItem) {
|
|
if (item)
|
|
*item = (void *)((HashItem *) enumerator->nextItem)->data;
|
|
if (key)
|
|
*key = (void *)((HashItem *) enumerator->nextItem)->key;
|
|
enumerator->nextItem = ((HashItem *) enumerator->nextItem)->next;
|
|
|
|
return True;
|
|
}
|
|
|
|
return False;
|
|
}
|
|
|
|
static Bool compareStrings(const char *key1, const char *key2)
|
|
{
|
|
return strcmp(key1, key2) == 0;
|
|
}
|
|
|
|
typedef unsigned (*hashFunc) (const void *);
|
|
typedef Bool(*isEqualFunc) (const void *, const void *);
|
|
typedef void *(*retainFunc) (const void *);
|
|
typedef void (*releaseFunc) (const void *);
|
|
|
|
const WMHashTableCallbacks WMIntHashCallbacks = {
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
const WMHashTableCallbacks WMStringHashCallbacks = {
|
|
(hashFunc) hashString,
|
|
(isEqualFunc) compareStrings,
|
|
(retainFunc) wstrdup,
|
|
(releaseFunc) wfree
|
|
};
|
|
|
|
const WMHashTableCallbacks WMStringPointerHashCallbacks = {
|
|
(hashFunc) hashString,
|
|
(isEqualFunc) compareStrings,
|
|
NULL,
|
|
NULL
|
|
};
|