1 /* $XConsortium: Hash.c /main/6 1995/10/25 20:06:11 cde-sun $ */
2 /*
3 * Motif
4 *
5 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
6 *
7 * These libraries and programs are free software; you can
8 * redistribute them and/or modify them under the terms of the GNU
9 * Lesser General Public License as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * These libraries and programs are distributed in the hope that
14 * they will be useful, but WITHOUT ANY WARRANTY; without even the
15 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 * PURPOSE. See the GNU Lesser General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with these librararies and programs; if not, write
21 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
22 * Floor, Boston, MA 02110-1301 USA
23 */
24 /*
25 * HISTORY
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32
33 #include "XmI.h"
34 #include "HashI.h"
35
36 #define FIX_1264
37
38 /* Private data structures */
39
40 typedef struct _XmHashBucketRec {
41 XmHashValue hashed_key;
42 XmHashKey hash_key;
43 XtPointer value;
44 struct _XmHashBucketRec *next;
45 } XmHashBucketRec, *XmHashBucket;
46
47 typedef struct _XmHashTableRec {
48 Cardinal size;
49 Cardinal count;
50 XmHashCompareProc compare;
51 XmHashFunction hasher;
52 XmHashBucket *buckets;
53 } XmHashTableRec;
54
55 /* Static functions */
56 static XmHashBucket NewBucket(void);
57 static void FreeBucket(XmHashBucket);
58
59 /* Static data */
60
61 static XmHashBucket FreeBucketList = NULL;
62
63 /* Dumb default hash functions */
64
65 static Boolean
Compare(XmHashKey x,XmHashKey y)66 Compare(XmHashKey x, XmHashKey y)
67 {
68 return(x == y);
69 }
70
71 static XmHashValue
Hash(XmHashKey x)72 Hash(XmHashKey x)
73 {
74 return((XmHashValue) (long) x);
75 }
76
77 /* Available table sizes, should be prime numbers */
78
79 static XmConst int size_table[] =
80 { 17, 31, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 0 };
81
82 XmHashTable
_XmAllocHashTable(Cardinal size_hint,XmHashCompareProc cproc,XmHashFunction hproc)83 _XmAllocHashTable(Cardinal size_hint, XmHashCompareProc cproc,
84 XmHashFunction hproc)
85 {
86 XmHashTable table;
87 int i;
88
89 table = (XmHashTable) XtMalloc(sizeof(XmHashTableRec));
90
91 if (hproc)
92 table -> hasher = hproc;
93 else
94 table -> hasher = Hash;
95
96 if (cproc)
97 table -> compare = cproc;
98 else
99 table -> compare = Compare;
100
101 i = 0;
102
103 /* Search size_table for size which is bigger than size_hint */
104 while(size_table[i] != 0 &&
105 size_table[i] < size_hint) i++;
106
107 if (size_table[i] == 0) i--;
108
109 table -> size = size_table[i];
110 table -> count = 0;
111
112 /* Create the array of pointers */
113 table->buckets = (XmHashBucket*) XtCalloc(table->size, sizeof(XmHashBucket));
114
115 return(table);
116 }
117
118 void
_XmFreeHashTable(XmHashTable table)119 _XmFreeHashTable(XmHashTable table)
120 {
121 int i;
122 XmHashBucket bucket, next;
123
124 for(i = 0; i < table -> size; i++) {
125 bucket = table -> buckets[i];
126 while(bucket) {
127 next = bucket -> next;
128 FreeBucket(bucket);
129 bucket = next;
130 }
131 }
132
133 XtFree((char*) table -> buckets);
134 XtFree((char*) table);
135 }
136
137 void
_XmResizeHashTable(XmHashTable table,Cardinal new_size)138 _XmResizeHashTable(XmHashTable table, Cardinal new_size)
139 {
140 int i, index;
141 int oldsize;
142 XmHashBucket current, last, next, new_h;
143
144 i = 0;
145
146 /* Search size_table for size which is bigger than size_hint */
147 while(size_table[i] != 0 &&
148 size_table[i] < new_size) i++;
149
150 if (size_table[i] == 0) i--;
151
152 /* New size should be larger, otherwise return */
153 if (size_table[i] <= table -> size) return;
154
155 /* Realloc table */
156 oldsize = table -> size;
157 table -> size = size_table[i];
158 table -> buckets =
159 (XmHashBucket*) XtRealloc((char*) table -> buckets,
160 table -> size * sizeof(XmHashBucket));
161 /* NULL new array entries */
162 for(i = oldsize; i < table -> size; i++) table -> buckets[i] = NULL;
163
164 /* Rearrange buckets, this is a slow method, but always
165 correct. We will end up rescanning any moved buckets */
166 for(i = 0; i < table -> size; i++) {
167 last = NULL;
168 current = table -> buckets[i];
169 while(current) {
170 /* If this bucket goes somewhere else, remove
171 from this chain and reinstall */
172 next = current -> next;
173 index = current -> hashed_key % table -> size;
174 if (index != i) {
175 if (last != NULL)
176 last -> next = current -> next;
177 else
178 table -> buckets[i] = current -> next;
179 /* Now put at end of new bucket chain, we must do
180 this to maintain ordering */
181 current -> next = NULL;
182 new_h = table -> buckets[index];
183 if (new_h != NULL) {
184 while(new_h -> next != NULL)
185 new_h = new_h -> next;
186 new_h -> next = current;
187 } else {
188 table -> buckets[index] = current;
189 }
190 }
191 #ifdef FIX_1264
192 else last = current;
193 #endif
194 current = next;
195 }
196 }
197 }
198
199 XtPointer
_XmGetHashEntryIterate(XmHashTable table,XmHashKey key,XtPointer * iterator)200 _XmGetHashEntryIterate(XmHashTable table, XmHashKey key, XtPointer *iterator)
201 {
202 XmHashValue index;
203 XmHashBucket entry;
204
205 if (iterator && *iterator != NULL) {
206 /* If iterating over a number of matching entries */
207 entry = (XmHashBucket) *iterator;
208 entry = entry -> next;
209 } else {
210 /* Normal case */
211 index = (table -> hasher(key)) % table -> size;
212 entry = table -> buckets[index];
213 }
214
215 while(entry) {
216 if (table -> compare(entry -> hash_key, key)) {
217 if (iterator) *iterator = (XtPointer) entry;
218 return(entry -> value);
219 }
220 entry = entry -> next;
221 }
222
223 if (iterator) *iterator = NULL;
224 return(NULL);
225 }
226
227 void
_XmAddHashEntry(XmHashTable table,XmHashKey key,XtPointer value)228 _XmAddHashEntry(XmHashTable table, XmHashKey key, XtPointer value)
229 {
230 int hash;
231 XmHashValue index;
232 XmHashBucket entry;
233
234 hash = table -> hasher(key);
235 index = hash % table -> size;
236
237 entry = NewBucket();
238 entry -> hashed_key = hash;
239 entry -> hash_key = key;
240 entry -> value = value;
241 entry -> next = table -> buckets[index];
242 table -> buckets[index] = entry;
243 table -> count++;
244 }
245
246 XtPointer
_XmRemoveHashEntry(XmHashTable table,XmHashKey key)247 _XmRemoveHashEntry(XmHashTable table, XmHashKey key)
248 {
249 XmHashValue index;
250 XmHashBucket entry, last = NULL;
251
252 index = (table -> hasher(key)) % table -> size;
253
254 entry = table -> buckets[index];
255 while(entry) {
256 if (table -> compare(entry -> hash_key, key)) {
257 if (last == NULL) {
258 /* First entry is handled differently */
259 table -> buckets[index] = entry -> next;
260 } else {
261 last -> next = entry -> next;
262 }
263
264 table -> count--;
265 FreeBucket(entry);
266 return(entry -> hash_key);
267 }
268 last = entry;
269 entry = entry -> next;
270 }
271 return(NULL);
272 }
273
274 XtPointer
_XmRemoveHashIterator(XmHashTable table,XtPointer * iterator)275 _XmRemoveHashIterator(XmHashTable table, XtPointer *iterator)
276 {
277 XmHashValue index;
278 XmHashBucket entry, current, last = NULL;
279
280 if (! iterator) return(NULL);
281
282 entry = (XmHashBucket) *iterator;
283
284 index = (table -> hasher(entry -> hash_key)) % table -> size;
285
286 current = table -> buckets[index];
287
288 while(current) {
289 if (current == entry) {
290 if (last == NULL) {
291 /* First entry is handled differently */
292 table -> buckets[index] = current -> next;
293 } else {
294 last -> next = current -> next;
295 }
296
297 table -> count--;
298 FreeBucket(current);
299 return(current -> hash_key);
300 }
301 last = current;
302 current = current -> next;
303 }
304 return(NULL);
305 }
306
307 Cardinal
_XmHashTableCount(XmHashTable table)308 _XmHashTableCount(XmHashTable table)
309 {
310 return(table -> count);
311 }
312
313 Cardinal
_XmHashTableSize(XmHashTable table)314 _XmHashTableSize(XmHashTable table)
315 {
316 return(table -> size);
317 }
318
319 /****************************************************************/
320 /* _XmMapHashTable(table, proc, client_data) */
321 /* Iterate over entire hash table. Mostly useful for debugging */
322 /* code using hash tables. */
323 /****************************************************************/
324
325 void
_XmMapHashTable(XmHashTable table,XmHashMapProc proc,XtPointer client_data)326 _XmMapHashTable(XmHashTable table, XmHashMapProc proc, XtPointer client_data)
327 {
328 int i;
329 XmHashBucket entry, next;
330
331 for(i = 0; i < table -> size; i++) {
332 entry = table -> buckets[i];
333 while(entry) {
334 /* Can free key and value in this proc */
335 next = entry -> next;
336
337 /* Terminate processing if the XmHashMapProc return True. */
338 if (proc (entry -> hash_key, entry -> value, client_data))
339 return;
340
341 entry = next;
342 }
343 }
344 }
345
346 /* Bucket management */
347 #define NUMBUCKETS 256
348
349 static XmHashBucket
NewBucket(void)350 NewBucket(void)
351 {
352 XmHashBucket rbucket;
353 XmHashBucket buckets;
354 int i;
355
356 if (FreeBucketList == NULL) {
357 /* Allocate alot of buckets at once to cut down on fragmentation */
358 buckets = (XmHashBucket) XtMalloc(NUMBUCKETS * sizeof(XmHashBucketRec));
359
360 /* Chain them together */
361 for(i = 0; i < NUMBUCKETS; i++) buckets[i].next = &buckets[i+1];
362
363 /* The last points to nothing */
364 buckets[NUMBUCKETS - 1].next = (XmHashBucket) NULL;
365
366 FreeBucketList = buckets;
367 }
368
369 rbucket = FreeBucketList;
370 FreeBucketList = FreeBucketList -> next;
371
372 return(rbucket);
373 }
374
375 static void
FreeBucket(XmHashBucket b)376 FreeBucket(XmHashBucket b)
377 {
378 b -> next = FreeBucketList;
379 FreeBucketList = b;
380 }
381
382
383 #ifdef DEBUG
384
385 void
_XmPrintHashTable(XmHashTable table)386 _XmPrintHashTable(XmHashTable table)
387 {
388 int i, max, total_in_use;
389 int count;
390 XmHashBucket entry;
391
392 max = 0;
393 total_in_use = 0;
394
395 printf("size %d hash function %lx compare function %lx\n",
396 table->size, (unsigned long) table->hasher,
397 (unsigned long) table->compare);
398
399 for (i = 0; i < table -> size; i++) {
400 entry = table -> buckets[i];
401 count = 0;
402 while(entry) {
403 count++;
404 entry = entry -> next;
405 }
406 if (count > 0) total_in_use++;
407 if (count > max) max = count;
408 }
409
410 printf("total entries %d\n", table -> count);
411 printf("total bucket chains in use %d or %d percent\n", total_in_use,
412 (total_in_use * 100)/table -> size);
413 printf("bucket chain length %d average / %d max\n",
414 table -> count / total_in_use, max);
415 }
416
417 #endif
418