2 * This file contains the hashing implementation.
4 * Copyright (C) 1991 Threaded Technologies Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 1, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should receive a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 $Header: /usr/user/dennis_glatting/ObjC/c-runtime/hash/RCS/hash.c,v 0.11 1992/01/03 02:55:03 dennisg Exp dennisg $
21 $Date: 1992/01/03 02:55:03 $
23 * Revision 0.11 1992/01/03 02:55:03 dennisg
24 * modified to handle new initialization scheme.
25 * fixed code structure.
27 * Revision 0.10 1991/12/10 12:05:28 dennisg
28 * Cleaned up file format for a distribution.
30 * Revision 0.9 1991/12/03 02:01:23 dennisg
32 * added memory allocation adjustment macro for hash size allocation.
34 * Revision 0.8 1991/11/24 01:20:02 dennisg
35 * changed shorts back to ints.
36 * the efficiency gained didn't out weight the grossness of the code.
38 * Revision 0.7 1991/11/23 22:18:29 dennisg
39 * deleted hashIndex() and moved it to hash-inline.h
40 * converted hash_value_for_key () to a inline and moved it to hash-inline.h.
42 * Revision 0.6 1991/11/21 22:27:06 dennisg
43 * changed hash value calculation.
44 * func name changed from hashValue () to hashIndex(). the
45 * func really calculated a index anyway.
46 * changed hash func impl. essentially it was calculating a hash value
47 * from a hash value. this is a implementation thing.
49 * Revision 0.5 1991/11/20 23:29:20 dennisg
50 * converted hashIndex() to a inline.
52 * Revision 0.4 1991/11/19 12:34:41 dennisg
53 * bug in hash_delete (). It was using void* to obtain nodes to
54 * pass to hash_remove (). The value passed to hash_removed () is a
55 * entry from the node structure rather than the node itself. Using
56 * void* removed compiler checking.
57 * Modified to implement cache expansion.
59 * Revision 0.3 1991/11/07 23:23:40 dennisg
60 * implemented hash table expansion as suggested by rms.
62 * Revision 0.2 1991/11/07 22:30:54 dennisg
65 * Revision 0.1 1991/10/24 00:45:39 dennisg
66 * Initial check in. Preliminary development stage.
74 #include <objc-protoP.h>
82 /* These two macros determine
83 when a hash table is full and
84 by how much it should be
85 expanded respectively.
89 #define FULLNESS(cache) \
90 ((((cache)->sizeOfHash * 75 ) / 100) <= (cache)->entriesInHash)
91 #define EXPANSION(cache) \
92 ((cache)->sizeOfHash * 2 )
95 hash_new (u_int sizeOfHash, HashFunc aHashFunc, CompareFunc aCompareFunc) {
100 /* Pass me a value greater
101 than 0 and a power of 2. */
103 assert( !(sizeOfHash & (sizeOfHash - 1)));
105 /* Allocate the cache
106 structure. calloc () insures
107 its initialization for
109 retCache = calloc (1, sizeof (Cache));
112 /* Allocate the array of
113 buckets for the cache.
114 calloc() initializes all of
115 the pointers to NULL. */
116 retCache->theNodeTable = calloc (sizeOfHash, sizeof (CacheNode_t));
117 assert(retCache->theNodeTable);
119 retCache->sizeOfHash = sizeOfHash;
121 /* This should work for all
122 processor architectures? */
123 retCache->mask = ( sizeOfHash - 1 );
125 /* Store the hashing function
128 retCache->hashFunc = aHashFunc;
130 /* Store the function that
131 compares hash keys to
132 determine if they are
134 retCache->compareFunc = aCompareFunc;
141 hash_delete (Cache_t theCache) {
146 /* Purge all key/value pairs
148 while (aNode = hash_next (theCache, NULL))
149 hash_remove (theCache, aNode->theKey);
151 /* Release the array of nodes
152 and the cache itself. */
153 free (theCache->theNodeTable);
159 hash_add (Cache_t* theCache, void* aKey, void* aValue) {
161 u_int indx = (* (*theCache)->hashFunc)(*theCache, aKey);
162 CacheNode_t aCacheNode = calloc (1, sizeof (CacheNode));
167 /* Initialize the new node. */
168 aCacheNode->theKey = aKey;
169 aCacheNode->theValue = aValue;
170 aCacheNode->nextNode = (* (*theCache)->theNodeTable)[ indx ];
174 Check the list for another
177 { CacheNode_t checkHashNode = (* (*theCache)->theNodeTable)[ indx ];
179 while (checkHashNode) {
181 assert(checkHashNode->theKey != aKey);
182 checkHashNode = checkHashNode->nextNode;
187 /* Install the node as the
188 first element on the list. */
189 (* (*theCache)->theNodeTable)[ indx ] = aCacheNode;
191 /* Bump the number of entries
193 ++ (*theCache)->entriesInHash;
195 /* Check the hash table's
196 fullness. We're going
197 to expand if it is above
198 the fullness level. */
199 if (FULLNESS (*theCache)) {
200 /* The hash table has reached
201 its fullness level. Time to
204 I'm using a slow method
205 here but is built on other
206 primitive functions thereby
209 CacheNode_t aNode = NULL;
210 Cache_t newCache = hash_new (EXPANSION (*theCache),
211 (*theCache)->hashFunc,
212 (*theCache)->compareFunc);
214 DEBUG_PRINTF (stderr, "Expanding cache %#x from %d to %d\n",
215 *theCache, (*theCache)->sizeOfHash, newCache->sizeOfHash);
217 /* Copy the nodes from the
218 first hash table to the
220 while (aNode = hash_next (*theCache, aNode))
221 hash_add (&newCache, aNode->theKey, aNode->theValue);
223 /* Trash the old cache. */
224 hash_delete (*theCache);
226 /* Return a pointer to the new
228 *theCache = newCache;
234 hash_remove (Cache_t theCache, void* aKey) {
236 u_int indx = (*theCache->hashFunc)(theCache, aKey);
237 CacheNode_t aCacheNode = (*theCache->theNodeTable)[ indx ];
240 /* We assume there is an entry
241 in the table. Error if it
245 /* Special case. First element
246 is the key/value pair to be
248 if ((*theCache->compareFunc)(aCacheNode->theKey, aKey)) {
249 (*theCache->theNodeTable)[ indx ] = aCacheNode->nextNode;
252 /* Otherwise, find the hash
254 CacheNode_t prevHashNode = aCacheNode;
259 if ((*theCache->compareFunc)(aCacheNode->theKey, aKey)) {
260 prevHashNode->nextNode = aCacheNode->nextNode, removed = YES;
263 prevHashNode = aCacheNode, aCacheNode = aCacheNode->nextNode;
264 } while (!removed && aCacheNode);
268 /* Decrement the number of
269 entries in the hash table. */
270 --theCache->entriesInHash;
275 hash_next (Cache_t theCache, CacheNode_t aCacheNode) {
277 CacheNode_t theCacheNode = aCacheNode;
280 /* If the scan is being started
281 then reset the last node
282 visitied pointer and bucket
285 theCache->lastBucket = 0;
287 /* If there is a node visited
288 last then check for another
289 entry in the same bucket;
290 Otherwise step to the next
293 if (theCacheNode->nextNode)
294 /* There is a node which
295 follows the last node
296 returned. Step to that node
298 return theCacheNode->nextNode;
300 ++theCache->lastBucket;
302 /* If the list isn't exhausted
303 then search the buckets for
305 if (theCache->lastBucket < theCache->sizeOfHash) {
306 /* Scan the remainder of the
307 buckets looking for an entry
308 at the head of the list.
309 Return the first item
311 while (theCache->lastBucket < theCache->sizeOfHash)
312 if ((*theCache->theNodeTable)[ theCache->lastBucket ])
313 return (*theCache->theNodeTable)[ theCache->lastBucket ];
315 ++theCache->lastBucket;
317 /* No further nodes were found
318 in the hash table. */
325 /* Given key, return its
326 value. Return NULL if the
327 key/value pair isn't in
330 hash_value_for_key (Cache_t theCache, void* aKey) {
332 CacheNode_t aCacheNode =
333 (*theCache->theNodeTable)[(*theCache->hashFunc)(theCache, aKey)];
339 if ((*theCache->compareFunc)(aCacheNode->theKey, aKey))
340 retVal = aCacheNode->theValue;
342 aCacheNode = aCacheNode->nextNode;
343 } while (!retVal && aCacheNode);