Commit 60cabde6 by Dennis Glatting

implemented hash table expansion as suggested by rms.

From-SVN: r59
parent eed5ef66
...@@ -16,10 +16,13 @@ ...@@ -16,10 +16,13 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *
$Header: /usr/user/dennis_glatting/ObjC/c-runtime/lib/RCS/hash.c,v 0.1 1991/10/24 00:45:39 dennisg Exp dennisg $ $Header: /usr/user/dennis_glatting/ObjC/c-runtime/lib/RCS/hash.c,v 0.2 1991/11/07 22:30:54 dennisg Exp dennisg $
$Author: dennisg $ $Author: dennisg $
$Date: 1991/10/24 00:45:39 $ $Date: 1991/11/07 22:30:54 $
$Log: hash.c,v $ $Log: hash.c,v $
* Revision 0.2 1991/11/07 22:30:54 dennisg
* added copyleft
*
* Revision 0.1 1991/10/24 00:45:39 dennisg * Revision 0.1 1991/10/24 00:45:39 dennisg
* Initial check in. Preliminary development stage. * Initial check in. Preliminary development stage.
* *
...@@ -33,18 +36,27 @@ ...@@ -33,18 +36,27 @@
#include <libc.h> #include <libc.h>
#include <math.h> #include <math.h>
/* These two macros determine
when a hash table is full and
by how much it should be
expanded respectively.
These equations are
percentages. */
#define FULLNESS ( 100 / 75 )
#define EXPANSION ( 135 / 100 )
/* Local forward decl. */ /* Local forward decl. */
u_int hashValue( Cache_t, void* ); u_int hashValue( Cache_t, void* );
Cache_t hash_new( u_int numberOfBuckets ) { Cache_t hash_new( u_int sizeOfHash ) {
Cache_t retCache; Cache_t retCache;
int i; int i;
assert( numberOfBuckets ); assert( sizeOfHash );
/* Allocate the cache /* Allocate the cache
structure. calloc() insures structure. calloc() insures
...@@ -57,16 +69,16 @@ Cache_t hash_new( u_int numberOfBuckets ) { ...@@ -57,16 +69,16 @@ Cache_t hash_new( u_int numberOfBuckets ) {
buckets for the cache. buckets for the cache.
calloc() initializes all of calloc() initializes all of
the pointers to NULL. */ the pointers to NULL. */
retCache->theNodeTable = calloc( numberOfBuckets, sizeof( CacheNode_t )); retCache->theNodeTable = calloc( sizeOfHash, sizeof( CacheNode_t ));
assert( retCache->theNodeTable ); assert( retCache->theNodeTable );
retCache->numberOfBuckets = numberOfBuckets; retCache->sizeOfHash = sizeOfHash;
/* Calculate the number of /* Calculate the number of
bits required to represent bits required to represent
the hash mask. */ the hash mask. */
retCache->numberOfMaskBits = retCache->numberOfMaskBits =
ceil( log( retCache->numberOfBuckets ) / log( 2 )); ceil( log( retCache->sizeOfHash ) / log( 2 ));
/* Form a bit mask for the /* Form a bit mask for the
hash. */ hash. */
...@@ -97,9 +109,9 @@ void hash_delete( Cache_t theCache ) { ...@@ -97,9 +109,9 @@ void hash_delete( Cache_t theCache ) {
} }
void hash_add( Cache_t theCache, void* aKey, void* aValue ) { void hash_add( Cache_t* theCache, void* aKey, void* aValue ) {
u_int indx = hashValue( theCache, aKey ); u_int indx = hashValue( *theCache, aKey );
CacheNode_t aCacheNode = calloc( 1, sizeof( CacheNode )); CacheNode_t aCacheNode = calloc( 1, sizeof( CacheNode ));
...@@ -108,14 +120,14 @@ void hash_add( Cache_t theCache, void* aKey, void* aValue ) { ...@@ -108,14 +120,14 @@ void hash_add( Cache_t theCache, void* aKey, void* aValue ) {
/* Initialize the new node. */ /* Initialize the new node. */
aCacheNode->theKey = aKey; aCacheNode->theKey = aKey;
aCacheNode->theValue = aValue; aCacheNode->theValue = aValue;
aCacheNode->nextNode = ( *theCache->theNodeTable )[ indx ]; aCacheNode->nextNode = ( *( **theCache ).theNodeTable )[ indx ];
/* Debugging. /* Debugging.
Check the list for another Check the list for another
key. */ key. */
#ifdef DEBUG #ifdef DEBUG
{ CacheNode_t checkHashNode = ( *theCache->theNodeTable )[ indx ]; { CacheNode_t checkHashNode = ( *( **theCache ).theNodeTable )[ indx ];
while( checkHashNode ) { while( checkHashNode ) {
...@@ -123,12 +135,46 @@ void hash_add( Cache_t theCache, void* aKey, void* aValue ) { ...@@ -123,12 +135,46 @@ void hash_add( Cache_t theCache, void* aKey, void* aValue ) {
checkHashNode = checkHashNode->nextNode; checkHashNode = checkHashNode->nextNode;
} }
} }
#endif
/* Install the node as the /* Install the node as the
first element on the list. */ first element on the list. */
( *theCache->theNodeTable )[ indx ] = aCacheNode; ( *( **theCache ).theNodeTable )[ indx ] = aCacheNode;
#endif /* Bump the number of entries
in the cache. */
++( **theCache ).entriesInHash;
/* Check the hash table's
fullness. We're going
to expand if it is above
the fullness level. */
if(( **theCache ).entriesInHash * FULLNESS ) {
/* The hash table has reached
its fullness level. Time to
expand it.
I'm using a slow method
here but is built on other
primitive functions thereby
increasing its
correctness. */
Cache_t newCache = hash_new(( **theCache ).sizeOfHash * EXPANSION );
CacheNode_t aNode = NULL;
/* Copy the nodes from the
first hash table to the
new one. */
while( aNode = hash_next( *theCache, aNode ))
hash_add( &newCache, aNode->theKey, aNode->theValue );
/* Trash the old cache. */
hash_delete( *theCache );
/* Return a pointer to the new
hash table. */
*theCache = newCache;
}
} }
...@@ -165,6 +211,10 @@ void hash_remove( Cache_t theCache, void* aKey ) { ...@@ -165,6 +211,10 @@ void hash_remove( Cache_t theCache, void* aKey ) {
} while( !removed && aCacheNode ); } while( !removed && aCacheNode );
assert( removed ); assert( removed );
} }
/* Decrement the number of
entries in the hash table. */
--theCache->entriesInHash;
} }
...@@ -220,13 +270,13 @@ CacheNode_t hash_next( Cache_t theCache, CacheNode_t aCacheNode ) { ...@@ -220,13 +270,13 @@ CacheNode_t hash_next( Cache_t theCache, CacheNode_t aCacheNode ) {
/* If the list isn't exhausted /* If the list isn't exhausted
then search the buckets for then search the buckets for
other nodes. */ other nodes. */
if( theCache->lastBucket < theCache->numberOfBuckets ) { if( theCache->lastBucket < theCache->sizeOfHash ) {
/* Scan the remainder of the /* Scan the remainder of the
buckets looking for an entry buckets looking for an entry
at the head of the list. at the head of the list.
Return the first item Return the first item
found. */ found. */
while( theCache->lastBucket < theCache->numberOfBuckets ) while( theCache->lastBucket < theCache->sizeOfHash )
if(( *theCache->theNodeTable )[ theCache->lastBucket ]) if(( *theCache->theNodeTable )[ theCache->lastBucket ])
return ( *theCache->theNodeTable )[ theCache->lastBucket ]; return ( *theCache->theNodeTable )[ theCache->lastBucket ];
else else
...@@ -250,6 +300,6 @@ u_int hashValue( Cache_t theCache, void* aKey ) { ...@@ -250,6 +300,6 @@ u_int hashValue( Cache_t theCache, void* aKey ) {
for( i = 0; i < ( sizeof( aKey ) * 8 ); i += theCache->numberOfMaskBits ) for( i = 0; i < ( sizeof( aKey ) * 8 ); i += theCache->numberOfMaskBits )
hash ^= (( u_int )aKey ) >> i ; hash ^= (( u_int )aKey ) >> i ;
return ( hash & theCache->mask ) % theCache->numberOfBuckets; return ( hash & theCache->mask ) % theCache->sizeOfHash;
} }
...@@ -21,10 +21,13 @@ ...@@ -21,10 +21,13 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *
$Header: /usr/user/dennis_glatting/ObjC/c-runtime/lib/RCS/hash.h,v 0.1 1991/10/24 00:45:39 dennisg Exp dennisg $ $Header: /usr/user/dennis_glatting/ObjC/c-runtime/lib/RCS/hash.h,v 0.2 1991/11/07 22:30:54 dennisg Exp dennisg $
$Author: dennisg $ $Author: dennisg $
$Date: 1991/10/24 00:45:39 $ $Date: 1991/11/07 22:30:54 $
$Log: hash.h,v $ $Log: hash.h,v $
* Revision 0.2 1991/11/07 22:30:54 dennisg
* added copyleft
*
* Revision 0.1 1991/10/24 00:45:39 dennisg * Revision 0.1 1991/10/24 00:45:39 dennisg
* Initial check in. Preliminary development stage. * Initial check in. Preliminary development stage.
* *
...@@ -79,15 +82,24 @@ typedef struct cache { ...@@ -79,15 +82,24 @@ typedef struct cache {
*/ */
CacheNode_t (* theNodeTable )[]; /* Pointer to an array of CacheNode_t (* theNodeTable )[]; /* Pointer to an array of
hash nodes. */ hash nodes. */
u_int numberOfBuckets, /* Number of buckets /*
* Variables used to track the size of the hash
* table so to determine when to resize it.
*/
u_int sizeOfHash, /* Number of buckets
allocated for the hash allocated for the hash
table (number of array table (number of array
entries allocated for entries allocated for
"theCache"). */ "theNodeTable"). */
mask, /* Mask used when computing entriesInHash; /* Current number of entries
a hash value. The number in ther hash table. */
of bits set in the mask /*
is contained in the next * Variables used to compute hash
* values.
*/
u_int mask, /* The number of bits set
in the mask that is
contained in the next
member variable. */ member variable. */
numberOfMaskBits; /* Number of bits used for numberOfMaskBits; /* Number of bits used for
the mask. Useful for the mask. Useful for
...@@ -110,16 +122,20 @@ typedef struct cache { ...@@ -110,16 +122,20 @@ typedef struct cache {
size taken as a parameter. size taken as a parameter.
A value of 0 is not A value of 0 is not
allowed. */ allowed. */
Cache_t hash_new( u_int numberOfBuckets ); Cache_t hash_new( u_int sizeOfHash );
/* Deallocate all of the /* Deallocate all of the
hash nodes and the cache hash nodes and the cache
itself. */ itself. */
void hash_delete( Cache_t theCache ); void hash_delete( Cache_t theCache );
/* Add the key/value pair /* Add the key/value pair
to the hash table. assert() to the hash table. If the
if the key is already in hash table reaches a
the hash. */ level of fullnes then
void hash_add( Cache_t theCache, void* aKey, void* aValue ); it will be resized.
assert() if the key is
already in the hash. */
void hash_add( Cache_t* theCache, void* aKey, void* aValue );
/* Remove the key/value pair /* Remove the key/value pair
from the hash table. from the hash table.
assert() if the key isn't assert() if the key isn't
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment