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