1 /*  Part of XPCE --- The SWI-Prolog GUI toolkit
2 
3     Author:        Jan Wielemaker and Anjo Anjewierden
4     E-mail:        jan@swi.psy.uva.nl
5     WWW:           http://www.swi.psy.uva.nl/projects/xpce/
6     Copyright (c)  1985-2002, University of Amsterdam
7     All rights reserved.
8 
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions
11     are met:
12 
13     1. Redistributions of source code must retain the above copyright
14        notice, this list of conditions and the following disclaimer.
15 
16     2. Redistributions in binary form must reproduce the above copyright
17        notice, this list of conditions and the following disclaimer in
18        the documentation and/or other materials provided with the
19        distribution.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32     POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 #include <h/kernel.h>
36 #include <h/unix.h>
37 
38 static status	unlinkHashTable(HashTable ht);
39 
40 static __inline void
assign_symbol_value(HashTable ht,Symbol symbol,Any value)41 assign_symbol_value(HashTable ht, Symbol symbol, Any value)
42 { if ( ht->refer == NAME_both || ht->refer == NAME_value )
43     assignField((Instance)(ht), &symbol->value, value);
44   else
45     symbol->value = value;
46 }
47 
48 static __inline void
assign_symbol_name(HashTable ht,Symbol symbol,Any name)49 assign_symbol_name(HashTable ht, Symbol symbol, Any name)
50 { if ( ht->refer == NAME_both || ht->refer == NAME_name )
51     assignField((Instance)(ht), &symbol->name, name);
52   else
53     symbol->name = name;
54 }
55 
56 
57 #if USE_PRIMES
58 static int
nextBucketSize(n)59 nextBucketSize(n)
60 int n;
61 { if ( !(n % 2) )
62     n++;
63 
64   for(;; n += 2)
65   { int m;
66     int to = isqrt(n);
67 
68     for(m=3 ; m <= to; m += 2)
69       if ( !(n % m) )
70 	break;
71 
72     if ( m > to )
73       return n;
74   }
75 }
76 
77 #else
78 
79 static int
nextBucketSize(int n)80 nextBucketSize(int n)
81 { int m;
82 
83   for(m=2; m<n; m<<=1)
84     ;
85   return m;
86 }
87 #endif
88 
89 
90 HashTable
createHashTable(Int buckets,Name refer)91 createHashTable(Int buckets, Name refer)
92 { HashTable ht = alloc(sizeof(struct hash_table));
93 
94   initHeaderObj(ht, ClassHashTable);
95   initialiseHashTable(ht, buckets);
96   ht->refer = refer;			/* is a protected object */
97   createdObject(ht, NAME_new);
98 
99   return ht;
100 }
101 
102 
103 status
freeHashTable(HashTable ht)104 freeHashTable(HashTable ht)
105 { unlinkHashTable(ht);
106   unalloc(sizeof(struct hash_table), ht);
107 
108   succeed;
109 }
110 
111 
112 status
initialiseHashTable(HashTable ht,Int buckets)113 initialiseHashTable(HashTable ht, Int buckets)
114 { int n = isDefault(buckets) ? 5 : valInt(buckets);
115   Symbol s;
116 
117   ht->refer = NAME_both;
118   n = nextBucketSize(n);
119   ht->size    = ZERO;
120   ht->buckets = n;
121   ht->symbols = alloc(n * sizeof(struct symbol));
122 
123   for(s = ht->symbols; s < &ht->symbols[ht->buckets]; s++)
124     s->name = s->value = NULL;
125 
126   succeed;
127 }
128 
129 
130 static status
unlinkHashTable(HashTable ht)131 unlinkHashTable(HashTable ht)
132 { if ( ht->symbols != NULL )
133   { if ( ht->refer != NAME_none )
134       clearHashTable(ht);
135 
136     unalloc(ht->buckets * sizeof(struct symbol), ht->symbols);
137     ht->symbols = NULL;
138   }
139 
140   succeed;
141 }
142 
143 
144 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
145 load/save format:
146 
147 <hashtable>		::= <pce-slots> {<symbol>} 'X'
148 
149 <symbol>		::= 's' <key> <value>
150 <key>			::= <any>
151 <value>			::= <any>
152 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
153 
154 
155 static status
storeHashTable(HashTable ht,FileObj file)156 storeHashTable(HashTable ht, FileObj file)
157 { TRY(storeSlotsObject(ht, file));
158 
159   for_hash_table(ht, s,
160 	       { storeCharFile(file, 's');
161 		 storeObject(s->name, file);
162 		 storeObject(s->value, file);
163 	       });
164 
165   storeCharFile(file, 'X');
166 
167   succeed;
168 }
169 
170 
171 static status
loadHashTable(HashTable ht,IOSTREAM * fd,ClassDef def)172 loadHashTable(HashTable ht, IOSTREAM *fd, ClassDef def)
173 { int buckets;
174   Symbol s;
175 
176   loadSlotsObject(ht, fd, def);
177 
178   buckets = (isNil(ht->size) ? 5 : ((valInt(ht->size) * 4) / 3 + 4));
179   buckets = nextBucketSize(buckets);
180   if ( isNil(ht->refer) || isOn(ht->refer) )
181     assign(ht, refer, NAME_both);
182 
183   assign(ht, size, ZERO);
184   ht->buckets = buckets;
185   ht->symbols = alloc(buckets * sizeof(struct symbol));
186 
187   for(s = ht->symbols; s < &ht->symbols[ht->buckets]; s++)
188     s->name = s->value = NULL;
189 
190   for(;;)
191   { int c;
192 
193     switch(c=Sgetc(fd))
194     { case 's':
195 	{ Any key, value;
196 
197 	  TRY( key   = loadObject(fd) );
198 	  TRY( value = loadObject(fd) );
199 	  if ( restoreVersion < 8 && instanceOfObject(ht, ClassChainTable) )
200 	    appendChainTable((ChainTable) ht, key, value);
201 	  else
202 	    appendHashTable(ht, key, value);
203 
204 	  break;
205 	}
206       case 'X':
207 	succeed;
208       default:
209 	return errorPce(LoadFile, NAME_illegalCharacter,
210 			toInt(c), toInt(Stell(fd)));
211     }
212   }
213 }
214 
215 
216 static status
convertOldSlotHashTable(HashTable ht,Name slot,Any value)217 convertOldSlotHashTable(HashTable ht, Name slot, Any value)
218 { if ( slot == NAME_unique && value == OFF )
219   { errorPce(ht, NAME_loadMessage,
220 	     CtoString("Migrating to a chain_table"));
221     ht->class = ClassChainTable;	/* Brrrrrr! */
222 
223     succeed;
224   } else
225   { Any argv[2];
226 
227     argv[0] = slot;
228     argv[1] = value;
229     return vm_send(ht, NAME_convertOldSlot, ht->class->super_class, 2, argv);
230   }
231 }
232 
233 
234 #ifndef O_RUNTIME
235 static int
checkMemberHashTable(const HashTable ht,const Any name,const Any value)236 checkMemberHashTable(const HashTable ht, const Any name, const Any value)
237 { int hashkey = hashKey(name, ht->buckets);
238   Symbol s = &ht->symbols[hashkey];
239   int shifts=0;
240 
241   for(;;)
242   { if ( s->name == name )
243     { assert(s->value == value);
244       return shifts;
245     }
246     if ( !s->name )
247       fail;
248     shifts++;
249     if ( ++hashkey == ht->buckets )
250     { hashkey = 0;
251       s = ht->symbols;
252     } else
253       s++;
254   }
255 
256   fail;
257 }
258 
259 static status
infoHashTable(HashTable ht)260 infoHashTable(HashTable ht)
261 { Symbol s = ht->symbols;
262   int n = ht->buckets;
263   int shifts = 0;
264   int members = 0;
265 
266   for( ; --n >= 0; s++ )
267   { if ( s->name )
268     { shifts += checkMemberHashTable(ht, s->name, s->value);
269       members++;
270     }
271   }
272 
273   Cprintf("%s: %d buckets holding %d members, %d shifts\n",
274 	  pp(ht), ht->buckets, members, shifts);
275 
276   succeed;
277 }
278 #endif /*O_RUNTIME*/
279 
280 
281 static status
bucketsHashTable(HashTable ht,Int buckets)282 bucketsHashTable(HashTable ht, Int buckets)
283 { int    bs    = valInt(buckets);
284   Symbol old   = ht->symbols;
285   int    size  = ht->buckets;
286   Name	 refer = ht->refer;
287   int    n;
288   Symbol s;
289 
290   bs = max(bs, (4*valInt(ht->size))/3);
291   bs = nextBucketSize(bs);
292   ht->size    = ZERO;
293   ht->buckets = bs;
294   ht->symbols = alloc(bs * sizeof(struct symbol));
295   ht->refer   = NAME_none;
296 
297   for( n=ht->buckets, s=ht->symbols; n-- > 0; s++ )
298     s->name = s->value = NULL;
299 
300   for( n = size, s = old; n-- > 0; s++)
301     if ( s->name )
302       appendHashTable(ht, s->name, s->value);
303 
304   ht->refer = refer;
305   unalloc(size * sizeof(struct symbol), old);
306 
307   COUNT(hash_resizes++);
308 
309   succeed;
310 }
311 
312 
313 status
appendHashTable(HashTable ht,Any name,Any value)314 appendHashTable(HashTable ht, Any name, Any value)
315 { int hashkey;
316   Symbol s;
317 
318   if ( 4 * valInt(ht->size) + 5 > 3 * ht->buckets )
319     bucketsHashTable(ht, toInt(2*ht->buckets));
320 
321   hashkey = hashKey(name, ht->buckets);
322   s       = &ht->symbols[hashkey];
323 
324 #if O_COUNT
325   DEBUG(NAME_key, Cprintf("Key for %s in %d buckets: %d\n",
326 			  pp(name), ht->buckets, hashkey));
327 #endif
328 
329   for(;;)
330   { if ( s->name == name )
331     { assign_symbol_value(ht, s, value);
332       succeed;
333     }
334     if ( s->name == NULL )
335     { s->name = s->value = NIL;
336       assign_symbol_name(ht, s, name);
337       assign_symbol_value(ht, s, value);
338       assign(ht, size, add(ht->size, ONE));
339       succeed;
340     }
341 
342     COUNT(hash_shifts++);
343     if ( ++hashkey == ht->buckets )
344     { hashkey = 0;
345       s = ht->symbols;
346     } else
347       s++;
348   }
349   /*NOTREACHED*/
350 }
351 
352 
353 #define EMPTY(ht, i) \
354   { assign_symbol_name(ht, &ht->symbols[i], NIL); \
355     assign_symbol_value(ht, &ht->symbols[i], NIL); \
356     ht->symbols[i].name = ht->symbols[i].value = NULL; \
357   }
358 
359 status
deleteHashTable(HashTable ht,Any name)360 deleteHashTable(HashTable ht, Any name)
361 { int i = hashKey(name, ht->buckets);
362   int j, r;
363 
364   while( ht->symbols[i].name && ht->symbols[i].name != name )
365     if ( ++i == ht->buckets )
366       i = 0;
367   if ( !ht->symbols[i].name )
368     fail;				/* not in table */
369 
370   assign(ht, size, sub(ht->size, ONE));
371 
372   EMPTY(ht, i);				/* R1 */
373   j = i;
374 
375   for(;;)
376   { if ( ++i == ht->buckets )		/* R2 */
377       i = 0;
378 
379     if ( !ht->symbols[i].name )
380       succeed;
381 
382     r = hashKey(ht->symbols[i].name, ht->buckets);
383     if ( (i >= r && r > j) || (r > j && j > i) || (j > i && i >= r) )
384       continue;
385 
386     ht->symbols[j] = ht->symbols[i];
387     ht->symbols[i].name = ht->symbols[i].value = NULL;
388     j = i;
389   }
390 }
391 
392 
393 status
clearHashTable(HashTable ht)394 clearHashTable(HashTable ht)
395 { int n;
396   Symbol s;
397 
398   for(n=0, s = ht->symbols; n<ht->buckets; n++, s++)
399   { assign_symbol_name(ht, s, NIL);
400     assign_symbol_value(ht, s, NIL);
401     s->name = s->value = NULL;
402   }
403 
404   ht->size = ZERO;
405 
406   succeed;
407 }
408 
409 
410 		/********************************
411 		*      ACCESS TO C-SLOTS	*
412 		********************************/
413 
414 static status
emptyHashTable(HashTable ht)415 emptyHashTable(HashTable ht)
416 { return ht->size == ZERO ? SUCCEED : FAIL;
417 }
418 
419 
420 static Int
getBucketsHashTable(HashTable ht)421 getBucketsHashTable(HashTable ht)
422 { answer(toInt(ht->buckets));
423 }
424 
425 
426 		/********************************
427 		*             FOR		*
428 		********************************/
429 
430 static status
forAllHashTable(HashTable ht,Code code,BoolObj safe)431 forAllHashTable(HashTable ht, Code code, BoolObj safe)
432 { int n, size = ht->buckets;
433   Symbol s;
434 
435   if ( safe == OFF )
436   { for(n=size, s=ht->symbols; n-->0; s++)
437       if ( s->name )
438 	TRY(forwardCode(code, s->name, s->value, EAV));
439   } else
440   { Symbol symbols = (Symbol)alloca(sizeof(struct symbol) * valInt(ht->size));
441     Symbol q = symbols;
442 
443     for(n=size, s=ht->symbols; n-- > 0; s++)
444       if ( s->name )
445 	*q++ = *s;
446 
447     for(n=valInt(ht->size), q=symbols; n-- > 0; q++)
448       if ( (nonObject(q->name) || !isFreedObj(q->name)) &&
449 	   (nonObject(q->value) || !isFreedObj(q->value)) )
450 	TRY(forwardCode(code, q->name, q->value, EAV));
451   }
452 
453   succeed;
454 }
455 
456 
457 static status
forSomeHashTable(HashTable ht,Code code,BoolObj safe)458 forSomeHashTable(HashTable ht, Code code, BoolObj safe)
459 { int n, size = ht->buckets;
460   Symbol s;
461 
462   if ( safe == OFF )
463   { for(n=size, s=ht->symbols; n-->0; s++)
464       if ( s->name )
465 	forwardCode(code, s->name, s->value, EAV);
466   } else
467   { Symbol symbols = (Symbol)alloca(sizeof(struct symbol) * valInt(ht->size));
468     Symbol q = symbols;
469 
470     for(n=size, s=ht->symbols; n-- > 0; s++)
471       if ( s->name )
472 	*q++ = *s;
473 
474     for(n=valInt(ht->size), q=symbols; n-- > 0; q++)
475       if ( (nonObject(q->name) || !isFreedObj(q->name)) &&
476 	   (nonObject(q->value) || !isFreedObj(q->value)) )
477 	forwardCode(code, q->name, q->value, EAV);
478   }
479 
480   succeed;
481 }
482 
483 
484 static Any
getFindKeyHashTable(HashTable ht,Code code)485 getFindKeyHashTable(HashTable ht, Code code)
486 { int n, size = ht->buckets;
487   Symbol s;
488 
489   for(n=size, s=ht->symbols; n-->0; s++)
490     if ( s->name )
491       if ( forwardCode(code, s->name, s->value, EAV) )
492 	answer(s->name);
493 
494   fail;
495 }
496 
497 
498 static Any
getFindValueHashTable(HashTable ht,Code code)499 getFindValueHashTable(HashTable ht, Code code)
500 { int n, size = ht->buckets;
501   Symbol s;
502 
503   for(n=size, s=ht->symbols; n-->0; s++)
504     if ( s->name )
505       if ( forwardCode(code, s->name, s->value, EAV) )
506 	answer(s->value);
507 
508   fail;
509 }
510 
511 
512 #if O_COUNT
513 
514 static status
printStatisticsHashTable(HashTable ht)515 printStatisticsHashTable(HashTable ht)
516 { Cprintf("Total hash_table statistics:\n");
517   Cprintf("\t# resizes:    %d\n", hash_resizes);
518   Cprintf("\t# lookups:    %d\n", hash_lookups);
519   Cprintf("\t# shifts:     %d\n", hash_shifts);
520   Cprintf("\t# mismatches: %d\n", hash_cmp_failed);
521 
522   succeed;
523 }
524 
525 
526 static Int
getShiftsHashTable(HashTable ht)527 getShiftsHashTable(HashTable ht)
528 { long old_cmps = hash_cmp_failed;
529   int n, size = ht->buckets;
530   Symbol s;
531 
532   for(n=size, s=ht->symbols; n-->0; s++)
533     if ( s->name )
534       getMemberHashTable(ht, s->name);
535 
536   answer(toInt(hash_cmp_failed - old_cmps));
537 }
538 
539 #endif /* O_COUNT */
540 
541 		 /*******************************
542 		 *	 CLASS DECLARATION	*
543 		 *******************************/
544 
545 /* Type declaractions */
546 
547 static char *T_actionAcode_safeADboolD[] =
548         { "action=code", "safe=[bool]" };
549 static char *T_append[] =
550         { "key=any", "value=any" };
551 static char *T_convertOldSlot[] =
552         { "name", "any" };
553 
554 /* Instance Variables */
555 
556 static vardecl var_hashTable[] =
557 { IV(NAME_refer, "{none,name,value,both}", IV_NONE,
558      NAME_oms, "Determines which fields create references (internal)"),
559   IV(NAME_size, "int", IV_GET,
560      NAME_cardinality, "Number of symbols in table"),
561   IV(NAME_buckets, "alien:int", IV_NONE,
562      NAME_cardinality, "Number of buckets in the table"),
563   IV(NAME_symbols, "alien:Symbol", IV_NONE,
564      NAME_storage, "Array of symbols")
565 };
566 
567 /* Send Methods */
568 
569 static senddecl send_hashTable[] =
570 { SM(NAME_initialise, 1, "buckets=[int]", initialiseHashTable,
571      DEFAULT, "Create from buckets"),
572   SM(NAME_unlink, 0, NULL, unlinkHashTable,
573      DEFAULT, "Clear table"),
574   SM(NAME_append, 2, T_append, appendHashTable,
575      NAME_add, "Append association to table"),
576   SM(NAME_convertOldSlot, 2, T_convertOldSlot, convertOldSlotHashTable,
577      NAME_compatibility, "File <-object conversion"),
578   SM(NAME_clear, 0, NULL, clearHashTable,
579      NAME_delete, "Delete all entries"),
580   SM(NAME_delete, 1, "key=any", deleteHashTable,
581      NAME_delete, "Delete all matching symbol"),
582   SM(NAME_forAll, 2, T_actionAcode_safeADboolD, forAllHashTable,
583      NAME_iterate, "Run code on all values; demand acceptance ([safe])"),
584   SM(NAME_forSome, 2, T_actionAcode_safeADboolD, forSomeHashTable,
585      NAME_iterate, "Run code on all values ([safe])"),
586 #ifndef O_RUNTIME
587   SM(NAME_info, 0, NULL, infoHashTable,
588      NAME_statistics, "Check consistency and print statistics"),
589 #endif
590 #if O_COUNT
591   SM(NAME_printStatistics, 0, NULL, printStatisticsHashTable,
592      NAME_statistics, "Print statistics on all tables"),
593 #endif
594   SM(NAME_buckets, 1, "int", bucketsHashTable,
595      NAME_storage, "Number of buckets in the table"),
596   SM(NAME_empty, 0, NULL, emptyHashTable,
597      NAME_test, "Test if hash_table has no elements")
598 };
599 
600 /* Get Methods */
601 
602 static getdecl get_hashTable[] =
603 { GM(NAME_member, 1, "value=any", "key=any", getMemberHashTable,
604      NAME_lookup, "Get associated value"),
605   GM(NAME_findKey, 1, "key=any", "test=code", getFindKeyHashTable,
606      NAME_search, "Find key accepted by code"),
607   GM(NAME_findValue, 1, "value=any", "test=code", getFindValueHashTable,
608      NAME_search, "Find value accepted by code"),
609 #if O_COUNT
610   GM(NAME_shifts, 0, "int", NULL, getShiftsHashTable,
611      NAME_statistics, "Number of shifted entries"),
612 #endif
613   GM(NAME_buckets, 0, "buckets=int", NULL, getBucketsHashTable,
614      NAME_storage, "Number of buckets in the table")
615 };
616 
617 /* Resources */
618 
619 #define rc_hashTable NULL
620 /*
621 static classvardecl rc_hashTable[] =
622 {
623 };
624 */
625 
626 /* Class Declaration */
627 
628 static Name hashTable_termnames[] = { NAME_buckets };
629 
630 ClassDecl(hashTable_decls,
631           var_hashTable, send_hashTable, get_hashTable, rc_hashTable,
632           1, hashTable_termnames,
633           "$Rev$");
634 
635 
636 status
makeClassHashTable(Class class)637 makeClassHashTable(Class class)
638 { declareClass(class, &hashTable_decls);
639 
640   setLoadStoreFunctionClass(class, loadHashTable, storeHashTable);
641 
642   succeed;
643 }
644