/* Table_t is an abstract table of long/void * pairs. The long value is * the key. Table_t is implemented as a standard hash table of HASH_SIZE * entries with linked list resolution. The hash_table is dynamically * allocated within table.c, and its address is returned as a void pointer * to the caller. */ #include #include "table.h" #ifndef YES #define YES 1 #endif #ifndef NO #define NO 0 #endif #define HASH_SIZE 1023 #define BLOCK_SIZE 100 extern void free(char* ptr); extern char* malloc(unsigned size); /* Linked list node for hash table lists. */ typedef struct hash_node_t { long key; void *data; struct hash_node_t *next; } hash_node_t; /* Node of block-allocated data to streamline table allocation and deallocation * It was taking too damn long the simple way. Field "data" is the block * of allocated space from which to draw new hash nodes. "next_index" * tells how many entries in "data" have been used or, equivalently, the * index of the next unused entry. "next_block" points to the next * block_node_t in the linked list of the things maintained by each table * object. */ typedef struct block_node_t { int next_index; hash_node_t data[BLOCK_SIZE]; struct block_node_t *next_block; } block_node_t; /* Main data structure for a table. "h_table" points to the hash table, * a block of head pointers to lists of hash_node_t's. "blocks" points to * to the list of allocation blocks used for the table so far. */ typedef struct { hash_node_t **h_table; block_node_t *blocks; long num_elements; } table_data_t; /* Node for iterator information. Table points to the table being iterated * upon. Ndx indicates the current hash_table entry, and "point" points to * the current node within the hash table entry. */ typedef struct { table_data_t *table; int ndx; hash_node_t *point; } iterator_data_t; /* Create_table allocates a block of hash_node_t pointers, all * initialized to NULL. It returns a pointer to the block. */ table_t create_table() { table_data_t *table; register int ndx; table = (table_data_t *) malloc(sizeof(table_data_t)); table->h_table = (hash_node_t **) malloc(sizeof(hash_node_t *) * HASH_SIZE); table->blocks = NULL; table->num_elements = 0; for (ndx = 0; ndx < HASH_SIZE; ndx++) table->h_table[ndx] = NULL; return table; } /* Delete an item out of the table. */ void destroy_item(table_t table, long key) { table_data_t *t = (table_data_t *) table; hash_node_t **finder = t->h_table + key % HASH_SIZE; for ( ; *finder != NULL && (*finder)->key != key; finder = &(*finder)->next) ; if (*finder == NULL) return; (*finder)->key = -1; *finder = (*finder)->next; t->num_elements--; } /* Add_item adds a new key and associated data item to the table assuming * the key is not already in the table. It returns YES on success, NO * if the key is already present. It uses a simple modulo by the hash * table size as a "hash function". */ int add_item(table_t table, long key, void *item) { hash_node_t **entry; register hash_node_t *search; table_data_t *t = (table_data_t *) table; register block_node_t *new_blk; entry = t->h_table + key % HASH_SIZE; for (search = *entry; search != NULL; search = search->next) if (search->key == key) break; if (search != NULL) return NO; else { if (t->blocks == NULL || t->blocks->next_index == BLOCK_SIZE) { new_blk = (block_node_t *) malloc(sizeof(block_node_t)); new_blk->next_index = 0; new_blk->next_block = t->blocks; t->blocks = new_blk; } search = t->blocks->data + t->blocks->next_index++; search->next = *entry; *entry = search; search->key = key; search->data = item; t->num_elements++; return YES; } } /* Num_items returns the number of elements in the table. */ long num_items(table_t table) { return (*(table_data_t *)table).num_elements; } /* Get_item returns the pointer to the data associated with the given key. * If the key is not recognized, then NULL is returned. Like add_item, * it uses a simple modulo for the "hash function". */ void *get_item(table_t table, long key) { register hash_node_t *search; search = ((table_data_t *)table)->h_table[key%HASH_SIZE]; while (search != NULL && search->key != key) search = search->next; return search == NULL ? NULL : search->data; } /* Destroy table deallocates all memory used to store the table. The memory * allocated to store the actual data is not de-allocated, but the * pointers maintained to it in the table are, along with the table itself. */ void destroy_table(table_t table) { register block_node_t *destroy; register block_node_t *temp; table_data_t *t = (table_data_t *)table; for (destroy = t->blocks; destroy != NULL;) { temp = destroy->next_block; free((char*)destroy); destroy = temp; } free((char*)t->h_table); free((char*)t); } /* Create_iterator creates an iterator on "table" and returns it. * The iterator is initially at the beginning of the table. The iterator * returns the key/data pairs in the table in some guaranteed order (not * necessarily sorted). If modifications are made to the table during * the life of the iterator, its behavior may or may not reflect the * modification. An arbitrary number of iterators may exist on the same * table at the same time. * * The iterator for the hash table is simply a structure showing present * hash entry list and position within that list. The order of iteration * is by hash table entries, and within an entry by nodes in the list. */ iterator_t create_iterator(table_t table) { iterator_data_t *itr; register int ndx; itr = (iterator_data_t *) malloc(sizeof(iterator_data_t)); itr->table = (table_data_t *)table; for (ndx = 0; ndx < HASH_SIZE && itr->table->h_table[ndx] == NULL;) ndx++; itr->ndx = ndx; itr->point = ndx < HASH_SIZE ? itr->table->h_table[ndx] : NULL; return itr; } /* Next_item returns the key/data pair at the present position of "itr", * and increments "itr" to the next position. If the present position of * "itr" is past the end of the table, next_item returns NO, otherwise * YES. If itr is past the end of the table, ndx will be left equal to * HASH_SIZE and point will be NULL. */ int next_item(iterator_t itr, long *key, void **data) { iterator_data_t *my_itr = (iterator_data_t *) itr; register int ndx; if ((ndx = my_itr->ndx) >= HASH_SIZE) return NO; else { *key = my_itr->point->key; *data = my_itr->point->data; if ((my_itr->point = my_itr->point->next) == NULL) { while (ndx++ < HASH_SIZE && my_itr->table->h_table[ndx] == NULL) ; my_itr->point = ndx < HASH_SIZE ? my_itr->table->h_table[ndx] : NULL; my_itr->ndx = ndx; } return YES; } } /* Destroy_iterator destroys "itr" and frees all storage associated * with the iterator. */ void destroy_iterator(iterator_t itr) { free((char*)itr); }