xref: /dflybsd-src/contrib/gcc-4.7/libobjc/hash.c (revision 04febcfb30580676d3e95f58a16c5137ee478b32)
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