source: trunk/third/gcc/objc/hash.c @ 8834

Revision 8834, 6.8 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r8833, which included commits to RCS files with non-trunk default branches.
Line 
1/* Hash tables for Objective C internal structures
2   Copyright (C) 1993 Free Software Foundation, Inc.
3
4This file is part of GNU CC.
5
6GNU CC is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU CC is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU CC; see the file COPYING.  If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.  */
20
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.  */
26
27#include "assert.h"
28
29#include "objc/hash.h"
30#include "objc/objc.h"
31
32#include "runtime.h"            /* for DEBUG_PRINTF */
33
34/* These two macros determine when a hash table is full and
35   by how much it should be expanded respectively.
36
37   These equations are percentages.  */
38#define FULLNESS(cache) \
39   ((((cache)->size * 75) / 100) <= (cache)->used)
40#define EXPANSION(cache) \
41  ((cache)->size * 2)
42
43void *__objc_xcalloc (size_t, size_t);
44
45cache_ptr
46hash_new (unsigned int size, hash_func_type hash_func,
47          compare_func_type compare_func)
48{
49  cache_ptr cache;
50
51  /* Pass me a value greater than 0 and a power of 2.  */
52  assert (size);
53  assert (!(size & (size - 1)));
54
55  /* Allocate the cache structure.  calloc insures
56     its initialization for default values.  */
57  cache = (cache_ptr) __objc_xcalloc (1, sizeof (struct cache));
58  assert (cache);
59
60  /* Allocate the array of buckets for the cache.
61     calloc initializes all of the pointers to NULL.  */
62  cache->node_table
63    = (node_ptr *) __objc_xcalloc (size, sizeof (node_ptr));
64  assert (cache->node_table);
65
66  cache->size  = size;
67
68  /* This should work for all processor architectures? */
69  cache->mask = (size - 1);
70       
71  /* Store the hashing function so that codes can be computed.  */
72  cache->hash_func = hash_func;
73
74  /* Store the function that compares hash keys to
75     determine if they are equal.  */
76  cache->compare_func = compare_func;
77
78  return cache;
79}
80
81
82void
83hash_delete (cache_ptr cache)
84{
85  node_ptr node;
86
87
88  /* Purge all key/value pairs from the table.  */
89  while ((node = hash_next (cache, NULL)))
90    hash_remove (cache, node->key);
91
92  /* Release the array of nodes and the cache itself.  */
93  free (cache->node_table);
94  free (cache);
95}
96
97
98void
99hash_add (cache_ptr *cachep, const void *key, void *value)
100{
101  size_t indx = (*(*cachep)->hash_func)(*cachep, key);
102  node_ptr node = (node_ptr) __objc_xcalloc (1, sizeof (struct cache_node));
103
104
105  assert (node);
106
107  /* Initialize the new node.  */
108  node->key    = key;
109  node->value  = value;
110  node->next  = (*cachep)->node_table[indx];
111
112  /* Debugging.
113     Check the list for another key.  */
114#ifdef DEBUG
115  { node_ptr node1 = (*cachep)->node_table[indx];
116
117    while (node1) {
118
119      assert (node1->key != key);
120      node1 = node1->next;
121    }
122  }
123#endif
124
125  /* Install the node as the first element on the list.  */
126  (*cachep)->node_table[indx] = node;
127
128  /* Bump the number of entries in the cache.  */
129  ++(*cachep)->used;
130
131  /* Check the hash table's fullness.   We're going
132     to expand if it is above the fullness level.  */
133  if (FULLNESS (*cachep)) {
134
135    /* The hash table has reached its fullness level.  Time to
136       expand it.
137
138       I'm using a slow method here but is built on other
139       primitive functions thereby increasing its
140       correctness.  */
141    node_ptr node1 = NULL;
142    cache_ptr new = hash_new (EXPANSION (*cachep),
143                              (*cachep)->hash_func,
144                              (*cachep)->compare_func);
145
146    DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n",
147                  *cachep, (*cachep)->size, new->size);
148
149    /* Copy the nodes from the first hash table to the new one.  */
150    while ((node1 = hash_next (*cachep, node1)))
151      hash_add (&new, node1->key, node1->value);
152
153    /* Trash the old cache.  */
154    hash_delete (*cachep);
155
156    /* Return a pointer to the new hash table.  */
157    *cachep = new;
158  }
159}
160
161
162void
163hash_remove (cache_ptr cache, const void *key)
164{
165  size_t indx = (*cache->hash_func)(cache, key);
166  node_ptr node = cache->node_table[indx];
167
168
169  /* We assume there is an entry in the table.  Error if it is not.  */
170  assert (node);
171
172  /* Special case.  First element is the key/value pair to be removed.  */
173  if ((*cache->compare_func)(node->key, key)) {
174    cache->node_table[indx] = node->next;
175    free (node);
176  } else {
177
178    /* Otherwise, find the hash entry.  */
179    node_ptr prev = node;
180    BOOL removed = NO;
181
182    do {
183
184      if ((*cache->compare_func)(node->key, key)) {
185        prev->next = node->next, removed = YES;
186        free (node);
187      } else
188        prev = node, node = node->next;
189    } while (!removed && node);
190    assert (removed);
191  }
192
193  /* Decrement the number of entries in the hash table.  */
194  --cache->used;
195}
196
197
198node_ptr
199hash_next (cache_ptr cache, node_ptr node)
200{
201  /* If the scan is being started then reset the last node
202     visitied pointer and bucket index.  */
203  if (!node)
204    cache->last_bucket  = 0;
205
206  /* If there is a node visited last then check for another
207     entry in the same bucket;  Otherwise step to the next bucket.  */
208  if (node) {
209    if (node->next)
210      /* There is a node which follows the last node
211         returned.  Step to that node and retun it.  */
212      return node->next;
213    else
214      ++cache->last_bucket;
215  }
216
217  /* If the list isn't exhausted then search the buckets for
218     other nodes.  */
219  if (cache->last_bucket < cache->size) {
220    /*  Scan the remainder of the buckets looking for an entry
221        at the head of the list.  Return the first item found.  */
222    while (cache->last_bucket < cache->size)
223      if (cache->node_table[cache->last_bucket])
224        return cache->node_table[cache->last_bucket];
225      else
226        ++cache->last_bucket;
227
228    /* No further nodes were found in the hash table.  */
229    return NULL;
230  } else
231    return NULL;
232}
233
234
235/* Given KEY, return corresponding value for it in CACHE.
236   Return NULL if the KEY is not recorded.  */
237
238void *
239hash_value_for_key (cache_ptr cache, const void *key)
240{
241  node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
242  void *retval = NULL;
243
244  if (node)
245    do {
246      if ((*cache->compare_func)(node->key, key)) {
247        retval = node->value;
248              break;
249      } else
250        node = node->next;
251    } while (!retval && node);
252
253  return retval;
254}
Note: See TracBrowser for help on using the repository browser.