2 * Copyright 1993, 1995 Christopher Seiwald.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * hash.c - simple in-memory hashing routines
12 * hashinit() - initialize a hash table, returning a handle
13 * hashitem() - find a record in the table, and optionally enter a new one
14 * hashdone() - free a hash table, given its handle
18 * hashrehash() - resize and rebuild hp->tab, the hash table
20 * 4/29/93 - ensure ITEM's are aligned
21 * 11/04/02 (seiwald) - const-ing for string literals
22 * 01/31/02 (seiwald) - keyval now unsigned (cray-ziness)
28 /* Header attached to all data items entered into a hash table. */
32 unsigned int keyval
; /* for quick comparisons */
35 /* This structure overlays the one handed to hashenter(). */
36 /* It's actual size is given to hashinit(). */
40 /* rest of user data */
53 * the hash table, just an array of item pointers
60 int bloat
; /* tab.nel / items.nel */
61 int inel
; /* initial number of elements */
64 * the array of records, maintained by these routines
65 * essentially a microallocator
68 int more
; /* how many more ITEMs fit in lists[ list ] */
69 char *next
; /* where to put more ITEMs in lists[ list ] */
70 int datalen
; /* length of records in this hash table */
71 int size
; /* sizeof( ITEM ) + aligned datalen */
72 int nel
; /* total ITEMs held by all lists[] */
73 int list
; /* index into lists[] */
76 int nel
; /* total ITEMs held by this list */
77 char *base
; /* base of ITEMs array */
81 const char *name
; /* just for hashstats() */
84 static void hashrehash( struct hash
*hp
);
85 static void hashstat( struct hash
*hp
);
88 * hashitem() - find a record in the table, and optionally enter a new one
93 register struct hash
*hp
,
99 unsigned char *b
= (unsigned char *)(*data
)->key
;
102 if( enter
&& !hp
->items
.more
)
105 if( !enter
&& !hp
->items
.nel
)
111 keyval
= keyval
* 2147059363 + *b
++;
113 base
= hp
->tab
.base
+ ( keyval
% hp
->tab
.nel
);
115 for( i
= *base
; i
; i
= i
->hdr
.next
)
116 if( keyval
== i
->hdr
.keyval
&&
117 !strcmp( i
->data
.key
, (*data
)->key
) )
125 i
= (ITEM
*)hp
->items
.next
;
126 hp
->items
.next
+= hp
->items
.size
;
128 memcpy( (char *)&i
->data
, (char *)*data
, hp
->items
.datalen
);
129 i
->hdr
.keyval
= keyval
;
139 * hashrehash() - resize and rebuild hp->tab, the hash table
142 static void hashrehash( register struct hash
*hp
)
144 int i
= ++hp
->items
.list
;
146 hp
->items
.more
= i
? 2 * hp
->items
.nel
: hp
->inel
;
147 hp
->items
.next
= (char *)malloc( hp
->items
.more
* hp
->items
.size
);
149 hp
->items
.lists
[i
].nel
= hp
->items
.more
;
150 hp
->items
.lists
[i
].base
= hp
->items
.next
;
151 hp
->items
.nel
+= hp
->items
.more
;
154 free( (char *)hp
->tab
.base
);
156 hp
->tab
.nel
= hp
->items
.nel
* hp
->bloat
;
157 hp
->tab
.base
= (ITEM
**)malloc( hp
->tab
.nel
* sizeof(ITEM
**) );
159 memset( (char *)hp
->tab
.base
, '\0', hp
->tab
.nel
* sizeof( ITEM
* ) );
161 for( i
= 0; i
< hp
->items
.list
; i
++ )
163 int nel
= hp
->items
.lists
[i
].nel
;
164 char *next
= hp
->items
.lists
[i
].base
;
166 for( ; nel
--; next
+= hp
->items
.size
)
168 register ITEM
*i
= (ITEM
*)next
;
169 ITEM
**ip
= hp
->tab
.base
+ i
->hdr
.keyval
% hp
->tab
.nel
;
179 # define ALIGNED(x) ( ( x + sizeof( ITEM ) - 1 ) & ~( sizeof( ITEM ) - 1 ) )
182 * hashinit() - initialize a hash table, returning a handle
190 struct hash
*hp
= (struct hash
*)malloc( sizeof( *hp
) );
194 hp
->tab
.base
= (ITEM
**)0;
196 hp
->items
.datalen
= datalen
;
197 hp
->items
.size
= sizeof( struct hashhdr
) + ALIGNED( datalen
);
207 * hashdone() - free a hash table, given its handle
211 hashdone( struct hash
*hp
)
222 free( (char *)hp
->tab
.base
);
223 for( i
= 0; i
<= hp
->items
.list
; i
++ )
224 free( hp
->items
.lists
[i
].base
);
231 hashstat( struct hash
*hp
)
233 ITEM
**tab
= hp
->tab
.base
;
234 int nel
= hp
->tab
.nel
;
237 int run
= ( tab
[ nel
- 1 ] != (ITEM
*)0 );
240 for( i
= nel
; i
> 0; i
-- )
242 if( here
= ( *tab
++ != (ITEM
*)0 ) )
249 printf( "%s table: %d+%d+%d (%dK+%dK) items+table+hash, %f density\n",
254 hp
->items
.nel
* hp
->items
.size
/ 1024,
255 hp
->tab
.nel
* sizeof( ITEM
** ) / 1024,
256 (float)count
/ (float)sets
);