1*e4b17023SJohn Marino /* Hash tables for Objective C internal structures
2*e4b17023SJohn Marino Copyright (C) 1993, 1996, 1997, 2004, 2009, 2010
3*e4b17023SJohn Marino Free Software Foundation, Inc.
4*e4b17023SJohn Marino
5*e4b17023SJohn Marino This file is part of GCC.
6*e4b17023SJohn Marino
7*e4b17023SJohn Marino GCC is free software; you can redistribute it and/or modify
8*e4b17023SJohn Marino it under the terms of the GNU General Public License as published by
9*e4b17023SJohn Marino the Free Software Foundation; either version 3, or (at your option)
10*e4b17023SJohn Marino any later version.
11*e4b17023SJohn Marino
12*e4b17023SJohn Marino GCC is distributed in the hope that it will be useful,
13*e4b17023SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
14*e4b17023SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15*e4b17023SJohn Marino GNU General Public License for more details.
16*e4b17023SJohn Marino
17*e4b17023SJohn Marino Under Section 7 of GPL version 3, you are granted additional
18*e4b17023SJohn Marino permissions described in the GCC Runtime Library Exception, version
19*e4b17023SJohn Marino 3.1, as published by the Free Software Foundation.
20*e4b17023SJohn Marino
21*e4b17023SJohn Marino You should have received a copy of the GNU General Public License and
22*e4b17023SJohn Marino a copy of the GCC Runtime Library Exception along with this program;
23*e4b17023SJohn Marino see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24*e4b17023SJohn Marino <http://www.gnu.org/licenses/>. */
25*e4b17023SJohn Marino
26*e4b17023SJohn Marino #include "objc-private/common.h"
27*e4b17023SJohn Marino #include <assert.h> /* For assert. */
28*e4b17023SJohn Marino
29*e4b17023SJohn Marino #include "objc/runtime.h" /* For objc_calloc. */
30*e4b17023SJohn Marino #include "objc-private/hash.h"
31*e4b17023SJohn Marino
32*e4b17023SJohn Marino /* These two macros determine when a hash table is full and
33*e4b17023SJohn Marino by how much it should be expanded respectively.
34*e4b17023SJohn Marino
35*e4b17023SJohn Marino These equations are percentages. */
36*e4b17023SJohn Marino #define FULLNESS(cache) \
37*e4b17023SJohn Marino ((((cache)->size * 75) / 100) <= (cache)->used)
38*e4b17023SJohn Marino #define EXPANSION(cache) \
39*e4b17023SJohn Marino ((cache)->size * 2)
40*e4b17023SJohn Marino
41*e4b17023SJohn Marino cache_ptr
objc_hash_new(unsigned int size,hash_func_type hash_func,compare_func_type compare_func)42*e4b17023SJohn Marino objc_hash_new (unsigned int size, hash_func_type hash_func,
43*e4b17023SJohn Marino compare_func_type compare_func)
44*e4b17023SJohn Marino {
45*e4b17023SJohn Marino cache_ptr cache;
46*e4b17023SJohn Marino
47*e4b17023SJohn Marino /* Pass me a value greater than 0 and a power of 2. */
48*e4b17023SJohn Marino assert (size);
49*e4b17023SJohn Marino assert (! (size & (size - 1)));
50*e4b17023SJohn Marino
51*e4b17023SJohn Marino /* Allocate the cache structure. calloc insures its initialization
52*e4b17023SJohn Marino for default values. */
53*e4b17023SJohn Marino cache = (cache_ptr) objc_calloc (1, sizeof (struct cache));
54*e4b17023SJohn Marino assert (cache);
55*e4b17023SJohn Marino
56*e4b17023SJohn Marino /* Allocate the array of buckets for the cache. calloc initializes
57*e4b17023SJohn Marino all of the pointers to NULL. */
58*e4b17023SJohn Marino cache->node_table
59*e4b17023SJohn Marino = (node_ptr *) objc_calloc (size, sizeof (node_ptr));
60*e4b17023SJohn Marino assert (cache->node_table);
61*e4b17023SJohn Marino
62*e4b17023SJohn Marino cache->size = size;
63*e4b17023SJohn Marino
64*e4b17023SJohn Marino /* This should work for all processor architectures (?). */
65*e4b17023SJohn Marino cache->mask = (size - 1);
66*e4b17023SJohn Marino
67*e4b17023SJohn Marino /* Store the hashing function so that codes can be computed. */
68*e4b17023SJohn Marino cache->hash_func = hash_func;
69*e4b17023SJohn Marino
70*e4b17023SJohn Marino /* Store the function that compares hash keys to determine if they
71*e4b17023SJohn Marino are equal. */
72*e4b17023SJohn Marino cache->compare_func = compare_func;
73*e4b17023SJohn Marino
74*e4b17023SJohn Marino return cache;
75*e4b17023SJohn Marino }
76*e4b17023SJohn Marino
77*e4b17023SJohn Marino
78*e4b17023SJohn Marino void
objc_hash_delete(cache_ptr cache)79*e4b17023SJohn Marino objc_hash_delete (cache_ptr cache)
80*e4b17023SJohn Marino {
81*e4b17023SJohn Marino node_ptr node;
82*e4b17023SJohn Marino node_ptr next_node;
83*e4b17023SJohn Marino unsigned int i;
84*e4b17023SJohn Marino
85*e4b17023SJohn Marino /* Purge all key/value pairs from the table. */
86*e4b17023SJohn Marino /* Step through the nodes one by one and remove every node WITHOUT
87*e4b17023SJohn Marino using objc_hash_next. this makes objc_hash_delete much more
88*e4b17023SJohn Marino efficient. */
89*e4b17023SJohn Marino for (i = 0; i < cache->size; i++)
90*e4b17023SJohn Marino {
91*e4b17023SJohn Marino if ((node = cache->node_table[i]))
92*e4b17023SJohn Marino {
93*e4b17023SJohn Marino /* An entry in the hash table has been found. Now step
94*e4b17023SJohn Marino through the nodes next in the list and free them. */
95*e4b17023SJohn Marino while ((next_node = node->next))
96*e4b17023SJohn Marino {
97*e4b17023SJohn Marino objc_hash_remove (cache,node->key);
98*e4b17023SJohn Marino node = next_node;
99*e4b17023SJohn Marino }
100*e4b17023SJohn Marino objc_hash_remove (cache,node->key);
101*e4b17023SJohn Marino }
102*e4b17023SJohn Marino }
103*e4b17023SJohn Marino
104*e4b17023SJohn Marino /* Release the array of nodes and the cache itself. */
105*e4b17023SJohn Marino objc_free(cache->node_table);
106*e4b17023SJohn Marino objc_free(cache);
107*e4b17023SJohn Marino }
108*e4b17023SJohn Marino
109*e4b17023SJohn Marino
110*e4b17023SJohn Marino void
objc_hash_add(cache_ptr * cachep,const void * key,void * value)111*e4b17023SJohn Marino objc_hash_add (cache_ptr *cachep, const void *key, void *value)
112*e4b17023SJohn Marino {
113*e4b17023SJohn Marino size_t indx = (*(*cachep)->hash_func) (*cachep, key);
114*e4b17023SJohn Marino node_ptr node = (node_ptr) objc_calloc (1, sizeof (struct cache_node));
115*e4b17023SJohn Marino
116*e4b17023SJohn Marino assert (node);
117*e4b17023SJohn Marino
118*e4b17023SJohn Marino /* Initialize the new node. */
119*e4b17023SJohn Marino node->key = key;
120*e4b17023SJohn Marino node->value = value;
121*e4b17023SJohn Marino node->next = (*cachep)->node_table[indx];
122*e4b17023SJohn Marino
123*e4b17023SJohn Marino /* Debugging. Check the list for another key. */
124*e4b17023SJohn Marino #ifdef DEBUG
125*e4b17023SJohn Marino {
126*e4b17023SJohn Marino node_ptr node1 = (*cachep)->node_table[indx];
127*e4b17023SJohn Marino while (node1)
128*e4b17023SJohn Marino {
129*e4b17023SJohn Marino assert (node1->key != key);
130*e4b17023SJohn Marino node1 = node1->next;
131*e4b17023SJohn Marino }
132*e4b17023SJohn Marino }
133*e4b17023SJohn Marino #endif
134*e4b17023SJohn Marino
135*e4b17023SJohn Marino /* Install the node as the first element on the list. */
136*e4b17023SJohn Marino (*cachep)->node_table[indx] = node;
137*e4b17023SJohn Marino
138*e4b17023SJohn Marino /* Bump the number of entries in the cache. */
139*e4b17023SJohn Marino ++(*cachep)->used;
140*e4b17023SJohn Marino
141*e4b17023SJohn Marino /* Check the hash table's fullness. We're going to expand if it is
142*e4b17023SJohn Marino above the fullness level. */
143*e4b17023SJohn Marino if (FULLNESS (*cachep))
144*e4b17023SJohn Marino {
145*e4b17023SJohn Marino /* The hash table has reached its fullness level. Time to
146*e4b17023SJohn Marino expand it.
147*e4b17023SJohn Marino
148*e4b17023SJohn Marino I'm using a slow method here but is built on other primitive
149*e4b17023SJohn Marino functions thereby increasing its correctness. */
150*e4b17023SJohn Marino node_ptr node1 = NULL;
151*e4b17023SJohn Marino cache_ptr new = objc_hash_new (EXPANSION (*cachep),
152*e4b17023SJohn Marino (*cachep)->hash_func,
153*e4b17023SJohn Marino (*cachep)->compare_func);
154*e4b17023SJohn Marino
155*e4b17023SJohn Marino DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n",
156*e4b17023SJohn Marino (int) *cachep, (*cachep)->size, new->size);
157*e4b17023SJohn Marino
158*e4b17023SJohn Marino /* Copy the nodes from the first hash table to the new one. */
159*e4b17023SJohn Marino while ((node1 = objc_hash_next (*cachep, node1)))
160*e4b17023SJohn Marino objc_hash_add (&new, node1->key, node1->value);
161*e4b17023SJohn Marino
162*e4b17023SJohn Marino /* Trash the old cache. */
163*e4b17023SJohn Marino objc_hash_delete (*cachep);
164*e4b17023SJohn Marino
165*e4b17023SJohn Marino /* Return a pointer to the new hash table. */
166*e4b17023SJohn Marino *cachep = new;
167*e4b17023SJohn Marino }
168*e4b17023SJohn Marino }
169*e4b17023SJohn Marino
170*e4b17023SJohn Marino void
objc_hash_remove(cache_ptr cache,const void * key)171*e4b17023SJohn Marino objc_hash_remove (cache_ptr cache, const void *key)
172*e4b17023SJohn Marino {
173*e4b17023SJohn Marino size_t indx = (*cache->hash_func) (cache, key);
174*e4b17023SJohn Marino node_ptr node = cache->node_table[indx];
175*e4b17023SJohn Marino
176*e4b17023SJohn Marino /* We assume there is an entry in the table. Error if it is
177*e4b17023SJohn Marino not. */
178*e4b17023SJohn Marino assert (node);
179*e4b17023SJohn Marino
180*e4b17023SJohn Marino /* Special case. First element is the key/value pair to be
181*e4b17023SJohn Marino removed. */
182*e4b17023SJohn Marino if ((*cache->compare_func) (node->key, key))
183*e4b17023SJohn Marino {
184*e4b17023SJohn Marino cache->node_table[indx] = node->next;
185*e4b17023SJohn Marino objc_free(node);
186*e4b17023SJohn Marino }
187*e4b17023SJohn Marino else
188*e4b17023SJohn Marino {
189*e4b17023SJohn Marino /* Otherwise, find the hash entry. */
190*e4b17023SJohn Marino node_ptr prev = node;
191*e4b17023SJohn Marino BOOL removed = NO;
192*e4b17023SJohn Marino do
193*e4b17023SJohn Marino {
194*e4b17023SJohn Marino if ((*cache->compare_func) (node->key, key))
195*e4b17023SJohn Marino {
196*e4b17023SJohn Marino prev->next = node->next, removed = YES;
197*e4b17023SJohn Marino objc_free(node);
198*e4b17023SJohn Marino }
199*e4b17023SJohn Marino else
200*e4b17023SJohn Marino prev = node, node = node->next;
201*e4b17023SJohn Marino }
202*e4b17023SJohn Marino while (!removed && node);
203*e4b17023SJohn Marino assert (removed);
204*e4b17023SJohn Marino }
205*e4b17023SJohn Marino
206*e4b17023SJohn Marino /* Decrement the number of entries in the hash table. */
207*e4b17023SJohn Marino --cache->used;
208*e4b17023SJohn Marino }
209*e4b17023SJohn Marino
210*e4b17023SJohn Marino
211*e4b17023SJohn Marino node_ptr
objc_hash_next(cache_ptr cache,node_ptr node)212*e4b17023SJohn Marino objc_hash_next (cache_ptr cache, node_ptr node)
213*e4b17023SJohn Marino {
214*e4b17023SJohn Marino /* If the scan is being started then reset the last node visitied
215*e4b17023SJohn Marino pointer and bucket index. */
216*e4b17023SJohn Marino if (!node)
217*e4b17023SJohn Marino cache->last_bucket = 0;
218*e4b17023SJohn Marino
219*e4b17023SJohn Marino /* If there is a node visited last then check for another entry in
220*e4b17023SJohn Marino the same bucket. Otherwise step to the next bucket. */
221*e4b17023SJohn Marino if (node)
222*e4b17023SJohn Marino {
223*e4b17023SJohn Marino if (node->next)
224*e4b17023SJohn Marino {
225*e4b17023SJohn Marino /* There is a node which follows the last node returned.
226*e4b17023SJohn Marino Step to that node and retun it. */
227*e4b17023SJohn Marino return node->next;
228*e4b17023SJohn Marino }
229*e4b17023SJohn Marino else
230*e4b17023SJohn Marino ++cache->last_bucket;
231*e4b17023SJohn Marino }
232*e4b17023SJohn Marino
233*e4b17023SJohn Marino /* If the list isn't exhausted then search the buckets for other
234*e4b17023SJohn Marino nodes. */
235*e4b17023SJohn Marino if (cache->last_bucket < cache->size)
236*e4b17023SJohn Marino {
237*e4b17023SJohn Marino /* Scan the remainder of the buckets looking for an entry at
238*e4b17023SJohn Marino the head of the list. Return the first item found. */
239*e4b17023SJohn Marino while (cache->last_bucket < cache->size)
240*e4b17023SJohn Marino if (cache->node_table[cache->last_bucket])
241*e4b17023SJohn Marino return cache->node_table[cache->last_bucket];
242*e4b17023SJohn Marino else
243*e4b17023SJohn Marino ++cache->last_bucket;
244*e4b17023SJohn Marino
245*e4b17023SJohn Marino /* No further nodes were found in the hash table. */
246*e4b17023SJohn Marino return NULL;
247*e4b17023SJohn Marino }
248*e4b17023SJohn Marino else
249*e4b17023SJohn Marino return NULL;
250*e4b17023SJohn Marino }
251*e4b17023SJohn Marino
252*e4b17023SJohn Marino
253*e4b17023SJohn Marino /* Given KEY, return corresponding value for it in CACHE. Return NULL
254*e4b17023SJohn Marino if the KEY is not recorded. */
255*e4b17023SJohn Marino void *
objc_hash_value_for_key(cache_ptr cache,const void * key)256*e4b17023SJohn Marino objc_hash_value_for_key (cache_ptr cache, const void *key)
257*e4b17023SJohn Marino {
258*e4b17023SJohn Marino node_ptr node = cache->node_table[(*cache->hash_func) (cache, key)];
259*e4b17023SJohn Marino void *retval = NULL;
260*e4b17023SJohn Marino
261*e4b17023SJohn Marino if (node)
262*e4b17023SJohn Marino do
263*e4b17023SJohn Marino {
264*e4b17023SJohn Marino if ((*cache->compare_func) (node->key, key))
265*e4b17023SJohn Marino {
266*e4b17023SJohn Marino retval = node->value;
267*e4b17023SJohn Marino break;
268*e4b17023SJohn Marino }
269*e4b17023SJohn Marino else
270*e4b17023SJohn Marino node = node->next;
271*e4b17023SJohn Marino }
272*e4b17023SJohn Marino while (! retval && node);
273*e4b17023SJohn Marino
274*e4b17023SJohn Marino return retval;
275*e4b17023SJohn Marino }
276*e4b17023SJohn Marino
277*e4b17023SJohn Marino /* Given KEY, return YES if it exists in the CACHE. Return NO if it
278*e4b17023SJohn Marino does not */
279*e4b17023SJohn Marino BOOL
objc_hash_is_key_in_hash(cache_ptr cache,const void * key)280*e4b17023SJohn Marino objc_hash_is_key_in_hash (cache_ptr cache, const void *key)
281*e4b17023SJohn Marino {
282*e4b17023SJohn Marino node_ptr node = cache->node_table[(*cache->hash_func) (cache, key)];
283*e4b17023SJohn Marino
284*e4b17023SJohn Marino if (node)
285*e4b17023SJohn Marino do
286*e4b17023SJohn Marino {
287*e4b17023SJohn Marino if ((*cache->compare_func)(node->key, key))
288*e4b17023SJohn Marino return YES;
289*e4b17023SJohn Marino else
290*e4b17023SJohn Marino node = node->next;
291*e4b17023SJohn Marino }
292*e4b17023SJohn Marino while (node);
293*e4b17023SJohn Marino
294*e4b17023SJohn Marino return NO;
295*e4b17023SJohn Marino }
296