| #include <cstdlib> |
| #include <cstring> |
| |
| #include "vtr_memory.h" |
| #include "vtr_log.h" |
| |
| #include "hash.h" |
| |
| t_hash** alloc_hash_table() { |
| /* Creates a hash table with HASHSIZE different locations (hash values). */ |
| |
| t_hash** hash_table; |
| |
| hash_table = (t_hash**)vtr::calloc(sizeof(t_hash*), HASHSIZE); |
| return (hash_table); |
| } |
| |
| void free_hash_table(t_hash** hash_table) { |
| /* Frees all the storage associated with a hash table. */ |
| |
| int i; |
| t_hash *h_ptr, *temp_ptr; |
| |
| for (i = 0; i < HASHSIZE; i++) { |
| h_ptr = hash_table[i]; |
| while (h_ptr != nullptr) { |
| free(h_ptr->name); |
| temp_ptr = h_ptr->next; |
| free(h_ptr); |
| h_ptr = temp_ptr; |
| } |
| } |
| |
| free(hash_table); |
| } |
| |
| t_hash_iterator start_hash_table_iterator() { |
| /* 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. */ |
| |
| t_hash_iterator hash_iterator; |
| |
| hash_iterator.i = -1; |
| hash_iterator.h_ptr = nullptr; |
| return (hash_iterator); |
| } |
| |
| t_hash* get_next_hash(t_hash** hash_table, t_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; |
| t_hash* h_ptr; |
| |
| i = hash_iterator->i; |
| h_ptr = hash_iterator->h_ptr; |
| |
| while (h_ptr == nullptr) { |
| i++; |
| if (i >= HASHSIZE) |
| return (nullptr); /* End of table */ |
| |
| h_ptr = hash_table[i]; |
| } |
| |
| hash_iterator->h_ptr = h_ptr->next; |
| hash_iterator->i = i; |
| return (h_ptr); |
| } |
| |
| t_hash* insert_in_hash_table(t_hash** hash_table, const 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; |
| t_hash *h_ptr, *prev_ptr; |
| |
| i = hash_value(name); |
| prev_ptr = nullptr; |
| h_ptr = hash_table[i]; |
| |
| while (h_ptr != nullptr) { |
| 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 = (t_hash*)vtr::malloc(sizeof(t_hash)); |
| if (prev_ptr == nullptr) { |
| hash_table[i] = h_ptr; |
| } else { |
| prev_ptr->next = h_ptr; |
| } |
| h_ptr->next = nullptr; |
| h_ptr->index = next_free_index; |
| h_ptr->count = 1; |
| h_ptr->name = (char*)vtr::malloc((strlen(name) + 1) * sizeof(char)); |
| strcpy(h_ptr->name, name); |
| return (h_ptr); |
| } |
| |
| t_hash* get_hash_entry(t_hash** hash_table, const char* name) { |
| /* Returns the hash entry with this name, or NULL if there is no * |
| * corresponding entry. */ |
| |
| int i; |
| t_hash* h_ptr; |
| |
| i = hash_value(name); |
| h_ptr = hash_table[i]; |
| |
| while (h_ptr != nullptr) { |
| if (strcmp(h_ptr->name, name) == 0) |
| return (h_ptr); |
| |
| h_ptr = h_ptr->next; |
| } |
| |
| return (nullptr); |
| } |
| |
| int hash_value(const 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; |
| unsigned val = 0, mult = 1; |
| |
| i = strlen(name); |
| for (i = strlen(name) - 1; i >= 0; i--) { |
| val += mult * ((unsigned)name[i]); |
| mult *= 7; |
| } |
| val += (unsigned)name[0]; |
| val %= HASHSIZE; |
| |
| return (val); |
| } |
| |
| void get_hash_stats(t_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; |
| t_hash* h_ptr; |
| |
| for (i = 0; i < HASHSIZE; i++) { |
| h_ptr = hash_table[i]; |
| curr_num = 0; |
| |
| if (h_ptr == nullptr) |
| num_NULL++; |
| else { |
| while (h_ptr != nullptr) { |
| 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); |
| |
| VTR_LOG("\n"); |
| VTR_LOG("The hash table '%s' is of size %d.\n", |
| hash_table_name, HASHSIZE); |
| VTR_LOG( |
| "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); |
| VTR_LOG("\n"); |
| } |