1 /* Hash tables for Objective C method dispatch.
2 Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
4 This file is part of GNU CC.
6 GNU CC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU CC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU CC; see the file COPYING. If not, write to
18 the Free Software Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* As a special exception, if you link this library with files
22 compiled with GCC to produce an executable, this does not cause
23 the resulting executable to be covered by the GNU General Public License.
24 This exception does not however invalidate any other reasons why
25 the executable file might be covered by the GNU General Public License. */
28 #ifndef __hash_INCLUDE_GNU
29 #define __hash_INCLUDE_GNU
33 #include <objc/objc.h>
34 #include <objc/objc-api.h>
37 * This data structure is used to hold items
38 * stored in a hash table. Each node holds
41 * Items in the cache are really of type void *.
43 typedef struct cache_node
45 struct cache_node *next; /* Pointer to next entry on the list.
46 NULL indicates end of list. */
47 const void *key; /* Key used to locate the value. Used
48 to locate value when more than one
49 key computes the same hash
51 void *value; /* Value stored for the key. */
55 * This data structure is the cache.
57 * It must be passed to all of the hashing routines
62 /* Variables used to implement the hash itself. */
63 node_ptr *node_table; /* Pointer to an array of hash nodes. */
64 /* Variables used to track the size of the hash table so to determine
66 unsigned int size; /* Number of buckets allocated for the hash table
67 (number of array entries allocated for
68 "node_table"). Must be a power of two. */
69 unsigned int used; /* Current number of entries in the hash table. */
70 unsigned int mask; /* Precomputed mask. */
72 /* Variables used to implement indexing through the hash table. */
74 unsigned int last_bucket; /* Tracks which entry in the array where
75 the last value was returned. */
77 char hashType; // 1 = string-hash, 0 = ptr-hash
80 static __inline__ unsigned int objc_call_hash(cache_ptr cache, const void *key)
82 if (cache->hashType == 1) { // string hash
83 register unsigned int ret = 0;
84 register unsigned int ctr = 0;
87 ret ^= *(char*)key++ << ctr;
88 ctr = (ctr + 1) % sizeof (void *);
91 return (ret & cache->mask);
93 else if (cache->hashType == 0) // ptr hash
94 return ((size_t)key / sizeof (void *)) & cache->mask;
98 static __inline__ int objc_call_compare(cache_ptr cache,
99 const void *k1, const void *k2)
101 if (cache->hashType == 1) { // string compare
104 else if (k1 == 0 || k2 == 0)
107 while ((*(char*)k1 == *(char*)k2) && (*(char*)k1 != '\0'))
110 return (*(char*)k1 == *(char*)k2) ? 1 : 0;
113 else if (cache->hashType == 0) // ptr compare
119 /* Two important hash tables. */
120 objc_EXPORT cache_ptr module_hash_table, class_hash_table;
122 #include <objc/objc-mem.h>
124 static __inline__ void hash_delete(cache_ptr cache);
125 static __inline__ void hash_remove(cache_ptr cache, const void *key);
126 static __inline__ unsigned int hash_ptr(cache_ptr cache, const void *key);
127 static __inline__ int compare_ptrs(register const void *k1, register const void *k2);
128 static __inline__ unsigned int hash_string(cache_ptr cache,register const void *key);
130 static __inline__ int
131 compare_strings(register const char *k1,register const char *k2);
133 /* Allocate and initialize a hash table. */
135 static __inline__ cache_ptr ptrhash_new(unsigned int size) {
136 cache_ptr hash_new (unsigned int size,
138 return hash_new(size, 0);
140 static __inline__ cache_ptr strhash_new(unsigned int size) {
141 cache_ptr hash_new (unsigned int size,
143 return hash_new(size, 1);
146 /* Deallocate all of the hash nodes and the cache itself. */
148 static __inline__ void hash_delete (cache_ptr cache) {
153 /* Purge all key/value pairs from the table. */
154 /* Step through the nodes one by one and remove every node WITHOUT
155 using hash_next. this makes hash_delete much more efficient. */
156 for (i = 0;i < cache->size;i++) {
157 if ((node = cache->node_table[i])) {
158 /* an entry in the hash table has been found, now step through the
159 nodes next in the list and free them. */
160 while ((next_node = node->next)) {
161 hash_remove (cache,node->key);
165 hash_remove (cache,node->key);
169 /* Release the array of nodes and the cache itself. */
171 GC_FREE(cache->node_table); cache->node_table = NULL;
172 GC_FREE(cache); cache = NULL;
174 objc_free(cache->node_table);
179 /* Add the key/value pair to the hash table. If the
180 hash table reaches a level of fullness then it will be resized.
182 assert if the key is already in the hash. */
184 void hash_add (cache_ptr *cachep, const void *key, void *value);
186 /* Remove the key/value pair from the hash table.
187 assert if the key isn't in the table. */
189 static __inline__ void hash_remove (cache_ptr cache, const void *key) {
190 size_t indx = objc_call_hash(cache, key);
191 //size_t indx = (*cache->hash_func)(cache, key);
192 node_ptr node = cache->node_table[indx];
195 /* We assume there is an entry in the table. Error if it is not. */
196 if (node == NULL) abort();
198 /* Special case. First element is the key/value pair to be removed. */
199 //if ((*cache->compare_func)(node->key, key)) {
200 if (objc_call_compare(cache, node->key, key)) {
201 cache->node_table[indx] = node->next;
203 GC_FREE(node); node = NULL;
209 /* Otherwise, find the hash entry. */
210 node_ptr prev = node;
214 //if ((*cache->compare_func)(node->key, key)) {
215 if (objc_call_compare(cache, node->key, key)) {
216 prev->next = node->next, removed = YES;
218 GC_FREE(node); node = NULL;
223 prev = node, node = node->next;
225 } while (!removed && node);
226 if(removed==0) abort();
229 /* Decrement the number of entries in the hash table. */
233 /* Used to index through the hash table. Start with NULL
234 to get the first entry.
236 Successive calls pass the value returned previously.
237 ** Don't modify the hash during this operation ***
239 Cache nodes are returned such that key or value can
242 static __inline__ node_ptr hash_next (cache_ptr cache, node_ptr node) {
243 /* If the scan is being started then reset the last node
244 visitied pointer and bucket index. */
246 cache->last_bucket = 0;
248 /* If there is a node visited last then check for another
249 entry in the same bucket; Otherwise step to the next bucket. */
252 /* There is a node which follows the last node
253 returned. Step to that node and retun it. */
256 ++cache->last_bucket;
259 /* If the list isn't exhausted then search the buckets for
261 if (cache->last_bucket < cache->size) {
262 /* Scan the remainder of the buckets looking for an entry
263 at the head of the list. Return the first item found. */
264 while (cache->last_bucket < cache->size)
265 if (cache->node_table[cache->last_bucket])
266 return cache->node_table[cache->last_bucket];
268 ++cache->last_bucket;
270 /* No further nodes were found in the hash table. */
276 /* Used to return a value from a hash table using a given key. */
278 static __inline__ void *hash_value_for_key (cache_ptr cache, const void *key) {
279 /* Given KEY, return corresponding value for it in CACHE.
280 Return NULL if the KEY is not recorded. */
282 node_ptr node = cache->node_table[objc_call_hash(cache, key)];
283 //node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
288 //if ((*cache->compare_func)(node->key, key)) {
289 if (objc_call_compare(cache, node->key, key)) {
290 retval = node->value;
294 } while (!retval && node);
299 /* Used to determine if the given key exists in the hash table */
301 static __inline__ BOOL hash_is_key_in_hash (cache_ptr cache, const void *key) {
302 /* Given KEY, return YES if it exists in the CACHE.
303 Return NO if it does not */
305 //node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
306 register node_ptr node = cache->node_table[objc_call_hash(cache, key)];
310 //if ((*cache->compare_func)(node->key, key))
311 if (objc_call_compare(cache, node->key, key))
320 /************************************************
322 Useful hashing functions.
324 Declared inline for your pleasure.
326 ************************************************/
328 /* Calculate a hash code by performing some
329 manipulation of the key pointer. (Use the lowest bits
330 except for those likely to be 0 due to alignment.) */
332 static __inline__ unsigned int hash_ptr (cache_ptr cache, const void *key)
334 return ((size_t)key / sizeof (void *)) & cache->mask;
338 /* Calculate a hash code by iterating over a NULL
340 static __inline__ unsigned int
341 hash_string (cache_ptr cache,register const void *key)
343 register unsigned int ret = 0;
344 register unsigned int ctr = 0;
346 while (*(char*)key) {
347 ret ^= *(char*)key++ << ctr;
348 ctr = (ctr + 1) % sizeof (void *);
351 return (ret & cache->mask);
355 /* Compare two pointers for equality. */
356 static __inline__ int
357 compare_ptrs (register const void *k1, register const void *k2)
363 /* Compare two strings. */
364 static __inline__ int
365 compare_strings(register const char *k1,register const char *k2)
369 else if (k1 == 0 || k2 == 0)
372 while ((*k1 == *k2) && (*k1 != '\0')) {
376 return (*k1 == *k2) ? 1 : 0;
378 //return !strcmp (k1, k2);
382 #endif /* not __hash_INCLUDE_GNU */