diff options
Diffstat (limited to 'libtecla-1.4.1/hash.c')
-rw-r--r-- | libtecla-1.4.1/hash.c | 748 |
1 files changed, 0 insertions, 748 deletions
diff --git a/libtecla-1.4.1/hash.c b/libtecla-1.4.1/hash.c deleted file mode 100644 index 89f8245..0000000 --- a/libtecla-1.4.1/hash.c +++ /dev/null @@ -1,748 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> - -#include "hash.h" -#include "strngmem.h" -#include "freelist.h" - -/* - * The following container object contains free-lists to be used - * for allocation of HashTable containers and nodes. - */ -struct HashMemory { - FreeList *hash_memory; /* HashTable free-list */ - FreeList *node_memory; /* HashNode free-list */ - StringMem *string_memory; /* Memory used to allocate hash strings */ -}; - -/* - * Define a hash symbol-table entry. - * See symbol.h for the definition of the Symbol container type. - */ -typedef struct HashNode HashNode; -struct HashNode { - Symbol symbol; /* The symbol stored in the hash-entry */ - HashNode *next; /* The next hash-table entry in a bucket list */ -}; - -/* - * Each hash-table bucket contains a linked list of entries that - * hash to the same bucket. - */ -typedef struct { - HashNode *head; /* The head of the bucket hash-node list */ - int count; /* The number of entries in the list */ -} HashBucket; - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -/* - * A hash-table consists of 'size' hash buckets. - * Note that the HashTable typedef for this struct is contained in hash.h. - */ -struct HashTable { - char errmsg[ERRLEN+1];/* Error-report buffer */ - HashMemory *mem; /* HashTable free-list */ - int internal_mem; /* True if 'mem' was allocated by _new_HashTable() */ - int case_sensitive; /* True if case is significant in lookup keys */ - int size; /* The number of hash buckets */ - HashBucket *bucket; /* An array of 'size' hash buckets */ - int (*keycmp)(const char *, const char *); /* Key comparison function */ - void *app_data; /* Application-provided data */ - HASH_DEL_FN(*del_fn); /* Application-provided 'app_data' destructor */ -}; - -static HashNode *_del_HashNode(HashTable *hash, HashNode *node); -static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, - void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); -static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, - const char *name, HashNode **prev); -static HashBucket *_find_HashBucket(HashTable *hash, const char *name); -static int _ht_lower_strcmp(const char *node_key, const char *look_key); -static int _ht_strcmp(const char *node_key, const char *look_key); - -/*....................................................................... - * Allocate a free-list for use in allocating hash tables and their nodes. - * - * Input: - * list_count int The number of HashTable containers per free-list - * block. - * node_count int The number of HashTable nodes per free-list block. - * Output: - * return HashMemory * The new free-list for use in allocating hash tables - * and their nodes. - */ -HashMemory *_new_HashMemory(int hash_count, int node_count) -{ - HashMemory *mem; -/* - * Allocate the free-list container. - */ - mem = (HashMemory *) malloc(sizeof(HashMemory)); - if(!mem) { - fprintf(stderr, "_new_HashMemory: Insufficient memory.\n"); - return NULL; - }; -/* - * Initialize the container at least up to the point at which it can - * safely be passed to _del_HashMemory(). - */ - mem->hash_memory = NULL; - mem->node_memory = NULL; - mem->string_memory = NULL; -/* - * Allocate the two free-lists. - */ - mem->hash_memory = _new_FreeList("_new_HashMemory", sizeof(HashTable), - hash_count); - if(!mem->hash_memory) - return _del_HashMemory(mem, 1); - mem->node_memory = _new_FreeList("_new_HashMemory", sizeof(HashNode), - node_count); - if(!mem->node_memory) - return _del_HashMemory(mem, 1); - mem->string_memory = _new_StringMem("_new_HashMemory", 64); - if(!mem->string_memory) - return _del_HashMemory(mem, 1); -/* - * Return the free-list container. - */ - return mem; -} - -/*....................................................................... - * Delete a HashTable free-list. An error will be displayed if the list is - * still in use and the deletion will be aborted. - * - * Input: - * mem HashMemory * The free-list container to be deleted. - * force int If force==0 then _del_HashMemory() will complain - * and refuse to delete the free-list if any - * of nodes have not been returned to the free-list. - * If force!=0 then _del_HashMemory() will not check - * whether any nodes are still in use and will - * always delete the list. - * Output: - * return HashMemory * Always NULL (even if the memory could not be - * deleted). - */ -HashMemory *_del_HashMemory(HashMemory *mem, int force) -{ - const char *caller = "_del_HashMemory"; - if(mem) { - if(!force && (_busy_FreeListNodes(mem->hash_memory) > 0 || - _busy_FreeListNodes(mem->node_memory) > 0)) { - fprintf(stderr, "%s: Free-list in use.\n", caller); - return NULL; - }; - mem->hash_memory = _del_FreeList(caller, mem->hash_memory, force); - mem->node_memory = _del_FreeList(caller, mem->node_memory, force); - mem->string_memory = _del_StringMem(caller, mem->string_memory, force); - free(mem); - }; - return NULL; -} - -/*....................................................................... - * Create a new hash table. - * - * Input: - * mem HashMemory * An optional free-list for use in allocating - * HashTable containers and nodes. See explanation - * in hash.h. If you are going to allocate more - * than one hash table, then it will be more - * efficient to allocate a single free-list for - * all of them than to force each hash table - * to allocate its own private free-list. - * size int The size of the hash table. Best performance - * will be acheived if this is a prime number. - * hcase HashCase Specify how symbol case is considered when - * looking up symbols, from: - * IGNORE_CASE - Upper and lower case versions - * of a letter are treated as - * being identical. - * HONOUR_CASE - Upper and lower case versions - * of a letter are treated as - * being distinct. - * characters in a lookup name is significant. - * app_data void * Optional application data to be registered - * to the table. This is presented to user - * provided SYM_DEL_FN() symbol destructors along - * with the symbol data. - * del_fn() HASH_DEL_FN(*) If you want app_data to be free'd when the - * hash-table is destroyed, register a suitable - * destructor function here. - * Output: - * return HashTable * The new hash table, or NULL on error. - */ -HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, - void *app_data, HASH_DEL_FN(*del_fn)) -{ - HashTable *hash; /* The table to be returned */ - int allocate_mem = !mem; /* True if mem should be internally allocated */ - int i; -/* - * Check arguments. - */ - if(size <= 0) { - fprintf(stderr, "_new_HashTable: Illegal table size (%d).\n", size); - return NULL; - }; -/* - * Allocate an internal free-list? - */ - if(allocate_mem) { - mem = _new_HashMemory(1, 100); - if(!mem) - return NULL; - }; -/* - * Allocate the container. - */ - hash = (HashTable *) _new_FreeListNode(mem->hash_memory); - if(!hash) { - fprintf(stderr, "_new_HashTable: Insufficient memory.\n"); - if(allocate_mem) - mem = _del_HashMemory(mem, 1); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize - * the container at least up to the point at which it can safely - * be passed to _del_HashTable(). - */ - hash->errmsg[0] = '\0'; - hash->mem = mem; - hash->internal_mem = allocate_mem; - hash->case_sensitive = hcase==HONOUR_CASE; - hash->size = size; - hash->bucket = NULL; - hash->keycmp = hash->case_sensitive ? _ht_strcmp : _ht_lower_strcmp; - hash->app_data = app_data; - hash->del_fn = del_fn; -/* - * Allocate the array of 'size' hash buckets. - */ - hash->bucket = (HashBucket *) malloc(sizeof(HashBucket) * size); - if(!hash->bucket) { - fprintf(stderr, "_new_HashTable: Insufficient memory for %d buckets.\n", - size); - return _del_HashTable(hash); - }; -/* - * Initialize the bucket array. - */ - for(i=0; i<size; i++) { - HashBucket *b = hash->bucket + i; - b->head = NULL; - b->count = 0; - }; -/* - * The table is ready for use - albeit currently empty. - */ - return hash; -} - -/*....................................................................... - * Delete a hash-table. - * - * Input: - * hash HashTable * The hash table to be deleted. - * Output: - * return HashTable * The deleted hash table (always NULL). - */ -HashTable *_del_HashTable(HashTable *hash) -{ - if(hash) { -/* - * Clear and delete the bucket array. - */ - if(hash->bucket) { - _clear_HashTable(hash); - free(hash->bucket); - hash->bucket = NULL; - }; -/* - * Delete application data. - */ - if(hash->del_fn) - hash->del_fn(hash->app_data); -/* - * If the hash table was allocated from an internal free-list, delete - * it and the hash table by deleting the free-list. Otherwise just - * return the hash-table to the external free-list. - */ - if(hash->internal_mem) - _del_HashMemory(hash->mem, 1); - else - hash = (HashTable *) _del_FreeListNode(hash->mem->hash_memory, hash); - }; - return NULL; -} - -/*....................................................................... - * Create and install a new entry in a hash table. If an entry with the - * same name already exists, replace its contents with the new data. - * - * Input: - * hash HashTable * The hash table to insert the symbol into. - * name const char * The name to tag the entry with. - * code int An application-specific code to be stored in - * the entry. - * fn void (*)(void) An application-specific function to be stored - * in the entry. - * data void * An application-specific pointer to data to be - * associated with the entry, or NULL if not - * relevant. - * del_fn SYM_DEL_FN(*) An optional destructor function. When the - * symbol is deleted this function will be called - * with the 'code' and 'data' arguments given - * above. Any application data that was registered - * to the table via the app_data argument of - * _new_HashTable() will also be passed. - * Output: - * return HashNode * The new entry, or NULL if there was insufficient - * memory or the arguments were invalid. - */ -Symbol *_new_HashSymbol(HashTable *hash, const char *name, int code, - void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) -{ - HashBucket *bucket; /* The hash-bucket associated with the name */ - HashNode *node; /* The new node */ -/* - * Check arguments. - */ - if(!hash || !name) - return NULL; -/* - * Get the hash bucket of the specified name. - */ - bucket = _find_HashBucket(hash, name); -/* - * See if a node with the same name already exists. - */ - node = _find_HashNode(hash, bucket, name, NULL); -/* - * If found, delete its contents by calling the user-supplied - * destructor function, if provided. - */ - if(node) { - if(node->symbol.data && node->symbol.del_fn) { - node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, - node->symbol.data); - }; -/* - * Allocate a new node if necessary. - */ - } else { - node = _new_HashNode(hash, name, code, fn, data, del_fn); - if(!node) - return NULL; - }; -/* - * Install the node at the head of the hash-bucket list. - */ - node->next = bucket->head; - bucket->head = node; - bucket->count++; - return &node->symbol; -} - -/*....................................................................... - * Remove and delete a given hash-table entry. - * - * Input: - * hash HashTable * The hash table to find the symbol in. - * name const char * The name of the entry. - * Output: - * return HashNode * The deleted hash node (always NULL). - */ -Symbol *_del_HashSymbol(HashTable *hash, const char *name) -{ - if(hash && name) { - HashBucket *bucket = _find_HashBucket(hash, name); - HashNode *prev; /* The node preceding the located node */ - HashNode *node = _find_HashNode(hash, bucket, name, &prev); -/* - * Node found? - */ - if(node) { -/* - * Remove the node from the bucket list. - */ - if(prev) { - prev->next = node->next; - } else { - bucket->head = node->next; - }; -/* - * Record the loss of a node. - */ - bucket->count--; -/* - * Delete the node. - */ - (void) _del_HashNode(hash, node); - }; - }; - return NULL; -} - -/*....................................................................... - * Look up a symbol in the hash table. - * - * Input: - * hash HashTable * The table to look up the string in. - * name const char * The name of the symbol to look up. - * Output: - * return Symbol * The located hash-table symbol, or NULL if not - * found. - */ -Symbol *_find_HashSymbol(HashTable *hash, const char *name) -{ - HashBucket *bucket; /* The hash-table bucket associated with name[] */ - HashNode *node; /* The hash-table node of the requested symbol */ -/* - * Check arguments. - */ - if(!hash) - return NULL; -/* - * Nothing to lookup? - */ - if(!name) - return NULL; -/* - * Hash the name to a hash-table bucket. - */ - bucket = _find_HashBucket(hash, name); -/* - * Find the bucket entry that exactly matches the name. - */ - node = _find_HashNode(hash, bucket, name, NULL); - if(!node) - return NULL; - return &node->symbol; -} - -/*....................................................................... - * Private function used to allocate a hash-table node. - * The caller is responsible for checking that the specified symbol - * is unique and for installing the returned entry in the table. - * - * Input: - * hash HashTable * The table to allocate the node for. - * name const char * The name of the new entry. - * code int A user-supplied context code. - * fn void (*)(void) A user-supplied function pointer. - * data void * A user-supplied data pointer. - * del_fn SYM_DEL_FN(*) An optional 'data' destructor function. - * Output: - * return HashNode * The new node, or NULL on error. - */ -static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, - void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) -{ - HashNode *node; /* The new node */ -/* - * Allocate the new node from the free list. - */ - node = (HashNode *) _new_FreeListNode(hash->mem->node_memory); - if(!node) - return NULL; -/* - * Before attempting any operation that might fail, initialize the - * contents of 'node' at least up to the point at which it can be - * safely passed to _del_HashNode(). - */ - node->symbol.name = NULL; - node->symbol.code = code; - node->symbol.fn = fn; - node->symbol.data = data; - node->symbol.del_fn = del_fn; - node->next = NULL; -/* - * Allocate a copy of 'name'. - */ - node->symbol.name = _new_StringMemString(hash->mem->string_memory, - strlen(name) + 1); - if(!node->symbol.name) - return _del_HashNode(hash, node); -/* - * If character-case is insignificant in the current table, convert the - * name to lower case while copying it. - */ - if(hash->case_sensitive) { - strcpy(node->symbol.name, name); - } else { - const char *src = name; - char *dst = node->symbol.name; - for( ; *src; src++,dst++) - *dst = tolower(*src); - *dst = '\0'; - }; - return node; -} - -/*....................................................................... - * Private function used to delete a hash-table node. - * The node must have been removed from its list before calling this - * function. - * - * Input: - * hash HashTable * The table for which the node was originally - * allocated. - * node HashNode * The node to be deleted. - * Output: - * return HashNode * The deleted node (always NULL). - */ -static HashNode *_del_HashNode(HashTable *hash, HashNode *node) -{ - if(node) { - node->symbol.name = _del_StringMemString(hash->mem->string_memory, - node->symbol.name); -/* - * Call the user-supplied data-destructor if provided. - */ - if(node->symbol.data && node->symbol.del_fn) - node->symbol.data = node->symbol.del_fn(hash->app_data, - node->symbol.code, - node->symbol.data); -/* - * Return the node to the free-list. - */ - node->next = NULL; - node = (HashNode *) _del_FreeListNode(hash->mem->node_memory, node); - }; - return NULL; -} - -/*....................................................................... - * Private function to locate the hash bucket associated with a given - * name. - * - * This uses a hash-function described in the dragon-book - * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and - * Ullman; pub. Adison Wesley) page 435. - * - * Input: - * hash HashTable * The table to look up the string in. - * name const char * The name of the symbol to look up. - * Output: - * return HashBucket * The located hash-bucket. - */ -static HashBucket *_find_HashBucket(HashTable *hash, const char *name) -{ - unsigned const char *kp; - unsigned long h = 0L; - if(hash->case_sensitive) { - for(kp=(unsigned const char *) name; *kp; kp++) - h = 65599UL * h + *kp; /* 65599 is a prime close to 2^16 */ - } else { - for(kp=(unsigned const char *) name; *kp; kp++) - h = 65599UL * h + tolower((int)*kp); /* 65599 is a prime close to 2^16 */ - }; - return hash->bucket + (h % hash->size); -} - -/*....................................................................... - * Search for a given name in the entries of a given bucket. - * - * Input: - * hash HashTable * The hash-table being searched. - * bucket HashBucket * The bucket to search (use _find_HashBucket()). - * name const char * The name to search for. - * Output: - * prev HashNode ** If prev!=NULL then the pointer to the node - * preceding the located node in the list will - * be recorded in *prev. This will be NULL either - * if the name is not found or the located node is - * at the head of the list of entries. - * return HashNode * The located hash-table node, or NULL if not - * found. - */ -static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, - const char *name, HashNode **prev) -{ - HashNode *last; /* The previously searched node */ - HashNode *node; /* The node that is being searched */ -/* - * Search the list for a node containing the specified name. - */ - for(last=NULL, node=bucket->head; - node && hash->keycmp(node->symbol.name, name)!=0; - last = node, node=node->next) - ; - if(prev) - *prev = node ? last : NULL; - return node; -} - -/*....................................................................... - * When hash->case_sensitive is zero this function is called - * in place of strcmp(). In such cases the hash-table names are stored - * as lower-case versions of the original strings so this function - * performs the comparison against lower-case copies of the characters - * of the string being compared. - * - * Input: - * node_key const char * The lower-case hash-node key being compared - * against. - * look_key const char * The lookup key. - * Output: - * return int <0 if node_key < look_key. - * 0 if node_key == look_key. - * >0 if node_key > look_key. - */ -static int _ht_lower_strcmp(const char *node_key, const char *look_key) -{ - int cn; /* The latest character from node_key[] */ - int cl; /* The latest character from look_key[] */ - do { - cn = *node_key++; - cl = *look_key++; - } while(cn && cn==tolower(cl)); - return cn - tolower(cl); -} - -/*....................................................................... - * This is a wrapper around strcmp for comparing hash-keys in a case - * sensitive manner. The reason for having this wrapper, instead of using - * strcmp() directly, is to make some C++ compilers happy. The problem - * is that when the library is compiled with a C++ compiler, the - * declaration of the comparison function is a C++ declaration, whereas - * strcmp() is a pure C function and thus although it appears to have the - * same declaration, the compiler disagrees. - * - * Input: - * node_key char * The lower-case hash-node key being compared against. - * look_key char * The lookup key. - * Output: - * return int <0 if node_key < look_key. - * 0 if node_key == look_key. - * >0 if node_key > look_key. - */ -static int _ht_strcmp(const char *node_key, const char *look_key) -{ - return strcmp(node_key, look_key); -} - -/*....................................................................... - * Empty a hash-table by deleting all of its entries. - * - * Input: - * hash HashTable * The hash table to clear. - * Output: - * return int 0 - OK. - * 1 - Invalid arguments. - */ -int _clear_HashTable(HashTable *hash) -{ - int i; -/* - * Check the arguments. - */ - if(!hash) - return 1; -/* - * Clear the contents of the bucket array. - */ - for(i=0; i<hash->size; i++) { - HashBucket *bucket = hash->bucket + i; -/* - * Delete the list of active hash nodes from the bucket. - */ - HashNode *node = bucket->head; - while(node) { - HashNode *next = node->next; - (void) _del_HashNode(hash, node); - node = next; - }; -/* - * Mark the bucket as empty. - */ - bucket->head = NULL; - bucket->count = 0; - }; - return 0; -} - -/*....................................................................... - * Execute a given function on each entry of a hash table, returning - * before completion if the the specified function returns non-zero. - * - * Input: - * hash HashTable * The table to traverse. - * scan_fn HASH_SCAN_FN(*) The function to call. - * context void * Optional caller-specific context data - * to be passed to scan_fn(). - * Output: - * return int 0 - OK. - * 1 - Either the arguments were invalid, or - * scan_fn() returned non-zero at some - * point. - */ -int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context) -{ - int i; -/* - * Check the arguments. - */ - if(!hash || !scan_fn) - return 1; -/* - * Iterate through the buckets of the table. - */ - for(i=0; i<hash->size; i++) { - HashBucket *bucket = hash->bucket + i; - HashNode *node; -/* - * Iterate through the list of symbols that fall into bucket i, - * passing each one to the caller-specified function. - */ - for(node=bucket->head; node; node=node->next) { - if(scan_fn(&node->symbol, context)) - return 1; - }; - }; - return 0; -} |