2 * Copyright 1993, 1995 Christopher Seiwald.
4 * This file is part of Jam - see jam.c for Copyright information.
14 * hash.c - simple in-memory hashing routines
18 * hashinit() - initialize a hash table, returning a handle
19 * hashitem() - find a record in the table, and optionally enter a new one
20 * hashdone() - free a hash table, given its handle
24 * hashrehash() - resize and rebuild hp->tab, the hash table
26 * 4/29/93 - ensure ITEM's are aligned
30 #define HASH_DEBUG_PROFILE 1
33 /* Header attached to all data items entered into a hash table. */
50 * the hash table, just an array of item pointers
57 int bloat; /* tab.nel / items.nel */
58 int inel; /* initial number of elements */
61 * the array of records, maintained by these routines
62 * essentially a microallocator
65 int more; /* how many more ITEMs fit in lists[ list ] */
66 ITEM *free; /* free list of items */
67 char *next; /* where to put more ITEMs in lists[ list ] */
68 int size; /* sizeof( ITEM ) + aligned datalen */
69 int nel; /* total ITEMs held by all lists[] */
70 int list; /* index into lists[] */
73 int nel; /* total ITEMs held by this list */
74 char *base; /* base of ITEMs array */
78 const char * name; /* just for hashstats() */
81 static void hashrehash( struct hash *hp );
82 static void hashstat( struct hash *hp );
84 static unsigned int hash_keyval( OBJECT * key )
86 return object_hash( key );
89 #define hash_bucket(hp,keyval) ((hp)->tab.base + ( (keyval) % (hp)->tab.nel ))
91 #define hash_data_key(data) (*(OBJECT * *)(data))
92 #define hash_item_data(item) ((HASHDATA *)((char *)item + sizeof(struct hashhdr)))
93 #define hash_item_key(item) (hash_data_key(hash_item_data(item)))
95 /* Find the hash item for the given data. Returns pointer to the
96 item and if given a pointer to the item before the found item.
97 If it's the first item in a bucket, there is no previous item,
98 and zero is returned for the previous item instead.
100 static ITEM * hash_search(
106 ITEM * i = *hash_bucket(hp,keyval);
109 for ( ; i; i = i->hdr.next )
111 if ( object_equal( hash_item_key( i ), keydata ) )
126 * hash_insert() - insert a record in the table or return the existing one
129 HASHDATA * hash_insert( struct hash * hp, OBJECT * key, int * found )
132 unsigned int keyval = hash_keyval( key );
134 #ifdef HASH_DEBUG_PROFILE
135 profile_frame prof[1];
137 profile_enter( 0, prof );
140 if ( !hp->items.more )
143 i = hash_search( hp, keyval, key, 0 );
150 ITEM * * base = hash_bucket( hp, keyval );
152 /* try to grab one from the free list */
153 if ( hp->items.free )
156 hp->items.free = i->hdr.next;
157 assert( hash_item_key( i ) == 0 );
161 i = (ITEM *)hp->items.next;
162 hp->items.next += hp->items.size;
170 #ifdef HASH_DEBUG_PROFILE
172 profile_exit( prof );
175 return hash_item_data( i );
179 * hash_find() - find a record in the table or NULL if none exists
182 HASHDATA * hash_find( struct hash *hp, OBJECT *key )
185 unsigned int keyval = hash_keyval(key);
187 #ifdef HASH_DEBUG_PROFILE
188 profile_frame prof[1];
190 profile_enter( 0, prof );
193 if ( !hp->items.nel )
195 #ifdef HASH_DEBUG_PROFILE
197 profile_exit( prof );
202 i = hash_search( hp, keyval, key, 0 );
204 #ifdef HASH_DEBUG_PROFILE
206 profile_exit( prof );
211 return hash_item_data( i );
220 * hashrehash() - resize and rebuild hp->tab, the hash table
223 static void hashrehash( register struct hash *hp )
225 int i = ++hp->items.list;
226 hp->items.more = i ? 2 * hp->items.nel : hp->inel;
227 hp->items.next = (char *)BJAM_MALLOC( hp->items.more * hp->items.size );
230 hp->items.lists[i].nel = hp->items.more;
231 hp->items.lists[i].base = hp->items.next;
232 hp->items.nel += hp->items.more;
235 BJAM_FREE( (char *)hp->tab.base );
237 hp->tab.nel = hp->items.nel * hp->bloat;
238 hp->tab.base = (ITEM **)BJAM_MALLOC( hp->tab.nel * sizeof(ITEM **) );
240 memset( (char *)hp->tab.base, '\0', hp->tab.nel * sizeof( ITEM * ) );
242 for ( i = 0; i < hp->items.list; ++i )
244 int nel = hp->items.lists[i].nel;
245 char *next = hp->items.lists[i].base;
247 for ( ; nel--; next += hp->items.size )
249 register ITEM *i = (ITEM *)next;
250 ITEM **ip = hp->tab.base + object_hash( hash_item_key( i ) ) % hp->tab.nel;
251 /* code currently assumes rehashing only when there are no free items */
252 assert( hash_item_key( i ) != 0 );
260 void hashenumerate( struct hash * hp, void (* f)( void *, void * ), void * data )
263 for ( i = 0; i <= hp->items.list; ++i )
265 char * next = hp->items.lists[i].base;
266 int nel = hp->items.lists[i].nel;
267 if ( i == hp->items.list )
268 nel -= hp->items.more;
270 for ( ; nel--; next += hp->items.size )
272 ITEM * i = (ITEM *)next;
273 if ( hash_item_key( i ) != 0 ) /* DO not enumerate freed items. */
274 f( hash_item_data( i ), data );
281 # define ALIGNED(x) ( ( x + sizeof( ITEM ) - 1 ) & ~( sizeof( ITEM ) - 1 ) )
284 * hashinit() - initialize a hash table, returning a handle
292 struct hash *hp = (struct hash *)BJAM_MALLOC( sizeof( *hp ) );
296 hp->tab.base = (ITEM **)0;
299 hp->items.size = sizeof( struct hashhdr ) + ALIGNED( datalen );
302 hp->inel = 11 /* 47 */;
308 void hashdone( struct hash * hp )
312 if ( DEBUG_MEM || DEBUG_PROFILE )
318 * hash_free() - free a hash table, given its handle
322 hash_free( struct hash * hp )
330 BJAM_FREE( (char *)hp->tab.base );
331 for ( i = 0; i <= hp->items.list; ++i )
332 BJAM_FREE( hp->items.lists[i].base );
333 BJAM_FREE( (char *)hp );
339 static void hashstat( struct hash * hp )
341 struct hashstats stats[ 1 ];
342 hashstats_init( stats );
343 hashstats_add( stats, hp );
344 hashstats_print( stats, hp->name );
347 void hashstats_init( struct hashstats * stats )
350 stats->num_items = 0;
352 stats->item_size = 0;
356 void hashstats_add( struct hashstats * stats, struct hash * hp )
360 ITEM * * tab = hp->tab.base;
361 int nel = hp->tab.nel;
366 for ( i = 0; i < nel; ++i )
370 for ( item = tab[ i ]; item != 0; item = item->hdr.next )
378 stats->count += count;
380 stats->num_items += hp->items.nel;
381 stats->tab_size += hp->tab.nel;
382 stats->item_size = hp->items.size;
386 void hashstats_print( struct hashstats * stats, const char * name )
388 printf( "%s table: %d+%d+%d (%dK+%luK) items+table+hash, %f density\n",
393 stats->num_items * stats->item_size / 1024,
394 (long unsigned)stats->tab_size * sizeof( ITEM ** ) / 1024,
395 (float)stats->count / (float)stats->sets );