|
|
1.1 root 1: /* Hash tables for Objective C method dispatch.
2: Copyright (C) 1992 Free Software Foundation, Inc.
3:
4: This file is part of GNU CC.
5:
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)
9: any later version.
10:
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.
15:
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, 675 Mass Ave, Cambridge, MA 02139, USA. */
19:
20: /* As a special exception, if you link this library with files
21: compiled with GCC to produce an executable, this does not cause
22: the resulting executable to be covered by the GNU General Public License.
23: This exception does not however invalidate any other reasons why
24: the executable file might be covered by the GNU General Public License. */
25:
26: #include "tconfig.h"
27: #include "gstddef.h"
28: #include "gstdarg.h"
29: #include "assert.h"
30:
31: #include "hash.h"
32: #include "objc.h"
33: #include "objc-proto.h"
34:
35:
36: /* These two macros determine when a hash table is full and
37: by how much it should be expanded respectively.
38:
39: These equations are percentages. */
40: #define FULLNESS(cache) \
41: ((((cache)->size * 75) / 100) <= (cache)->used)
42: #define EXPANSION(cache) \
43: ((cache)->size * 2)
44:
45: cache_ptr
46: hash_new (unsigned int size, hash_func_type hash_func,
47: compare_func_type compare_func)
48: {
49: cache_ptr cache;
50:
51:
52: /* Pass me a value greater than 0 and a power of 2. */
53: assert (size);
54: assert (!(size & (size - 1)));
55:
56: /* Allocate the cache structure. calloc insures
57: its initialization for default values. */
58: cache = (cache_ptr) calloc (1, sizeof (struct cache));
59: assert (cache);
60:
61: /* Allocate the array of buckets for the cache.
62: calloc initializes all of the pointers to NULL. */
63: cache->node_table
64: = (node_ptr *) calloc (size, sizeof (node_ptr));
65: assert (cache->node_table);
66:
67: cache->size = size;
68:
69: /* This should work for all processor architectures? */
70: cache->mask = (size - 1);
71:
72: /* Store the hashing function so that codes can be computed. */
73: cache->hash_func = hash_func;
74:
75: /* Store the function that compares hash keys to
76: determine if they are equal. */
77: cache->compare_func = compare_func;
78:
79: return cache;
80: }
81:
82:
83: void
84: hash_delete (cache_ptr cache)
85: {
86: node_ptr node;
87:
88:
89: /* Purge all key/value pairs from the table. */
90: while (node = hash_next (cache, NULL))
91: hash_remove (cache, node->key);
92:
93: /* Release the array of nodes and the cache itself. */
94: free (cache->node_table);
95: free (cache);
96: }
97:
98:
99: void
100: hash_add (cache_ptr *cachep, const void *key, void *value)
101: {
102: size_t indx = (*(*cachep)->hash_func)(*cachep, key);
103: node_ptr node = (node_ptr) calloc (1, sizeof (struct cache_node));
104:
105:
106: assert (node);
107:
108: /* Initialize the new node. */
109: node->key = key;
110: node->value = value;
111: node->next = (*cachep)->node_table[indx];
112:
113: /* Debugging.
114: Check the list for another key. */
115: #ifdef DEBUG
116: { node_ptr node1 = (*cachep)->node_table[indx];
117:
118: while (node1) {
119:
120: assert (node1->key != key);
121: node1 = node1->next;
122: }
123: }
124: #endif
125:
126: /* Install the node as the first element on the list. */
127: (*cachep)->node_table[indx] = node;
128:
129: /* Bump the number of entries in the cache. */
130: ++(*cachep)->used;
131:
132: /* Check the hash table's fullness. We're going
133: to expand if it is above the fullness level. */
134: if (FULLNESS (*cachep)) {
135:
136: /* The hash table has reached its fullness level. Time to
137: expand it.
138:
139: I'm using a slow method here but is built on other
140: primitive functions thereby increasing its
141: correctness. */
142: node_ptr node1 = NULL;
143: cache_ptr new = hash_new (EXPANSION (*cachep),
144: (*cachep)->hash_func,
145: (*cachep)->compare_func);
146:
147: DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n",
148: *cachep, (*cachep)->size, new->size);
149:
150: /* Copy the nodes from the first hash table to the new one. */
151: while (node1 = hash_next (*cachep, node1))
152: hash_add (&new, node1->key, node1->value);
153:
154: /* Trash the old cache. */
155: hash_delete (*cachep);
156:
157: /* Return a pointer to the new hash table. */
158: *cachep = new;
159: }
160: }
161:
162:
163: void
164: hash_remove (cache_ptr cache, const void *key)
165: {
166: size_t indx = (*cache->hash_func)(cache, key);
167: node_ptr node = cache->node_table[indx];
168:
169:
170: /* We assume there is an entry in the table. Error if it is not. */
171: assert (node);
172:
173: /* Special case. First element is the key/value pair to be removed. */
174: if ((*cache->compare_func)(node->key, key)) {
175: cache->node_table[indx] = node->next;
176: free (node);
177: } else {
178:
179: /* Otherwise, find the hash entry. */
180: node_ptr prev = node;
181: BOOL removed = NO;
182:
183: do {
184:
185: if ((*cache->compare_func)(node->key, key)) {
186: prev->next = node->next, removed = YES;
187: free (node);
188: } else
189: prev = node, node = node->next;
190: } while (!removed && node);
191: assert (removed);
192: }
193:
194: /* Decrement the number of entries in the hash table. */
195: --cache->used;
196: }
197:
198:
199: node_ptr
200: hash_next (cache_ptr cache, node_ptr node)
201: {
202: /* If the scan is being started then reset the last node
203: visitied pointer and bucket index. */
204: if (!node)
205: cache->last_bucket = 0;
206:
207: /* If there is a node visited last then check for another
208: entry in the same bucket; Otherwise step to the next bucket. */
209: if (node) {
210: if (node->next)
211: /* There is a node which follows the last node
212: returned. Step to that node and retun it. */
213: return node->next;
214: else
215: ++cache->last_bucket;
216: }
217:
218: /* If the list isn't exhausted then search the buckets for
219: other nodes. */
220: if (cache->last_bucket < cache->size) {
221: /* Scan the remainder of the buckets looking for an entry
222: at the head of the list. Return the first item found. */
223: while (cache->last_bucket < cache->size)
224: if (cache->node_table[cache->last_bucket])
225: return cache->node_table[cache->last_bucket];
226: else
227: ++cache->last_bucket;
228:
229: /* No further nodes were found in the hash table. */
230: return NULL;
231: } else
232: return NULL;
233: }
234:
235:
236: /* Given KEY, return corresponding value for it in CACHE.
237: Return NULL if the KEY is not recorded. */
238:
239: void *
240: hash_value_for_key (cache_ptr cache, const void *key)
241: {
242: node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
243: void *retval = NULL;
244:
245: if (node)
246: do {
247: if ((*cache->compare_func)(node->key, key))
248: retval = node->value;
249: else
250: node = node->next;
251: } while (!retval && node);
252:
253: return retval;
254: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.