mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
16 KiB
730 lines
16 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Eric W. Sink [email protected]
|
|
Jim Seidman [email protected]
|
|
*/
|
|
|
|
#include "all.h"
|
|
|
|
int Hash_Init(struct hash_table *hash)
|
|
{
|
|
memset(hash, 0, sizeof(struct hash_table));
|
|
hash->pool_space = INIT_POOL_SPACE;
|
|
hash->pool = (char *) GTR_MALLOC(hash->pool_space);
|
|
if (!hash->pool)
|
|
{
|
|
return -1;
|
|
}
|
|
memset(hash->pool, 0, hash->pool_space);
|
|
hash->pool_size = 0;
|
|
|
|
hash->entry_space = INIT_ENTRY_SPACE;
|
|
hash->entry = (struct hash_entry *) GTR_MALLOC(hash->entry_space * sizeof(struct hash_entry));
|
|
if (!hash->entry)
|
|
{
|
|
GTR_FREE(hash->pool);
|
|
return -1;
|
|
}
|
|
memset(hash->entry, 0, hash->entry_space * sizeof(struct hash_entry));
|
|
hash->entry_size = 0;
|
|
|
|
hash->table = (struct hash_bucket *) GTR_MALLOC(NUM_OF_BUCKETS * sizeof(struct hash_bucket));
|
|
if (!hash->table)
|
|
{
|
|
GTR_FREE(hash->entry);
|
|
GTR_FREE(hash->pool);
|
|
return -1;
|
|
}
|
|
memset(hash->table, 0, sizeof(struct hash_bucket) * NUM_OF_BUCKETS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Hash_FreeContents(struct hash_table *hash)
|
|
{
|
|
if (hash)
|
|
{
|
|
if (hash->pool)
|
|
{
|
|
GTR_FREE(hash->pool);
|
|
}
|
|
if (hash->entry)
|
|
{
|
|
GTR_FREE(hash->entry);
|
|
}
|
|
if (hash->table)
|
|
{
|
|
GTR_FREE(hash->table);
|
|
}
|
|
memset(hash, 0, sizeof(struct hash_table));
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
struct hash_table *Hash_Create(void)
|
|
{
|
|
struct hash_table *hash;
|
|
|
|
hash = (struct hash_table *) GTR_MALLOC(sizeof(struct hash_table));
|
|
if (hash)
|
|
{
|
|
if (Hash_Init(hash))
|
|
{
|
|
GTR_FREE(hash);
|
|
hash = NULL;
|
|
}
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
int Hash_Destroy(struct hash_table *hash)
|
|
{
|
|
if (hash)
|
|
{
|
|
Hash_FreeContents(hash);
|
|
GTR_FREE(hash);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int Hash_AddStringToPool(struct hash_table *hash, const char *s)
|
|
{
|
|
int len;
|
|
int ndx;
|
|
|
|
if (!s)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
len = strlen(s);
|
|
|
|
if ((hash->pool_size + len) >= hash->pool_space)
|
|
{
|
|
int new_pool_space;
|
|
char *new_pool;
|
|
|
|
new_pool_space = hash->pool_space * 2;
|
|
new_pool = (char *) GTR_MALLOC(new_pool_space);
|
|
if (new_pool)
|
|
{
|
|
memset(new_pool, 0, new_pool_space);
|
|
memcpy(new_pool, hash->pool, hash->pool_space);
|
|
GTR_FREE(hash->pool);
|
|
hash->pool = new_pool;
|
|
hash->pool_space = new_pool_space;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ndx = hash->pool_size;
|
|
|
|
hash->pool_size += (len + 1);
|
|
strcpy(&(hash->pool[ndx]), s);
|
|
|
|
return ndx;
|
|
}
|
|
|
|
static int Hash_AddEntry(struct hash_table *hash, int s1_offset, int s2_offset, void *data)
|
|
{
|
|
int ndx;
|
|
|
|
if (hash->entry_size >= hash->entry_space)
|
|
{
|
|
int new_entry_space;
|
|
struct hash_entry *new_entry;
|
|
|
|
new_entry_space = hash->entry_space * 2;
|
|
new_entry = (struct hash_entry *) GTR_MALLOC(new_entry_space * sizeof(struct hash_entry));
|
|
if (new_entry)
|
|
{
|
|
memset(new_entry, 0, new_entry_space * sizeof(struct hash_entry));
|
|
memcpy(new_entry, hash->entry, hash->entry_space * sizeof(struct hash_entry));
|
|
GTR_FREE(hash->entry);
|
|
hash->entry = new_entry;
|
|
hash->entry_space = new_entry_space;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ndx = hash->entry_size;
|
|
|
|
hash->entry_size++;
|
|
hash->entry[ndx].s1_offset = s1_offset;
|
|
hash->entry[ndx].s2_offset = s2_offset;
|
|
hash->entry[ndx].data = data;
|
|
hash->entry[ndx].next = -1;
|
|
|
|
return ndx;
|
|
}
|
|
|
|
static int Hash_StringHashFunction(const char *s)
|
|
{
|
|
int val;
|
|
const char *p;
|
|
|
|
if (!s)
|
|
return 0;
|
|
val = 0;
|
|
for (p = s; *p; p++)
|
|
val = (val * 3 + *p) % NUM_OF_BUCKETS;
|
|
|
|
return val % NUM_OF_BUCKETS;
|
|
}
|
|
|
|
int Hash_Find(struct hash_table *hash, const char *s, char **s2, void **data)
|
|
{
|
|
int ndx;
|
|
int entry_ndx;
|
|
int i;
|
|
|
|
if (!s)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ndx = Hash_StringHashFunction(s);
|
|
if (hash->table[ndx].count == 0)
|
|
{
|
|
entry_ndx = -1;
|
|
}
|
|
else
|
|
{
|
|
entry_ndx = hash->table[ndx].head;
|
|
for (i = 0; i < hash->table[ndx].count; i++)
|
|
{
|
|
if (0 == strcmp(s, &(hash->pool[hash->entry[entry_ndx].s1_offset])))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
entry_ndx = hash->entry[entry_ndx].next;
|
|
}
|
|
}
|
|
}
|
|
if (entry_ndx >= 0)
|
|
{
|
|
if (s2)
|
|
{
|
|
if (hash->entry[entry_ndx].s2_offset >= 0)
|
|
{
|
|
*s2 = &(hash->pool[hash->entry[entry_ndx].s2_offset]);
|
|
}
|
|
else
|
|
{
|
|
*s2 = NULL;
|
|
}
|
|
}
|
|
|
|
if (data)
|
|
{
|
|
*data = hash->entry[entry_ndx].data;
|
|
}
|
|
return entry_ndx;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int Hash_FindByData(struct hash_table *hash, char **s, char **s2, void *data)
|
|
{
|
|
int entry_ndx;
|
|
int i;
|
|
|
|
entry_ndx = -1;
|
|
for (i = 0; i < hash->entry_size; i++)
|
|
{
|
|
if (data == hash->entry[i].data)
|
|
{
|
|
entry_ndx = i;
|
|
break;
|
|
}
|
|
}
|
|
if (entry_ndx >= 0)
|
|
{
|
|
if (s)
|
|
{
|
|
if (hash->entry[entry_ndx].s1_offset >= 0)
|
|
{
|
|
*s = &(hash->pool[hash->entry[entry_ndx].s1_offset]);
|
|
}
|
|
else
|
|
{
|
|
*s = NULL;
|
|
}
|
|
}
|
|
if (s2)
|
|
{
|
|
if (hash->entry[entry_ndx].s2_offset >= 0)
|
|
{
|
|
*s2 = &(hash->pool[hash->entry[entry_ndx].s2_offset]);
|
|
}
|
|
else
|
|
{
|
|
*s2 = NULL;
|
|
}
|
|
}
|
|
}
|
|
return entry_ndx;
|
|
}
|
|
|
|
int Hash_AddAndReturnIndex(struct hash_table *hash, const char *s1, const char *s2, void *data)
|
|
{
|
|
int ndx;
|
|
int s1_offset;
|
|
int s2_offset;
|
|
int i;
|
|
int cur_entry;
|
|
int my_index;
|
|
|
|
my_index = -1;
|
|
|
|
if (!s1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (Hash_Find(hash, s1, NULL, NULL) >= 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
s1_offset = Hash_AddStringToPool(hash, s1);
|
|
s2_offset = Hash_AddStringToPool(hash, s2);
|
|
|
|
ndx = Hash_StringHashFunction(s1);
|
|
if (hash->table[ndx].count == 0)
|
|
{
|
|
hash->table[ndx].head = my_index = Hash_AddEntry(hash, s1_offset, s2_offset, data);
|
|
if (hash->table[ndx].head < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
hash->table[ndx].count = 1;
|
|
}
|
|
else
|
|
{
|
|
cur_entry = hash->table[ndx].head;
|
|
for (i = 0; i < (hash->table[ndx].count - 1); i++)
|
|
{
|
|
cur_entry = hash->entry[cur_entry].next;
|
|
}
|
|
hash->entry[cur_entry].next = my_index = Hash_AddEntry(hash, s1_offset, s2_offset, data);
|
|
if (hash->entry[cur_entry].next < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
hash->table[ndx].count++;
|
|
}
|
|
return my_index;
|
|
}
|
|
|
|
int Hash_Add(struct hash_table *hash, const char *s1, const char *s2, void *data)
|
|
{
|
|
int ndx;
|
|
|
|
ndx = Hash_AddAndReturnIndex(hash, s1, s2, data);
|
|
if (ndx >= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Find the name specified by s1 in the hash table. If not present, add it.
|
|
Currently, s2 and data are only used if the item is being added. They are
|
|
not set with the current values if the item is found. */
|
|
int Hash_FindOrAdd(struct hash_table *hash, const char *s1, const char *s2, void *data)
|
|
{
|
|
int ndx;
|
|
int entry_ndx;
|
|
int i;
|
|
int s1_offset, s2_offset;
|
|
|
|
if (!s1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ndx = Hash_StringHashFunction(s1);
|
|
if (ndx < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (hash->table[ndx].count == 0)
|
|
{
|
|
entry_ndx = -1;
|
|
}
|
|
else
|
|
{
|
|
entry_ndx = hash->table[ndx].head;
|
|
for (i = 0; i < hash->table[ndx].count; i++)
|
|
{
|
|
if (0 == strcmp(s1, &(hash->pool[hash->entry[entry_ndx].s1_offset])))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
entry_ndx = hash->entry[entry_ndx].next;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entry_ndx < 0)
|
|
{
|
|
s1_offset = Hash_AddStringToPool(hash, s1);
|
|
s2_offset = Hash_AddStringToPool(hash, s2);
|
|
|
|
if (hash->table[ndx].count == 0)
|
|
{
|
|
entry_ndx = Hash_AddEntry(hash, s1_offset, s2_offset, data);
|
|
if (entry_ndx < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
hash->table[ndx].head = entry_ndx;
|
|
hash->table[ndx].count = 1;
|
|
}
|
|
else
|
|
{
|
|
entry_ndx = hash->table[ndx].head;
|
|
for (i = 0; i < (hash->table[ndx].count - 1); i++)
|
|
{
|
|
entry_ndx = hash->entry[entry_ndx].next;
|
|
}
|
|
hash->entry[entry_ndx].next = Hash_AddEntry(hash, s1_offset, s2_offset, data);
|
|
if (hash->entry[entry_ndx].next < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
entry_ndx = hash->entry[entry_ndx].next;
|
|
hash->table[ndx].count++;
|
|
}
|
|
}
|
|
return entry_ndx;
|
|
}
|
|
|
|
int Hash_Count(struct hash_table *hash)
|
|
{
|
|
return hash->entry_size;
|
|
}
|
|
|
|
int Hash_SetData(struct hash_table *hash, int entry_ndx, void * data)
|
|
{
|
|
if (!hash)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
hash->entry[entry_ndx].data = data;;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Hash_SetString2(struct hash_table *hash, int entry_ndx, char *s2)
|
|
{
|
|
if (!hash)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
hash->entry[entry_ndx].s2_offset = Hash_AddStringToPool(hash, s2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Hash_ChangeIndexedEntry(struct hash_table *hash, int entry_ndx, const char *s1, const char *s2, void *data)
|
|
{
|
|
/*
|
|
This function is like half a delete and half an add. We want
|
|
the same entry index to be used, to maintain its position and
|
|
order. However, we want to change the strings, including
|
|
the string on which we compute hash functions, so its position
|
|
within the linked lists will change.
|
|
*/
|
|
|
|
int old_ndx;
|
|
|
|
if (!hash)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if ((entry_ndx < 0) || (entry_ndx >= hash->entry_size))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (!s1 || !*s1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
old_ndx = Hash_Find(hash, s1, NULL, NULL);
|
|
if (old_ndx >= 0 && old_ndx != entry_ndx)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
Delete phase
|
|
*/
|
|
{
|
|
int i;
|
|
int new_next;
|
|
int bucket;
|
|
|
|
/*
|
|
Remove the current entry from its linked lists and bucket
|
|
*/
|
|
|
|
/*
|
|
Decrement the count of things on this bucket.
|
|
*/
|
|
bucket = Hash_StringHashFunction(&(hash->pool[hash->entry[entry_ndx].s1_offset]));
|
|
hash->table[bucket].count--;
|
|
|
|
/*
|
|
Remove entry_ndx from the middle of any linked list.
|
|
*/
|
|
new_next = hash->entry[entry_ndx].next;
|
|
for (i = 0; i < hash->entry_size; i++)
|
|
{
|
|
if (hash->entry[i].next == entry_ndx)
|
|
{
|
|
hash->entry[i].next = new_next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Remove entry_ndx from the head of any linked list
|
|
*/
|
|
for (i = 0; i < NUM_OF_BUCKETS; i++)
|
|
{
|
|
if (hash->table[i].head == entry_ndx)
|
|
{
|
|
hash->table[i].head = new_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Add Phase
|
|
*/
|
|
{
|
|
int ndx;
|
|
int s1_offset;
|
|
int s2_offset;
|
|
int i;
|
|
int cur_entry;
|
|
|
|
s1_offset = Hash_AddStringToPool(hash, s1);
|
|
s2_offset = Hash_AddStringToPool(hash, s2);
|
|
|
|
ndx = Hash_StringHashFunction(s1);
|
|
if (hash->table[ndx].count == 0)
|
|
{
|
|
hash->table[ndx].head = entry_ndx;
|
|
hash->table[ndx].count = 1;
|
|
}
|
|
else
|
|
{
|
|
cur_entry = hash->table[ndx].head;
|
|
for (i = 0; i < (hash->table[ndx].count - 1); i++)
|
|
{
|
|
cur_entry = hash->entry[cur_entry].next;
|
|
}
|
|
hash->entry[cur_entry].next = entry_ndx;
|
|
hash->table[ndx].count++;
|
|
}
|
|
hash->entry[entry_ndx].s1_offset = s1_offset;
|
|
hash->entry[entry_ndx].s2_offset = s2_offset;
|
|
hash->entry[entry_ndx].data = data;
|
|
hash->entry[entry_ndx].next = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Hash_GetIndexedEntry(struct hash_table *hash, int entry_ndx, char **s1, char **s2, void **data)
|
|
{
|
|
if (!hash)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if ((entry_ndx < 0) || (entry_ndx >= hash->entry_size))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (s1)
|
|
{
|
|
if (hash->entry[entry_ndx].s1_offset >= 0)
|
|
{
|
|
*s1 = &(hash->pool[hash->entry[entry_ndx].s1_offset]);
|
|
}
|
|
else
|
|
{
|
|
*s1 = NULL;
|
|
}
|
|
}
|
|
|
|
if (s2)
|
|
{
|
|
if (hash->entry[entry_ndx].s2_offset >= 0)
|
|
{
|
|
*s2 = &(hash->pool[hash->entry[entry_ndx].s2_offset]);
|
|
}
|
|
else
|
|
{
|
|
*s2 = NULL;
|
|
}
|
|
}
|
|
|
|
if (data)
|
|
{
|
|
*data = hash->entry[entry_ndx].data;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Hash_DeleteIndexedEntry(struct hash_table *hash, int ndx)
|
|
{
|
|
int i;
|
|
int new_next;
|
|
int bucket;
|
|
|
|
if ((ndx < 0) || (ndx >= hash->entry_size))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
Decrement the count of things on this bucket.
|
|
*/
|
|
bucket = Hash_StringHashFunction(&(hash->pool[hash->entry[ndx].s1_offset]));
|
|
hash->table[bucket].count--;
|
|
|
|
/*
|
|
Remove ndx from the middle of any linked list.
|
|
*/
|
|
new_next = hash->entry[ndx].next;
|
|
for (i = 0; i < hash->entry_size; i++)
|
|
{
|
|
if (hash->entry[i].next == ndx)
|
|
{
|
|
hash->entry[i].next = new_next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Remove ndx from the head of any linked list
|
|
*/
|
|
for (i = 0; i < NUM_OF_BUCKETS; i++)
|
|
{
|
|
if (hash->table[i].head == ndx)
|
|
{
|
|
hash->table[i].head = new_next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
We are about to delete the actual entry, sliding all
|
|
the entries above ndx down by 1. This means any linked
|
|
list reference pointing above ndx needs to be
|
|
decremented.
|
|
*/
|
|
for (i = 0; i < hash->entry_size; i++)
|
|
{
|
|
if (hash->entry[i].next > ndx)
|
|
{
|
|
hash->entry[i].next--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
For the same reason, any linked list head pointing above ndx needs
|
|
to be decremented.
|
|
*/
|
|
for (i = 0; i < NUM_OF_BUCKETS; i++)
|
|
{
|
|
if (hash->table[i].head > ndx)
|
|
{
|
|
hash->table[i].head--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Now, we actually move all the entries above ndx
|
|
down by 1.
|
|
*/
|
|
for (i = ndx + 1; i < hash->entry_size; i++)
|
|
{
|
|
hash->entry[i - 1] = hash->entry[i];
|
|
}
|
|
|
|
/*
|
|
The table contains one less entry.
|
|
*/
|
|
hash->entry_size--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _CRTAPI1 x_compare_entries_descending(const void *elem1, const void *elem2)
|
|
{
|
|
if (((struct hash_entry *) elem1)->data < ((struct hash_entry *) elem2)->data)
|
|
{
|
|
return 1;
|
|
}
|
|
if (((struct hash_entry *) elem1)->data > ((struct hash_entry *) elem2)->data)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Hash_SortByDataDescending(struct hash_table *hash)
|
|
{
|
|
struct hash_table new_hash;
|
|
int i;
|
|
|
|
/*
|
|
We sort the hash table entries in place. After the sort, the data structure is corrupted.
|
|
So, we rebuild a new one one entry at a time.
|
|
*/
|
|
qsort(hash->entry, hash->entry_size, sizeof(struct hash_entry), x_compare_entries_descending);
|
|
Hash_Init(&new_hash);
|
|
for (i = 0; i < hash->entry_size; i++)
|
|
{
|
|
Hash_Add(&new_hash,
|
|
&(hash->pool[hash->entry[i].s1_offset]),
|
|
&(hash->pool[hash->entry[i].s2_offset]),
|
|
(void *) hash->entry[i].data);
|
|
}
|
|
Hash_FreeContents(hash);
|
|
*hash = new_hash;
|
|
return 0;
|
|
}
|