📄 hash.c
字号:
#ifndef lint#ifdef sccsstatic char sccsid[] = "@(#)hash.c 1.1 92/07/30";#endif#endif/* * Copyright (c) 1985, 1988 by Sun Microsystems, Inc. *//* * This file contains a bunch of routines that implement a general purpose * hash table. */#include <sunwindow/sun.h> /* Get things like TRUE and FALSE defined */#include <sunwindow/hash.h> /* Include hash table exported routines *//* * Internally used routines: */void free_data();void hash_free_memory();long *get_data();char *hash_get_memory();char hash_int_equal();int hash_int_hash();int hash_int_insert();void hash_initialize();void hash_resize();extern char *valloc();/* * hash_create(Size, Key_Empty, Key_Equal, Key_Hash, Key_Insert, Value_Empty, * Value_Insert, 7) will create and return a hash table using the parameters. * Due to the large number of arguments, the last argument must be the number * 7 so that a quick check can be made to make sure that they are all there. * All of the arguments except the last one can be NULL'ed out. */Hashhash_create(size, key_empty, key_equal, key_hash, key_insert, value_empty, value_insert, check) register int size; /* Number of initial slots */ int key_empty; /* Empty key value */ char (*key_equal)(); /* Key equality routine */ int (*key_hash)(); /* Key hash function */ int (*key_insert)();/* Key insertion routine */ int value_empty; /* Empty value */ int (*value_insert)();/* Value insertion routine */ int check; /* Argument count check */{ register Hash hash; /* New hash table */ if (check != 7){ (void)fprintf(stderr, "HASH_CREATE: Wrong number of arguments\n"); exit(1); } hash = (Hash)hash_get_memory(sizeof *hash); hash->count = 0; hash->key_empty = key_empty; hash->key_equal = (key_equal != NULL) ? key_equal : hash_int_equal; hash->key_hash = (key_hash != NULL) ? key_hash : hash_int_hash; hash->key_insert = (key_insert != NULL) ? key_insert : hash_int_insert; hash->value_empty = value_empty; hash->value_insert = (value_insert != NULL) ? value_insert : hash_int_insert; hash_initialize(hash, size); return hash;}/* * hash_destroy(Hash, Key_Destroy, Value_Destroy) will deallocate the storage * associated with Hash. Each key-value pair in Hash will be destroyed by * calling Key_Destroy(Key) and Value_Destroy(Value). If Key_Destroy is NULL, * it will not be called. Likewise, if Value_Destroy is NULL, it will not * be called. */voidhash_destroy(hash, key_destroy, value_destroy) Hash hash; /* Hash table to destroy */ void (*key_destroy)(); /* Key destroy routine */ void (*value_destroy)(); /* Value destroy routine */{ register int count; /* Number of remaining slots */ register long key_empty; /* Empty key */ register long key; /* Current key */ register long *keys; /* Hash keys */ register long slots; /* Number of slots */ register long *values; /* Hash values */ key_empty = hash->key_empty; keys = hash->keys; slots = hash->slots; values = hash->values; if ((key_destroy != NULL) || (value_destroy != NULL)){ for (count = slots; count > 0; count--){ key = *keys++; if (key != key_empty){ if (key_destroy != NULL) key_destroy(key); if (value_destroy != NULL) value_destroy(*values); } values++; } } free_data(hash->keys, (int)slots); free_data(hash->values, (int)slots); hash_free_memory((char *)hash, sizeof *hash);}/* * hash_find(Hash, Key, &Value)=>{True,False} will lookup Key in Hash. If Key * is found in Hash, True will be returned and the associated value will be * stored into Value, provided Value is non-Null. Otherwise, False will be * returned. */Boolhash_find(hash, key, value) register Hash hash; /* Hash table */ long key; /* Key to lookup */ long *value; /* Place to store value */{ int index; /* Index into table */ index = hash_index(hash, key); if (hash->keys[index] == hash->key_empty) return False; if (value != NULL) *value = hash->values[index]; return True;}/* * hash_get(Hash, Key)=>Value will lookup the value for Key in Hash. If Key * is not in Hash, a fatal error occurs. */longhash_get(hash, key) register Hash hash; /* Hash table */ long key; /* Key to lookup */{ int index; /* Index into table */ index = hash_index(hash, key); if (hash->keys[index] == hash->key_empty){ (void)fprintf(stderr, "HASH_GET:Could not find key (%x) in table (%x)\n", key, hash); exit(1); /*NOTREACHED*/ } else return hash->values[index];}/* * hash_insert(Hash, Key, Value)=>{True,False} will insert Value into Hash * under Key. If Key is already in Hash, False will be returned and the * previous value will not be changed. Otherwise, True will be returned. *//*VARARGS1*/Boolhash_insert(hash, key, value) Hash hash; /* Hash table */ long key; /* Key to insert under */ long value; /* Value to insert */{ register int index; /* Index into hash table */ if (hash->count == hash->limit) hash_resize(hash); index = hash_index(hash, key); if (hash->keys[index] == hash->key_empty){ hash->keys[index] = hash->key_insert(key); hash->values[index] = hash->value_insert(value); hash->count++; return True; } else return False;}/* * hash_lookup(Hash, Key)=>value will lookup Key in Hash. If Key is not * in Hash, Empty_Value will be returned. *//*VARARGS1*/longhash_lookup(hash, key) Hash hash; /* Hash table */ long key; /* Key to lookup */{ return hash->values[hash_index(hash, key)];}/* * hash_replace(Hash, Key, Value)=>{True,False} will insert Value into Hash * under Key. If Key was already in Hash, True will be returned and the * previous value will be replaced. Otherwise, False will be returned and * the Value will be inserted under Key. */Boolhash_replace(hash, key, value) register Hash hash; /* Hash table */ long key; /* Key to insert under */ long value; /* Value to insert */{ register int index; /* Index into hash table */ if (hash->count == hash->limit) hash_resize(hash); index = hash_index(hash, key); if (hash->keys[index] == hash->key_empty){ hash->keys[index] = hash->key_insert(key); hash->values[index] = hash->value_insert(value); hash->count++; return False; } hash->values[index] = hash->value_insert(value); return True;}/* * hash_scan(Hash, Routine, Data) will scan the entire contents of the hash * table calling Routine(Key, Value, Data) for each key-value pair in Hash. * The sum of the values returned by routine will be returned. */inthash_scan(hash, routine, data) Hash hash; /* Hash table */ register int (*routine)(); /* Routine to scan with */ register long data; /* Data value */{ register int count; /* Remaining slots to scan */ register int sum; /* Sum of returned values */ register long key_empty; /* Empty key value */ register long key; /* Current key */ register long *keys; /* Hash table keys */ register long *values; /* Hash table values */ sum = 0; key_empty = hash->key_empty; keys = hash->keys; values = hash->values; for (count = hash->slots; count > 0; count--){ key = *keys++; if (key != key_empty) sum += routine(key, *values, data); values++; } return sum;}/* * hash_show(Hash) will show the contents of Hash on the console. This * routine is used for testing and debugging purposes only. */voidhash_show(hash) register Hash hash; /* Hash table */{ int index; /* Index into table */ register long *keys; /* Keys array */ int slots; /* Slots in table */ register long *values;/* Values array */ (void)printf("Hash:%x Count:%d Limit:%d Slots:%d\n", hash, hash->count, hash->limit, hash->slots); (void)printf("Key_Empty:%d Key_Equal:%x Key_Hash:%x Key_Insert:%x\n", hash->key_empty, hash->key_equal, hash->key_hash, hash->key_insert); (void)printf("Value_Empty:%d Value_Insert:%d Keys:%x Values:%x\n", hash->value_empty, hash->value_insert, hash->keys, hash->values); keys = hash->keys; slots = hash->slots; values = hash->values; for (index = 0; index < slots; index++) (void)printf("[%d] %d:%d\n", index, *keys++, *values++);}/* * hash_size(Hash) returns the number of entries in Hash. */inthash_size(hash) Hash hash; /* Hash table to use */{ return hash->count;}/*****************************************************************//* Internal routines: *//*****************************************************************//* * hash_index(Hash, Key) will compute the index into Hash where Key should * go (if it is not already there.) */static inthash_index(hash, key) register Hash hash; /* Hash table */ long key; /* Key to use */{ register unsigned int index; /* Index into table */ long key_empty; /* Empty key */ char (*key_equal)(); /* Key equality function */ long key_temp; /* Temporary key */ register long *keys; /* Hash table keys */ int slots; /* Number of slots in table */ key_empty = hash->key_empty; key_equal = hash->key_equal; keys = hash->keys; slots = hash->slots; index = hash->key_hash(key, slots); if (index >= slots) index %= slots; while (True){ key_temp = keys[index]; if (key_temp == key_empty) return index; if (key_equal(key, key_temp)) return index; if (++index >= slots) index = 0; }}/* * hash_initialize(Hash, Size) will initialize Hash to contain Size table * entries. */static voidhash_initialize(hash, size) register Hash hash; /* Hash table */ register int size; /* Number of slots */{ hash->keys = get_data(size, (int)hash->key_empty); hash->values = get_data(size, (int)hash->value_empty); hash->slots = size; hash->limit = size * 8 / 10;}/* * hash_int_equal(Int1, Int2) will return True if Int1 equals Int2. */static charhash_int_equal(int1, int2) int int1; /* First integer */ int int2; /* Second integer */{ return (int1 == int2);}/* * hash_int_hash(number) will return a hash on number. */static inthash_int_hash(number) int number; /* Number to hash */{ return number;}/* * hash_int_insert(number) will return a copy of number. */static inthash_int_insert(number) int number; /* Number to insert */{ return number;}/* * hash_resize(Hash) will increase the number of slots in the hash table. */static voidhash_resize(hash) register Hash hash; /* Hash table to resize. */{ int count; /* Number of entries to reinsert */ int index; /* Index into hash table */ long key; /* Current key */ long key_empty; /* Empty key value */ register long *key_pointer; /* Pointer into old keys */ long *new_keys; /* New keys */ long *new_values; /* New values */ long *old_keys; /* Old keys */ long *old_values; /* Old values */ int slots; /* Number of slots in old table */ long value; /* Current value */ register long *value_pointer; /* Pointer into old values */ key_empty = hash->key_empty; old_keys = hash->keys; old_values = hash->values; slots = hash->slots; hash_initialize(hash, slots<<1); new_keys = hash->keys; new_values = hash->values; key_pointer = old_keys; value_pointer = old_values; for (count = slots; count > 0; count--){ key = *key_pointer++; value = *value_pointer++; if (key != key_empty){ index = hash_index(hash, key); new_keys[index] = key; new_values[index] = value; } } free_data(old_keys, slots); free_data(old_values, slots);}/* * get_data(Size, Value) will get Size words of data and initialize them to * Value. */static long *get_data(size, value) register int size; /* Number of words to allocate */ register int value; /* Value to initialize to */{ long *data; /* Data array */ register long *pointer; /* Pointer into data */ data = (long *)hash_get_memory(size<<2); /* Machine dependent! */ pointer = data; while (size-- > 0) *pointer++ = value; return data;}/* * hash_get_memory(Size) will allocate Size bytes of memory. * If the memory is not available, a fatal error will occur. * * Private to defaults, hash and parse packages. * Replaces the private get_memory routines that use to be in those packages. * Introduced after 4.0Beta2 to put all defaults related storage into * separate pages to avoid thrashing through VM. * * hash_get_memory() does not support deallocation, thus hash_free_memory() * is a no-op. */static char *end_plus_one, *next_free; /* = 0 for -A-R */#ifdef HASH_ALLOC_COUNTint hash_requested_count, hash_wasted_count; /* = 0 for -A-R */#endifchar *hash_get_memory(size) int size; /* Number of bytes to allocate */{ register char *data; /* Newly allocated data */ register int page_size, size_to_valloc; /* * guarantee at least one word of usable data, * make sure align on boundary */ if (size < SMALLEST_BLK) { size = SMALLEST_BLK; } else { size = roundup(size, ALIGNSIZ); }#ifdef HASH_ALLOC_COUNT hash_requested_count += size;#endif if (end_plus_one < (next_free+size)) { /* Request too big for remaining space in current block, so * discard rest of current block and get new block. */#ifdef HASH_ALLOC_COUNT hash_wasted_count += end_plus_one - next_free;#endif page_size = getpagesize(); size_to_valloc = ((size + page_size - 1) / page_size) * page_size; /* Try to stay in page, so leave room for the malloc node * overhead if size is not too big. */ if (size_to_valloc >= size + 8) size_to_valloc -= 8; next_free = valloc(size_to_valloc); if (next_free == (char *)0) { (void)fprintf(stderr, "Could not valloc %d bytes (Out of swap space?)\n", size_to_valloc); exit(1); /*NOTREACHED*/ } end_plus_one = next_free + size_to_valloc; } data = next_free; next_free += size; return data;}/* * free_data(Data, Size) will free Size words of Data. */static voidfree_data(data, size) long *data; /* Data to free */ int size; /* Number of words to free */{ hash_free_memory((char *)data, size<<2); /* Machine dependent! */}/* * hash_free_memory(Data, Size) should free the Size bytes of Data, but due * to the quick-and-dirty nature of hash_get_memory does not. *//*ARGSUSED*/static voidhash_free_memory(data, size) char *data; /* Data to free */ int size; /* Number of bytes to free */{}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -