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