201 lines
5.0 KiB
C
201 lines
5.0 KiB
C
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include "hash.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
struct s_hash **
|
||
|
alloc_hash_table(void) {
|
||
|
|
||
|
/* Creates a hash table with HASHSIZE different locations (hash values). */
|
||
|
|
||
|
struct s_hash **hash_table;
|
||
|
|
||
|
hash_table = (struct s_hash **) my_calloc(sizeof(struct s_hash *),
|
||
|
HASHSIZE);
|
||
|
return (hash_table);
|
||
|
}
|
||
|
|
||
|
void free_hash_table(struct s_hash **hash_table) {
|
||
|
|
||
|
/* Frees all the storage associated with a hash table. */
|
||
|
|
||
|
int i;
|
||
|
struct s_hash *h_ptr, *temp_ptr;
|
||
|
|
||
|
for (i = 0; i < HASHSIZE; i++) {
|
||
|
h_ptr = hash_table[i];
|
||
|
while (h_ptr != NULL) {
|
||
|
free(h_ptr->name);
|
||
|
temp_ptr = h_ptr->next;
|
||
|
free(h_ptr);
|
||
|
h_ptr = temp_ptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(hash_table);
|
||
|
}
|
||
|
|
||
|
struct s_hash_iterator start_hash_table_iterator(void) {
|
||
|
|
||
|
/* Call this routine before you start going through all the elements in *
|
||
|
* a hash table. It sets the internal indices to the start of the table. */
|
||
|
|
||
|
struct s_hash_iterator hash_iterator;
|
||
|
|
||
|
hash_iterator.i = -1;
|
||
|
hash_iterator.h_ptr = NULL;
|
||
|
return (hash_iterator);
|
||
|
}
|
||
|
|
||
|
struct s_hash *
|
||
|
get_next_hash(struct s_hash **hash_table, struct s_hash_iterator *hash_iterator) {
|
||
|
|
||
|
/* Returns the next occupied hash entry, and moves the iterator structure *
|
||
|
* forward so the next call gets the next entry. */
|
||
|
|
||
|
int i;
|
||
|
struct s_hash *h_ptr;
|
||
|
|
||
|
i = hash_iterator->i;
|
||
|
h_ptr = hash_iterator->h_ptr;
|
||
|
|
||
|
while (h_ptr == NULL) {
|
||
|
i++;
|
||
|
if (i >= HASHSIZE)
|
||
|
return (NULL); /* End of table */
|
||
|
|
||
|
h_ptr = hash_table[i];
|
||
|
}
|
||
|
|
||
|
hash_iterator->h_ptr = h_ptr->next;
|
||
|
hash_iterator->i = i;
|
||
|
return (h_ptr);
|
||
|
}
|
||
|
|
||
|
struct s_hash *
|
||
|
insert_in_hash_table(struct s_hash **hash_table, char *name,
|
||
|
int next_free_index) {
|
||
|
|
||
|
/* Adds the string pointed to by name to the hash table, and returns the *
|
||
|
* hash structure created or updated. If name is already in the hash table *
|
||
|
* the count member of that hash element is incremented. Otherwise a new *
|
||
|
* hash entry with a count of zero and an index of next_free_index is *
|
||
|
* created. */
|
||
|
|
||
|
int i;
|
||
|
struct s_hash *h_ptr, *prev_ptr;
|
||
|
|
||
|
i = hash_value(name);
|
||
|
prev_ptr = NULL;
|
||
|
h_ptr = hash_table[i];
|
||
|
|
||
|
while (h_ptr != NULL) {
|
||
|
if (strcmp(h_ptr->name, name) == 0) {
|
||
|
h_ptr->count++;
|
||
|
return (h_ptr);
|
||
|
}
|
||
|
|
||
|
prev_ptr = h_ptr;
|
||
|
h_ptr = h_ptr->next;
|
||
|
}
|
||
|
|
||
|
/* Name string wasn't in the hash table. Add it. */
|
||
|
|
||
|
h_ptr = (struct s_hash *) my_malloc(sizeof(struct s_hash));
|
||
|
if (prev_ptr == NULL) {
|
||
|
hash_table[i] = h_ptr;
|
||
|
} else {
|
||
|
prev_ptr->next = h_ptr;
|
||
|
}
|
||
|
h_ptr->next = NULL;
|
||
|
h_ptr->index = next_free_index;
|
||
|
h_ptr->count = 1;
|
||
|
h_ptr->name = (char *) my_malloc((strlen(name) + 1) * sizeof(char));
|
||
|
strcpy(h_ptr->name, name);
|
||
|
return (h_ptr);
|
||
|
}
|
||
|
|
||
|
struct s_hash *
|
||
|
get_hash_entry(struct s_hash **hash_table, char *name) {
|
||
|
|
||
|
/* Returns the hash entry with this name, or NULL if there is no *
|
||
|
* corresponding entry. */
|
||
|
|
||
|
int i;
|
||
|
struct s_hash *h_ptr;
|
||
|
|
||
|
i = hash_value(name);
|
||
|
h_ptr = hash_table[i];
|
||
|
|
||
|
while (h_ptr != NULL) {
|
||
|
if (strcmp(h_ptr->name, name) == 0)
|
||
|
return (h_ptr);
|
||
|
|
||
|
h_ptr = h_ptr->next;
|
||
|
}
|
||
|
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
int hash_value(char *name) {
|
||
|
/* Creates a hash key from a character string. The absolute value is taken *
|
||
|
* for the final val to compensate for long strlen that cause val to *
|
||
|
* overflow. */
|
||
|
|
||
|
int i;
|
||
|
int val = 0, mult = 1;
|
||
|
|
||
|
i = strlen(name);
|
||
|
for (i = strlen(name) - 1; i >= 0; i--) {
|
||
|
val += mult * ((int) name[i]);
|
||
|
mult *= 7;
|
||
|
}
|
||
|
val += (int) name[0];
|
||
|
val %= HASHSIZE;
|
||
|
|
||
|
val = abs(val);
|
||
|
return (val);
|
||
|
}
|
||
|
|
||
|
void get_hash_stats(struct s_hash **hash_table, char *hash_table_name){
|
||
|
|
||
|
/* Checks to see how well elements are distributed within the hash table. *
|
||
|
* Will traverse through the hash_table and count the length of the linked *
|
||
|
* list. Will output the hash number, the number of array elements that are *
|
||
|
* NULL, the average number of linked lists and the maximum length of linked *
|
||
|
* lists. */
|
||
|
|
||
|
int num_NULL = 0, total_elements = 0, max_num = 0, curr_num;
|
||
|
double avg_num = 0;
|
||
|
int i;
|
||
|
struct s_hash *h_ptr;
|
||
|
|
||
|
for (i = 0; i<HASHSIZE; i++){
|
||
|
h_ptr = hash_table[i];
|
||
|
curr_num = 0;
|
||
|
|
||
|
if (h_ptr == NULL)
|
||
|
num_NULL++;
|
||
|
else{
|
||
|
while (h_ptr != NULL){
|
||
|
curr_num ++;
|
||
|
h_ptr = h_ptr->next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (curr_num > max_num)
|
||
|
max_num = curr_num;
|
||
|
|
||
|
total_elements = total_elements + curr_num;
|
||
|
}
|
||
|
|
||
|
avg_num = (float) total_elements / ((float)HASHSIZE - (float)num_NULL);
|
||
|
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "The hash table '%s' is of size %d.\n",
|
||
|
hash_table_name, HASHSIZE);
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "It has: %d keys that are never used; total of %d elements; an average linked-list length of %.1f; and a maximum linked-list length of %d.\n",
|
||
|
num_NULL, total_elements, avg_num, max_num);
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
||
|
}
|